blob: 8d6d7d7b700793c6745292499f992f65dfe77f16 [file] [log] [blame]
/*
* 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 "Hessian", "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.alibaba.com.caucho.hessian.io;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Input stream for Hessian requests.
* <p>
* <p>HessianInput is unbuffered, so any client needs to provide
* its own buffering.
* <p>
* <pre>
* InputStream is = ...; // from http connection
* HessianInput in = new HessianInput(is);
* String value;
*
* in.startReply(); // read reply header
* value = in.readString(); // read string value
* in.completeReply(); // read reply footer
* </pre>
*/
public class Hessian2Input
extends AbstractHessianInput
implements Hessian2Constants {
private static final Logger log
= Logger.getLogger(Hessian2Input.class.getName());
private static final double D_256 = 1.0 / 256.0;
private static final int END_OF_DATA = -2;
private static final int SIZE = 256;
private static final int GAP = 16;
private static Field _detailMessageField;
private static boolean _isCloseStreamOnClose;
static {
try {
_detailMessageField = Throwable.class.getDeclaredField("detailMessage");
_detailMessageField.setAccessible(true);
} catch (Throwable e) {
}
}
private final byte[] _buffer = new byte[SIZE];
// factory for deserializing objects in the input stream
protected SerializerFactory _serializerFactory;
protected ArrayList _refs;
protected ArrayList _classDefs;
protected ArrayList _types;
// the underlying input stream
private InputStream _is;
// a peek character
private int _offset;
private int _length;
// true for streaming data
private boolean _isStreaming;
// the method for a call
private String _method;
private int _argLength;
private Reader _chunkReader;
private InputStream _chunkInputStream;
private Throwable _replyFault;
private StringBuilder _sbuf = new StringBuilder();
// true if this is the last chunk
private boolean _isLastChunk;
// the chunk length
private int _chunkLength;
/**
* Creates a new Hessian input stream, initialized with an
* underlying input stream.
*
* @param is the underlying input stream.
*/
public Hessian2Input(InputStream is) {
_is = is;
}
/**
* Gets the serializer factory.
*/
public SerializerFactory getSerializerFactory() {
return _serializerFactory;
}
/**
* Sets the serializer factory.
*/
@Override
public void setSerializerFactory(SerializerFactory factory) {
_serializerFactory = factory;
}
/**
* Gets the serializer factory, creating a default if necessary.
*/
public final SerializerFactory findSerializerFactory() {
SerializerFactory factory = _serializerFactory;
if (factory == null)
_serializerFactory = factory = new SerializerFactory();
return factory;
}
public boolean isCloseStreamOnClose() {
return _isCloseStreamOnClose;
}
public void setCloseStreamOnClose(boolean isClose) {
_isCloseStreamOnClose = isClose;
}
/**
* Returns the calls method
*/
@Override
public String getMethod() {
return _method;
}
/**
* Returns any reply fault.
*/
public Throwable getReplyFault() {
return _replyFault;
}
/**
* Starts reading the call
* <p>
* <pre>
* c major minor
* </pre>
*/
@Override
public int readCall()
throws IOException {
int tag = read();
if (tag != 'C')
throw error("expected hessian call ('C') at " + codeName(tag));
return 0;
}
/**
* Starts reading the envelope
* <p>
* <pre>
* E major minor
* </pre>
*/
public int readEnvelope()
throws IOException {
int tag = read();
int version = 0;
if (tag == 'H') {
int major = read();
int minor = read();
version = (major << 16) + minor;
tag = read();
}
if (tag != 'E')
throw error("expected hessian Envelope ('E') at " + codeName(tag));
return version;
}
/**
* Completes reading the envelope
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* Z
* </pre>
*/
public void completeEnvelope()
throws IOException {
int tag = read();
if (tag != 'Z')
error("expected end of envelope at " + codeName(tag));
}
/**
* Starts reading the call
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* string
* </pre>
*/
@Override
public String readMethod()
throws IOException {
_method = readString();
return _method;
}
/**
* Returns the number of method arguments
* <p>
* <pre>
* int
* </pre>
*/
@Override
public int readMethodArgLength()
throws IOException {
return readInt();
}
/**
* Starts reading the call, including the headers.
* <p>
* <p>The call expects the following protocol data
* <p>
* <pre>
* c major minor
* m b16 b8 method
* </pre>
*/
@Override
public void startCall()
throws IOException {
readCall();
readMethod();
}
/**
* Completes reading the call
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* </pre>
*/
@Override
public void completeCall()
throws IOException {
}
/**
* Reads a reply as an object.
* If the reply has a fault, throws the exception.
*/
@Override
public Object readReply(Class expectedClass)
throws Throwable {
int tag = read();
if (tag == 'R')
return readObject(expectedClass);
else if (tag == 'F') {
HashMap map = (HashMap) readObject(HashMap.class);
throw prepareFault(map);
} else {
StringBuilder sb = new StringBuilder();
sb.append((char) tag);
try {
int ch;
while ((ch = read()) >= 0) {
sb.append((char) ch);
}
} catch (IOException e) {
log.log(Level.FINE, e.toString(), e);
}
throw error("expected hessian reply at " + codeName(tag) + "\n"
+ sb);
}
}
/**
* Starts reading the reply
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* r
* </pre>
*/
@Override
public void startReply()
throws Throwable {
// XXX: for variable length (?)
readReply(Object.class);
}
/**
* Prepares the fault.
*/
private Throwable prepareFault(HashMap fault)
throws IOException {
Object detail = fault.get("detail");
String message = (String) fault.get("message");
if (detail instanceof Throwable) {
_replyFault = (Throwable) detail;
if (message != null && _detailMessageField != null) {
try {
_detailMessageField.set(_replyFault, message);
} catch (Throwable e) {
}
}
return _replyFault;
} else {
String code = (String) fault.get("code");
_replyFault = new HessianServiceException(message, code, detail);
return _replyFault;
}
}
/**
* Completes reading the call
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* z
* </pre>
*/
@Override
public void completeReply()
throws IOException {
}
/**
* Completes reading the call
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* z
* </pre>
*/
public void completeValueReply()
throws IOException {
int tag = read();
if (tag != 'Z')
error("expected end of reply at " + codeName(tag));
}
/**
* Reads a header, returning null if there are no headers.
* <p>
* <pre>
* H b16 b8 value
* </pre>
*/
@Override
public String readHeader()
throws IOException {
return null;
}
/**
* Starts reading the message
* <p>
* <pre>
* p major minor
* </pre>
*/
public int startMessage()
throws IOException {
int tag = read();
if (tag == 'p')
_isStreaming = false;
else if (tag == 'P')
_isStreaming = true;
else
throw error("expected Hessian message ('p') at " + codeName(tag));
int major = read();
int minor = read();
return (major << 16) + minor;
}
/**
* Completes reading the message
* <p>
* <p>A successful completion will have a single value:
* <p>
* <pre>
* z
* </pre>
*/
public void completeMessage()
throws IOException {
int tag = read();
if (tag != 'Z')
error("expected end of message at " + codeName(tag));
}
/**
* Reads a null
* <p>
* <pre>
* N
* </pre>
*/
@Override
public void readNull()
throws IOException {
int tag = read();
switch (tag) {
case 'N':
return;
default:
throw expect("null", tag);
}
}
/**
* Reads a boolean
* <p>
* <pre>
* T
* F
* </pre>
*/
@Override
public boolean readBoolean()
throws IOException {
int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
switch (tag) {
case 'T':
return true;
case 'F':
return false;
// direct integer
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf:
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
return tag != BC_INT_ZERO;
// INT_BYTE = 0
case 0xc8:
return read() != 0;
// INT_BYTE != 0
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
read();
return true;
// INT_SHORT = 0
case 0xd4:
return (256 * read() + read()) != 0;
// INT_SHORT != 0
case 0xd0:
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd5:
case 0xd6:
case 0xd7:
read();
read();
return true;
case 'I':
return
parseInt() != 0;
case 0xd8:
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return tag != BC_LONG_ZERO;
// LONG_BYTE = 0
case 0xf8:
return read() != 0;
// LONG_BYTE != 0
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
read();
return true;
// INT_SHORT = 0
case 0x3c:
return (256 * read() + read()) != 0;
// INT_SHORT != 0
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3d:
case 0x3e:
case 0x3f:
read();
read();
return true;
case BC_LONG_INT:
return (0x1000000L * read()
+ 0x10000L * read()
+ 0x100 * read()
+ read()) != 0;
case 'L':
return parseLong() != 0;
case BC_DOUBLE_ZERO:
return false;
case BC_DOUBLE_ONE:
return true;
case BC_DOUBLE_BYTE:
return read() != 0;
case BC_DOUBLE_SHORT:
return (0x100 * read() + read()) != 0;
case BC_DOUBLE_MILL: {
int mills = parseInt();
return mills != 0;
}
case 'D':
return parseDouble() != 0.0;
case 'N':
return false;
default:
throw expect("boolean", tag);
}
}
/**
* Reads a short
* <p>
* <pre>
* I b32 b24 b16 b8
* </pre>
*/
public short readShort()
throws IOException {
return (short) readInt();
}
/**
* Reads an integer
* <p>
* <pre>
* I b32 b24 b16 b8
* </pre>
*/
@Override
public final int readInt()
throws IOException {
//int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
int tag = read();
switch (tag) {
case 'N':
return 0;
case 'F':
return 0;
case 'T':
return 1;
// direct integer
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf:
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
return tag - BC_INT_ZERO;
/* byte int */
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
return ((tag - BC_INT_BYTE_ZERO) << 8) + read();
/* short int */
case 0xd0:
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd4:
case 0xd5:
case 0xd6:
case 0xd7:
return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read();
case 'I':
case BC_LONG_INT:
return ((read() << 24)
+ (read() << 16)
+ (read() << 8)
+ read());
// direct long
case 0xd8:
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return tag - BC_LONG_ZERO;
/* byte long */
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
return ((tag - BC_LONG_BYTE_ZERO) << 8) + read();
/* short long */
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read();
case 'L':
return (int) parseLong();
case BC_DOUBLE_ZERO:
return 0;
case BC_DOUBLE_ONE:
return 1;
//case LONG_BYTE:
case BC_DOUBLE_BYTE:
return (byte) (_offset < _length ? _buffer[_offset++] : read());
//case INT_SHORT:
//case LONG_SHORT:
case BC_DOUBLE_SHORT:
return (short) (256 * read() + read());
case BC_DOUBLE_MILL: {
int mills = parseInt();
return (int) (0.001 * mills);
}
case 'D':
return (int) parseDouble();
default:
throw expect("integer", tag);
}
}
/**
* Reads a long
* <p>
* <pre>
* L b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
@Override
public long readLong()
throws IOException {
int tag = read();
switch (tag) {
case 'N':
return 0;
case 'F':
return 0;
case 'T':
return 1;
// direct integer
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf:
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
return tag - BC_INT_ZERO;
/* byte int */
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
return ((tag - BC_INT_BYTE_ZERO) << 8) + read();
/* short int */
case 0xd0:
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd4:
case 0xd5:
case 0xd6:
case 0xd7:
return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read();
//case LONG_BYTE:
case BC_DOUBLE_BYTE:
return (byte) (_offset < _length ? _buffer[_offset++] : read());
//case INT_SHORT:
//case LONG_SHORT:
case BC_DOUBLE_SHORT:
return (short) (256 * read() + read());
case 'I':
case BC_LONG_INT:
return parseInt();
// direct long
case 0xd8:
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return tag - BC_LONG_ZERO;
/* byte long */
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
return ((tag - BC_LONG_BYTE_ZERO) << 8) + read();
/* short long */
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read();
case 'L':
return parseLong();
case BC_DOUBLE_ZERO:
return 0;
case BC_DOUBLE_ONE:
return 1;
case BC_DOUBLE_MILL: {
int mills = parseInt();
return (long) (0.001 * mills);
}
case 'D':
return (long) parseDouble();
default:
throw expect("long", tag);
}
}
/**
* Reads a float
* <p>
* <pre>
* D b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
public float readFloat()
throws IOException {
return (float) readDouble();
}
/**
* Reads a double
* <p>
* <pre>
* D b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
@Override
public double readDouble()
throws IOException {
int tag = read();
switch (tag) {
case 'N':
return 0;
case 'F':
return 0;
case 'T':
return 1;
// direct integer
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf:
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
return tag - 0x90;
/* byte int */
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
return ((tag - BC_INT_BYTE_ZERO) << 8) + read();
/* short int */
case 0xd0:
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd4:
case 0xd5:
case 0xd6:
case 0xd7:
return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read();
case 'I':
case BC_LONG_INT:
return parseInt();
// direct long
case 0xd8:
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return tag - BC_LONG_ZERO;
/* byte long */
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
return ((tag - BC_LONG_BYTE_ZERO) << 8) + read();
/* short long */
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read();
case 'L':
return (double) parseLong();
case BC_DOUBLE_ZERO:
return 0;
case BC_DOUBLE_ONE:
return 1;
case BC_DOUBLE_BYTE:
return (byte) (_offset < _length ? _buffer[_offset++] : read());
case BC_DOUBLE_SHORT:
return (short) (256 * read() + read());
case BC_DOUBLE_MILL: {
int mills = parseInt();
return 0.001 * mills;
}
case 'D':
return parseDouble();
default:
throw expect("double", tag);
}
}
/**
* Reads a date.
* <p>
* <pre>
* T b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
@Override
public long readUTCDate()
throws IOException {
int tag = read();
if (tag == BC_DATE) {
return parseLong();
} else if (tag == BC_DATE_MINUTE) {
return parseInt() * 60000L;
} else
throw expect("date", tag);
}
/**
* Reads a byte from the stream.
*/
public int readChar()
throws IOException {
if (_chunkLength > 0) {
_chunkLength--;
if (_chunkLength == 0 && _isLastChunk)
_chunkLength = END_OF_DATA;
int ch = parseUTF8Char();
return ch;
} else if (_chunkLength == END_OF_DATA) {
_chunkLength = 0;
return -1;
}
int tag = read();
switch (tag) {
case 'N':
return -1;
case 'S':
case BC_STRING_CHUNK:
_isLastChunk = tag == 'S';
_chunkLength = (read() << 8) + read();
_chunkLength--;
int value = parseUTF8Char();
// special code so successive read byte won't
// be read as a single object.
if (_chunkLength == 0 && _isLastChunk)
_chunkLength = END_OF_DATA;
return value;
default:
throw expect("char", tag);
}
}
/**
* Reads a byte array from the stream.
*/
public int readString(char[] buffer, int offset, int length)
throws IOException {
int readLength = 0;
if (_chunkLength == END_OF_DATA) {
_chunkLength = 0;
return -1;
} else if (_chunkLength == 0) {
int tag = read();
switch (tag) {
case 'N':
return -1;
case 'S':
case BC_STRING_CHUNK:
_isLastChunk = tag == 'S';
_chunkLength = (read() << 8) + read();
break;
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
_isLastChunk = true;
_chunkLength = tag - 0x00;
break;
default:
throw expect("string", tag);
}
}
while (length > 0) {
if (_chunkLength > 0) {
buffer[offset++] = (char) parseUTF8Char();
_chunkLength--;
length--;
readLength++;
} else if (_isLastChunk) {
if (readLength == 0)
return -1;
else {
_chunkLength = END_OF_DATA;
return readLength;
}
} else {
int tag = read();
switch (tag) {
case 'S':
case BC_STRING_CHUNK:
_isLastChunk = tag == 'S';
_chunkLength = (read() << 8) + read();
break;
default:
throw expect("string", tag);
}
}
}
if (readLength == 0)
return -1;
else if (_chunkLength > 0 || !_isLastChunk)
return readLength;
else {
_chunkLength = END_OF_DATA;
return readLength;
}
}
/**
* Reads a string
* <p>
* <pre>
* S b16 b8 string value
* </pre>
*/
@Override
public String readString()
throws IOException {
int tag = read();
switch (tag) {
case 'N':
return null;
case 'T':
return "true";
case 'F':
return "false";
// direct integer
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf:
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
return String.valueOf((tag - 0x90));
/* byte int */
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
return String.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read());
/* short int */
case 0xd0:
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd4:
case 0xd5:
case 0xd6:
case 0xd7:
return String.valueOf(((tag - BC_INT_SHORT_ZERO) << 16)
+ 256 * read() + read());
case 'I':
case BC_LONG_INT:
return String.valueOf(parseInt());
// direct long
case 0xd8:
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return String.valueOf(tag - BC_LONG_ZERO);
/* byte long */
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
return String.valueOf(((tag - BC_LONG_BYTE_ZERO) << 8) + read());
/* short long */
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
return String.valueOf(((tag - BC_LONG_SHORT_ZERO) << 16)
+ 256 * read() + read());
case 'L':
return String.valueOf(parseLong());
case BC_DOUBLE_ZERO:
return "0.0";
case BC_DOUBLE_ONE:
return "1.0";
case BC_DOUBLE_BYTE:
return String.valueOf((byte) (_offset < _length
? _buffer[_offset++]
: read()));
case BC_DOUBLE_SHORT:
return String.valueOf(((short) (256 * read() + read())));
case BC_DOUBLE_MILL: {
int mills = parseInt();
return String.valueOf(0.001 * mills);
}
case 'D':
return String.valueOf(parseDouble());
case 'S':
case BC_STRING_CHUNK:
_isLastChunk = tag == 'S';
_chunkLength = (read() << 8) + read();
_sbuf.setLength(0);
int ch;
while ((ch = parseChar()) >= 0)
_sbuf.append((char) ch);
return _sbuf.toString();
// 0-byte string
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
_isLastChunk = true;
_chunkLength = tag - 0x00;
_sbuf.setLength(0);
while ((ch = parseChar()) >= 0)
_sbuf.append((char) ch);
return _sbuf.toString();
case 0x30:
case 0x31:
case 0x32:
case 0x33:
_isLastChunk = true;
_chunkLength = (tag - 0x30) * 256 + read();
_sbuf.setLength(0);
while ((ch = parseChar()) >= 0)
_sbuf.append((char) ch);
return _sbuf.toString();
default:
throw expect("string", tag);
}
}
/**
* Reads a byte array
* <p>
* <pre>
* B b16 b8 data value
* </pre>
*/
@Override
public byte[] readBytes()
throws IOException {
int tag = read();
switch (tag) {
case 'N':
return null;
case 'B':
case BC_BINARY_CHUNK:
_isLastChunk = tag == 'B';
_chunkLength = (read() << 8) + read();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int data;
while ((data = parseByte()) >= 0)
bos.write(data);
return bos.toByteArray();
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f: {
_isLastChunk = true;
_chunkLength = tag - 0x20;
byte[] buffer = new byte[_chunkLength];
int k = 0;
while ((data = parseByte()) >= 0)
buffer[k++] = (byte) data;
return buffer;
}
case 0x34:
case 0x35:
case 0x36:
case 0x37: {
_isLastChunk = true;
_chunkLength = (tag - 0x34) * 256 + read();
byte[] buffer = new byte[_chunkLength];
int k = 0;
while ((data = parseByte()) >= 0) {
buffer[k++] = (byte) data;
}
return buffer;
}
default:
throw expect("bytes", tag);
}
}
/**
* Reads a byte from the stream.
*/
public int readByte()
throws IOException {
if (_chunkLength > 0) {
_chunkLength--;
if (_chunkLength == 0 && _isLastChunk)
_chunkLength = END_OF_DATA;
return read();
} else if (_chunkLength == END_OF_DATA) {
_chunkLength = 0;
return -1;
}
int tag = read();
switch (tag) {
case 'N':
return -1;
case 'B':
case BC_BINARY_CHUNK:
_isLastChunk = tag == 'B';
_chunkLength = (read() << 8) + read();
int value = parseByte();
// special code so successive read byte won't
// be read as a single object.
if (_chunkLength == 0 && _isLastChunk)
_chunkLength = END_OF_DATA;
return value;
default:
throw expect("binary", tag);
}
}
/**
* Reads a byte array from the stream.
*/
public int readBytes(byte[] buffer, int offset, int length)
throws IOException {
int readLength = 0;
if (_chunkLength == END_OF_DATA) {
_chunkLength = 0;
return -1;
} else if (_chunkLength == 0) {
int tag = read();
switch (tag) {
case 'N':
return -1;
case 'B':
case BC_BINARY_CHUNK:
_isLastChunk = tag == 'B';
_chunkLength = (read() << 8) + read();
break;
default:
throw expect("binary", tag);
}
}
while (length > 0) {
if (_chunkLength > 0) {
buffer[offset++] = (byte) read();
_chunkLength--;
length--;
readLength++;
} else if (_isLastChunk) {
if (readLength == 0)
return -1;
else {
_chunkLength = END_OF_DATA;
return readLength;
}
} else {
int tag = read();
switch (tag) {
case 'B':
case BC_BINARY_CHUNK:
_isLastChunk = tag == 'B';
_chunkLength = (read() << 8) + read();
break;
default:
throw expect("binary", tag);
}
}
}
if (readLength == 0)
return -1;
else if (_chunkLength > 0 || !_isLastChunk)
return readLength;
else {
_chunkLength = END_OF_DATA;
return readLength;
}
}
/**
* Reads a fault.
*/
private HashMap readFault()
throws IOException {
HashMap map = new HashMap();
int code = read();
for (; code > 0 && code != 'Z'; code = read()) {
_offset--;
Object key = readObject();
Object value = readObject();
if (key != null && value != null)
map.put(key, value);
}
if (code != 'Z')
throw expect("fault", code);
return map;
}
/**
* Reads an object from the input stream with an expected type.
*/
@Override
public Object readObject(Class cl)
throws IOException {
return readObject(cl, null, null);
}
@Override
public Object readObject(Class expectedClass, Class<?>... expectedTypes) throws IOException {
if (expectedClass == null || expectedClass == Object.class)
return readObject();
int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
switch (tag) {
case 'N':
return null;
case 'H': {
Deserializer reader = findSerializerFactory().getDeserializer(expectedClass);
boolean keyValuePair = expectedTypes != null && expectedTypes.length == 2;
// fix deserialize of short type
return reader.readMap(this
, keyValuePair ? expectedTypes[0] : null
, keyValuePair ? expectedTypes[1] : null);
}
case 'M': {
String type = readType();
// hessian/3bb3
if ("".equals(type)) {
Deserializer reader;
reader = findSerializerFactory().getDeserializer(expectedClass);
return reader.readMap(this);
} else {
Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type, expectedClass);
return reader.readMap(this);
}
}
case 'C': {
readObjectDefinition(expectedClass);
return readObject(expectedClass);
}
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f: {
int ref = tag - 0x60;
int size = _classDefs.size();
if (ref < 0 || size <= ref)
throw new HessianProtocolException("'" + ref + "' is an unknown class definition");
ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
return readObjectInstance(expectedClass, def);
}
case 'O': {
int ref = readInt();
int size = _classDefs.size();
if (ref < 0 || size <= ref)
throw new HessianProtocolException("'" + ref + "' is an unknown class definition");
ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
return readObjectInstance(expectedClass, def);
}
case BC_LIST_VARIABLE: {
String type = readType();
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(type, expectedClass);
Object v = reader.readList(this, -1);
return v;
}
case BC_LIST_FIXED: {
String type = readType();
int length = readInt();
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(type, expectedClass);
boolean valueType = expectedTypes != null && expectedTypes.length == 1;
Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
return v;
}
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77: {
int length = tag - 0x70;
String type = readType();
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(null, expectedClass);
boolean valueType = expectedTypes != null && expectedTypes.length == 1;
// fix deserialize of short type
Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
return v;
}
case BC_LIST_VARIABLE_UNTYPED: {
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(null, expectedClass);
boolean valueType = expectedTypes != null && expectedTypes.length == 1;
// fix deserialize of short type
Object v = reader.readList(this, -1, valueType ? expectedTypes[0] : null);
return v;
}
case BC_LIST_FIXED_UNTYPED: {
int length = readInt();
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(null, expectedClass);
boolean valueType = expectedTypes != null && expectedTypes.length == 1;
// fix deserialize of short type
Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
return v;
}
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f: {
int length = tag - 0x78;
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(null, expectedClass);
boolean valueType = expectedTypes != null && expectedTypes.length == 1;
// fix deserialize of short type
Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
return v;
}
case BC_REF: {
int ref = readInt();
return _refs.get(ref);
}
}
if (tag >= 0)
_offset--;
// hessian/3b2i vs hessian/3406
// return readObject();
Object value = findSerializerFactory().getDeserializer(expectedClass).readObject(this);
return value;
}
/**
* Reads an arbitrary object from the input stream when the type
* is unknown.
*/
@Override
public Object readObject()
throws IOException {
return readObject((List<Class<?>>) null);
}
@Override
public Object readObject(List<Class<?>> expectedTypes) throws IOException {
int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
switch (tag) {
case 'N':
return null;
case 'T':
return Boolean.valueOf(true);
case 'F':
return Boolean.valueOf(false);
// direct integer
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf:
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
return Integer.valueOf(tag - BC_INT_ZERO);
/* byte int */
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
return Integer.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read());
/* short int */
case 0xd0:
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd4:
case 0xd5:
case 0xd6:
case 0xd7:
return Integer.valueOf(((tag - BC_INT_SHORT_ZERO) << 16)
+ 256 * read() + read());
case 'I':
return Integer.valueOf(parseInt());
// direct long
case 0xd8:
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return Long.valueOf(tag - BC_LONG_ZERO);
/* byte long */
case 0xf0:
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
return Long.valueOf(((tag - BC_LONG_BYTE_ZERO) << 8) + read());
/* short long */
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
return Long.valueOf(((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read());
case BC_LONG_INT:
return Long.valueOf(parseInt());
case 'L':
return Long.valueOf(parseLong());
case BC_DOUBLE_ZERO:
return Double.valueOf(0);
case BC_DOUBLE_ONE:
return Double.valueOf(1);
case BC_DOUBLE_BYTE:
return Double.valueOf((byte) read());
case BC_DOUBLE_SHORT:
return Double.valueOf((short) (256 * read() + read()));
case BC_DOUBLE_MILL: {
int mills = parseInt();
return Double.valueOf(0.001 * mills);
}
case 'D':
return Double.valueOf(parseDouble());
case BC_DATE:
return new Date(parseLong());
case BC_DATE_MINUTE:
return new Date(parseInt() * 60000L);
case BC_STRING_CHUNK:
case 'S': {
_isLastChunk = tag == 'S';
_chunkLength = (read() << 8) + read();
_sbuf.setLength(0);
parseString(_sbuf);
return _sbuf.toString();
}
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f: {
_isLastChunk = true;
_chunkLength = tag - 0x00;
_sbuf.setLength(0);
parseString(_sbuf);
return _sbuf.toString();
}
case 0x30:
case 0x31:
case 0x32:
case 0x33: {
_isLastChunk = true;
_chunkLength = (tag - 0x30) * 256 + read();
_sbuf.setLength(0);
parseString(_sbuf);
return _sbuf.toString();
}
case BC_BINARY_CHUNK:
case 'B': {
_isLastChunk = tag == 'B';
_chunkLength = (read() << 8) + read();
int data;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((data = parseByte()) >= 0)
bos.write(data);
return bos.toByteArray();
}
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f: {
_isLastChunk = true;
int len = tag - 0x20;
_chunkLength = 0;
byte[] data = new byte[len];
for (int i = 0; i < len; i++)
data[i] = (byte) read();
return data;
}
case 0x34:
case 0x35:
case 0x36:
case 0x37: {
_isLastChunk = true;
int len = (tag - 0x34) * 256 + read();
_chunkLength = 0;
byte[] buffer = new byte[len];
for (int i = 0; i < len; i++) {
buffer[i] = (byte) read();
}
return buffer;
}
case BC_LIST_VARIABLE: {
// variable length list
String type = readType();
return findSerializerFactory().readList(this, -1, type);
}
case BC_LIST_VARIABLE_UNTYPED: {
return findSerializerFactory().readList(this, -1, null);
}
case BC_LIST_FIXED: {
// fixed length lists
String type = readType();
int length = readInt();
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(type, null);
boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
}
case BC_LIST_FIXED_UNTYPED: {
// fixed length lists
int length = readInt();
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(null, null);
boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
}
// compact fixed list
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77: {
// fixed length lists
String type = readType();
int length = tag - 0x70;
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(type, null);
boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
}
// compact fixed untyped list
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f: {
// fixed length lists
int length = tag - 0x78;
Deserializer reader;
reader = findSerializerFactory().getListDeserializer(null, null);
boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
}
case 'H': {
boolean keyValuePair = expectedTypes != null && expectedTypes.size() == 2;
// fix deserialize of short type
Deserializer reader;
reader = findSerializerFactory().getDeserializer(Map.class);
return reader.readMap(this
, keyValuePair ? expectedTypes.get(0) : null
, keyValuePair ? expectedTypes.get(1) : null);
}
case 'M': {
String type = readType();
return findSerializerFactory().readMap(this, type);
}
case 'C': {
readObjectDefinition(null);
return readObject();
}
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f: {
int ref = tag - 0x60;
if (_classDefs == null)
throw error("No classes defined at reference '{0}'" + tag);
ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
return readObjectInstance(null, def);
}
case 'O': {
int ref = readInt();
ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
return readObjectInstance(null, def);
}
case BC_REF: {
int ref = readInt();
return _refs.get(ref);
}
default:
if (tag < 0)
throw new EOFException("readObject: unexpected end of file");
else
throw error("readObject: unknown code " + codeName(tag));
}
}
private void parseString(StringBuilder sbuf)
throws IOException {
while (true) {
if (_chunkLength <= 0) {
if (!parseChunkLength())
return;
}
int length = _chunkLength;
_chunkLength = 0;
while (length-- > 0) {
sbuf.append((char) parseUTF8Char());
}
}
}
/**
* Reads an object definition:
* <p>
* <pre>
* O string <int> (string)* <value>*
* </pre>
*/
private void readObjectDefinition(Class cl)
throws IOException {
String type = readString();
int len = readInt();
String[] fieldNames = new String[len];
for (int i = 0; i < len; i++)
fieldNames[i] = readString();
ObjectDefinition def = new ObjectDefinition(type, fieldNames);
if (_classDefs == null)
_classDefs = new ArrayList();
_classDefs.add(def);
}
private Object readObjectInstance(Class cl, ObjectDefinition def)
throws IOException {
String type = def.getType();
String[] fieldNames = def.getFieldNames();
if (cl != null) {
Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type, cl);
return reader.readObject(this, fieldNames);
} else {
return findSerializerFactory().readObject(this, type, fieldNames);
}
}
private String readLenString()
throws IOException {
int len = readInt();
_isLastChunk = true;
_chunkLength = len;
_sbuf.setLength(0);
int ch;
while ((ch = parseChar()) >= 0)
_sbuf.append((char) ch);
return _sbuf.toString();
}
private String readLenString(int len)
throws IOException {
_isLastChunk = true;
_chunkLength = len;
_sbuf.setLength(0);
int ch;
while ((ch = parseChar()) >= 0)
_sbuf.append((char) ch);
return _sbuf.toString();
}
/**
* Reads a remote object.
*/
@Override
public Object readRemote()
throws IOException {
String type = readType();
String url = readString();
return resolveRemote(type, url);
}
/**
* Reads a reference.
*/
@Override
public Object readRef()
throws IOException {
return _refs.get(parseInt());
}
/**
* Reads the start of a list.
*/
@Override
public int readListStart()
throws IOException {
return read();
}
/**
* Reads the start of a list.
*/
@Override
public int readMapStart()
throws IOException {
return read();
}
/**
* Returns true if this is the end of a list or a map.
*/
@Override
public boolean isEnd()
throws IOException {
int code;
if (_offset < _length)
code = (_buffer[_offset] & 0xff);
else {
code = read();
if (code >= 0)
_offset--;
}
return (code < 0 || code == 'Z');
}
/**
* Reads the end byte.
*/
@Override
public void readEnd()
throws IOException {
int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
if (code == 'Z')
return;
else if (code < 0)
throw error("unexpected end of file");
else
throw error("unknown code:" + codeName(code));
}
/**
* Reads the end byte.
*/
@Override
public void readMapEnd()
throws IOException {
int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
if (code != 'Z')
throw error("expected end of map ('Z') at '" + codeName(code) + "'");
}
/**
* Reads the end byte.
*/
@Override
public void readListEnd()
throws IOException {
int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
if (code != 'Z')
throw error("expected end of list ('Z') at '" + codeName(code) + "'");
}
/**
* Adds a list/map reference.
*/
@Override
public int addRef(Object ref) {
if (_refs == null)
_refs = new ArrayList();
_refs.add(ref);
return _refs.size() - 1;
}
/**
* Adds a list/map reference.
*/
@Override
public void setRef(int i, Object ref) {
_refs.set(i, ref);
}
/**
* Resets the references for streaming.
*/
@Override
public void resetReferences() {
if (_refs != null)
_refs.clear();
}
public Object readStreamingObject()
throws IOException {
if (_refs != null)
_refs.clear();
return readObject();
}
/**
* Resolves a remote object.
*/
public Object resolveRemote(String type, String url)
throws IOException {
HessianRemoteResolver resolver = getRemoteResolver();
if (resolver != null)
return resolver.lookup(type, url);
else
return new HessianRemote(type, url);
}
/**
* Parses a type from the stream.
* <p>
* <pre>
* type ::= string
* type ::= int
* </pre>
*/
@Override
public String readType()
throws IOException {
int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
_offset--;
switch (code) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case BC_STRING_CHUNK:
case 'S': {
String type = readString();
if (_types == null)
_types = new ArrayList();
_types.add(type);
return type;
}
default: {
int ref = readInt();
if (_types.size() <= ref)
throw new IndexOutOfBoundsException("type ref #" + ref + " is greater than the number of valid types (" + _types.size() + ")");
return (String) _types.get(ref);
}
}
}
/**
* Parses the length for an array
* <p>
* <pre>
* l b32 b24 b16 b8
* </pre>
*/
@Override
public int readLength()
throws IOException {
throw new UnsupportedOperationException();
}
/**
* Parses a 32-bit integer value from the stream.
* <p>
* <pre>
* b32 b24 b16 b8
* </pre>
*/
private int parseInt()
throws IOException {
int offset = _offset;
if (offset + 3 < _length) {
byte[] buffer = _buffer;
int b32 = buffer[offset + 0] & 0xff;
int b24 = buffer[offset + 1] & 0xff;
int b16 = buffer[offset + 2] & 0xff;
int b8 = buffer[offset + 3] & 0xff;
_offset = offset + 4;
return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8;
} else {
int b32 = read();
int b24 = read();
int b16 = read();
int b8 = read();
return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8;
}
}
/**
* Parses a 64-bit long value from the stream.
* <p>
* <pre>
* b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
private long parseLong()
throws IOException {
long b64 = read();
long b56 = read();
long b48 = read();
long b40 = read();
long b32 = read();
long b24 = read();
long b16 = read();
long b8 = read();
return ((b64 << 56)
+ (b56 << 48)
+ (b48 << 40)
+ (b40 << 32)
+ (b32 << 24)
+ (b24 << 16)
+ (b16 << 8)
+ b8);
}
/**
* Parses a 64-bit double value from the stream.
* <p>
* <pre>
* b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
private double parseDouble()
throws IOException {
long bits = parseLong();
return Double.longBitsToDouble(bits);
}
org.w3c.dom.Node parseXML()
throws IOException {
throw new UnsupportedOperationException();
}
private boolean parseChunkLength()
throws IOException {
if (_isLastChunk)
return false;
int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
switch (code) {
case BC_STRING_CHUNK:
_isLastChunk = false;
_chunkLength = (read() << 8) + read();
break;
case 'S':
_isLastChunk = true;
_chunkLength = (read() << 8) + read();
break;
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
_isLastChunk = true;
_chunkLength = code - 0x00;
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
_isLastChunk = true;
_chunkLength = (code - 0x30) * 256 + read();
break;
default:
throw expect("string", code);
}
return true;
}
/**
* Reads a character from the underlying stream.
*/
private int parseChar()
throws IOException {
while (_chunkLength <= 0) {
if (!parseChunkLength())
return -1;
}
_chunkLength--;
return parseUTF8Char();
}
/**
* Parses a single UTF8 character.
*/
private int parseUTF8Char()
throws IOException {
int ch = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
if (ch < 0x80)
return ch;
else if ((ch & 0xe0) == 0xc0) {
int ch1 = read();
int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f);
return v;
} else if ((ch & 0xf0) == 0xe0) {
int ch1 = read();
int ch2 = read();
int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f);
return v;
} else
throw error("bad utf-8 encoding at " + codeName(ch));
}
/**
* Reads a byte from the underlying stream.
*/
private int parseByte()
throws IOException {
while (_chunkLength <= 0) {
if (_isLastChunk) {
return -1;
}
int code = read();
switch (code) {
case BC_BINARY_CHUNK:
_isLastChunk = false;
_chunkLength = (read() << 8) + read();
break;
case 'B':
_isLastChunk = true;
_chunkLength = (read() << 8) + read();
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
_isLastChunk = true;
_chunkLength = code - 0x20;
break;
case 0x34:
case 0x35:
case 0x36:
case 0x37:
_isLastChunk = true;
_chunkLength = (code - 0x34) * 256 + read();
break;
default:
throw expect("byte[]", code);
}
}
_chunkLength--;
return read();
}
/**
* Reads bytes based on an input stream.
*/
@Override
public InputStream readInputStream()
throws IOException {
int tag = read();
switch (tag) {
case 'N':
return null;
case 'B':
case 'b':
_isLastChunk = tag == 'B';
_chunkLength = (read() << 8) + read();
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
_isLastChunk = true;
_chunkLength = tag - 0x20;
break;
default:
throw expect("binary", tag);
}
return new ReadInputStream();
}
/**
* Reads bytes from the underlying stream.
*/
int read(byte[] buffer, int offset, int length)
throws IOException {
int readLength = 0;
while (length > 0) {
while (_chunkLength <= 0) {
if (_isLastChunk)
return readLength == 0 ? -1 : readLength;
int code = read();
switch (code) {
case 'b':
_isLastChunk = false;
_chunkLength = (read() << 8) + read();
break;
case 'B':
_isLastChunk = true;
_chunkLength = (read() << 8) + read();
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
_isLastChunk = true;
_chunkLength = code - 0x20;
break;
default:
throw expect("byte[]", code);
}
}
int sublen = _chunkLength;
if (length < sublen)
sublen = length;
if (_length <= _offset && !readBuffer())
return -1;
if (_length - _offset < sublen)
sublen = _length - _offset;
System.arraycopy(_buffer, _offset, buffer, offset, sublen);
_offset += sublen;
offset += sublen;
readLength += sublen;
length -= sublen;
_chunkLength -= sublen;
}
return readLength;
}
/**
* Normally, shouldn't be called externally, but needed for QA, e.g.
* ejb/3b01.
*/
public final int read()
throws IOException {
if (_length <= _offset && !readBuffer())
return -1;
return _buffer[_offset++] & 0xff;
}
private final boolean readBuffer()
throws IOException {
byte[] buffer = _buffer;
int offset = _offset;
int length = _length;
if (offset < length) {
System.arraycopy(buffer, offset, buffer, 0, length - offset);
offset = length - offset;
} else
offset = 0;
int len = _is.read(buffer, offset, SIZE - offset);
if (len <= 0) {
_length = offset;
_offset = 0;
return offset > 0;
}
_length = offset + len;
_offset = 0;
return true;
}
@Override
public Reader getReader() {
return null;
}
protected IOException expect(String expect, int ch)
throws IOException {
if (ch < 0)
return error("expected " + expect + " at end of file");
else {
_offset--;
try {
Object obj = readObject();
if (obj != null) {
return error("expected " + expect
+ " at 0x" + Integer.toHexString(ch & 0xff)
+ " " + obj.getClass().getName() + " (" + obj + ")");
} else
return error("expected " + expect
+ " at 0x" + Integer.toHexString(ch & 0xff) + " null");
} catch (IOException e) {
log.log(Level.FINE, e.toString(), e);
return error("expected " + expect
+ " at 0x" + Integer.toHexString(ch & 0xff));
}
}
}
protected String codeName(int ch) {
if (ch < 0)
return "end of file";
else
return "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) +ch + ")";
}
protected IOException error(String message) {
if (_method != null)
return new HessianProtocolException(_method + ": " + message);
else
return new HessianProtocolException(message);
}
@Override
public void close()
throws IOException {
InputStream is = _is;
_is = null;
if (_isCloseStreamOnClose && is != null)
is.close();
}
;
final static class ObjectDefinition {
private final String _type;
private final String[] _fields;
ObjectDefinition(String type, String[] fields) {
_type = type;
_fields = fields;
}
String getType() {
return _type;
}
String[] getFieldNames() {
return _fields;
}
}
class ReadInputStream extends InputStream {
boolean _isClosed = false;
@Override
public int read()
throws IOException {
if (_isClosed)
return -1;
int ch = parseByte();
if (ch < 0)
_isClosed = true;
return ch;
}
@Override
public int read(byte[] buffer, int offset, int length)
throws IOException {
if (_isClosed)
return -1;
int len = Hessian2Input.this.read(buffer, offset, length);
if (len < 0)
_isClosed = true;
return len;
}
@Override
public void close()
throws IOException {
while (read() >= 0) {
}
}
}
}