/*
 * 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
 *    info@caucho.com.
 *
 * 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
/** * Output stream for Hessian 2 requests. * * <p>Since HessianOutput does not depend on any classes other than * in the JDK, it can be extracted independently into a smaller package. * * <p>HessianOutput is unbuffered, so any client needs to provide * its own buffering. * * <pre> * 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 * </pre> */
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<String,Integer> _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.
Params:
  • os – the underlying output stream.
/** * 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.
Params:
  • os – the underlying output stream.
/** * 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.
/** * 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.
/** * 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
Params:
  • method – the method name to call.
/** * Starts the method call. Clients would use <code>startCall</code> * instead of <code>call</code> if they wanted finer control over * writing the arguments, or needed to write headers. * * <code><pre> * C * string # method name * int # arg count * </pre></code> * * @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
Params:
  • method – the method name to call.
/** * Writes the call tag. This would be followed by the * method and the arguments * * <code><pre> * C * </pre></code> * * @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
Params:
  • method – the method name to call.
/** * Starts an envelope. * * <code><pre> * E major minor * m b16 b8 method-name * </pre></code> * * @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
/** * Completes an envelope. * * <p>A successful completion will have a single value: * * <pre> * Z * </pre> */
public void completeEnvelope() throws IOException { flushIfFull(); _buffer[_offset++] = (byte) 'Z'; }
Writes the method tag.
string
Params:
  • method – the method name to call.
/** * Writes the method tag. * * <code><pre> * string * </pre></code> * * @param method the method name to call. */
public void writeMethod(String method) throws IOException { writeString(method); }
Completes.
z
/** * Completes. * * <code><pre> * z * </pre></code> */
@Override public void completeCall() throws IOException { /* flushIfFull(); _buffer[_offset++] = (byte) 'Z'; */ }
Starts the reply

A successful completion will have a single value:

R
/** * Starts the reply * * <p>A successful completion will have a single value: * * <pre> * R * </pre> */
@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
/** * Completes reading the reply * * <p>A successful completion will have a single value: * * <pre> * z * </pre> */
@Override public void completeReply() throws IOException { }
Starts a packet

A message contains several objects encapsulated by a length

p x02 x00
/** * Starts a packet * * <p>A message contains several objects encapsulated by a length</p> * * <pre> * p x02 x00 * </pre> */
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
/** * Completes reading the message * * <p>A successful completion will have a single value: * * <pre> * z * </pre> */
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
Params:
  • code – the fault code, a three digit
/** * Writes a fault. The fault will be written * as a descriptive string followed by an object: * * <code><pre> * F map * </pre></code> * * <code><pre> * F H * \x04code * \x10the fault code * * \x07message * \x11the fault message * * \x06detail * M\xnnjavax.ejb.FinderException * ... * Z * Z * </pre></code> * * @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.
/** * 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*
Returns:true for variable lists, false for fixed lists
/** * Writes the list header to the stream. List writers will call * <code>writeListBegin</code> followed by the list contents and then * call <code>writeListEnd</code>. * * <code><pre> * list ::= V type value* Z * ::= v type int value* * </pre></code> * * @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.
/** * 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
/** * Writes the map header to the stream. Map writers will call * <code>writeMapBegin</code> followed by the map contents and then * call <code>writeMapEnd</code>. * * <code><pre> * map ::= M type (<value> <value>)* Z * ::= H (<value> <value>)* Z * </pre></code> */
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.
/** * 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>*
/** * Writes the object definition * * <code><pre> * C &lt;string> &lt;int> &lt;string>* * </pre></code> */
@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.
/** * 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.
/** * Writes the tail of the object definition to the stream. */
@Override public void writeObjectEnd() throws IOException { }
type ::= string
     ::= int
/** * <code><pre> * type ::= string * ::= int * </code></pre> */
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<String,Integer>(); 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
Params:
  • value – the boolean value to write.
/** * Writes a boolean value to the stream. The boolean will be written * with the following syntax: * * <code><pre> * T * F * </pre></code> * * @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
Params:
  • value – the integer value to write.
/** * Writes an integer value to the stream. The integer will be written * with the following syntax: * * <code><pre> * I b32 b24 b16 b8 * </pre></code> * * @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
Params:
  • value – the long value to write.
/** * Writes a long value to the stream. The long will be written * with the following syntax: * * <code><pre> * L b64 b56 b48 b40 b32 b24 b16 b8 * </pre></code> * * @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
Params:
  • value – the double value to write.
/** * Writes a double value to the stream. The double will be written * with the following syntax: * * <code><pre> * D b64 b56 b48 b40 b32 b24 b16 b8 * </pre></code> * * @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
Params:
  • time – the date in milliseconds from the epoch in UTC
/** * Writes a date to the stream. * * <code><pre> * date ::= d b7 b6 b5 b4 b3 b2 b1 b0 * ::= x65 b3 b2 b1 b0 * </pre></code> * * @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
Params:
  • value – the string value to write.
/** * Writes a null value to the stream. * The null will be written with the following syntax * * <code><pre> * N * </pre></code> * * @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
Params:
  • value – the string value to write.
/** * Writes a string value to the stream using UTF-8 encoding. * The string will be written with the following syntax: * * <code><pre> * S b16 b8 string-value * </pre></code> * * If the value is null, it will be written as * * <code><pre> * N * </pre></code> * * @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
Params:
  • value – the string value to write.
/** * Writes a string value to the stream using UTF-8 encoding. * The string will be written with the following syntax: * * <code><pre> * S b16 b8 string-value * </pre></code> * * If the value is null, it will be written as * * <code><pre> * N * </pre></code> * * @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
Params:
  • value – the string value to write.
/** * Writes a byte array to the stream. * The array will be written with the following syntax: * * <code><pre> * B b16 b18 bytes * </pre></code> * * If the value is null, it will be written as * * <code><pre> * N * </pre></code> * * @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
Params:
  • value – the string value to write.
/** * Writes a byte array to the stream. * The array will be written with the following syntax: * * <code><pre> * B b16 b18 bytes * </pre></code> * * If the value is null, it will be written as * * <code><pre> * N * </pre></code> * * @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.
/** * Writes a byte buffer to the stream. * * <code><pre> * </pre></code> */
public void writeByteBufferStart() throws IOException { }
Writes a byte buffer to the stream.
b b16 b18 bytes
/** * Writes a byte buffer to the stream. * * <code><pre> * b b16 b18 bytes * </pre></code> */
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
/** * Writes a byte buffer to the stream. * * <code><pre> * b b16 b18 bytes * </pre></code> */
public void writeByteBufferEnd(byte []buffer, int offset, int length) throws IOException { writeBytes(buffer, offset, length); }
Returns an output stream to write binary data.
/** * Returns an output stream to write binary data. */
public OutputStream getBytesOutputStream() throws IOException { return new BytesOutputStream(); }
Writes a full output stream.
/** * 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>
Params:
  • value – the integer value to write.
/** * Writes a reference. * * <code><pre> * x51 &lt;int> * </pre></code> * * @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.
Returns:true if we're writing a ref.
/** * 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.
/** * 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.
/** * 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
/** * Starts the streaming message * * <p>A streaming message starts with 'P'</p> * * <pre> * P x02 x00 * </pre> */
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

/** * Starts a streaming packet * * <p>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</p> */
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
Params:
  • v – the string to print.
/** * 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
Params:
  • v – the string to print.
/** * 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
Params:
  • v – the string to print.
/** * 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
Params:
  • v – the string to print.
/** * 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.
/** * Resets the references for streaming. */
@Override public void resetReferences() { if (_refs != null) { _refs.clear(); _refCount = 0; } }
Resets all counters and references
/** * 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(); } } }