| /* |
| * Copyright (c) 2001-2004 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.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.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * 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 HessianInput extends AbstractHessianInput { |
| private static int END_OF_DATA = -2; |
| |
| private static Field _detailMessageField; |
| |
| static { |
| try { |
| _detailMessageField = Throwable.class.getDeclaredField("detailMessage"); |
| _detailMessageField.setAccessible(true); |
| } catch (Throwable e) { |
| } |
| } |
| |
| // factory for deserializing objects in the input stream |
| protected SerializerFactory _serializerFactory; |
| protected ArrayList _refs; |
| // a peek character |
| protected int _peek = -1; |
| // the underlying input stream |
| private InputStream _is; |
| // the method for a call |
| private String _method; |
| private Reader _chunkReader; |
| private InputStream _chunkInputStream; |
| private Throwable _replyFault; |
| private StringBuffer _sbuf = new StringBuffer(); |
| // true if this is the last chunk |
| private boolean _isLastChunk; |
| // the chunk length |
| private int _chunkLength; |
| |
| /** |
| * Creates an uninitialized Hessian input stream. |
| */ |
| public HessianInput() { |
| } |
| |
| /** |
| * Creates a new Hessian input stream, initialized with an |
| * underlying input stream. |
| * |
| * @param is the underlying input stream. |
| */ |
| public HessianInput(InputStream is) { |
| init(is); |
| } |
| |
| /** |
| * Gets the serializer factory. |
| */ |
| public SerializerFactory getSerializerFactory() { |
| return _serializerFactory; |
| } |
| |
| /** |
| * Sets the serializer factory. |
| */ |
| @Override |
| public void setSerializerFactory(SerializerFactory factory) { |
| _serializerFactory = factory; |
| } |
| |
| /** |
| * Initialize the hessian stream with the underlying input stream. |
| */ |
| @Override |
| public void init(InputStream is) { |
| _is = is; |
| _method = null; |
| _isLastChunk = true; |
| _chunkLength = 0; |
| _peek = -1; |
| _refs = null; |
| _replyFault = null; |
| |
| if (_serializerFactory == null) |
| _serializerFactory = new SerializerFactory(); |
| } |
| |
| /** |
| * 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)); |
| |
| int major = read(); |
| int minor = read(); |
| |
| return (major << 16) + minor; |
| } |
| |
| /** |
| * For backward compatibility with HessianSkeleton |
| */ |
| @Override |
| public void skipOptionalCall() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag == 'c') { |
| read(); |
| read(); |
| } else |
| _peek = tag; |
| } |
| |
| /** |
| * Starts reading the call |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * m b16 b8 method |
| * </pre> |
| */ |
| @Override |
| public String readMethod() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag != 'm') |
| throw error("expected hessian method ('m') at " + codeName(tag)); |
| int d1 = read(); |
| int d2 = read(); |
| |
| _isLastChunk = true; |
| _chunkLength = d1 * 256 + d2; |
| _sbuf.setLength(0); |
| int ch; |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| _method = _sbuf.toString(); |
| |
| return _method; |
| } |
| |
| /** |
| * 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(); |
| |
| while (readHeader() != null) { |
| readObject(); |
| } |
| |
| readMethod(); |
| } |
| |
| /** |
| * Completes reading the call |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * z |
| * </pre> |
| */ |
| @Override |
| public void completeCall() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag == 'z') { |
| } else |
| throw error("expected end of call ('z') at " + codeName(tag) + ". Check method arguments and ensure method overloading is enabled if necessary"); |
| } |
| |
| /** |
| * 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') |
| error("expected hessian reply at " + codeName(tag)); |
| |
| int major = read(); |
| int minor = read(); |
| |
| tag = read(); |
| if (tag == 'f') |
| throw prepareFault(); |
| else { |
| _peek = tag; |
| |
| Object value = readObject(expectedClass); |
| |
| completeValueReply(); |
| |
| return value; |
| } |
| } |
| |
| /** |
| * Starts reading the reply |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * r |
| * </pre> |
| */ |
| @Override |
| public void startReply() |
| throws Throwable { |
| int tag = read(); |
| |
| if (tag != 'r') |
| error("expected hessian reply at " + codeName(tag)); |
| |
| int major = read(); |
| int minor = read(); |
| |
| tag = read(); |
| if (tag == 'f') |
| throw prepareFault(); |
| else |
| _peek = tag; |
| } |
| |
| /** |
| * Prepares the fault. |
| */ |
| private Throwable prepareFault() |
| throws IOException { |
| HashMap fault = readFault(); |
| |
| 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 { |
| int tag = read(); |
| |
| if (tag != 'z') |
| error("expected end of reply at " + codeName(tag)); |
| } |
| |
| /** |
| * 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 { |
| int tag = read(); |
| |
| if (tag == 'H') { |
| _isLastChunk = true; |
| _chunkLength = (read() << 8) + read(); |
| |
| _sbuf.setLength(0); |
| int ch; |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| } |
| |
| _peek = tag; |
| |
| return null; |
| } |
| |
| /** |
| * 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 byte |
| * |
| * <pre> |
| * I b32 b24 b16 b8 |
| * </pre> |
| */ |
| /* |
| public byte readByte() |
| throws IOException |
| { |
| return (byte) readInt(); |
| } |
| */ |
| |
| /** |
| * Reads a boolean |
| * <p> |
| * <pre> |
| * T |
| * F |
| * </pre> |
| */ |
| @Override |
| public boolean readBoolean() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'T': |
| return true; |
| case 'F': |
| return false; |
| case 'I': |
| return parseInt() == 0; |
| case 'L': |
| return parseLong() == 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 int readInt() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'T': |
| return 1; |
| case 'F': |
| return 0; |
| case 'I': |
| return parseInt(); |
| case 'L': |
| return (int) parseLong(); |
| case 'D': |
| return (int) parseDouble(); |
| |
| default: |
| throw expect("int", 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 'T': |
| return 1; |
| case 'F': |
| return 0; |
| case 'I': |
| return parseInt(); |
| case 'L': |
| return parseLong(); |
| 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 'T': |
| return 1; |
| case 'F': |
| return 0; |
| case 'I': |
| return parseInt(); |
| case 'L': |
| return (double) parseLong(); |
| case 'D': |
| return parseDouble(); |
| |
| default: |
| throw expect("long", 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 != 'd') |
| throw error("expected date at " + codeName(tag)); |
| |
| 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); |
| } |
| |
| /** |
| * 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 's': |
| case 'X': |
| case 'x': |
| _isLastChunk = tag == 'S' || tag == 'X'; |
| _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 new IOException("expected 'S' at " + (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 's': |
| case 'X': |
| case 'x': |
| _isLastChunk = tag == 'S' || tag == 'X'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw new IOException("expected 'S' at " + (char) 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 's': |
| case 'X': |
| case 'x': |
| _isLastChunk = tag == 'S' || tag == 'X'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw new IOException("expected 'S' at " + (char) 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 'I': |
| return String.valueOf(parseInt()); |
| case 'L': |
| return String.valueOf(parseLong()); |
| case 'D': |
| return String.valueOf(parseDouble()); |
| |
| case 'S': |
| case 's': |
| case 'X': |
| case 'x': |
| _isLastChunk = tag == 'S' || tag == 'X'; |
| _chunkLength = (read() << 8) + read(); |
| |
| _sbuf.setLength(0); |
| int ch; |
| |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| |
| default: |
| throw expect("string", tag); |
| } |
| } |
| |
| /** |
| * Reads an XML node. |
| * <p> |
| * <pre> |
| * S b16 b8 string value |
| * </pre> |
| */ |
| @Override |
| public org.w3c.dom.Node readNode() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case 'S': |
| case 's': |
| case 'X': |
| case 'x': |
| _isLastChunk = tag == 'S' || tag == 'X'; |
| _chunkLength = (read() << 8) + read(); |
| |
| throw error("Can't handle string in this context"); |
| |
| 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 'b': |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| |
| int data; |
| while ((data = parseByte()) >= 0) |
| bos.write(data); |
| |
| return bos.toByteArray(); |
| |
| 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 'b': |
| _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 new IOException("expected 'B' at " + (char) 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 'b': |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw new IOException("expected 'B' at " + (char) 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 'b': |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw new IOException("expected 'B' at " + (char) 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()) { |
| _peek = code; |
| |
| 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); |
| } |
| |
| /** |
| * Reads an object from the input stream with an expected type. |
| */ |
| public Object readObject(Class expectedClass, Class<?>... expectedTypes) |
| throws IOException { |
| if (expectedClass == null || expectedClass == Object.class) |
| return readObject(); |
| |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case 'M': { |
| String type = readType(); |
| |
| boolean keyValuePair = expectedTypes != null && expectedTypes.length == 2; |
| |
| // hessian/3386 |
| if ("".equals(type)) { |
| Deserializer reader; |
| reader = _serializerFactory.getDeserializer(expectedClass); |
| |
| return reader.readMap(this |
| , keyValuePair ? expectedTypes[0] : null |
| , keyValuePair ? expectedTypes[1] : null); |
| } else { |
| Deserializer reader; |
| reader = _serializerFactory.getObjectDeserializer(type, expectedClass); |
| |
| return reader.readMap(this |
| , keyValuePair ? expectedTypes[0] : null |
| , keyValuePair ? expectedTypes[1] : null); |
| } |
| } |
| |
| case 'V': { |
| String type = readType(); |
| int length = readLength(); |
| |
| Deserializer reader; |
| reader = _serializerFactory.getObjectDeserializer(type); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.length == 1; |
| |
| if (expectedClass != reader.getType() && expectedClass.isAssignableFrom(reader.getType())) |
| return reader.readList(this, length, valueType ? expectedTypes[0] : null); |
| |
| reader = _serializerFactory.getDeserializer(expectedClass); |
| |
| Object v = reader.readList(this, length, valueType ? expectedTypes[0] : null); |
| |
| return v; |
| } |
| |
| case 'R': { |
| int ref = parseInt(); |
| |
| return _refs.get(ref); |
| } |
| |
| case 'r': { |
| String type = readType(); |
| String url = readString(); |
| |
| return resolveRemote(type, url); |
| } |
| } |
| |
| _peek = tag; |
| |
| // hessian/332i vs hessian/3406 |
| //return readObject(); |
| |
| Object value = _serializerFactory.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); |
| } |
| |
| /** |
| * Reads an arbitrary object from the input stream when the type |
| * is unknown. |
| */ |
| public Object readObject(List<Class<?>> expectedTypes) |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case 'T': |
| return Boolean.valueOf(true); |
| |
| case 'F': |
| return Boolean.valueOf(false); |
| |
| case 'I': |
| return Integer.valueOf(parseInt()); |
| |
| case 'L': |
| return Long.valueOf(parseLong()); |
| |
| case 'D': |
| return Double.valueOf(parseDouble()); |
| |
| case 'd': |
| return new Date(parseLong()); |
| |
| case 'x': |
| case 'X': { |
| _isLastChunk = tag == 'X'; |
| _chunkLength = (read() << 8) + read(); |
| |
| return parseXML(); |
| } |
| |
| case 's': |
| case 'S': { |
| _isLastChunk = tag == 'S'; |
| _chunkLength = (read() << 8) + read(); |
| |
| int data; |
| _sbuf.setLength(0); |
| |
| while ((data = parseChar()) >= 0) |
| _sbuf.append((char) data); |
| |
| return _sbuf.toString(); |
| } |
| |
| case 'b': |
| 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 'V': { |
| String type = readType(); |
| int length = readLength(); |
| |
| Deserializer reader; |
| reader = _serializerFactory.getObjectDeserializer(type); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.size() == 1; |
| |
| if (List.class != reader.getType() && List.class.isAssignableFrom(reader.getType())) |
| return reader.readList(this, length, valueType ? expectedTypes.get(0) : null); |
| Class clazz = type.equals(HashSet.class.getName()) ? Set.class : List.class; |
| reader = _serializerFactory.getDeserializer(clazz); |
| |
| Object v = reader.readList(this, length, valueType ? expectedTypes.get(0) : null); |
| |
| return v; |
| } |
| |
| case 'M': { |
| String type = readType(); |
| |
| boolean keyValuePair = expectedTypes != null && expectedTypes.size() == 2; |
| |
| return _serializerFactory.readMap(this, type |
| , keyValuePair ? expectedTypes.get(0) : null |
| , keyValuePair ? expectedTypes.get(1) : null); |
| } |
| |
| case 'R': { |
| int ref = parseInt(); |
| |
| return _refs.get(ref); |
| } |
| |
| case 'r': { |
| String type = readType(); |
| String url = readString(); |
| |
| return resolveRemote(type, url); |
| } |
| |
| default: |
| throw error("unknown code for readObject at " + codeName(tag)); |
| } |
| } |
| |
| /** |
| * 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 = read(); |
| |
| _peek = code; |
| |
| return (code < 0 || code == 'z'); |
| } |
| |
| /** |
| * Reads the end byte. |
| */ |
| @Override |
| public void readEnd() |
| throws IOException { |
| int code = read(); |
| |
| if (code != 'z') |
| throw error("unknown code at " + codeName(code)); |
| } |
| |
| /** |
| * Reads the end byte. |
| */ |
| @Override |
| public void readMapEnd() |
| throws IOException { |
| int code = 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 = 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(); |
| } |
| |
| /** |
| * 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> |
| * t b16 b8 |
| * </pre> |
| */ |
| @Override |
| public String readType() |
| throws IOException { |
| int code = read(); |
| |
| if (code != 't') { |
| _peek = code; |
| return ""; |
| } |
| |
| _isLastChunk = true; |
| _chunkLength = (read() << 8) + read(); |
| |
| _sbuf.setLength(0); |
| int ch; |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| } |
| |
| /** |
| * Parses the length for an array |
| * <p> |
| * <pre> |
| * l b32 b24 b16 b8 |
| * </pre> |
| */ |
| @Override |
| public int readLength() |
| throws IOException { |
| int code = read(); |
| |
| if (code != 'l') { |
| _peek = code; |
| return -1; |
| } |
| |
| return parseInt(); |
| } |
| |
| /** |
| * Parses a 32-bit integer value from the stream. |
| * <p> |
| * <pre> |
| * b32 b24 b16 b8 |
| * </pre> |
| */ |
| private int parseInt() |
| throws IOException { |
| 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 b64 = read(); |
| long b56 = read(); |
| long b48 = read(); |
| long b40 = read(); |
| long b32 = read(); |
| long b24 = read(); |
| long b16 = read(); |
| long b8 = read(); |
| |
| long bits = ((b64 << 56) + |
| (b56 << 48) + |
| (b48 << 40) + |
| (b40 << 32) + |
| (b32 << 24) + |
| (b24 << 16) + |
| (b16 << 8) + |
| b8); |
| |
| return Double.longBitsToDouble(bits); |
| } |
| |
| org.w3c.dom.Node parseXML() |
| throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Reads a character from the underlying stream. |
| */ |
| private int parseChar() |
| throws IOException { |
| while (_chunkLength <= 0) { |
| if (_isLastChunk) |
| return -1; |
| |
| int code = read(); |
| |
| switch (code) { |
| case 's': |
| case 'x': |
| _isLastChunk = false; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 'S': |
| case 'X': |
| _isLastChunk = true; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw expect("string", code); |
| } |
| |
| } |
| |
| _chunkLength--; |
| |
| return parseUTF8Char(); |
| } |
| |
| /** |
| * Parses a single UTF8 character. |
| */ |
| private int parseUTF8Char() |
| throws IOException { |
| int ch = 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 'b': |
| _isLastChunk = false; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 'B': |
| _isLastChunk = true; |
| |
| _chunkLength = (read() << 8) + 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; |
| |
| default: |
| throw expect("inputStream", tag); |
| } |
| |
| return new InputStream() { |
| boolean _isClosed = false; |
| |
| @Override |
| public int read() |
| throws IOException { |
| if (_isClosed || _is == null) |
| 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 || _is == null) |
| return -1; |
| |
| int len = HessianInput.this.read(buffer, offset, length); |
| if (len < 0) |
| _isClosed = true; |
| |
| return len; |
| } |
| |
| @Override |
| public void close() |
| throws IOException { |
| while (read() >= 0) { |
| } |
| |
| _isClosed = true; |
| } |
| }; |
| } |
| |
| /** |
| * 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; |
| |
| default: |
| throw expect("byte[]", code); |
| } |
| } |
| |
| int sublen = _chunkLength; |
| if (length < sublen) |
| sublen = length; |
| |
| sublen = _is.read(buffer, offset, sublen); |
| offset += sublen; |
| readLength += sublen; |
| length -= sublen; |
| _chunkLength -= sublen; |
| } |
| |
| return readLength; |
| } |
| |
| final int read() |
| throws IOException { |
| if (_peek >= 0) { |
| int value = _peek; |
| _peek = -1; |
| return value; |
| } |
| |
| int ch = _is.read(); |
| |
| return ch; |
| } |
| |
| @Override |
| public void close() { |
| _is = null; |
| } |
| |
| @Override |
| public Reader getReader() { |
| return null; |
| } |
| |
| protected IOException expect(String expect, int ch) { |
| return error("expected " + expect + " at " + codeName(ch)); |
| } |
| |
| 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); |
| } |
| } |