0001: /*
0002: * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved.
0003: *
0004: * The Apache Software License, Version 1.1
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * 1. Redistributions of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * 2. Redistributions in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * 3. The end-user documentation included with the redistribution, if
0019: * any, must include the following acknowlegement:
0020: * "This product includes software developed by the
0021: * Caucho Technology (http://www.caucho.com/)."
0022: * Alternately, this acknowlegement may appear in the software itself,
0023: * if and wherever such third-party acknowlegements normally appear.
0024: *
0025: * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
0026: * endorse or promote products derived from this software without prior
0027: * written permission. For written permission, please contact
0028: * info@caucho.com.
0029: *
0030: * 5. Products derived from this software may not be called "Resin"
0031: * nor may "Resin" appear in their names without prior written
0032: * permission of Caucho Technology.
0033: *
0034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
0038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
0039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
0040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
0041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
0044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0045: *
0046: * @author Scott Ferguson
0047: */
0048:
0049: package com.caucho.hessian.io;
0050:
0051: import com.caucho.hessian.util.IdentityIntMap;
0052:
0053: import java.io.IOException;
0054: import java.io.OutputStream;
0055: import java.util.HashMap;
0056:
0057: /**
0058: * Output stream for Hessian 2 requests.
0059: *
0060: * <p>Since HessianOutput does not depend on any classes other than
0061: * in the JDK, it can be extracted independently into a smaller package.
0062: *
0063: * <p>HessianOutput is unbuffered, so any client needs to provide
0064: * its own buffering.
0065: *
0066: * <pre>
0067: * OutputStream os = ...; // from http connection
0068: * Hessian2Output out = new Hessian11Output(os);
0069: * String value;
0070: *
0071: * out.startCall("hello"); // start hello call
0072: * out.writeString("arg1"); // write a string argument
0073: * out.completeCall(); // complete the call
0074: * </pre>
0075: */
0076: public class Hessian2Output extends AbstractHessianOutput implements
0077: Hessian2Constants {
0078: // the output stream/
0079: protected OutputStream _os;
0080:
0081: // map of references
0082: private IdentityIntMap _refs = new IdentityIntMap();
0083:
0084: private HashMap _serializerMap = new HashMap();
0085:
0086: private boolean _isCloseStreamOnClose;
0087:
0088: // map of classes
0089: private HashMap _classRefs;
0090:
0091: // map of types
0092: private HashMap _typeRefs;
0093:
0094: private final static int SIZE = 1024;
0095:
0096: private final byte[] _buffer = new byte[SIZE];
0097: private int _offset;
0098:
0099: private boolean _isStreaming;
0100:
0101: /**
0102: * Creates a new Hessian output stream, initialized with an
0103: * underlying output stream.
0104: *
0105: * @param os the underlying output stream.
0106: */
0107: public Hessian2Output(OutputStream os) {
0108: _os = os;
0109: }
0110:
0111: public void setCloseStreamOnClose(boolean isClose) {
0112: _isCloseStreamOnClose = isClose;
0113: }
0114:
0115: public boolean isCloseStreamOnClose() {
0116: return _isCloseStreamOnClose;
0117: }
0118:
0119: /**
0120: * Writes a complete method call.
0121: */
0122: public void call(String method, Object[] args) throws IOException {
0123: startCall(method);
0124:
0125: if (args != null) {
0126: for (int i = 0; i < args.length; i++)
0127: writeObject(args[i]);
0128: }
0129:
0130: completeCall();
0131: }
0132:
0133: /**
0134: * Starts the method call. Clients would use <code>startCall</code>
0135: * instead of <code>call</code> if they wanted finer control over
0136: * writing the arguments, or needed to write headers.
0137: *
0138: * <code><pre>
0139: * c major minor
0140: * m b16 b8 method-name
0141: * </pre></code>
0142: *
0143: * @param method the method name to call.
0144: */
0145: public void startCall(String method) throws IOException {
0146: int offset = _offset;
0147:
0148: if (SIZE < offset + 32) {
0149: flush();
0150: offset = 0;
0151: }
0152:
0153: byte[] buffer = _buffer;
0154:
0155: buffer[offset++] = (byte) 'c';
0156: buffer[offset++] = (byte) 2;
0157: buffer[offset++] = (byte) 0;
0158:
0159: buffer[offset++] = (byte) 'm';
0160: int len = method.length();
0161: buffer[offset++] = (byte) (len >> 8);
0162: buffer[offset++] = (byte) len;
0163:
0164: _offset = offset;
0165:
0166: printString(method, 0, len);
0167: }
0168:
0169: /**
0170: * Writes the call tag. This would be followed by the
0171: * headers and the method tag.
0172: *
0173: * <code><pre>
0174: * c major minor
0175: * </pre></code>
0176: *
0177: * @param method the method name to call.
0178: */
0179: public void startCall() throws IOException {
0180: flushIfFull();
0181:
0182: int offset = _offset;
0183: byte[] buffer = _buffer;
0184:
0185: buffer[offset++] = (byte) 'c';
0186: buffer[offset++] = (byte) 2;
0187: buffer[offset++] = (byte) 0;
0188: }
0189:
0190: /**
0191: * Writes the streaming call tag. This would be followed by the
0192: * headers and the method tag.
0193: *
0194: * <code><pre>
0195: * C major minor
0196: * </pre></code>
0197: *
0198: * @param method the method name to call.
0199: */
0200: public void startStreamingCall() throws IOException {
0201: flushIfFull();
0202:
0203: int offset = _offset;
0204: byte[] buffer = _buffer;
0205:
0206: buffer[offset++] = (byte) 'C';
0207: buffer[offset++] = (byte) 2;
0208: buffer[offset++] = (byte) 0;
0209: }
0210:
0211: /**
0212: * Starts an envelope.
0213: *
0214: * <code><pre>
0215: * E major minor
0216: * m b16 b8 method-name
0217: * </pre></code>
0218: *
0219: * @param method the method name to call.
0220: */
0221: public void startEnvelope(String method) throws IOException {
0222: int offset = _offset;
0223:
0224: if (SIZE < offset + 32) {
0225: flush();
0226: offset = 0;
0227: }
0228:
0229: byte[] buffer = _buffer;
0230:
0231: buffer[offset++] = (byte) 'E';
0232: buffer[offset++] = (byte) 2;
0233: buffer[offset++] = (byte) 0;
0234:
0235: buffer[offset++] = (byte) 'm';
0236: int len = method.length();
0237: buffer[offset++] = (byte) (len >> 8);
0238: buffer[offset++] = (byte) len;
0239:
0240: _offset = offset;
0241:
0242: printString(method, 0, len);
0243: }
0244:
0245: /**
0246: * Completes an envelope.
0247: *
0248: * <p>A successful completion will have a single value:
0249: *
0250: * <pre>
0251: * z
0252: * </pre>
0253: */
0254: public void completeEnvelope() throws IOException {
0255: flushIfFull();
0256:
0257: _buffer[_offset++] = (byte) 'z';
0258: }
0259:
0260: /**
0261: * Writes the method tag.
0262: *
0263: * <code><pre>
0264: * m b16 b8 method-name
0265: * </pre></code>
0266: *
0267: * @param method the method name to call.
0268: */
0269: public void writeMethod(String method) throws IOException {
0270: flushIfFull();
0271:
0272: byte[] buffer = _buffer;
0273: int offset = _offset;
0274:
0275: buffer[offset++] = (byte) 'm';
0276: int len = method.length();
0277: buffer[offset++] = (byte) (len >> 8);
0278: buffer[offset++] = (byte) len;
0279:
0280: _offset = offset;
0281:
0282: printString(method, 0, len);
0283: }
0284:
0285: /**
0286: * Completes.
0287: *
0288: * <code><pre>
0289: * z
0290: * </pre></code>
0291: */
0292: public void completeCall() throws IOException {
0293: flushIfFull();
0294:
0295: _buffer[_offset++] = (byte) 'z';
0296: }
0297:
0298: /**
0299: * Starts the reply
0300: *
0301: * <p>A successful completion will have a single value:
0302: *
0303: * <pre>
0304: * r
0305: * </pre>
0306: */
0307: public void startReply() throws IOException {
0308: flushIfFull();
0309:
0310: _buffer[_offset++] = (byte) 'r';
0311: _buffer[_offset++] = (byte) 2;
0312: _buffer[_offset++] = (byte) 0;
0313: }
0314:
0315: /**
0316: * Starts the streaming reply
0317: *
0318: * <p>A successful completion will have a single value:
0319: *
0320: * <pre>
0321: * r
0322: * </pre>
0323: */
0324: public void startStreamingReply() throws IOException {
0325: flushIfFull();
0326:
0327: _buffer[_offset++] = (byte) 'R';
0328: _buffer[_offset++] = (byte) 2;
0329: _buffer[_offset++] = (byte) 0;
0330: }
0331:
0332: /**
0333: * Completes reading the reply
0334: *
0335: * <p>A successful completion will have a single value:
0336: *
0337: * <pre>
0338: * z
0339: * </pre>
0340: */
0341: public void completeReply() throws IOException {
0342: flushIfFull();
0343:
0344: _buffer[_offset++] = (byte) 'z';
0345: }
0346:
0347: /**
0348: * Starts the message
0349: *
0350: * <p>A message contains several objects followed by a 'z'</p>
0351: *
0352: * <pre>
0353: * p x02 x00
0354: * </pre>
0355: */
0356: public void startMessage() throws IOException {
0357: flushIfFull();
0358:
0359: _buffer[_offset++] = (byte) 'p';
0360: _buffer[_offset++] = (byte) 2;
0361: _buffer[_offset++] = (byte) 0;
0362: }
0363:
0364: /**
0365: * Completes reading the message
0366: *
0367: * <p>A successful completion will have a single value:
0368: *
0369: * <pre>
0370: * z
0371: * </pre>
0372: */
0373: public void completeMessage() throws IOException {
0374: flushIfFull();
0375:
0376: _buffer[_offset++] = (byte) 'z';
0377: }
0378:
0379: /**
0380: * Writes a header name. The header value must immediately follow.
0381: *
0382: * <code><pre>
0383: * H b16 b8 foo <em>value</em>
0384: * </pre></code>
0385: */
0386: public void writeHeader(String name) throws IOException {
0387: int len = name.length();
0388:
0389: flushIfFull();
0390:
0391: _buffer[_offset++] = (byte) 'H';
0392: _buffer[_offset++] = (byte) (len >> 8);
0393: _buffer[_offset++] = (byte) (len);
0394:
0395: printString(name);
0396: }
0397:
0398: /**
0399: * Writes a fault. The fault will be written
0400: * as a descriptive string followed by an object:
0401: *
0402: * <code><pre>
0403: * f
0404: * <string>code
0405: * <string>the fault code
0406: *
0407: * <string>message
0408: * <string>the fault mesage
0409: *
0410: * <string>detail
0411: * mt\x00\xnnjavax.ejb.FinderException
0412: * ...
0413: * z
0414: * z
0415: * </pre></code>
0416: *
0417: * @param code the fault code, a three digit
0418: */
0419: public void writeFault(String code, String message, Object detail)
0420: throws IOException {
0421: flushIfFull();
0422:
0423: _buffer[_offset++] = (byte) 'f';
0424: writeString("code");
0425: writeString(code);
0426:
0427: writeString("message");
0428: writeString(message);
0429:
0430: if (detail != null) {
0431: writeString("detail");
0432: writeObject(detail);
0433: }
0434:
0435: flushIfFull();
0436: _buffer[_offset++] = (byte) ('z');
0437: }
0438:
0439: /**
0440: * Writes any object to the output stream.
0441: */
0442: public void writeObject(Object object) throws IOException {
0443: if (object == null) {
0444: writeNull();
0445: return;
0446: }
0447:
0448: Serializer serializer;
0449:
0450: serializer = findSerializerFactory().getSerializer(
0451: object.getClass());
0452:
0453: serializer.writeObject(object, this );
0454: }
0455:
0456: /**
0457: * Writes the list header to the stream. List writers will call
0458: * <code>writeListBegin</code> followed by the list contents and then
0459: * call <code>writeListEnd</code>.
0460: *
0461: * <code><pre>
0462: * V
0463: * t b16 b8 type
0464: * l b32 b24 b16 b8
0465: * </pre></code>
0466: */
0467: public boolean writeListBegin(int length, String type)
0468: throws IOException {
0469: flushIfFull();
0470:
0471: if (_typeRefs != null) {
0472: Integer refV = (Integer) _typeRefs.get(type);
0473:
0474: if (refV != null) {
0475: _buffer[_offset++] = (byte) (LIST_FIXED);
0476: writeInt(refV.intValue());
0477: writeInt(length);
0478:
0479: return false;
0480: }
0481: }
0482:
0483: _buffer[_offset++] = (byte) 'V';
0484:
0485: writeType(type);
0486:
0487: flushIfFull();
0488:
0489: if (length < 0) {
0490: } else if (length < 0x100) {
0491: _buffer[_offset++] = (byte) (LENGTH_BYTE);
0492: _buffer[_offset++] = (byte) (length);
0493: } else {
0494: _buffer[_offset++] = (byte) ('l');
0495: _buffer[_offset++] = (byte) (length >> 24);
0496: _buffer[_offset++] = (byte) (length >> 16);
0497: _buffer[_offset++] = (byte) (length >> 8);
0498: _buffer[_offset++] = (byte) (length);
0499: }
0500:
0501: return true;
0502: }
0503:
0504: /**
0505: * Writes the tail of the list to the stream.
0506: */
0507: public void writeListEnd() throws IOException {
0508: flushIfFull();
0509:
0510: _buffer[_offset++] = (byte) 'z';
0511: }
0512:
0513: /**
0514: * Writes the map header to the stream. Map writers will call
0515: * <code>writeMapBegin</code> followed by the map contents and then
0516: * call <code>writeMapEnd</code>.
0517: *
0518: * <code><pre>
0519: * Mt b16 b8 (<key> <value>)z
0520: * </pre></code>
0521: */
0522: public void writeMapBegin(String type) throws IOException {
0523: if (SIZE < _offset + 32)
0524: flush();
0525:
0526: _buffer[_offset++] = 'M';
0527:
0528: writeType(type);
0529: }
0530:
0531: /**
0532: * Writes the tail of the map to the stream.
0533: */
0534: public void writeMapEnd() throws IOException {
0535: if (SIZE < _offset + 32)
0536: flush();
0537:
0538: _buffer[_offset++] = (byte) 'z';
0539: }
0540:
0541: /**
0542: * Writes the object definition
0543: *
0544: * <code><pre>
0545: * O t b16 b8 <string>*
0546: * </pre></code>
0547: */
0548: public int writeObjectBegin(String type) throws IOException {
0549: if (_classRefs == null)
0550: _classRefs = new HashMap();
0551:
0552: Integer refV = (Integer) _classRefs.get(type);
0553:
0554: if (refV != null) {
0555: int ref = refV.intValue();
0556:
0557: if (SIZE < _offset + 32)
0558: flush();
0559:
0560: _buffer[_offset++] = (byte) 'o';
0561: writeInt(ref);
0562:
0563: return ref;
0564: } else {
0565: int ref = _classRefs.size();
0566:
0567: _classRefs.put(type, Integer.valueOf(ref));
0568:
0569: if (SIZE < _offset + 32)
0570: flush();
0571:
0572: _buffer[_offset++] = (byte) 'O';
0573:
0574: writeString(type);
0575:
0576: return -1;
0577: }
0578: }
0579:
0580: /**
0581: * Writes the tail of the class definition to the stream.
0582: */
0583: public void writeClassFieldLength(int len) throws IOException {
0584: writeInt(len);
0585: }
0586:
0587: /**
0588: * Writes the tail of the object definition to the stream.
0589: */
0590: public void writeObjectEnd() throws IOException {
0591: }
0592:
0593: /**
0594: * Writes a remote object reference to the stream. The type is the
0595: * type of the remote interface.
0596: *
0597: * <code><pre>
0598: * 'r' 't' b16 b8 type url
0599: * </pre></code>
0600: */
0601: public void writeRemote(String type, String url) throws IOException {
0602: if (SIZE < _offset + 32)
0603: flush();
0604:
0605: _buffer[_offset++] = (byte) 'r';
0606:
0607: writeType(type);
0608:
0609: if (SIZE < _offset + 32)
0610: flush();
0611:
0612: _buffer[_offset++] = (byte) 'S';
0613:
0614: printLenString(url);
0615: }
0616:
0617: private void writeType(String type) throws IOException {
0618: if (type == null)
0619: return;
0620:
0621: int len = type.length();
0622: if (len == 0)
0623: return;
0624:
0625: if (_typeRefs == null)
0626: _typeRefs = new HashMap();
0627:
0628: Integer typeRefV = (Integer) _typeRefs.get(type);
0629:
0630: if (typeRefV != null) {
0631: int typeRef = typeRefV.intValue();
0632:
0633: flushIfFull();
0634:
0635: _buffer[_offset++] = (byte) TYPE_REF;
0636:
0637: writeInt(typeRef);
0638: } else {
0639: _typeRefs.put(type, Integer.valueOf(_typeRefs.size()));
0640:
0641: if (SIZE < _offset + 32)
0642: flush();
0643:
0644: _buffer[_offset++] = (byte) 't';
0645:
0646: printLenString(type);
0647: }
0648: }
0649:
0650: /**
0651: * Writes a boolean value to the stream. The boolean will be written
0652: * with the following syntax:
0653: *
0654: * <code><pre>
0655: * T
0656: * F
0657: * </pre></code>
0658: *
0659: * @param value the boolean value to write.
0660: */
0661: public void writeBoolean(boolean value) throws IOException {
0662: if (SIZE < _offset + 16)
0663: flush();
0664:
0665: if (value)
0666: _buffer[_offset++] = (byte) 'T';
0667: else
0668: _buffer[_offset++] = (byte) 'F';
0669: }
0670:
0671: /**
0672: * Writes an integer value to the stream. The integer will be written
0673: * with the following syntax:
0674: *
0675: * <code><pre>
0676: * I b32 b24 b16 b8
0677: * </pre></code>
0678: *
0679: * @param value the integer value to write.
0680: */
0681: public void writeInt(int value) throws IOException {
0682: int offset = _offset;
0683: byte[] buffer = _buffer;
0684:
0685: if (SIZE <= offset + 16) {
0686: flush();
0687: offset = 0;
0688: }
0689:
0690: if (INT_DIRECT_MIN <= value && value <= INT_DIRECT_MAX)
0691: buffer[offset++] = (byte) (value + INT_ZERO);
0692: else if (INT_BYTE_MIN <= value && value <= INT_BYTE_MAX) {
0693: buffer[offset++] = (byte) (INT_BYTE_ZERO + (value >> 8));
0694: buffer[offset++] = (byte) (value);
0695: } else if (INT_SHORT_MIN <= value && value <= INT_SHORT_MAX) {
0696: buffer[offset++] = (byte) (INT_SHORT_ZERO + (value >> 16));
0697: buffer[offset++] = (byte) (value >> 8);
0698: buffer[offset++] = (byte) (value);
0699: } else {
0700: buffer[offset++] = (byte) ('I');
0701: buffer[offset++] = (byte) (value >> 24);
0702: buffer[offset++] = (byte) (value >> 16);
0703: buffer[offset++] = (byte) (value >> 8);
0704: buffer[offset++] = (byte) (value);
0705: }
0706:
0707: _offset = offset;
0708: }
0709:
0710: /**
0711: * Writes a long value to the stream. The long will be written
0712: * with the following syntax:
0713: *
0714: * <code><pre>
0715: * L b64 b56 b48 b40 b32 b24 b16 b8
0716: * </pre></code>
0717: *
0718: * @param value the long value to write.
0719: */
0720: public void writeLong(long value) throws IOException {
0721: int offset = _offset;
0722: byte[] buffer = _buffer;
0723:
0724: if (SIZE <= offset + 16) {
0725: flush();
0726: offset = 0;
0727: }
0728:
0729: if (LONG_DIRECT_MIN <= value && value <= LONG_DIRECT_MAX) {
0730: buffer[offset++] = (byte) (value + LONG_ZERO);
0731: } else if (LONG_BYTE_MIN <= value && value <= LONG_BYTE_MAX) {
0732: buffer[offset++] = (byte) (LONG_BYTE_ZERO + (value >> 8));
0733: buffer[offset++] = (byte) (value);
0734: } else if (LONG_SHORT_MIN <= value && value <= LONG_SHORT_MAX) {
0735: buffer[offset++] = (byte) (LONG_SHORT_ZERO + (value >> 16));
0736: buffer[offset++] = (byte) (value >> 8);
0737: buffer[offset++] = (byte) (value);
0738: } else if (-0x80000000L <= value && value <= 0x7fffffffL) {
0739: buffer[offset + 0] = (byte) LONG_INT;
0740: buffer[offset + 1] = (byte) (value >> 24);
0741: buffer[offset + 2] = (byte) (value >> 16);
0742: buffer[offset + 3] = (byte) (value >> 8);
0743: buffer[offset + 4] = (byte) (value);
0744:
0745: offset += 5;
0746: } else {
0747: buffer[offset + 0] = (byte) 'L';
0748: buffer[offset + 1] = (byte) (value >> 56);
0749: buffer[offset + 2] = (byte) (value >> 48);
0750: buffer[offset + 3] = (byte) (value >> 40);
0751: buffer[offset + 4] = (byte) (value >> 32);
0752: buffer[offset + 5] = (byte) (value >> 24);
0753: buffer[offset + 6] = (byte) (value >> 16);
0754: buffer[offset + 7] = (byte) (value >> 8);
0755: buffer[offset + 8] = (byte) (value);
0756:
0757: offset += 9;
0758: }
0759:
0760: _offset = offset;
0761: }
0762:
0763: /**
0764: * Writes a double value to the stream. The double will be written
0765: * with the following syntax:
0766: *
0767: * <code><pre>
0768: * D b64 b56 b48 b40 b32 b24 b16 b8
0769: * </pre></code>
0770: *
0771: * @param value the double value to write.
0772: */
0773: public void writeDouble(double value) throws IOException {
0774: int offset = _offset;
0775: byte[] buffer = _buffer;
0776:
0777: if (SIZE <= offset + 16) {
0778: flush();
0779: offset = 0;
0780: }
0781:
0782: int intValue = (int) value;
0783:
0784: if (intValue == value) {
0785: if (intValue == 0) {
0786: buffer[offset++] = (byte) DOUBLE_ZERO;
0787:
0788: _offset = offset;
0789:
0790: return;
0791: } else if (intValue == 1) {
0792: buffer[offset++] = (byte) DOUBLE_ONE;
0793:
0794: _offset = offset;
0795:
0796: return;
0797: } else if (-0x80 <= intValue && intValue < 0x80) {
0798: buffer[offset++] = (byte) DOUBLE_BYTE;
0799: buffer[offset++] = (byte) intValue;
0800:
0801: _offset = offset;
0802:
0803: return;
0804: } else if (-0x8000 <= intValue && intValue < 0x8000) {
0805: buffer[offset + 0] = (byte) DOUBLE_SHORT;
0806: buffer[offset + 1] = (byte) (intValue >> 8);
0807: buffer[offset + 2] = (byte) intValue;
0808:
0809: _offset = offset + 3;
0810:
0811: return;
0812: }
0813: }
0814:
0815: float f = (float) value;
0816:
0817: if (f == value) {
0818: int bits = Float.floatToIntBits(f);
0819:
0820: buffer[offset + 0] = (byte) (DOUBLE_FLOAT);
0821: buffer[offset + 1] = (byte) (bits >> 24);
0822: buffer[offset + 2] = (byte) (bits >> 16);
0823: buffer[offset + 3] = (byte) (bits >> 8);
0824: buffer[offset + 4] = (byte) (bits);
0825:
0826: _offset = offset + 5;
0827:
0828: return;
0829: }
0830:
0831: long bits = Double.doubleToLongBits(value);
0832:
0833: buffer[offset + 0] = (byte) 'D';
0834: buffer[offset + 1] = (byte) (bits >> 56);
0835: buffer[offset + 2] = (byte) (bits >> 48);
0836: buffer[offset + 3] = (byte) (bits >> 40);
0837: buffer[offset + 4] = (byte) (bits >> 32);
0838: buffer[offset + 5] = (byte) (bits >> 24);
0839: buffer[offset + 6] = (byte) (bits >> 16);
0840: buffer[offset + 7] = (byte) (bits >> 8);
0841: buffer[offset + 8] = (byte) (bits);
0842:
0843: _offset = offset + 9;
0844: }
0845:
0846: /**
0847: * Writes a date to the stream.
0848: *
0849: * <code><pre>
0850: * T b64 b56 b48 b40 b32 b24 b16 b8
0851: * </pre></code>
0852: *
0853: * @param time the date in milliseconds from the epoch in UTC
0854: */
0855: public void writeUTCDate(long time) throws IOException {
0856: if (SIZE < _offset + 32)
0857: flush();
0858:
0859: int offset = _offset;
0860: byte[] buffer = _buffer;
0861:
0862: buffer[offset++] = (byte) ('d');
0863: buffer[offset++] = ((byte) (time >> 56));
0864: buffer[offset++] = ((byte) (time >> 48));
0865: buffer[offset++] = ((byte) (time >> 40));
0866: buffer[offset++] = ((byte) (time >> 32));
0867: buffer[offset++] = ((byte) (time >> 24));
0868: buffer[offset++] = ((byte) (time >> 16));
0869: buffer[offset++] = ((byte) (time >> 8));
0870: buffer[offset++] = ((byte) (time));
0871:
0872: _offset = offset;
0873: }
0874:
0875: /**
0876: * Writes a null value to the stream.
0877: * The null will be written with the following syntax
0878: *
0879: * <code><pre>
0880: * N
0881: * </pre></code>
0882: *
0883: * @param value the string value to write.
0884: */
0885: public void writeNull() throws IOException {
0886: int offset = _offset;
0887: byte[] buffer = _buffer;
0888:
0889: if (SIZE <= offset + 16) {
0890: flush();
0891: offset = 0;
0892: }
0893:
0894: buffer[offset++] = 'N';
0895:
0896: _offset = offset;
0897: }
0898:
0899: /**
0900: * Writes a string value to the stream using UTF-8 encoding.
0901: * The string will be written with the following syntax:
0902: *
0903: * <code><pre>
0904: * S b16 b8 string-value
0905: * </pre></code>
0906: *
0907: * If the value is null, it will be written as
0908: *
0909: * <code><pre>
0910: * N
0911: * </pre></code>
0912: *
0913: * @param value the string value to write.
0914: */
0915: public void writeString(String value) throws IOException {
0916: int offset = _offset;
0917: byte[] buffer = _buffer;
0918:
0919: if (SIZE <= offset + 16) {
0920: flush();
0921: offset = 0;
0922: }
0923:
0924: if (value == null) {
0925: buffer[offset++] = (byte) 'N';
0926:
0927: _offset = offset;
0928: } else {
0929: int length = value.length();
0930: int strOffset = 0;
0931:
0932: while (length > 0x8000) {
0933: int sublen = 0x8000;
0934:
0935: offset = _offset;
0936:
0937: if (SIZE <= offset + 16) {
0938: flush();
0939: offset = 0;
0940: }
0941:
0942: // chunk can't end in high surrogate
0943: char tail = value.charAt(strOffset + sublen - 1);
0944:
0945: if (0xd800 <= tail && tail <= 0xdbff)
0946: sublen--;
0947:
0948: buffer[offset + 0] = (byte) 's';
0949: buffer[offset + 1] = (byte) (sublen >> 8);
0950: buffer[offset + 2] = (byte) (sublen);
0951:
0952: _offset = offset + 3;
0953:
0954: printString(value, strOffset, sublen);
0955:
0956: length -= sublen;
0957: strOffset += sublen;
0958: }
0959:
0960: offset = _offset;
0961:
0962: if (SIZE <= offset + 16) {
0963: flush();
0964: offset = 0;
0965: }
0966:
0967: if (length <= STRING_DIRECT_MAX) {
0968: buffer[offset++] = (byte) (STRING_DIRECT + length);
0969: } else {
0970: buffer[offset++] = (byte) ('S');
0971: buffer[offset++] = (byte) (length >> 8);
0972: buffer[offset++] = (byte) (length);
0973: }
0974:
0975: _offset = offset;
0976:
0977: printString(value, strOffset, length);
0978: }
0979: }
0980:
0981: /**
0982: * Writes a string value to the stream using UTF-8 encoding.
0983: * The string will be written with the following syntax:
0984: *
0985: * <code><pre>
0986: * S b16 b8 string-value
0987: * </pre></code>
0988: *
0989: * If the value is null, it will be written as
0990: *
0991: * <code><pre>
0992: * N
0993: * </pre></code>
0994: *
0995: * @param value the string value to write.
0996: */
0997: public void writeString(char[] buffer, int offset, int length)
0998: throws IOException {
0999: if (buffer == null) {
1000: if (SIZE < _offset + 16)
1001: flush();
1002:
1003: _buffer[_offset++] = (byte) ('N');
1004: } else {
1005: while (length > 0x8000) {
1006: int sublen = 0x8000;
1007:
1008: if (SIZE < _offset + 16)
1009: flush();
1010:
1011: // chunk can't end in high surrogate
1012: char tail = buffer[offset + sublen - 1];
1013:
1014: if (0xd800 <= tail && tail <= 0xdbff)
1015: sublen--;
1016:
1017: _buffer[_offset++] = (byte) 's';
1018: _buffer[_offset++] = (byte) (sublen >> 8);
1019: _buffer[_offset++] = (byte) (sublen);
1020:
1021: printString(buffer, offset, sublen);
1022:
1023: length -= sublen;
1024: offset += sublen;
1025: }
1026:
1027: if (SIZE < _offset + 16)
1028: flush();
1029:
1030: if (length <= STRING_DIRECT_MAX) {
1031: _buffer[_offset++] = (byte) (STRING_DIRECT + length);
1032: } else {
1033: _buffer[_offset++] = (byte) ('S');
1034: _buffer[_offset++] = (byte) (length >> 8);
1035: _buffer[_offset++] = (byte) (length);
1036: }
1037:
1038: printString(buffer, offset, length);
1039: }
1040: }
1041:
1042: /**
1043: * Writes a byte array to the stream.
1044: * The array will be written with the following syntax:
1045: *
1046: * <code><pre>
1047: * B b16 b18 bytes
1048: * </pre></code>
1049: *
1050: * If the value is null, it will be written as
1051: *
1052: * <code><pre>
1053: * N
1054: * </pre></code>
1055: *
1056: * @param value the string value to write.
1057: */
1058: public void writeBytes(byte[] buffer) throws IOException {
1059: if (buffer == null) {
1060: if (SIZE < _offset + 16)
1061: flush();
1062:
1063: _buffer[_offset++] = 'N';
1064: } else
1065: writeBytes(buffer, 0, buffer.length);
1066: }
1067:
1068: /**
1069: * Writes a byte array to the stream.
1070: * The array will be written with the following syntax:
1071: *
1072: * <code><pre>
1073: * B b16 b18 bytes
1074: * </pre></code>
1075: *
1076: * If the value is null, it will be written as
1077: *
1078: * <code><pre>
1079: * N
1080: * </pre></code>
1081: *
1082: * @param value the string value to write.
1083: */
1084: public void writeBytes(byte[] buffer, int offset, int length)
1085: throws IOException {
1086: if (buffer == null) {
1087: if (SIZE < _offset + 16)
1088: flush();
1089:
1090: _buffer[_offset++] = (byte) 'N';
1091: } else {
1092: flush();
1093:
1094: while (length > 0x8000) {
1095: int sublen = 0x8000;
1096:
1097: _os.write('b');
1098: _os.write(sublen >> 8);
1099: _os.write(sublen);
1100:
1101: _os.write(buffer, offset, sublen);
1102:
1103: length -= sublen;
1104: offset += sublen;
1105: }
1106:
1107: if (length < 0x10) {
1108: _os.write(BYTES_DIRECT + length);
1109: } else {
1110: _os.write('B');
1111: _os.write(length >> 8);
1112: _os.write(length);
1113: }
1114:
1115: _os.write(buffer, offset, length);
1116: }
1117: }
1118:
1119: /**
1120: * Writes a byte buffer to the stream.
1121: *
1122: * <code><pre>
1123: * </pre></code>
1124: */
1125: public void writeByteBufferStart() throws IOException {
1126: }
1127:
1128: /**
1129: * Writes a byte buffer to the stream.
1130: *
1131: * <code><pre>
1132: * b b16 b18 bytes
1133: * </pre></code>
1134: */
1135: public void writeByteBufferPart(byte[] buffer, int offset,
1136: int length) throws IOException {
1137: while (length > 0) {
1138: int sublen = length;
1139:
1140: if (0x8000 < sublen)
1141: sublen = 0x8000;
1142:
1143: flush(); // bypass buffer
1144:
1145: _os.write('b');
1146: _os.write(sublen >> 8);
1147: _os.write(sublen);
1148:
1149: _os.write(buffer, offset, sublen);
1150:
1151: length -= sublen;
1152: offset += sublen;
1153: }
1154: }
1155:
1156: /**
1157: * Writes a byte buffer to the stream.
1158: *
1159: * <code><pre>
1160: * b b16 b18 bytes
1161: * </pre></code>
1162: */
1163: public void writeByteBufferEnd(byte[] buffer, int offset, int length)
1164: throws IOException {
1165: writeBytes(buffer, offset, length);
1166: }
1167:
1168: /**
1169: * Returns an output stream to write binary data.
1170: */
1171: public OutputStream getBytesOutputStream() throws IOException {
1172: return new BytesOutputStream();
1173: }
1174:
1175: /**
1176: * Writes a reference.
1177: *
1178: * <code><pre>
1179: * R b32 b24 b16 b8
1180: * </pre></code>
1181: *
1182: * @param value the integer value to write.
1183: */
1184: public void writeRef(int value) throws IOException {
1185: if (SIZE < _offset + 16)
1186: flush();
1187:
1188: if (value < 0x100) {
1189: _buffer[_offset++] = (byte) (REF_BYTE);
1190: _buffer[_offset++] = (byte) (value);
1191: } else if (value < 0x10000) {
1192: _buffer[_offset++] = (byte) (REF_SHORT);
1193: _buffer[_offset++] = (byte) (value >> 8);
1194: _buffer[_offset++] = (byte) (value);
1195: } else {
1196: _buffer[_offset++] = (byte) ('R');
1197: _buffer[_offset++] = (byte) (value >> 24);
1198: _buffer[_offset++] = (byte) (value >> 16);
1199: _buffer[_offset++] = (byte) (value >> 8);
1200: _buffer[_offset++] = (byte) (value);
1201: }
1202: }
1203:
1204: /**
1205: * If the object has already been written, just write its ref.
1206: *
1207: * @return true if we're writing a ref.
1208: */
1209: public boolean addRef(Object object) throws IOException {
1210: int ref = _refs.get(object);
1211:
1212: if (ref >= 0) {
1213: writeRef(ref);
1214:
1215: return true;
1216: } else {
1217: _refs.put(object, _refs.size());
1218:
1219: return false;
1220: }
1221: }
1222:
1223: /**
1224: * Removes a reference.
1225: */
1226: public boolean removeRef(Object obj) throws IOException {
1227: if (_refs != null) {
1228: _refs.remove(obj);
1229:
1230: return true;
1231: } else
1232: return false;
1233: }
1234:
1235: /**
1236: * Replaces a reference from one object to another.
1237: */
1238: public boolean replaceRef(Object oldRef, Object newRef)
1239: throws IOException {
1240: Integer value = (Integer) _refs.remove(oldRef);
1241:
1242: if (value != null) {
1243: _refs.put(newRef, value);
1244: return true;
1245: } else
1246: return false;
1247: }
1248:
1249: /**
1250: * Resets the references for streaming.
1251: */
1252: public void resetReferences() {
1253: if (_refs != null)
1254: _refs.clear();
1255: }
1256:
1257: /**
1258: * Starts the streaming message
1259: *
1260: * <p>A streaming message starts with 'P'</p>
1261: *
1262: * <pre>
1263: * P x02 x00
1264: * </pre>
1265: */
1266: public void writeStreamingObject(Object obj) throws IOException {
1267: if (_refs != null)
1268: _refs.clear();
1269:
1270: flush();
1271:
1272: _isStreaming = true;
1273: _offset = 3;
1274:
1275: writeObject(obj);
1276:
1277: int len = _offset - 3;
1278:
1279: _buffer[0] = (byte) 'P';
1280: _buffer[1] = (byte) (len >> 8);
1281: _buffer[2] = (byte) len;
1282:
1283: _isStreaming = false;
1284:
1285: flush();
1286: }
1287:
1288: /**
1289: * Prints a string to the stream, encoded as UTF-8 with preceeding length
1290: *
1291: * @param v the string to print.
1292: */
1293: public void printLenString(String v) throws IOException {
1294: if (SIZE < _offset + 16)
1295: flush();
1296:
1297: if (v == null) {
1298: _buffer[_offset++] = (byte) (0);
1299: _buffer[_offset++] = (byte) (0);
1300: } else {
1301: int len = v.length();
1302: _buffer[_offset++] = (byte) (len >> 8);
1303: _buffer[_offset++] = (byte) (len);
1304:
1305: printString(v, 0, len);
1306: }
1307: }
1308:
1309: /**
1310: * Prints a string to the stream, encoded as UTF-8
1311: *
1312: * @param v the string to print.
1313: */
1314: public void printString(String v) throws IOException {
1315: printString(v, 0, v.length());
1316: }
1317:
1318: /**
1319: * Prints a string to the stream, encoded as UTF-8
1320: *
1321: * @param v the string to print.
1322: */
1323: public void printString(String v, int strOffset, int length)
1324: throws IOException {
1325: int offset = _offset;
1326: byte[] buffer = _buffer;
1327:
1328: for (int i = 0; i < length; i++) {
1329: if (SIZE <= offset + 16) {
1330: _offset = offset;
1331: flush();
1332: offset = 0;
1333: }
1334:
1335: char ch = v.charAt(i + strOffset);
1336:
1337: if (ch < 0x80)
1338: buffer[offset++] = (byte) (ch);
1339: else if (ch < 0x800) {
1340: buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
1341: buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1342: } else {
1343: buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf));
1344: buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
1345: buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1346: }
1347: }
1348:
1349: _offset = offset;
1350: }
1351:
1352: /**
1353: * Prints a string to the stream, encoded as UTF-8
1354: *
1355: * @param v the string to print.
1356: */
1357: public void printString(char[] v, int strOffset, int length)
1358: throws IOException {
1359: int offset = _offset;
1360: byte[] buffer = _buffer;
1361:
1362: for (int i = 0; i < length; i++) {
1363: if (SIZE <= offset + 16) {
1364: _offset = offset;
1365: flush();
1366: offset = 0;
1367: }
1368:
1369: char ch = v[i + strOffset];
1370:
1371: if (ch < 0x80)
1372: buffer[offset++] = (byte) (ch);
1373: else if (ch < 0x800) {
1374: buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
1375: buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1376: } else {
1377: buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf));
1378: buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
1379: buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1380: }
1381: }
1382:
1383: _offset = offset;
1384: }
1385:
1386: private final void flushIfFull() throws IOException {
1387: int offset = _offset;
1388:
1389: if (SIZE < offset + 32) {
1390: _offset = 0;
1391: _os.write(_buffer, 0, offset);
1392: }
1393: }
1394:
1395: public final void flush() throws IOException {
1396: int offset = _offset;
1397:
1398: if (offset > 0) {
1399: if (_isStreaming) {
1400: int len = offset - 3;
1401: _buffer[0] = 'p';
1402: _buffer[1] = (byte) (len >> 8);
1403: _buffer[2] = (byte) len;
1404: }
1405:
1406: _offset = 0;
1407: _os.write(_buffer, 0, offset);
1408: }
1409: }
1410:
1411: public final void close() throws IOException {
1412: flush();
1413:
1414: OutputStream os = _os;
1415: _os = null;
1416:
1417: if (os != null) {
1418: if (_isCloseStreamOnClose)
1419: os.close();
1420: }
1421: }
1422:
1423: class BytesOutputStream extends OutputStream {
1424: private int _startOffset;
1425:
1426: BytesOutputStream() throws IOException {
1427: if (SIZE < _offset + 16)
1428: flush();
1429:
1430: _startOffset = _offset;
1431: _offset += 3; // skip 'b' xNN xNN
1432: }
1433:
1434: public void write(int ch) throws IOException {
1435: if (SIZE <= _offset) {
1436: int length = (_offset - _startOffset) - 3;
1437:
1438: _buffer[_startOffset] = (byte) 'b';
1439: _buffer[_startOffset + 1] = (byte) (length >> 8);
1440: _buffer[_startOffset + 2] = (byte) (length);
1441:
1442: flush();
1443:
1444: _startOffset = _offset;
1445: _offset += 3;
1446: }
1447:
1448: _buffer[_offset++] = (byte) ch;
1449: }
1450:
1451: public void write(byte[] buffer, int offset, int length)
1452: throws IOException {
1453: while (length > 0) {
1454: int sublen = SIZE - _offset;
1455:
1456: if (length < sublen)
1457: sublen = length;
1458:
1459: if (sublen > 0) {
1460: System.arraycopy(buffer, offset, _buffer, _offset,
1461: sublen);
1462: _offset += sublen;
1463: }
1464:
1465: length -= sublen;
1466: offset += sublen;
1467:
1468: if (SIZE <= _offset) {
1469: int chunkLength = (_offset - _startOffset) - 3;
1470:
1471: _buffer[_startOffset] = (byte) 'b';
1472: _buffer[_startOffset + 1] = (byte) (chunkLength >> 8);
1473: _buffer[_startOffset + 2] = (byte) (chunkLength);
1474:
1475: flush();
1476:
1477: _startOffset = _offset;
1478: _offset += 3;
1479: }
1480: }
1481: }
1482:
1483: public void close() throws IOException {
1484: int startOffset = _startOffset;
1485: _startOffset = -1;
1486:
1487: if (startOffset < 0)
1488: return;
1489:
1490: int length = (_offset - startOffset) - 3;
1491:
1492: _buffer[startOffset] = (byte) 'B';
1493: _buffer[startOffset + 1] = (byte) (length >> 8);
1494: _buffer[startOffset + 2] = (byte) (length);
1495: }
1496: }
1497: }
|