KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > burlap > client > MicroBurlapOutput


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution, if
19  * any, must include the following acknowlegement:
20  * "This product includes software developed by the
21  * Caucho Technology (http://www.caucho.com/)."
22  * Alternately, this acknowlegement may appear in the software itself,
23  * if and wherever such third-party acknowlegements normally appear.
24  *
25  * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * info@caucho.com.
29  *
30  * 5. Products derived from this software may not be called "Resin"
31  * nor may "Resin" appear in their names without prior written
32  * permission of Caucho Technology.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37  * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
38  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
39  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
40  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
43  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
44  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45  *
46  * @author Scott Ferguson
47  */

48
49 package com.caucho.burlap.client;
50
51 import java.io.IOException JavaDoc;
52 import java.io.OutputStream JavaDoc;
53 import java.util.Calendar JavaDoc;
54 import java.util.Date JavaDoc;
55 import java.util.Enumeration JavaDoc;
56 import java.util.Hashtable JavaDoc;
57 import java.util.TimeZone JavaDoc;
58 import java.util.Vector JavaDoc;
59
60 /**
61  * Output stream for Burlap requests, compatible with microedition
62  * Java. It only uses classes and types available to J2ME. In
63  * particular, it does not have any support for the <double> type.
64  *
65  * <p>MicroBurlapOutput does not depend on any classes other than
66  * in J2ME, so it can be extracted independently into a smaller package.
67  *
68  * <p>MicroBurlapOutput is unbuffered, so any client needs to provide
69  * its own buffering.
70  *
71  * <pre>
72  * OutputStream os = ...; // from http connection
73  * MicroBurlapOutput out = new MicroBurlapOutput(os);
74  * String value;
75  *
76  * out.startCall("hello"); // start hello call
77  * out.writeString("arg1"); // write a string argument
78  * out.completeCall(); // complete the call
79  * </pre>
80  */

81 public class MicroBurlapOutput {
82   private OutputStream JavaDoc os;
83   private Date JavaDoc date;
84   private Calendar JavaDoc utcCalendar;
85   private Calendar JavaDoc localCalendar;
86
87   /**
88    * Creates a new Burlap output stream, initialized with an
89    * underlying output stream.
90    *
91    * @param os the underlying output stream.
92    */

93   public MicroBurlapOutput(OutputStream JavaDoc os)
94   {
95     init(os);
96   }
97
98   /**
99    * Creates an uninitialized Burlap output stream.
100    */

101   public MicroBurlapOutput()
102   {
103   }
104
105   public void init(OutputStream JavaDoc os)
106   {
107     this.os = os;
108   }
109
110   /**
111    * Writes a complete method call.
112    */

113   public void call(String JavaDoc method, Object JavaDoc []args)
114     throws IOException JavaDoc
115   {
116     startCall(method);
117     
118     if (args != null) {
119       for (int i = 0; i < args.length; i++)
120         writeObject(args[i]);
121     }
122     
123     completeCall();
124   }
125
126   /**
127    * Writes the method call:
128    *
129    * <code><pre>
130    * &lt;burlap:request>
131    * &lt;method>add&lt;/method>
132    * </pre></code>
133    *
134    * @param method the method name to call.
135    */

136   public void startCall(String JavaDoc method)
137     throws IOException JavaDoc
138   {
139     print("<burlap:call><method>");
140     print(method);
141     print("</method>");
142   }
143
144   /**
145    * Writes the method call:
146    *
147    * <code><pre>
148    * &lt;/burlap:request>
149    * </pre></code>
150    */

151   public void completeCall()
152     throws IOException JavaDoc
153   {
154     print("</burlap:call>");
155   }
156
157   /**
158    * Writes a boolean value to the stream. The boolean will be written
159    * with the following syntax:
160    *
161    * <code><pre>
162    * &lt;boolean>1&lt;/boolean>
163    * </pre></code>
164    *
165    * @param value the boolean value to write.
166    */

167   public void writeBoolean(boolean value)
168     throws IOException JavaDoc
169   {
170     print("<boolean>");
171     printInt(value ? 1 : 0);
172     print("</boolean>");
173   }
174
175   /**
176    * Writes an integer value to the stream. The integer will be written
177    * with the following syntax:
178    *
179    * <code><pre>
180    * &lt;int>123&lt;/int>
181    * </pre></code>
182    *
183    * @param value the integer value to write.
184    */

185   public void writeInt(int value)
186     throws IOException JavaDoc
187   {
188     print("<int>");
189     printInt(value);
190     print("</int>");
191   }
192
193   /**
194    * Writes a long value to the stream. The long will be written
195    * with the following syntax:
196    *
197    * <code><pre>
198    * &lt;long>123&lt;/long>
199    * </pre></code>
200    *
201    * @param value the long value to write.
202    */

203   public void writeLong(long value)
204     throws IOException JavaDoc
205   {
206     print("<long>");
207     printLong(value);
208     print("</long>");
209   }
210
211   /**
212    * Writes a null value to the stream.
213    * The null will be written with the following syntax
214    *
215    * <code><pre>
216    * &lt;null>&lt;/null>
217    * </pre></code>
218    *
219    * @param value the string value to write.
220    */

221   public void writeNull()
222     throws IOException JavaDoc
223   {
224     print("<null></null>");
225   }
226
227   /**
228    * Writes a string value to the stream using UTF-8 encoding.
229    * The string will be written with the following syntax:
230    *
231    * <code><pre>
232    * &lt;string>12.3e10&lt;/string>
233    * </pre></code>
234    *
235    * If the value is null, it will be written as
236    *
237    * <code><pre>
238    * &lt;null>&lt;/null>
239    * </pre></code>
240    *
241    * @param value the string value to write.
242    */

243   public void writeString(String JavaDoc value)
244     throws IOException JavaDoc
245   {
246     if (value == null) {
247       print("<null></null>");
248     }
249     else {
250       print("<string>");
251       printString(value);
252       print("</string>");
253     }
254   }
255
256   /**
257    * Writes a byte array to the stream using base64 encoding.
258    * The array will be written with the following syntax:
259    *
260    * <code><pre>
261    * &lt;base64>dJmO==&lt;/base64>
262    * </pre></code>
263    *
264    * If the value is null, it will be written as
265    *
266    * <code><pre>
267    * &lt;null>&lt;/null>
268    * </pre></code>
269    *
270    * @param value the string value to write.
271    */

272   public void writeBytes(byte []buffer, int offset, int length)
273     throws IOException JavaDoc
274   {
275     if (buffer == null) {
276       print("<null></null>");
277     }
278     else {
279       print("<base64>");
280       printBytes(buffer, offset, length);
281       print("</base64>");
282     }
283   }
284
285   /**
286    * Writes a date to the stream using ISO8609.
287    *
288    * <code><pre>
289    * &lt;date>19980508T095131Z&lt;/date>
290    * </pre></code>
291    *
292    * @param value the date in milliseconds from the epoch in UTC
293    */

294   public void writeUTCDate(long time)
295     throws IOException JavaDoc
296   {
297     print("<date>");
298     if (utcCalendar == null) {
299       utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
300       date = new Date JavaDoc();
301     }
302
303     date.setTime(time);
304     utcCalendar.setTime(date);
305
306     printDate(utcCalendar);
307     print("</date>");
308   }
309
310   /**
311    * Writes a date to the stream using ISO8609.
312    *
313    * <code><pre>
314    * &lt;date>19980508T095131Z&lt;/date>
315    * </pre></code>
316    *
317    * @param value the date in milliseconds from the epoch in local timezone
318    */

319   public void writeLocalDate(long time)
320     throws IOException JavaDoc
321   {
322     print("<date>");
323     if (localCalendar == null) {
324       localCalendar = Calendar.getInstance();
325       date = new Date JavaDoc();
326     }
327
328     date.setTime(time);
329     localCalendar.setTime(date);
330
331     printDate(localCalendar);
332     print("</date>");
333   }
334
335   /**
336    * Writes a reference.
337    *
338    * <code><pre>
339    * &lt;ref>123&lt;/ref>
340    * </pre></code>
341    *
342    * @param value the integer value to write.
343    */

344   public void writeRef(int value)
345     throws IOException JavaDoc
346   {
347     print("<ref>");
348     printInt(value);
349     print("</ref>");
350   }
351
352   /**
353    * Writes a generic object. writeObject understands the following types:
354    *
355    * <ul>
356    * <li>null
357    * <li>java.lang.String
358    * <li>java.lang.Boolean
359    * <li>java.lang.Integer
360    * <li>java.lang.Long
361    * <li>java.util.Date
362    * <li>byte[]
363    * <li>java.util.Vector
364    * <li>java.util.Hashtable
365    * </ul>
366    *
367    * Unknown objects will call <code>writeCustomObject</code>.
368    */

369   public void writeObject(Object JavaDoc object)
370     throws IOException JavaDoc
371   {
372     if (object == null)
373       writeNull();
374     else if (object instanceof String JavaDoc)
375       writeString((String JavaDoc) object);
376     else if (object instanceof Boolean JavaDoc)
377       writeBoolean(((Boolean JavaDoc) object).booleanValue());
378     else if (object instanceof Integer JavaDoc)
379       writeInt(((Integer JavaDoc) object).intValue());
380     else if (object instanceof Long JavaDoc)
381       writeLong(((Long JavaDoc) object).longValue());
382     else if (object instanceof Date JavaDoc)
383       writeUTCDate(((Date JavaDoc) object).getTime());
384     else if (object instanceof byte[]) {
385       byte []data = (byte []) object;
386       writeBytes(data, 0, data.length);
387     }
388     else if (object instanceof Vector JavaDoc) {
389       Vector JavaDoc vector = (Vector JavaDoc) object;
390
391       int size = vector.size();
392       writeListBegin(size, null);
393       for (int i = 0; i < size; i++)
394         writeObject(vector.elementAt(i));
395       
396       writeListEnd();
397     }
398     else if (object instanceof Hashtable JavaDoc) {
399       Hashtable JavaDoc hashtable = (Hashtable JavaDoc) object;
400
401       writeMapBegin(null);
402       Enumeration JavaDoc e = hashtable.keys();
403       while (e.hasMoreElements()) {
404         Object JavaDoc key = e.nextElement();
405         Object JavaDoc value = hashtable.get(key);
406
407         writeObject(key);
408         writeObject(value);
409       }
410       writeMapEnd();
411     }
412     else
413       writeCustomObject(object);
414   }
415   
416   /**
417    * Applications which override this can do custom serialization.
418    *
419    * @param object the object to write.
420    */

421   public void writeCustomObject(Object JavaDoc object)
422     throws IOException JavaDoc
423   {
424     throw new IOException JavaDoc("unexpected object: " + object);
425   }
426
427   /**
428    * Writes the list header to the stream. List writers will call
429    * <code>writeListBegin</code> followed by the list contents and then
430    * call <code>writeListEnd</code>.
431    *
432    * <code><pre>
433    * &lt;list>
434    * &lt;type>java.util.ArrayList&lt;/type>
435    * &lt;length>3&lt;/length>
436    * &lt;int>1&lt;/int>
437    * &lt;int>2&lt;/int>
438    * &lt;int>3&lt;/int>
439    * &lt;/list>
440    * </pre></code>
441    */

442   public void writeListBegin(int length, String JavaDoc type)
443     throws IOException JavaDoc
444   {
445     print("<list><type>");
446     if (type != null)
447       print(type);
448     print("</type><length>");
449     printInt(length);
450     print("</length>");
451   }
452
453   /**
454    * Writes the tail of the list to the stream.
455    */

456   public void writeListEnd()
457     throws IOException JavaDoc
458   {
459     print("</list>");
460   }
461
462   /**
463    * Writes the map header to the stream. Map writers will call
464    * <code>writeMapBegin</code> followed by the map contents and then
465    * call <code>writeMapEnd</code>.
466    *
467    * <code><pre>
468    * &lt;map>
469    * &lt;type>java.util.Hashtable&lt;/type>
470    * &lt;string>a&lt;/string;&lt;int>1&lt;/int>
471    * &lt;string>b&lt;/string;&lt;int>2&lt;/int>
472    * &lt;string>c&lt;/string;&lt;int>3&lt;/int>
473    * &lt;/map>
474    * </pre></code>
475    */

476   public void writeMapBegin(String JavaDoc type)
477     throws IOException JavaDoc
478   {
479     print("<map><type>");
480     if (type != null)
481       print(type);
482     print("</type>");
483   }
484
485   /**
486    * Writes the tail of the map to the stream.
487    */

488   public void writeMapEnd()
489     throws IOException JavaDoc
490   {
491     print("</map>");
492   }
493
494   /**
495    * Writes a remote object reference to the stream. The type is the
496    * type of the remote interface.
497    *
498    * <code><pre>
499    * &lt;remote>
500    * &lt;type>test.account.Account&lt;/type>
501    * &lt;string>http://caucho.com/foo;ejbid=bar&lt;/string>
502    * &lt;/remote>
503    * </pre></code>
504    */

505   public void writeRemote(String JavaDoc type, String JavaDoc url)
506     throws IOException JavaDoc
507   {
508     print("<remote><type>");
509     if (type != null)
510       print(type);
511     print("</type><string>");
512     print(url);
513     print("</string></remote>");
514   }
515   
516   /**
517    * Prints an integer to the stream.
518    *
519    * @param v the integer to print.
520    */

521   public void printInt(int v)
522     throws IOException JavaDoc
523   {
524     print(String.valueOf(v));
525   }
526
527   /**
528    * Prints a long to the stream.
529    *
530    * @param v the long to print.
531    */

532   public void printLong(long v)
533     throws IOException JavaDoc
534   {
535     print(String.valueOf(v));
536   }
537
538   /**
539    * Prints a string to the stream, properly encoded.
540    *
541    * @param v the string to print.
542    */

543   public void printString(String JavaDoc v)
544     throws IOException JavaDoc
545   {
546     int len = v.length();
547     
548     for (int i = 0; i < len; i++) {
549       char ch = v.charAt(i);
550       
551       switch (ch) {
552       case '<':
553         print("&lt;");
554         break;
555         
556       case '&':
557         print("&amp;");
558         break;
559         
560       case '\r':
561         print("&#13;");
562         break;
563         
564       default:
565         if (ch < 0x80)
566           os.write(ch);
567         else if (ch < 0x800) {
568           os.write(0xc0 + ((ch >> 6) & 0x1f));
569           os.write(0x80 + (ch & 0x3f));
570         }
571         else {
572           os.write(0xe0 + ((ch >> 12) & 0xf));
573           os.write(0x80 + ((ch >> 6) & 0x3f));
574           os.write(0x80 + (ch & 0x3f));
575         }
576         break;
577       }
578     }
579   }
580     
581   /**
582    * Prints a byte array to the stream, properly encoded in base64.
583    *
584    * @param data the bytes to print.
585    */

586   public void printBytes(byte []data, int offset, int length)
587     throws IOException JavaDoc
588   {
589     int i;
590     
591     for (; length >= 3; length -= 3) {
592       int chunk = (((data[offset] & 0xff) << 16) +
593                    ((data[offset + 1] & 0xff) << 8) +
594                    (data[offset + 2] & 0xff));
595
596       os.write(base64encode(chunk >> 18));
597       os.write(base64encode(chunk >> 12));
598       os.write(base64encode(chunk >> 6));
599       os.write(base64encode(chunk));
600
601       offset += 3;
602     }
603
604     if (length == 2) {
605       int chunk = ((data[offset] & 0xff) << 8) + (data[offset + 1] & 0xff);
606
607       os.write(base64encode(chunk >> 12));
608       os.write(base64encode(chunk >> 6));
609       os.write(base64encode(chunk));
610       os.write('=');
611     } else if (length == 1) {
612       int chunk = data[offset] & 0xff;
613       os.write(base64encode(chunk >> 6));
614       os.write(base64encode(chunk));
615       os.write('=');
616       os.write('=');
617     }
618   }
619
620   /**
621    * Converts the digit to its base64 encoding.
622    */

623   public static char base64encode(int d)
624   {
625     d &= 0x3f;
626     if (d < 26)
627       return (char) (d + 'A');
628     else if (d < 52)
629       return (char) (d + 'a' - 26);
630     else if (d < 62)
631       return (char) (d + '0' - 52);
632     else if (d == 62)
633       return '+';
634     else
635       return '/';
636   }
637   
638   /**
639    * Prints a date.
640    *
641    * @param date the date to print.
642    */

643   public void printDate(Calendar JavaDoc calendar)
644     throws IOException JavaDoc
645   {
646     int year = calendar.get(Calendar.YEAR);
647
648     os.write((char) ('0' + (year / 1000 % 10)));
649     os.write((char) ('0' + (year / 100 % 10)));
650     os.write((char) ('0' + (year / 10 % 10)));
651     os.write((char) ('0' + (year % 10)));
652
653     int month = calendar.get(Calendar.MONTH) + 1;
654     os.write((char) ('0' + (month / 10 % 10)));
655     os.write((char) ('0' + (month % 10)));
656
657     int day = calendar.get(Calendar.DAY_OF_MONTH);
658     os.write((char) ('0' + (day / 10 % 10)));
659     os.write((char) ('0' + (day % 10)));
660
661     os.write('T');
662
663     int hour = calendar.get(Calendar.HOUR_OF_DAY);
664     os.write((char) ('0' + (hour / 10 % 10)));
665     os.write((char) ('0' + (hour % 10)));
666
667     int minute = calendar.get(Calendar.MINUTE);
668     os.write((char) ('0' + (minute / 10 % 10)));
669     os.write((char) ('0' + (minute % 10)));
670
671     int second = calendar.get(Calendar.SECOND);
672     os.write((char) ('0' + (second / 10 % 10)));
673     os.write((char) ('0' + (second % 10)));
674
675     os.write('Z');
676   }
677
678   /**
679    * Prints a string as ascii to the stream. Used for tags, etc.
680    * that are known to the ascii.
681    *
682    * @param s the ascii string to print.
683    */

684   public void print(String JavaDoc s)
685     throws IOException JavaDoc
686   {
687     int len = s.length();
688     for (int i = 0; i < len; i++) {
689       int ch = s.charAt(i);
690
691       os.write(ch);
692     }
693   }
694 }
695
Popular Tags