All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.hessian.io.Hessian2Output Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
 *
 * The Apache Software License, Version 1.1
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Caucho Technology (http://www.caucho.com/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
 *    endorse or promote products derived from this software without prior
 *    written permission. For written permission, please contact
 *    [email protected].
 *
 * 5. Products derived from this software may not be called "Resin"
 *    nor may "Resin" appear in their names without prior written
 *    permission of Caucho Technology.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Scott Ferguson
 */

package com.caucho.hessian.io;

import com.caucho.hessian.util.IdentityIntMap;

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;

/**
 * Output stream for Hessian 2 requests.
 *
 * 

Since HessianOutput does not depend on any classes other than * in the JDK, it can be extracted independently into a smaller package. * *

HessianOutput is unbuffered, so any client needs to provide * its own buffering. * *

 * OutputStream os = ...; // from http connection
 * Hessian2Output out = new Hessian2Output(os);
 * String value;
 *
 * out.startCall("hello", 1); // start hello call
 * out.writeString("arg1");   // write a string argument
 * out.completeCall();        // complete the call
 * 
*/ public class Hessian2Output extends AbstractHessianOutput implements Hessian2Constants { // should match Resin buffer size for perf public final static int SIZE = 8 * 1024; // the output stream/ protected OutputStream _os; // map of references private final IdentityIntMap _refs = new IdentityIntMap(256); private int _refCount = 0; private boolean _isCloseStreamOnClose; // map of classes private final IdentityIntMap _classRefs = new IdentityIntMap(256); // map of types private HashMap _typeRefs; private final byte []_buffer = new byte[SIZE]; private int _offset; private boolean _isPacket; private boolean _isUnshared; /** * Creates a new Hessian output stream, initialized with an * underlying output stream. * * @param os the underlying output stream. */ public Hessian2Output() { } /** * Creates a new Hessian output stream, initialized with an * underlying output stream. * * @param os the underlying output stream. */ public Hessian2Output(OutputStream os) { init(os); } @Override public void init(OutputStream os) { reset(); _os = os; } public void initPacket(OutputStream os) { resetReferences(); _os = os; } public void setCloseStreamOnClose(boolean isClose) { _isCloseStreamOnClose = isClose; } public boolean isCloseStreamOnClose() { return _isCloseStreamOnClose; } /** * Sets hessian to be "unshared", meaning it will not detect * duplicate or circular references. */ @Override public boolean setUnshared(boolean isUnshared) { boolean oldIsUnshared = _isUnshared; _isUnshared = isUnshared; return oldIsUnshared; } /** * Writes a complete method call. */ @Override public void call(String method, Object []args) throws IOException { writeVersion(); int length = args != null ? args.length : 0; startCall(method, length); for (int i = 0; i < length; i++) { writeObject(args[i]); } completeCall(); flush(); } /** * Starts the method call. Clients would use startCall * instead of call if they wanted finer control over * writing the arguments, or needed to write headers. * *
   * C
   * string # method name
   * int    # arg count
   * 
* * @param method the method name to call. */ @Override public void startCall(String method, int length) throws IOException { int offset = _offset; if (SIZE < offset + 32) { flushBuffer(); offset = _offset; } byte []buffer = _buffer; buffer[_offset++] = (byte) 'C'; writeString(method); writeInt(length); } /** * Writes the call tag. This would be followed by the * method and the arguments * *
   * C
   * 
* * @param method the method name to call. */ @Override public void startCall() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) 'C'; } /** * Starts an envelope. * *
   * E major minor
   * m b16 b8 method-name
   * 
* * @param method the method name to call. */ public void startEnvelope(String method) throws IOException { int offset = _offset; if (SIZE < offset + 32) { flushBuffer(); offset = _offset; } _buffer[_offset++] = (byte) 'E'; writeString(method); } /** * Completes an envelope. * *

A successful completion will have a single value: * *

   * Z
   * 
*/ public void completeEnvelope() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) 'Z'; } /** * Writes the method tag. * *
   * string
   * 
* * @param method the method name to call. */ public void writeMethod(String method) throws IOException { writeString(method); } /** * Completes. * *
   * z
   * 
*/ @Override public void completeCall() throws IOException { /* flushIfFull(); _buffer[_offset++] = (byte) 'Z'; */ } /** * Starts the reply * *

A successful completion will have a single value: * *

   * R
   * 
*/ @Override public void startReply() throws IOException { writeVersion(); flushIfFull(); _buffer[_offset++] = (byte) 'R'; } public void writeVersion() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) 'H'; _buffer[_offset++] = (byte) 2; _buffer[_offset++] = (byte) 0; } /** * Completes reading the reply * *

A successful completion will have a single value: * *

   * z
   * 
*/ @Override public void completeReply() throws IOException { } /** * Starts a packet * *

A message contains several objects encapsulated by a length

* *
   * p x02 x00
   * 
*/ public void startMessage() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) 'p'; _buffer[_offset++] = (byte) 2; _buffer[_offset++] = (byte) 0; } /** * Completes reading the message * *

A successful completion will have a single value: * *

   * z
   * 
*/ public void completeMessage() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) 'z'; } /** * Writes a fault. The fault will be written * as a descriptive string followed by an object: * *
   * F map
   * 
* *
   * F H
   * \x04code
   * \x10the fault code
   *
   * \x07message
   * \x11the fault message
   *
   * \x06detail
   * M\xnnjavax.ejb.FinderException
   *     ...
   * Z
   * Z
   * 
* * @param code the fault code, a three digit */ public void writeFault(String code, String message, Object detail) throws IOException { flushIfFull(); writeVersion(); _buffer[_offset++] = (byte) 'F'; _buffer[_offset++] = (byte) 'H'; addRef(new Object(), _refCount++, false); writeString("code"); writeString(code); writeString("message"); writeString(message); if (detail != null) { writeString("detail"); writeObject(detail); } flushIfFull(); _buffer[_offset++] = (byte) 'Z'; } /** * Writes any object to the output stream. */ @Override public void writeObject(Object object) throws IOException { if (object == null) { writeNull(); return; } Serializer serializer = findSerializerFactory().getObjectSerializer(object.getClass()); serializer.writeObject(object, this); } /** * Writes the list header to the stream. List writers will call * writeListBegin followed by the list contents and then * call writeListEnd. * *
   * list ::= V type value* Z
   *      ::= v type int value*
   * 
* * @return true for variable lists, false for fixed lists */ public boolean writeListBegin(int length, String type) throws IOException { flushIfFull(); if (length < 0) { if (type != null) { _buffer[_offset++] = (byte) BC_LIST_VARIABLE; writeType(type); } else _buffer[_offset++] = (byte) BC_LIST_VARIABLE_UNTYPED; return true; } else if (length <= LIST_DIRECT_MAX) { if (type != null) { _buffer[_offset++] = (byte) (BC_LIST_DIRECT + length); writeType(type); } else { _buffer[_offset++] = (byte) (BC_LIST_DIRECT_UNTYPED + length); } return false; } else { if (type != null) { _buffer[_offset++] = (byte) BC_LIST_FIXED; writeType(type); } else { _buffer[_offset++] = (byte) BC_LIST_FIXED_UNTYPED; } writeInt(length); return false; } } /** * Writes the tail of the list to the stream for a variable-length list. */ public void writeListEnd() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) BC_END; } /** * Writes the map header to the stream. Map writers will call * writeMapBegin followed by the map contents and then * call writeMapEnd. * *
   * map ::= M type ( )* Z
   *     ::= H ( )* Z
   * 
*/ public void writeMapBegin(String type) throws IOException { if (SIZE < _offset + 32) flushBuffer(); if (type != null) { _buffer[_offset++] = BC_MAP; writeType(type); } else _buffer[_offset++] = BC_MAP_UNTYPED; } /** * Writes the tail of the map to the stream. */ public void writeMapEnd() throws IOException { if (SIZE < _offset + 32) flushBuffer(); _buffer[_offset++] = (byte) BC_END; } /** * Writes the object definition * *
   * C <string> <int> <string>*
   * 
*/ @Override public int writeObjectBegin(String type) throws IOException { int newRef = _classRefs.size(); int ref = _classRefs.put(type, newRef, false); if (newRef != ref) { if (SIZE < _offset + 32) flushBuffer(); if (ref <= OBJECT_DIRECT_MAX) { _buffer[_offset++] = (byte) (BC_OBJECT_DIRECT + ref); } else { _buffer[_offset++] = (byte) 'O'; writeInt(ref); } return ref; } else { if (SIZE < _offset + 32) flushBuffer(); _buffer[_offset++] = (byte) 'C'; writeString(type); return -1; } } /** * Writes the tail of the class definition to the stream. */ @Override public void writeClassFieldLength(int len) throws IOException { writeInt(len); } /** * Writes the tail of the object definition to the stream. */ @Override public void writeObjectEnd() throws IOException { } /** *
   * type ::= string
   *      ::= int
   * 
*/ private void writeType(String type) throws IOException { flushIfFull(); int len = type.length(); if (len == 0) { throw new IllegalArgumentException("empty type is not allowed"); } if (_typeRefs == null) _typeRefs = new HashMap(); Integer typeRefV = (Integer) _typeRefs.get(type); if (typeRefV != null) { int typeRef = typeRefV.intValue(); writeInt(typeRef); } else { _typeRefs.put(type, Integer.valueOf(_typeRefs.size())); writeString(type); } } /** * Writes a boolean value to the stream. The boolean will be written * with the following syntax: * *
   * T
   * F
   * 
* * @param value the boolean value to write. */ @Override public void writeBoolean(boolean value) throws IOException { if (SIZE < _offset + 16) flushBuffer(); if (value) _buffer[_offset++] = (byte) 'T'; else _buffer[_offset++] = (byte) 'F'; } /** * Writes an integer value to the stream. The integer will be written * with the following syntax: * *
   * I b32 b24 b16 b8
   * 
* * @param value the integer value to write. */ @Override public void writeInt(int value) throws IOException { int offset = _offset; byte []buffer = _buffer; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } if (INT_DIRECT_MIN <= value && value <= INT_DIRECT_MAX) buffer[offset++] = (byte) (value + BC_INT_ZERO); else if (INT_BYTE_MIN <= value && value <= INT_BYTE_MAX) { buffer[offset++] = (byte) (BC_INT_BYTE_ZERO + (value >> 8)); buffer[offset++] = (byte) (value); } else if (INT_SHORT_MIN <= value && value <= INT_SHORT_MAX) { buffer[offset++] = (byte) (BC_INT_SHORT_ZERO + (value >> 16)); buffer[offset++] = (byte) (value >> 8); buffer[offset++] = (byte) (value); } else { buffer[offset++] = (byte) ('I'); buffer[offset++] = (byte) (value >> 24); buffer[offset++] = (byte) (value >> 16); buffer[offset++] = (byte) (value >> 8); buffer[offset++] = (byte) (value); } _offset = offset; } /** * Writes a long value to the stream. The long will be written * with the following syntax: * *
   * L b64 b56 b48 b40 b32 b24 b16 b8
   * 
* * @param value the long value to write. */ public void writeLong(long value) throws IOException { int offset = _offset; byte []buffer = _buffer; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } if (LONG_DIRECT_MIN <= value && value <= LONG_DIRECT_MAX) { buffer[offset++] = (byte) (value + BC_LONG_ZERO); } else if (LONG_BYTE_MIN <= value && value <= LONG_BYTE_MAX) { buffer[offset++] = (byte) (BC_LONG_BYTE_ZERO + (value >> 8)); buffer[offset++] = (byte) (value); } else if (LONG_SHORT_MIN <= value && value <= LONG_SHORT_MAX) { buffer[offset++] = (byte) (BC_LONG_SHORT_ZERO + (value >> 16)); buffer[offset++] = (byte) (value >> 8); buffer[offset++] = (byte) (value); } else if (-0x80000000L <= value && value <= 0x7fffffffL) { buffer[offset + 0] = (byte) BC_LONG_INT; buffer[offset + 1] = (byte) (value >> 24); buffer[offset + 2] = (byte) (value >> 16); buffer[offset + 3] = (byte) (value >> 8); buffer[offset + 4] = (byte) (value); offset += 5; } else { buffer[offset + 0] = (byte) 'L'; buffer[offset + 1] = (byte) (value >> 56); buffer[offset + 2] = (byte) (value >> 48); buffer[offset + 3] = (byte) (value >> 40); buffer[offset + 4] = (byte) (value >> 32); buffer[offset + 5] = (byte) (value >> 24); buffer[offset + 6] = (byte) (value >> 16); buffer[offset + 7] = (byte) (value >> 8); buffer[offset + 8] = (byte) (value); offset += 9; } _offset = offset; } /** * Writes a double value to the stream. The double will be written * with the following syntax: * *
   * D b64 b56 b48 b40 b32 b24 b16 b8
   * 
* * @param value the double value to write. */ public void writeDouble(double value) throws IOException { int offset = _offset; byte []buffer = _buffer; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } int intValue = (int) value; if (intValue == value) { if (intValue == 0) { buffer[offset++] = (byte) BC_DOUBLE_ZERO; _offset = offset; return; } else if (intValue == 1) { buffer[offset++] = (byte) BC_DOUBLE_ONE; _offset = offset; return; } else if (-0x80 <= intValue && intValue < 0x80) { buffer[offset++] = (byte) BC_DOUBLE_BYTE; buffer[offset++] = (byte) intValue; _offset = offset; return; } else if (-0x8000 <= intValue && intValue < 0x8000) { buffer[offset + 0] = (byte) BC_DOUBLE_SHORT; buffer[offset + 1] = (byte) (intValue >> 8); buffer[offset + 2] = (byte) intValue; _offset = offset + 3; return; } } int mills = (int) (value * 1000); if (0.001 * mills == value) { buffer[offset + 0] = (byte) (BC_DOUBLE_MILL); buffer[offset + 1] = (byte) (mills >> 24); buffer[offset + 2] = (byte) (mills >> 16); buffer[offset + 3] = (byte) (mills >> 8); buffer[offset + 4] = (byte) (mills); _offset = offset + 5; return; } long bits = Double.doubleToLongBits(value); buffer[offset + 0] = (byte) 'D'; buffer[offset + 1] = (byte) (bits >> 56); buffer[offset + 2] = (byte) (bits >> 48); buffer[offset + 3] = (byte) (bits >> 40); buffer[offset + 4] = (byte) (bits >> 32); buffer[offset + 5] = (byte) (bits >> 24); buffer[offset + 6] = (byte) (bits >> 16); buffer[offset + 7] = (byte) (bits >> 8); buffer[offset + 8] = (byte) (bits); _offset = offset + 9; } /** * Writes a date to the stream. * *
   * date ::= d   b7 b6 b5 b4 b3 b2 b1 b0
   *      ::= x65 b3 b2 b1 b0
   * 
* * @param time the date in milliseconds from the epoch in UTC */ public void writeUTCDate(long time) throws IOException { if (SIZE < _offset + 32) flushBuffer(); int offset = _offset; byte []buffer = _buffer; if (time % 60000L == 0) { // compact date ::= x65 b3 b2 b1 b0 long minutes = time / 60000L; if ((minutes >> 31) == 0 || (minutes >> 31) == -1) { buffer[offset++] = (byte) BC_DATE_MINUTE; buffer[offset++] = ((byte) (minutes >> 24)); buffer[offset++] = ((byte) (minutes >> 16)); buffer[offset++] = ((byte) (minutes >> 8)); buffer[offset++] = ((byte) (minutes >> 0)); _offset = offset; return; } } buffer[offset++] = (byte) BC_DATE; buffer[offset++] = ((byte) (time >> 56)); buffer[offset++] = ((byte) (time >> 48)); buffer[offset++] = ((byte) (time >> 40)); buffer[offset++] = ((byte) (time >> 32)); buffer[offset++] = ((byte) (time >> 24)); buffer[offset++] = ((byte) (time >> 16)); buffer[offset++] = ((byte) (time >> 8)); buffer[offset++] = ((byte) (time)); _offset = offset; } /** * Writes a null value to the stream. * The null will be written with the following syntax * *
   * N
   * 
* * @param value the string value to write. */ public void writeNull() throws IOException { int offset = _offset; byte []buffer = _buffer; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } buffer[offset++] = 'N'; _offset = offset; } /** * Writes a string value to the stream using UTF-8 encoding. * The string will be written with the following syntax: * *
   * S b16 b8 string-value
   * 
* * If the value is null, it will be written as * *
   * N
   * 
* * @param value the string value to write. */ public void writeString(String value) throws IOException { int offset = _offset; byte []buffer = _buffer; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } if (value == null) { buffer[offset++] = (byte) 'N'; _offset = offset; } else { int length = value.length(); int strOffset = 0; while (length > 0x8000) { int sublen = 0x8000; offset = _offset; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } // chunk can't end in high surrogate char tail = value.charAt(strOffset + sublen - 1); if (0xd800 <= tail && tail <= 0xdbff) sublen--; buffer[offset + 0] = (byte) BC_STRING_CHUNK; buffer[offset + 1] = (byte) (sublen >> 8); buffer[offset + 2] = (byte) (sublen); _offset = offset + 3; printString(value, strOffset, sublen); length -= sublen; strOffset += sublen; } offset = _offset; if (SIZE <= offset + 16) { flushBuffer(); offset = _offset; } if (length <= STRING_DIRECT_MAX) { buffer[offset++] = (byte) (BC_STRING_DIRECT + length); } else if (length <= STRING_SHORT_MAX) { buffer[offset++] = (byte) (BC_STRING_SHORT + (length >> 8)); buffer[offset++] = (byte) (length); } else { buffer[offset++] = (byte) ('S'); buffer[offset++] = (byte) (length >> 8); buffer[offset++] = (byte) (length); } _offset = offset; printString(value, strOffset, length); } } /** * Writes a string value to the stream using UTF-8 encoding. * The string will be written with the following syntax: * *
   * S b16 b8 string-value
   * 
* * If the value is null, it will be written as * *
   * N
   * 
* * @param value the string value to write. */ public void writeString(char []buffer, int offset, int length) throws IOException { if (buffer == null) { if (SIZE < _offset + 16) flushBuffer(); _buffer[_offset++] = (byte) ('N'); } else { while (length > 0x8000) { int sublen = 0x8000; if (SIZE < _offset + 16) flushBuffer(); // chunk can't end in high surrogate char tail = buffer[offset + sublen - 1]; if (0xd800 <= tail && tail <= 0xdbff) sublen--; _buffer[_offset++] = (byte) BC_STRING_CHUNK; _buffer[_offset++] = (byte) (sublen >> 8); _buffer[_offset++] = (byte) (sublen); printString(buffer, offset, sublen); length -= sublen; offset += sublen; } if (SIZE < _offset + 16) flushBuffer(); if (length <= STRING_DIRECT_MAX) { _buffer[_offset++] = (byte) (BC_STRING_DIRECT + length); } else if (length <= STRING_SHORT_MAX) { _buffer[_offset++] = (byte) (BC_STRING_SHORT + (length >> 8)); _buffer[_offset++] = (byte) length; } else { _buffer[_offset++] = (byte) ('S'); _buffer[_offset++] = (byte) (length >> 8); _buffer[_offset++] = (byte) (length); } printString(buffer, offset, length); } } /** * Writes a byte array to the stream. * The array will be written with the following syntax: * *
   * B b16 b18 bytes
   * 
* * If the value is null, it will be written as * *
   * N
   * 
* * @param value the string value to write. */ public void writeBytes(byte []buffer) throws IOException { if (buffer == null) { if (SIZE < _offset + 16) flushBuffer(); _buffer[_offset++] = 'N'; } else writeBytes(buffer, 0, buffer.length); } /** * Writes a byte array to the stream. * The array will be written with the following syntax: * *
   * B b16 b18 bytes
   * 
* * If the value is null, it will be written as * *
   * N
   * 
* * @param value the string value to write. */ public void writeBytes(byte []buffer, int offset, int length) throws IOException { if (buffer == null) { if (SIZE < _offset + 16) flushBuffer(); _buffer[_offset++] = (byte) 'N'; } else { while (SIZE - _offset - 3 < length) { int sublen = SIZE - _offset - 3; if (sublen < 16) { flushBuffer(); sublen = SIZE - _offset - 3; if (length < sublen) sublen = length; } _buffer[_offset++] = (byte) BC_BINARY_CHUNK; _buffer[_offset++] = (byte) (sublen >> 8); _buffer[_offset++] = (byte) sublen; System.arraycopy(buffer, offset, _buffer, _offset, sublen); _offset += sublen; length -= sublen; offset += sublen; flushBuffer(); } if (SIZE < _offset + 16) flushBuffer(); if (length <= BINARY_DIRECT_MAX) { _buffer[_offset++] = (byte) (BC_BINARY_DIRECT + length); } else if (length <= BINARY_SHORT_MAX) { _buffer[_offset++] = (byte) (BC_BINARY_SHORT + (length >> 8)); _buffer[_offset++] = (byte) (length); } else { _buffer[_offset++] = (byte) 'B'; _buffer[_offset++] = (byte) (length >> 8); _buffer[_offset++] = (byte) (length); } System.arraycopy(buffer, offset, _buffer, _offset, length); _offset += length; } } /** * Writes a byte buffer to the stream. * *
   * 
*/ public void writeByteBufferStart() throws IOException { } /** * Writes a byte buffer to the stream. * *
   * b b16 b18 bytes
   * 
*/ public void writeByteBufferPart(byte []buffer, int offset, int length) throws IOException { while (length > 0) { flushIfFull(); int sublen = _buffer.length - _offset; if (length < sublen) sublen = length; _buffer[_offset++] = BC_BINARY_CHUNK; _buffer[_offset++] = (byte) (sublen >> 8); _buffer[_offset++] = (byte) sublen; System.arraycopy(buffer, offset, _buffer, _offset, sublen); _offset += sublen; length -= sublen; offset += sublen; } } /** * Writes a byte buffer to the stream. * *
   * b b16 b18 bytes
   * 
*/ public void writeByteBufferEnd(byte []buffer, int offset, int length) throws IOException { writeBytes(buffer, offset, length); } /** * Returns an output stream to write binary data. */ public OutputStream getBytesOutputStream() throws IOException { return new BytesOutputStream(); } /** * Writes a full output stream. */ @Override public void writeByteStream(InputStream is) throws IOException { while (true) { int len = SIZE - _offset - 3; if (len < 16) { flushBuffer(); len = SIZE - _offset - 3; } len = is.read(_buffer, _offset + 3, len); if (len <= 0) { _buffer[_offset++] = BC_BINARY_DIRECT; return; } _buffer[_offset + 0] = (byte) BC_BINARY_CHUNK; _buffer[_offset + 1] = (byte) (len >> 8); _buffer[_offset + 2] = (byte) (len); _offset += len + 3; } } /** * Writes a reference. * *
   * x51 <int>
   * 
* * @param value the integer value to write. */ @Override protected void writeRef(int value) throws IOException { if (SIZE < _offset + 16) flushBuffer(); _buffer[_offset++] = (byte) BC_REF; writeInt(value); } /** * If the object has already been written, just write its ref. * * @return true if we're writing a ref. */ @Override public boolean addRef(Object object) throws IOException { if (_isUnshared) { _refCount++; return false; } int newRef = _refCount; int ref = addRef(object, newRef, false); if (ref != newRef) { writeRef(ref); return true; } else { _refCount++; return false; } } @Override public int getRef(Object obj) { if (_isUnshared) return -1; return _refs.get(obj); } /** * Removes a reference. */ @Override public boolean removeRef(Object obj) throws IOException { if (_isUnshared) { return false; } else if (_refs != null) { _refs.remove(obj); return true; } else return false; } /** * Replaces a reference from one object to another. */ @Override public boolean replaceRef(Object oldRef, Object newRef) throws IOException { if (_isUnshared) { return false; } int value = _refs.get(oldRef); if (value >= 0) { addRef(newRef, value, true); _refs.remove(oldRef); return true; } else return false; } private int addRef(Object value, int newRef, boolean isReplace) { int prevRef = _refs.put(value, newRef, isReplace); return prevRef; } /** * Starts the streaming message * *

A streaming message starts with 'P'

* *
   * P x02 x00
   * 
*/ public void writeStreamingObject(Object obj) throws IOException { startPacket(); writeObject(obj); endPacket(); } /** * Starts a streaming packet * *

A streaming contains a set of chunks, ending with a zero chunk. * Each chunk is a length followed by data where the length is * encoded by (b1xxxxxxxx)* b0xxxxxxxx

*/ public void startPacket() throws IOException { if (_refs != null) { _refs.clear(); _refCount = 0; } flushBuffer(); _isPacket = true; _offset = 4; _buffer[0] = (byte) 0x05; // 0x05 = binary _buffer[1] = (byte) 0x55; _buffer[2] = (byte) 0x55; _buffer[3] = (byte) 0x55; } public void endPacket() throws IOException { int offset = _offset; OutputStream os = _os; if (os == null) { _offset = 0; return; } int len = offset - 4; if (len < 0x7e) { _buffer[2] = _buffer[0]; _buffer[3] = (byte) (len); } else { _buffer[1] = (byte) (0x7e); _buffer[2] = (byte) (len >> 8); _buffer[3] = (byte) (len); } _isPacket = false; _offset = 0; if (os == null) { } else if (len < 0x7e) { os.write(_buffer, 2, offset - 2); } else { os.write(_buffer, 0, offset); } } /** * Prints a string to the stream, encoded as UTF-8 with preceeding length * * @param v the string to print. */ public void printLenString(String v) throws IOException { if (SIZE < _offset + 16) flushBuffer(); if (v == null) { _buffer[_offset++] = (byte) (0); _buffer[_offset++] = (byte) (0); } else { int len = v.length(); _buffer[_offset++] = (byte) (len >> 8); _buffer[_offset++] = (byte) (len); printString(v, 0, len); } } /** * Prints a string to the stream, encoded as UTF-8 * * @param v the string to print. */ public void printString(String v) throws IOException { printString(v, 0, v.length()); } /** * Prints a string to the stream, encoded as UTF-8 * * @param v the string to print. */ public void printString(String v, int strOffset, int length) throws IOException { int offset = _offset; byte []buffer = _buffer; for (int i = 0; i < length; i++) { if (SIZE <= offset + 16) { _offset = offset; flushBuffer(); offset = _offset; } char ch = v.charAt(i + strOffset); if (ch < 0x80) buffer[offset++] = (byte) (ch); else if (ch < 0x800) { buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f)); buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); } else { buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf)); buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f)); buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); } } _offset = offset; } /** * Prints a string to the stream, encoded as UTF-8 * * @param v the string to print. */ public void printString(char []v, int strOffset, int length) throws IOException { int offset = _offset; byte []buffer = _buffer; for (int i = 0; i < length; i++) { if (SIZE <= offset + 16) { _offset = offset; flushBuffer(); offset = _offset; } char ch = v[i + strOffset]; if (ch < 0x80) buffer[offset++] = (byte) (ch); else if (ch < 0x800) { buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f)); buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); } else { buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf)); buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f)); buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); } } _offset = offset; } private final void flushIfFull() throws IOException { int offset = _offset; if (SIZE < offset + 32) { flushBuffer(); } } public final void flush() throws IOException { flushBuffer(); if (_os != null) _os.flush(); } public final void flushBuffer() throws IOException { int offset = _offset; OutputStream os = _os; if (! _isPacket && offset > 0) { _offset = 0; if (os != null) os.write(_buffer, 0, offset); } else if (_isPacket && offset > 4) { int len = offset - 4; _buffer[0] |= (byte) 0x80; _buffer[1] = (byte) (0x7e); _buffer[2] = (byte) (len >> 8); _buffer[3] = (byte) (len); _offset = 4; if (os != null) os.write(_buffer, 0, offset); _buffer[0] = (byte) 0x00; _buffer[1] = (byte) 0x56; _buffer[2] = (byte) 0x56; _buffer[3] = (byte) 0x56; } } @Override public void close() throws IOException { // hessian/3a8c flush(); OutputStream os = _os; _os = null; if (os != null) { if (_isCloseStreamOnClose) os.close(); } } public void free() { reset(); _os = null; _isCloseStreamOnClose = false; } /** * Resets the references for streaming. */ @Override public void resetReferences() { if (_refs != null) { _refs.clear(); _refCount = 0; } } /** * Resets all counters and references */ public void reset() { if (_refs != null) { _refs.clear(); _refCount = 0; } _classRefs.clear(); _typeRefs = null; _offset = 0; _isPacket = false; _isUnshared = false; } class BytesOutputStream extends OutputStream { private int _startOffset; BytesOutputStream() throws IOException { if (SIZE < _offset + 16) { Hessian2Output.this.flushBuffer(); } _startOffset = _offset; _offset += 3; // skip 'b' xNN xNN } @Override public void write(int ch) throws IOException { if (SIZE <= _offset) { int length = (_offset - _startOffset) - 3; _buffer[_startOffset] = (byte) BC_BINARY_CHUNK; _buffer[_startOffset + 1] = (byte) (length >> 8); _buffer[_startOffset + 2] = (byte) (length); Hessian2Output.this.flushBuffer(); _startOffset = _offset; _offset += 3; } _buffer[_offset++] = (byte) ch; } @Override public void write(byte []buffer, int offset, int length) throws IOException { while (length > 0) { int sublen = SIZE - _offset; if (length < sublen) sublen = length; if (sublen > 0) { System.arraycopy(buffer, offset, _buffer, _offset, sublen); _offset += sublen; } length -= sublen; offset += sublen; if (SIZE <= _offset) { int chunkLength = (_offset - _startOffset) - 3; _buffer[_startOffset] = (byte) BC_BINARY_CHUNK; _buffer[_startOffset + 1] = (byte) (chunkLength >> 8); _buffer[_startOffset + 2] = (byte) (chunkLength); Hessian2Output.this.flushBuffer(); _startOffset = _offset; _offset += 3; } } } @Override public void close() throws IOException { int startOffset = _startOffset; _startOffset = -1; if (startOffset < 0) return; int length = (_offset - startOffset) - 3; _buffer[startOffset] = (byte) 'B'; _buffer[startOffset + 1] = (byte) (length >> 8); _buffer[startOffset + 2] = (byte) (length); Hessian2Output.this.flushBuffer(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy