KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > hessian > io > HessianOutput


1 /*
2  * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
3  *
4  * The Apache Software License, Version 1.1
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.hessian.io;
50
51 import java.io.IOException JavaDoc;
52 import java.io.OutputStream JavaDoc;
53 import java.util.IdentityHashMap JavaDoc;
54
55 /**
56  * Output stream for Hessian requests, compatible with microedition
57  * Java. It only uses classes and types available in JDK.
58  *
59  * <p>Since HessianOutput does not depend on any classes other than
60  * in the JDK, it can be extracted independently into a smaller package.
61  *
62  * <p>HessianOutput is unbuffered, so any client needs to provide
63  * its own buffering.
64  *
65  * <pre>
66  * OutputStream os = ...; // from http connection
67  * HessianOutput out = new HessianOutput(os);
68  * String value;
69  *
70  * out.startCall("hello"); // start hello call
71  * out.writeString("arg1"); // write a string argument
72  * out.completeCall(); // complete the call
73  * </pre>
74  */

75 public class HessianOutput extends AbstractHessianOutput {
76   // the output stream/
77
protected OutputStream JavaDoc os;
78   // map of references
79
private IdentityHashMap JavaDoc _refs;
80   private int _version = 1;
81   
82   /**
83    * Creates a new Hessian output stream, initialized with an
84    * underlying output stream.
85    *
86    * @param os the underlying output stream.
87    */

88   public HessianOutput(OutputStream JavaDoc os)
89   {
90     init(os);
91   }
92
93   /**
94    * Creates an uninitialized Hessian output stream.
95    */

96   public HessianOutput()
97   {
98   }
99
100   /**
101    * Initializes the output
102    */

103   public void init(OutputStream JavaDoc os)
104   {
105     this.os = os;
106
107     _refs = null;
108
109     if (_serializerFactory == null)
110       _serializerFactory = new SerializerFactory();
111   }
112
113   /**
114    * Sets the client's version.
115    */

116   public void setVersion(int version)
117   {
118     _version = version;
119   }
120
121   /**
122    * Writes a complete method call.
123    */

124   public void call(String JavaDoc method, Object JavaDoc []args)
125     throws IOException JavaDoc
126   {
127     startCall(method);
128     
129     if (args != null) {
130       for (int i = 0; i < args.length; i++)
131         writeObject(args[i]);
132     }
133     
134     completeCall();
135   }
136
137   /**
138    * Starts the method call. Clients would use <code>startCall</code>
139    * instead of <code>call</code> if they wanted finer control over
140    * writing the arguments, or needed to write headers.
141    *
142    * <code><pre>
143    * c major minor
144    * m b16 b8 method-name
145    * </pre></code>
146    *
147    * @param method the method name to call.
148    */

149   public void startCall(String JavaDoc method)
150     throws IOException JavaDoc
151   {
152     os.write('c');
153     os.write(_version);
154     os.write(0);
155
156     os.write('m');
157     int len = method.length();
158     os.write(len >> 8);
159     os.write(len);
160     printString(method, 0, len);
161   }
162
163   /**
164    * Writes the call tag. This would be followed by the
165    * headers and the method tag.
166    *
167    * <code><pre>
168    * c major minor
169    * </pre></code>
170    *
171    * @param method the method name to call.
172    */

173   public void startCall()
174     throws IOException JavaDoc
175   {
176     os.write('c');
177     os.write(0);
178     os.write(1);
179   }
180
181   /**
182    * Writes the method tag.
183    *
184    * <code><pre>
185    * m b16 b8 method-name
186    * </pre></code>
187    *
188    * @param method the method name to call.
189    */

190   public void writeMethod(String JavaDoc method)
191     throws IOException JavaDoc
192   {
193     os.write('m');
194     int len = method.length();
195     os.write(len >> 8);
196     os.write(len);
197     printString(method, 0, len);
198   }
199
200   /**
201    * Completes.
202    *
203    * <code><pre>
204    * z
205    * </pre></code>
206    */

207   public void completeCall()
208     throws IOException JavaDoc
209   {
210     os.write('z');
211   }
212
213   /**
214    * Starts the reply
215    *
216    * <p>A successful completion will have a single value:
217    *
218    * <pre>
219    * r
220    * </pre>
221    */

222   public void startReply()
223     throws IOException JavaDoc
224   {
225     os.write('r');
226     os.write(1);
227     os.write(0);
228   }
229
230   /**
231    * Completes reading the reply
232    *
233    * <p>A successful completion will have a single value:
234    *
235    * <pre>
236    * z
237    * </pre>
238    */

239   public void completeReply()
240     throws IOException JavaDoc
241   {
242     os.write('z');
243   }
244
245   /**
246    * Writes a header name. The header value must immediately follow.
247    *
248    * <code><pre>
249    * H b16 b8 foo <em>value</em>
250    * </pre></code>
251    */

252   public void writeHeader(String JavaDoc name)
253     throws IOException JavaDoc
254   {
255     int len = name.length();
256     
257     os.write('H');
258     os.write(len >> 8);
259     os.write(len);
260
261     printString(name);
262   }
263
264   /**
265    * Writes a fault. The fault will be written
266    * as a descriptive string followed by an object:
267    *
268    * <code><pre>
269    * f
270    * &lt;string>code
271    * &lt;string>the fault code
272    *
273    * &lt;string>message
274    * &lt;string>the fault mesage
275    *
276    * &lt;string>detail
277    * mt\x00\xnnjavax.ejb.FinderException
278    * ...
279    * z
280    * z
281    * </pre></code>
282    *
283    * @param code the fault code, a three digit
284    */

285   public void writeFault(String JavaDoc code, String JavaDoc message, Object JavaDoc detail)
286     throws IOException JavaDoc
287   {
288     os.write('f');
289     writeString("code");
290     writeString(code);
291
292     writeString("message");
293     writeString(message);
294
295     if (detail != null) {
296       writeString("detail");
297       writeObject(detail);
298     }
299     os.write('z');
300   }
301
302   /**
303    * Writes any object to the output stream.
304    */

305   public void writeObject(Object JavaDoc object)
306     throws IOException JavaDoc
307   {
308     if (object == null) {
309       writeNull();
310       return;
311     }
312
313     Serializer serializer;
314
315     serializer = _serializerFactory.getSerializer(object.getClass());
316
317     serializer.writeObject(object, this);
318   }
319
320   /**
321    * Writes the list header to the stream. List writers will call
322    * <code>writeListBegin</code> followed by the list contents and then
323    * call <code>writeListEnd</code>.
324    *
325    * <code><pre>
326    * V
327    * t b16 b8 type
328    * l b32 b24 b16 b8
329    * </pre></code>
330    */

331   public boolean writeListBegin(int length, String JavaDoc type)
332     throws IOException JavaDoc
333   {
334     os.write('V');
335
336     if (type != null) {
337       os.write('t');
338       printLenString(type);
339     }
340
341     if (length >= 0) {
342       os.write('l');
343       os.write(length >> 24);
344       os.write(length >> 16);
345       os.write(length >> 8);
346       os.write(length);
347     }
348
349     return true;
350   }
351
352   /**
353    * Writes the tail of the list to the stream.
354    */

355   public void writeListEnd()
356     throws IOException JavaDoc
357   {
358     os.write('z');
359   }
360
361   /**
362    * Writes the map header to the stream. Map writers will call
363    * <code>writeMapBegin</code> followed by the map contents and then
364    * call <code>writeMapEnd</code>.
365    *
366    * <code><pre>
367    * Mt b16 b8 (<key> <value>)z
368    * </pre></code>
369    */

370   public void writeMapBegin(String JavaDoc type)
371     throws IOException JavaDoc
372   {
373     os.write('M');
374     os.write('t');
375     printLenString(type);
376   }
377
378   /**
379    * Writes the tail of the map to the stream.
380    */

381   public void writeMapEnd()
382     throws IOException JavaDoc
383   {
384     os.write('z');
385   }
386
387   /**
388    * Writes a remote object reference to the stream. The type is the
389    * type of the remote interface.
390    *
391    * <code><pre>
392    * 'r' 't' b16 b8 type url
393    * </pre></code>
394    */

395   public void writeRemote(String JavaDoc type, String JavaDoc url)
396     throws IOException JavaDoc
397   {
398     os.write('r');
399     os.write('t');
400     printLenString(type);
401     os.write('S');
402     printLenString(url);
403   }
404
405   /**
406    * Writes a boolean value to the stream. The boolean will be written
407    * with the following syntax:
408    *
409    * <code><pre>
410    * T
411    * F
412    * </pre></code>
413    *
414    * @param value the boolean value to write.
415    */

416   public void writeBoolean(boolean value)
417     throws IOException JavaDoc
418   {
419     if (value)
420       os.write('T');
421     else
422       os.write('F');
423   }
424
425   /**
426    * Writes an integer value to the stream. The integer will be written
427    * with the following syntax:
428    *
429    * <code><pre>
430    * I b32 b24 b16 b8
431    * </pre></code>
432    *
433    * @param value the integer value to write.
434    */

435   public void writeInt(int value)
436     throws IOException JavaDoc
437   {
438     os.write('I');
439     os.write(value >> 24);
440     os.write(value >> 16);
441     os.write(value >> 8);
442     os.write(value);
443   }
444
445   /**
446    * Writes a long value to the stream. The long will be written
447    * with the following syntax:
448    *
449    * <code><pre>
450    * L b64 b56 b48 b40 b32 b24 b16 b8
451    * </pre></code>
452    *
453    * @param value the long value to write.
454    */

455   public void writeLong(long value)
456     throws IOException JavaDoc
457   {
458     os.write('L');
459     os.write((byte) (value >> 56));
460     os.write((byte) (value >> 48));
461     os.write((byte) (value >> 40));
462     os.write((byte) (value >> 32));
463     os.write((byte) (value >> 24));
464     os.write((byte) (value >> 16));
465     os.write((byte) (value >> 8));
466     os.write((byte) (value));
467   }
468
469   /**
470    * Writes a double value to the stream. The double will be written
471    * with the following syntax:
472    *
473    * <code><pre>
474    * D b64 b56 b48 b40 b32 b24 b16 b8
475    * </pre></code>
476    *
477    * @param value the double value to write.
478    */

479   public void writeDouble(double value)
480     throws IOException JavaDoc
481   {
482     long bits = Double.doubleToLongBits(value);
483     
484     os.write('D');
485     os.write((byte) (bits >> 56));
486     os.write((byte) (bits >> 48));
487     os.write((byte) (bits >> 40));
488     os.write((byte) (bits >> 32));
489     os.write((byte) (bits >> 24));
490     os.write((byte) (bits >> 16));
491     os.write((byte) (bits >> 8));
492     os.write((byte) (bits));
493   }
494
495   /**
496    * Writes a date to the stream.
497    *
498    * <code><pre>
499    * T b64 b56 b48 b40 b32 b24 b16 b8
500    * </pre></code>
501    *
502    * @param time the date in milliseconds from the epoch in UTC
503    */

504   public void writeUTCDate(long time)
505     throws IOException JavaDoc
506   {
507     os.write('d');
508     os.write((byte) (time >> 56));
509     os.write((byte) (time >> 48));
510     os.write((byte) (time >> 40));
511     os.write((byte) (time >> 32));
512     os.write((byte) (time >> 24));
513     os.write((byte) (time >> 16));
514     os.write((byte) (time >> 8));
515     os.write((byte) (time));
516   }
517
518   /**
519    * Writes a null value to the stream.
520    * The null will be written with the following syntax
521    *
522    * <code><pre>
523    * N
524    * </pre></code>
525    *
526    * @param value the string value to write.
527    */

528   public void writeNull()
529     throws IOException JavaDoc
530   {
531     os.write('N');
532   }
533
534   /**
535    * Writes a string value to the stream using UTF-8 encoding.
536    * The string will be written with the following syntax:
537    *
538    * <code><pre>
539    * S b16 b8 string-value
540    * </pre></code>
541    *
542    * If the value is null, it will be written as
543    *
544    * <code><pre>
545    * N
546    * </pre></code>
547    *
548    * @param value the string value to write.
549    */

550   public void writeString(String JavaDoc value)
551     throws IOException JavaDoc
552   {
553     if (value == null) {
554       os.write('N');
555     }
556     else {
557       int length = value.length();
558       int offset = 0;
559       
560       while (length > 0x8000) {
561         int sublen = 0x8000;
562
563     // chunk can't end in high surrogate
564
char tail = value.charAt(offset + sublen - 1);
565
566     if (0xd800 <= tail && tail <= 0xdbff)
567       sublen--;
568         
569         os.write('s');
570         os.write(sublen >> 8);
571         os.write(sublen);
572
573         printString(value, offset, sublen);
574
575         length -= sublen;
576         offset += sublen;
577       }
578
579       os.write('S');
580       os.write(length >> 8);
581       os.write(length);
582
583       printString(value, offset, length);
584     }
585   }
586
587   /**
588    * Writes a string value to the stream using UTF-8 encoding.
589    * The string will be written with the following syntax:
590    *
591    * <code><pre>
592    * S b16 b8 string-value
593    * </pre></code>
594    *
595    * If the value is null, it will be written as
596    *
597    * <code><pre>
598    * N
599    * </pre></code>
600    *
601    * @param value the string value to write.
602    */

603   public void writeString(char []buffer, int offset, int length)
604     throws IOException JavaDoc
605   {
606     if (buffer == null) {
607       os.write('N');
608     }
609     else {
610       while (length > 0x8000) {
611         int sublen = 0x8000;
612
613     // chunk can't end in high surrogate
614
char tail = buffer[offset + sublen - 1];
615
616     if (0xd800 <= tail && tail <= 0xdbff)
617       sublen--;
618         
619         os.write('s');
620         os.write(sublen >> 8);
621         os.write(sublen);
622
623         printString(buffer, offset, sublen);
624
625         length -= sublen;
626         offset += sublen;
627       }
628
629       os.write('S');
630       os.write(length >> 8);
631       os.write(length);
632
633       printString(buffer, offset, length);
634     }
635   }
636
637   /**
638    * Writes a byte array to the stream.
639    * The array will be written with the following syntax:
640    *
641    * <code><pre>
642    * B b16 b18 bytes
643    * </pre></code>
644    *
645    * If the value is null, it will be written as
646    *
647    * <code><pre>
648    * N
649    * </pre></code>
650    *
651    * @param value the string value to write.
652    */

653   public void writeBytes(byte []buffer)
654     throws IOException JavaDoc
655   {
656     if (buffer == null)
657       os.write('N');
658     else
659       writeBytes(buffer, 0, buffer.length);
660   }
661   
662   /**
663    * Writes a byte array to the stream.
664    * The array will be written with the following syntax:
665    *
666    * <code><pre>
667    * B b16 b18 bytes
668    * </pre></code>
669    *
670    * If the value is null, it will be written as
671    *
672    * <code><pre>
673    * N
674    * </pre></code>
675    *
676    * @param value the string value to write.
677    */

678   public void writeBytes(byte []buffer, int offset, int length)
679     throws IOException JavaDoc
680   {
681     if (buffer == null) {
682       os.write('N');
683     }
684     else {
685       while (length > 0x8000) {
686         int sublen = 0x8000;
687         
688         os.write('b');
689         os.write(sublen >> 8);
690         os.write(sublen);
691
692         os.write(buffer, offset, sublen);
693
694         length -= sublen;
695         offset += sublen;
696       }
697
698       os.write('B');
699       os.write(length >> 8);
700       os.write(length);
701       os.write(buffer, offset, length);
702     }
703   }
704   
705   /**
706    * Writes a byte buffer to the stream.
707    *
708    * <code><pre>
709    * </pre></code>
710    */

711   public void writeByteBufferStart()
712     throws IOException JavaDoc
713   {
714   }
715   
716   /**
717    * Writes a byte buffer to the stream.
718    *
719    * <code><pre>
720    * b b16 b18 bytes
721    * </pre></code>
722    */

723   public void writeByteBufferPart(byte []buffer, int offset, int length)
724     throws IOException JavaDoc
725   {
726     while (length > 0) {
727       int sublen = length;
728
729       if (0x8000 < sublen)
730     sublen = 0x8000;
731
732       os.write('b');
733       os.write(sublen >> 8);
734       os.write(sublen);
735
736       os.write(buffer, offset, sublen);
737
738       length -= sublen;
739       offset += sublen;
740     }
741   }
742   
743   /**
744    * Writes a byte buffer to the stream.
745    *
746    * <code><pre>
747    * b b16 b18 bytes
748    * </pre></code>
749    */

750   public void writeByteBufferEnd(byte []buffer, int offset, int length)
751     throws IOException JavaDoc
752   {
753     writeBytes(buffer, offset, length);
754   }
755
756   /**
757    * Writes a reference.
758    *
759    * <code><pre>
760    * R b32 b24 b16 b8
761    * </pre></code>
762    *
763    * @param value the integer value to write.
764    */

765   public void writeRef(int value)
766     throws IOException JavaDoc
767   {
768     os.write('R');
769     os.write(value >> 24);
770     os.write(value >> 16);
771     os.write(value >> 8);
772     os.write(value);
773   }
774
775   /**
776    * Writes a placeholder.
777    *
778    * <code><pre>
779    * P
780    * </pre></code>
781    */

782   public void writePlaceholder()
783     throws IOException JavaDoc
784   {
785     os.write('P');
786   }
787
788   /**
789    * If the object has already been written, just write its ref.
790    *
791    * @return true if we're writing a ref.
792    */

793   public boolean addRef(Object JavaDoc object)
794     throws IOException JavaDoc
795   {
796     if (_refs == null)
797       _refs = new IdentityHashMap JavaDoc();
798
799     Integer JavaDoc ref = (Integer JavaDoc) _refs.get(object);
800
801     if (ref != null) {
802       int value = ref.intValue();
803       
804       writeRef(value);
805       return true;
806     }
807     else {
808       _refs.put(object, new Integer JavaDoc(_refs.size()));
809       
810       return false;
811     }
812   }
813
814   /**
815    * Removes a reference.
816    */

817   public boolean removeRef(Object JavaDoc obj)
818     throws IOException JavaDoc
819   {
820     if (_refs != null) {
821       _refs.remove(obj);
822
823       return true;
824     }
825     else
826       return false;
827   }
828
829   /**
830    * Replaces a reference from one object to another.
831    */

832   public boolean replaceRef(Object JavaDoc oldRef, Object JavaDoc newRef)
833     throws IOException JavaDoc
834   {
835     Integer JavaDoc value = (Integer JavaDoc) _refs.remove(oldRef);
836
837     if (value != null) {
838       _refs.put(newRef, value);
839       return true;
840     }
841     else
842       return false;
843   }
844
845   /**
846    * Prints a string to the stream, encoded as UTF-8 with preceeding length
847    *
848    * @param v the string to print.
849    */

850   public void printLenString(String JavaDoc v)
851     throws IOException JavaDoc
852   {
853     if (v == null) {
854       os.write(0);
855       os.write(0);
856     }
857     else {
858       int len = v.length();
859       os.write(len >> 8);
860       os.write(len);
861
862       printString(v, 0, len);
863     }
864   }
865
866   /**
867    * Prints a string to the stream, encoded as UTF-8
868    *
869    * @param v the string to print.
870    */

871   public void printString(String JavaDoc v)
872     throws IOException JavaDoc
873   {
874     printString(v, 0, v.length());
875   }
876   
877   /**
878    * Prints a string to the stream, encoded as UTF-8
879    *
880    * @param v the string to print.
881    */

882   public void printString(String JavaDoc v, int offset, int length)
883     throws IOException JavaDoc
884   {
885     for (int i = 0; i < length; i++) {
886       char ch = v.charAt(i + offset);
887
888       if (ch < 0x80)
889         os.write(ch);
890       else if (ch < 0x800) {
891         os.write(0xc0 + ((ch >> 6) & 0x1f));
892         os.write(0x80 + (ch & 0x3f));
893       }
894       else {
895         os.write(0xe0 + ((ch >> 12) & 0xf));
896         os.write(0x80 + ((ch >> 6) & 0x3f));
897         os.write(0x80 + (ch & 0x3f));
898       }
899     }
900   }
901   
902   /**
903    * Prints a string to the stream, encoded as UTF-8
904    *
905    * @param v the string to print.
906    */

907   public void printString(char []v, int offset, int length)
908     throws IOException JavaDoc
909   {
910     for (int i = 0; i < length; i++) {
911       char ch = v[i + offset];
912
913       if (ch < 0x80)
914         os.write(ch);
915       else if (ch < 0x800) {
916         os.write(0xc0 + ((ch >> 6) & 0x1f));
917         os.write(0x80 + (ch & 0x3f));
918       }
919       else {
920         os.write(0xe0 + ((ch >> 12) & 0xf));
921         os.write(0x80 + ((ch >> 6) & 0x3f));
922         os.write(0x80 + (ch & 0x3f));
923       }
924     }
925   }
926 }
927
Popular Tags