Merge pull request #69 from AlbumenJ/merge/hessian4

Merge hessian4 into hessian-lite
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 43cd92a..f870703 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -19,15 +19,11 @@
 
     steps:
     - uses: actions/checkout@v3
-    - name: Set up JDK 11
+    - name: Set up JDK 8
       uses: actions/setup-java@v3
       with:
-        java-version: '11'
+        java-version: '8'
         distribution: 'temurin'
         cache: maven
     - name: Build with Maven
       run: mvn -B package --file pom.xml
-
-    # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
-    - name: Update dependency graph
-      uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
diff --git a/NOTICE b/NOTICE
index 1e0f705..2744a49 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 Apache Dubbo
-Copyright 2021 The Apache Software Foundation
+Copyright 2018-2024 The Apache Software Foundation
 
 This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
\ No newline at end of file
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/pom.xml b/pom.xml
index eba1164..74df34a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,15 +19,15 @@
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
-        <groupId>org.sonatype.oss</groupId>
-        <artifactId>oss-parent</artifactId>
-        <version>7</version>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>31</version>
     </parent>
 
-    <groupId>com.alibaba</groupId>
+    <groupId>org.apache.dubbo</groupId>
     <artifactId>hessian-lite</artifactId>
     <packaging>jar</packaging>
-    <version>3.2.13</version>
+    <version>4.0.0-SNAPSHOT</version>
     <name>Hessian Lite(Dubbo embed version)</name>
 
     <properties>
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/HessianUnshared.java
similarity index 78%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/HessianUnshared.java
index 72a9822..88377c8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/HessianUnshared.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,20 +46,22 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
+package com.alibaba.com.caucho.hessian;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Marks the class and all children as unshared.
+ * A reference item will be created, but will not be stored in a
+ * mapped or checked for duplicated.
+ * <p>
+ * Used for efficiency to avoid the cost of the map lookup.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
-    }
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HessianUnshared {
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializer.java
index 042d14e..eabdbf9 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializer.java
@@ -53,13 +53,17 @@
 /**
  * Deserializing an object.
  */
-abstract public class AbstractDeserializer implements Deserializer {
-    @Override
-    public Class getType() {
+public class AbstractDeserializer implements Deserializer {
+    public static final NullDeserializer NULL = new NullDeserializer();
+
+    public Class<?> getType() {
         return Object.class;
     }
 
-    @Override
+    public boolean isReadResolve() {
+        return false;
+    }
+
     public Object readObject(AbstractHessianInput in)
             throws IOException {
         Object obj = in.readObject();
@@ -72,35 +76,16 @@
             throw error(className + ": unexpected null value");
     }
 
-    @Override
     public Object readList(AbstractHessianInput in, int length)
             throws IOException {
         throw new UnsupportedOperationException(String.valueOf(this));
     }
 
-    @Override
-    public Object readList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
-        if (expectType == null) {
-            return readList(in, length);
-        }
-        throw new UnsupportedOperationException(String.valueOf(this));
-    }
-
-    @Override
     public Object readLengthList(AbstractHessianInput in, int length)
             throws IOException {
         throw new UnsupportedOperationException(String.valueOf(this));
     }
 
-    @Override
-    public Object readLengthList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
-        if (expectType == null) {
-            return readLengthList(in, length);
-        }
-        throw new UnsupportedOperationException(String.valueOf(this));
-    }
-
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         Object obj = in.readObject();
@@ -113,18 +98,43 @@
             throw error(className + ": unexpected null value");
     }
 
-    @Override
-    public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {
-        if (expectKeyType == null && expectValueType == null) {
-            return readMap(in);
-        }
-        throw new UnsupportedOperationException(String.valueOf(this));
+    /**
+     * Creates the field array for a class. The default
+     * implementation returns a String[] array.
+     *
+     * @param len number of items in the array
+     * @return the new empty array
+     */
+    public Object[] createFields(int len) {
+        return new String[len];
+    }
+
+    /**
+     * Creates a field value class. The default
+     * implementation returns the String.
+     *
+     * @param len number of items in the array
+     * @return the new empty array
+     */
+    public Object createField(String name) {
+        return name;
     }
 
     @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object readObject(AbstractHessianInput in,
+                             String[] fieldNames)
             throws IOException {
-        throw new UnsupportedOperationException(String.valueOf(this));
+        return readObject(in, (Object[]) fieldNames);
+    }
+
+    /**
+     * Reads an object instance from the input stream
+     */
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
+            throws IOException {
+        throw new UnsupportedOperationException(toString());
     }
 
     protected HessianProtocolException error(String msg) {
@@ -138,13 +148,10 @@
             return "0x" + Integer.toHexString(ch & 0xff);
     }
 
-    protected SerializerFactory findSerializerFactory(AbstractHessianInput in) {
-        SerializerFactory serializerFactory = null;
-        if (in instanceof Hessian2Input) {
-            serializerFactory = ((Hessian2Input) in).findSerializerFactory();
-        } else if (in instanceof HessianInput) {
-            serializerFactory = ((HessianInput) in).getSerializerFactory();
-        }
-        return serializerFactory == null ? new SerializerFactory() : serializerFactory;
+    /**
+     * The NullDeserializer exists as a marker for the factory classes so
+     * they save a null result.
+     */
+    static final class NullDeserializer extends AbstractDeserializer {
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializerWrapper.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializerWrapper.java
new file mode 100644
index 0000000..f38977e
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractDeserializerWrapper.java
@@ -0,0 +1,133 @@
+/*
+ * 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 "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.alibaba.com.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Deserializing an object.
+ */
+abstract public class AbstractDeserializerWrapper implements Deserializer {
+    abstract protected Deserializer getDelegate();
+
+    @Override
+    public Class<?> getType() {
+        return getDelegate().getType();
+    }
+
+    @Override
+    public boolean isReadResolve() {
+        return getDelegate().isReadResolve();
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in)
+            throws IOException {
+        return getDelegate().readObject(in);
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length)
+            throws IOException {
+        return getDelegate().readList(in, length);
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length)
+            throws IOException {
+        return getDelegate().readLengthList(in, length);
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        return getDelegate().readMap(in);
+    }
+
+    /**
+     * Creates the field array for a class. The default
+     * implementation returns a String[] array.
+     *
+     * @param len number of items in the array
+     * @return the new empty array
+     */
+    @Override
+    public Object[] createFields(int len) {
+        return getDelegate().createFields(len);
+    }
+
+    /**
+     * Creates a field value class. The default
+     * implementation returns the String.
+     *
+     * @param len number of items in the array
+     * @return the new empty array
+     */
+    @Override
+    public Object createField(String name) {
+        return getDelegate().createField(name);
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             String[] fieldNames)
+            throws IOException {
+        return getDelegate().readObject(in, fieldNames);
+    }
+
+    /**
+     * Reads an object instance from the input stream
+     */
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
+            throws IOException {
+        return getDelegate().readObject(in, fields);
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianInput.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianInput.java
index db0e574..ebb1539 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianInput.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianInput.java
@@ -50,13 +50,13 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.Reader;
-import java.util.List;
 
 /**
  * Abstract base class for Hessian requests.  Hessian users should only
  * need to use the methods in this class.
- * <p>
+ *
  * <pre>
  * AbstractHessianInput in = ...; // get input
  * String value;
@@ -68,6 +68,7 @@
  */
 abstract public class AbstractHessianInput {
     private HessianRemoteResolver resolver;
+    private byte[] _buffer;
 
     /**
      * Initialize the Hessian stream with the underlying input stream.
@@ -100,11 +101,9 @@
     public void setSerializerFactory(SerializerFactory ser) {
     }
 
-    public abstract boolean checkAndReadNull();
-
     /**
      * Reads the call
-     * <p>
+     *
      * <pre>
      * c major minor
      * </pre>
@@ -121,7 +120,7 @@
 
     /**
      * Reads a header, returning null if there are no headers.
-     * <p>
+     *
      * <pre>
      * H b16 b8 value
      * </pre>
@@ -131,9 +130,9 @@
 
     /**
      * Starts reading the call
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * m b16 b8 method
      * </pre>
@@ -153,9 +152,9 @@
 
     /**
      * 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
@@ -166,9 +165,9 @@
 
     /**
      * Completes reading the call
-     * <p>
+     *
      * <p>The call expects the following protocol data
-     * <p>
+     *
      * <pre>
      * Z
      * </pre>
@@ -185,9 +184,9 @@
 
     /**
      * Starts reading the reply
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * r
      * v
@@ -197,10 +196,18 @@
             throws Throwable;
 
     /**
+     * Starts reading the body of the reply, i.e. after the 'r' has been
+     * parsed.
+     */
+    public void startReplyBody()
+            throws Throwable {
+    }
+
+    /**
      * Completes reading the call
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * z
      * </pre>
@@ -210,7 +217,7 @@
 
     /**
      * Reads a boolean
-     * <p>
+     *
      * <pre>
      * T
      * F
@@ -221,7 +228,7 @@
 
     /**
      * Reads a null
-     * <p>
+     *
      * <pre>
      * N
      * </pre>
@@ -231,7 +238,7 @@
 
     /**
      * Reads an integer
-     * <p>
+     *
      * <pre>
      * I b32 b24 b16 b8
      * </pre>
@@ -241,7 +248,7 @@
 
     /**
      * Reads a long
-     * <p>
+     *
      * <pre>
      * L b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
@@ -251,7 +258,7 @@
 
     /**
      * Reads a double.
-     * <p>
+     *
      * <pre>
      * D b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
@@ -261,7 +268,7 @@
 
     /**
      * Reads a date.
-     * <p>
+     *
      * <pre>
      * T b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
@@ -271,7 +278,7 @@
 
     /**
      * Reads a string encoded in UTF-8
-     * <p>
+     *
      * <pre>
      * s b16 b8 non-final string chunk
      * S b16 b8 final string chunk
@@ -282,7 +289,7 @@
 
     /**
      * Reads an XML node encoded in UTF-8
-     * <p>
+     *
      * <pre>
      * x b16 b8 non-final xml chunk
      * X b16 b8 final xml chunk
@@ -297,7 +304,7 @@
      * Starts reading a string.  All the characters must be read before
      * calling the next method.  The actual characters will be read with
      * the reader's read() or read(char [], int, int).
-     * <p>
+     *
      * <pre>
      * s b16 b8 non-final string chunk
      * S b16 b8 final string chunk
@@ -309,7 +316,7 @@
     /**
      * Starts reading a byte array using an input stream.  All the bytes
      * must be read before calling the following method.
-     * <p>
+     *
      * <pre>
      * b b16 b8 non-final binary chunk
      * B b16 b8 final binary chunk
@@ -319,8 +326,40 @@
             throws IOException;
 
     /**
+     * Reads data to an output stream.
+     *
+     * <pre>
+     * b b16 b8 non-final binary chunk
+     * B b16 b8 final binary chunk
+     * </pre>
+     */
+    public boolean readToOutputStream(OutputStream os)
+            throws IOException {
+        InputStream is = readInputStream();
+
+        if (is == null)
+            return false;
+
+        if (_buffer == null)
+            _buffer = new byte[256];
+
+        try {
+            int len;
+
+            while ((len = is.read(_buffer, 0, _buffer.length)) > 0) {
+                os.write(_buffer, 0, len);
+            }
+
+            return true;
+        } finally {
+            is.close();
+        }
+    }
+
+
+    /**
      * Reads a byte array.
-     * <p>
+     *
      * <pre>
      * b b16 b8 non-final binary chunk
      * B b16 b8 final binary chunk
@@ -339,37 +378,14 @@
 
     /**
      * Reads an arbitrary object from the input stream.
-     *
-     * @param expectedClass the expected class if the protocol doesn't supply it.
-     * @param expectedTypes the runtime type hints, eg: expectedClass equals Map, expectedTypes can
-     *                      equals String.class, Short.class
-     */
-    public Object readObject(Class expectedClass, Class<?>... expectedTypes)
-            throws IOException {
-        throw new UnsupportedOperationException(String.valueOf(this));
-    }
-
-    /**
-     * Reads an arbitrary object from the input stream.
      */
     abstract public Object readObject()
             throws IOException;
 
     /**
-     * Reads an arbitrary object from the input stream.
-     *
-     * @param expectedTypes the runtime type hints, eg: expectedTypes can
-     *                      equals String.class, Short.class for HashMap
-     */
-    public Object readObject(List<Class<?>> expectedTypes)
-            throws IOException {
-        throw new UnsupportedOperationException(String.valueOf(this));
-    }
-
-    /**
      * Reads a remote object reference to the stream.  The type is the
      * type of the remote interface.
-     * <p>
+     *
      * <code><pre>
      * 'r' 't' b16 b8 type url
      * </pre></code>
@@ -379,7 +395,7 @@
 
     /**
      * Reads a reference
-     * <p>
+     *
      * <pre>
      * R b32 b24 b16 b8
      * </pre>
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianOutput.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianOutput.java
index 6021408..515b009 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianOutput.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianOutput.java
@@ -49,11 +49,12 @@
 package com.alibaba.com.caucho.hessian.io;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 
 /**
  * Abstract output stream for Hessian requests.
- * <p>
+ *
  * <pre>
  * OutputStream os = ...; // from http connection
  * AbstractOutput out = new HessianSerializerOutput(os);
@@ -67,11 +68,20 @@
 abstract public class AbstractHessianOutput {
     // serializer factory
     protected SerializerFactory _serializerFactory;
+    // serializer factory
+    private SerializerFactory _defaultSerializerFactory;
+    private byte[] _byteBuffer;
 
     /**
      * Gets the serializer factory.
      */
     public SerializerFactory getSerializerFactory() {
+        // the default serializer factory cannot be modified by external
+        // callers
+        if (_serializerFactory == _defaultSerializerFactory) {
+            _serializerFactory = new SerializerFactory();
+        }
+
         return _serializerFactory;
     }
 
@@ -85,11 +95,14 @@
     /**
      * Gets the serializer factory.
      */
-    public final SerializerFactory findSerializerFactory() {
+    protected final SerializerFactory findSerializerFactory() {
         SerializerFactory factory = _serializerFactory;
 
-        if (factory == null)
-            _serializerFactory = factory = new SerializerFactory();
+        if (factory == null) {
+            factory = SerializerFactory.createDefault();
+            _defaultSerializerFactory = factory;
+            _serializerFactory = factory;
+        }
 
         return factory;
     }
@@ -100,6 +113,10 @@
     public void init(OutputStream os) {
     }
 
+    public boolean setUnshared(boolean isUnshared) {
+        throw new UnsupportedOperationException(getClass().getSimpleName());
+    }
+
     /**
      * Writes a complete method call.
      */
@@ -117,7 +134,7 @@
 
     /**
      * Starts the method call:
-     * <p>
+     *
      * <code><pre>
      * C
      * </pre></code>
@@ -129,7 +146,7 @@
 
     /**
      * Starts the method call:
-     * <p>
+     *
      * <code><pre>
      * C string int
      * </pre></code>
@@ -151,7 +168,7 @@
 
     /**
      * Writes the method tag.
-     * <p>
+     *
      * <code><pre>
      * string
      * </pre></code>
@@ -163,7 +180,7 @@
 
     /**
      * Completes the method call:
-     * <p>
+     *
      * <code><pre>
      * </pre></code>
      */
@@ -173,7 +190,7 @@
     /**
      * Writes a boolean value to the stream.  The boolean will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * T
      * F
@@ -187,7 +204,7 @@
     /**
      * Writes an integer value to the stream.  The integer will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * I b32 b24 b16 b8
      * </pre></code>
@@ -200,7 +217,7 @@
     /**
      * Writes a long value to the stream.  The long will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * L b64 b56 b48 b40 b32 b24 b16 b8
      * </pre></code>
@@ -213,7 +230,7 @@
     /**
      * Writes a double value to the stream.  The double will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * D b64 b56 b48 b40 b32 b24 b16 b8
      * </pre></code>
@@ -225,7 +242,7 @@
 
     /**
      * Writes a date to the stream.
-     * <p>
+     *
      * <code><pre>
      * T  b64 b56 b48 b40 b32 b24 b16 b8
      * </pre></code>
@@ -238,7 +255,7 @@
     /**
      * Writes a null value to the stream.
      * The null will be written with the following syntax
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
@@ -251,13 +268,13 @@
     /**
      * Writes a string value to the stream using UTF-8 encoding.
      * The string will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * S b16 b8 string-value
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
@@ -270,13 +287,13 @@
     /**
      * Writes a string value to the stream using UTF-8 encoding.
      * The string will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * S b16 b8 string-value
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
@@ -289,13 +306,13 @@
     /**
      * Writes a byte array to the stream.
      * The array will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * B b16 b18 bytes
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
@@ -308,13 +325,13 @@
     /**
      * Writes a byte array to the stream.
      * The array will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * B b16 b18 bytes
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
@@ -332,7 +349,7 @@
 
     /**
      * Writes a byte buffer to the stream.
-     * <p>
+     *
      * <code><pre>
      * b b16 b18 bytes
      * </pre></code>
@@ -346,7 +363,7 @@
 
     /**
      * Writes the last chunk of a byte buffer to the stream.
-     * <p>
+     *
      * <code><pre>
      * b b16 b18 bytes
      * </pre></code>
@@ -359,8 +376,39 @@
             throws IOException;
 
     /**
+     * Writes a full output stream.
+     */
+    public void writeByteStream(InputStream is)
+            throws IOException {
+        writeByteBufferStart();
+
+        if (_byteBuffer == null)
+            _byteBuffer = new byte[1024];
+
+        byte[] buffer = _byteBuffer;
+
+        int len;
+        while ((len = is.read(buffer, 0, buffer.length)) > 0) {
+            if (len < buffer.length) {
+                int len2 = is.read(buffer, len, buffer.length - len);
+
+                if (len2 < 0) {
+                    writeByteBufferEnd(buffer, 0, len);
+                    return;
+                }
+
+                len += len2;
+            }
+
+            writeByteBufferPart(buffer, 0, len);
+        }
+
+        writeByteBufferEnd(buffer, 0, 0);
+    }
+
+    /**
      * Writes a reference.
-     * <p>
+     *
      * <code><pre>
      * Q int
      * </pre></code>
@@ -373,8 +421,10 @@
     /**
      * Removes a reference.
      */
-    abstract public boolean removeRef(Object obj)
-            throws IOException;
+    public boolean removeRef(Object obj)
+            throws IOException {
+        return false;
+    }
 
     /**
      * Replaces a reference from one object to another.
@@ -386,7 +436,7 @@
      * Adds an object to the reference list.  If the object already exists,
      * writes the reference, otherwise, the caller is responsible for
      * the serialization.
-     * <p>
+     *
      * <code><pre>
      * R b32 b24 b16 b8
      * </pre></code>
@@ -398,6 +448,12 @@
             throws IOException;
 
     /**
+     * @param obj
+     * @return
+     */
+    abstract public int getRef(Object obj);
+
+    /**
      * Resets the references for streaming.
      */
     public void resetReferences() {
@@ -413,7 +469,7 @@
      * 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>.
-     * <p>
+     *
      * <code><pre>
      * V
      *   x13 java.util.ArrayList   # type
@@ -437,7 +493,7 @@
      * 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>.
-     * <p>
+     *
      * <code><pre>
      * M type (<key> <value>)* Z
      * </pre></code>
@@ -456,7 +512,7 @@
      * Map for Hessian 1.0.  Object writers will call
      * <code>writeObjectBegin</code> followed by the map contents and then
      * call <code>writeObjectEnd</code>.
-     * <p>
+     *
      * <code><pre>
      * C type int <key>*
      * C int <value>*
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractListDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractListDeserializer.java
index aad1b38..8cf8717 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractListDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractListDeserializer.java
@@ -54,7 +54,6 @@
  * Deserializing a JDK 1.2 Collection.
  */
 public class AbstractListDeserializer extends AbstractDeserializer {
-    @Override
     public Object readObject(AbstractHessianInput in)
             throws IOException {
         Object obj = in.readObject();
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractMapDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractMapDeserializer.java
index d18faf1..df265b3 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractMapDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractMapDeserializer.java
@@ -56,12 +56,10 @@
  */
 public class AbstractMapDeserializer extends AbstractDeserializer {
 
-    @Override
     public Class getType() {
         return HashMap.class;
     }
 
-    @Override
     public Object readObject(AbstractHessianInput in)
             throws IOException {
         Object obj = in.readObject();
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializer.java
index 088cf25..199d17a 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializer.java
@@ -48,6 +48,8 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import com.alibaba.com.caucho.hessian.HessianException;
+
 import java.io.IOException;
 import java.util.logging.Logger;
 
@@ -55,10 +57,88 @@
  * Serializing an object.
  */
 abstract public class AbstractSerializer implements Serializer {
+    public static final NullSerializer NULL = new NullSerializer();
+
     protected static final Logger log
             = Logger.getLogger(AbstractSerializer.class.getName());
 
     @Override
-    abstract public void writeObject(Object obj, AbstractHessianOutput out)
-            throws IOException;
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        if (out.addRef(obj)) {
+            return;
+        }
+
+        try {
+            Object replace = writeReplace(obj);
+
+            if (replace != null) {
+                // out.removeRef(obj);
+
+                out.writeObject(replace);
+
+                out.replaceRef(replace, obj);
+
+                return;
+            }
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            // log.log(Level.FINE, e.toString(), e);
+            throw new HessianException(e);
+        }
+
+        Class<?> cl = getClass(obj);
+
+        int ref = out.writeObjectBegin(cl.getName());
+
+        if (ref < -1) {
+            writeObject10(obj, out);
+        } else {
+            if (ref == -1) {
+                writeDefinition20(cl, out);
+
+                out.writeObjectBegin(cl.getName());
+            }
+
+            writeInstance(obj, out);
+        }
+    }
+
+    protected Object writeReplace(Object obj) {
+        return null;
+    }
+
+    protected Class<?> getClass(Object obj) {
+        return obj.getClass();
+    }
+
+    protected void writeObject10(Object obj,
+                                 AbstractHessianOutput out)
+            throws IOException {
+        throw new UnsupportedOperationException(getClass().getName());
+    }
+
+    protected void writeDefinition20(Class<?> cl,
+                                     AbstractHessianOutput out)
+            throws IOException {
+        throw new UnsupportedOperationException(getClass().getName());
+    }
+
+    protected void writeInstance(Object obj,
+                                 AbstractHessianOutput out)
+            throws IOException {
+        throw new UnsupportedOperationException(getClass().getName());
+    }
+
+    /**
+     * The NullSerializer exists as a marker for the factory classes so
+     * they save a null result.
+     */
+    static final class NullSerializer extends AbstractSerializer {
+        public void writeObject(Object obj, AbstractHessianOutput out)
+                throws IOException {
+            throw new IllegalStateException(getClass().getName());
+        }
+    }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializerWrapper.java
similarity index 82%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializerWrapper.java
index 72a9822..078664c 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractSerializerWrapper.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -49,17 +49,20 @@
 package com.alibaba.com.caucho.hessian.io;
 
 import java.io.IOException;
+import java.util.logging.Logger;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+abstract public class AbstractSerializerWrapper implements Serializer {
+    protected static final Logger log
+            = Logger.getLogger(AbstractSerializerWrapper.class.getName());
+
+    abstract protected Serializer getDelegate();
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        getDelegate().writeObject(obj, out);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStreamDeserializer.java
similarity index 67%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStreamDeserializer.java
index 72a9822..eb7deac 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStreamDeserializer.java
@@ -51,15 +51,52 @@
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing a byte stream
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+abstract public class AbstractStreamDeserializer extends AbstractDeserializer {
+    abstract public Class<?> getType();
+
     /**
-     * Looks up a proxy object.
+     * Reads the Hessian 1.0 style map.
      */
     @Override
-    public Object lookup(String type, String url)
+    public Object readMap(AbstractHessianInput in)
             throws IOException {
-        return new HessianRemote(type, url);
+        Object value = null;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("value"))
+                value = readStreamValue(in);
+            else
+                in.readObject();
+        }
+
+        in.readMapEnd();
+
+        return value;
     }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, Object[] fields)
+            throws IOException {
+        String[] fieldNames = (String[]) fields;
+
+        Object value = null;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            if ("value".equals(fieldNames[i])) {
+                value = readStreamValue(in);
+                in.addRef(value);
+            } else {
+                in.readObject();
+            }
+        }
+
+        return value;
+    }
+
+    abstract protected Object readStreamValue(AbstractHessianInput in)
+            throws IOException;
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStreamSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStreamSerializer.java
new file mode 100644
index 0000000..794d537
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStreamSerializer.java
@@ -0,0 +1,127 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+
+/**
+ * Serializing an object containing a byte stream.
+ */
+abstract public class AbstractStreamSerializer extends AbstractSerializer {
+    /**
+     * Writes the object to the output stream.
+     */
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        if (out.addRef(obj)) {
+            return;
+        }
+
+        int ref = out.writeObjectBegin(getClassName(obj));
+
+        if (ref < -1) {
+            out.writeString("value");
+
+            InputStream is = null;
+
+            try {
+                is = getInputStream(obj);
+            } catch (Exception e) {
+                log.log(Level.WARNING, e.toString(), e);
+            }
+
+            if (is != null) {
+                try {
+                    out.writeByteStream(is);
+                } finally {
+                    is.close();
+                }
+            } else {
+                out.writeNull();
+            }
+
+            out.writeMapEnd();
+        } else {
+            if (ref == -1) {
+                out.writeClassFieldLength(1);
+                out.writeString("value");
+
+                out.writeObjectBegin(getClassName(obj));
+            }
+
+            InputStream is = null;
+
+            try {
+                is = getInputStream(obj);
+            } catch (Exception e) {
+                log.log(Level.WARNING, e.toString(), e);
+            }
+
+            try {
+                if (is != null)
+                    out.writeByteStream(is);
+                else
+                    out.writeNull();
+            } finally {
+                if (is != null)
+                    is.close();
+            }
+        }
+    }
+
+    protected String getClassName(Object obj) {
+        return obj.getClass().getName();
+    }
+
+    abstract protected InputStream getInputStream(Object obj)
+            throws IOException;
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStringValueDeserializer.java
similarity index 67%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStringValueDeserializer.java
index 72a9822..9824158 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractStringValueDeserializer.java
@@ -51,15 +51,54 @@
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializes a string-valued object like BigDecimal.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+abstract public class AbstractStringValueDeserializer
+        extends AbstractDeserializer {
+    abstract protected Object create(String value)
+            throws IOException;
+
     @Override
-    public Object lookup(String type, String url)
+    public Object readMap(AbstractHessianInput in)
             throws IOException {
-        return new HessianRemote(type, url);
+        String value = null;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("value"))
+                value = in.readString();
+            else
+                in.readObject();
+        }
+
+        in.readMapEnd();
+
+        Object object = create(value);
+
+        in.addRef(object);
+
+        return object;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, Object[] fields)
+            throws IOException {
+        String[] fieldNames = (String[]) fields;
+
+        String value = null;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            if ("value".equals(fieldNames[i]))
+                value = in.readString();
+            else
+                in.readObject();
+        }
+
+        Object object = create(value);
+
+        in.addRef(object);
+
+        return object;
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationDeserializer.java
new file mode 100644
index 0000000..7b04ccf
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationDeserializer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import com.alibaba.com.caucho.hessian.HessianException;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.logging.Logger;
+
+/**
+ * Deserializing a java annotation for known object types.
+ */
+public class AnnotationDeserializer extends AbstractMapDeserializer {
+    private static final Logger log
+            = Logger.getLogger(AnnotationDeserializer.class.getName());
+
+    private Class _annType;
+
+    public AnnotationDeserializer(Class annType) {
+        _annType = annType;
+    }
+
+    public Class getType() {
+        return _annType;
+    }
+
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        try {
+            int ref = in.addRef(null);
+
+            HashMap<String, Object> valueMap = new HashMap<String, Object>(8);
+
+            while (!in.isEnd()) {
+                String key = in.readString();
+                Object value = in.readObject();
+
+                valueMap.put(key, value);
+            }
+
+            in.readMapEnd();
+
+            return Proxy.newProxyInstance(_annType.getClassLoader(),
+                    new Class[]{_annType},
+                    new AnnotationInvocationHandler(_annType, valueMap));
+
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
+            throws IOException {
+        String[] fieldNames = (String[]) fields;
+
+        try {
+            in.addRef(null);
+
+            HashMap<String, Object> valueMap = new HashMap<String, Object>(8);
+
+            for (int i = 0; i < fieldNames.length; i++) {
+                String name = fieldNames[i];
+
+                valueMap.put(name, in.readObject());
+            }
+
+            return Proxy.newProxyInstance(_annType.getClassLoader(),
+                    new Class[]{_annType},
+                    new AnnotationInvocationHandler(_annType, valueMap));
+
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HessianException(_annType.getName() + ":" + e, e);
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationInvocationHandler.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationInvocationHandler.java
new file mode 100644
index 0000000..34fef0f
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationInvocationHandler.java
@@ -0,0 +1,133 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Proxy for a java annotation for known object types.
+ */
+public class AnnotationInvocationHandler implements InvocationHandler {
+    private Class _annType;
+    private HashMap<String, Object> _valueMap;
+
+    public AnnotationInvocationHandler(Class annType,
+                                       HashMap<String, Object> valueMap) {
+        _annType = annType;
+        _valueMap = valueMap;
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args)
+            throws Throwable {
+        String name = method.getName();
+
+        boolean zeroArgs = args == null || args.length == 0;
+
+        if (name.equals("annotationType") && zeroArgs)
+            return _annType;
+        else if (name.equals("toString") && zeroArgs)
+            return toString();
+        else if (name.equals("hashCode") && zeroArgs)
+            return doHashCode();
+        else if (name.equals("equals") && !zeroArgs && args.length == 1)
+            return doEquals(args[0]);
+        else if (!zeroArgs)
+            return null;
+
+
+        return _valueMap.get(method.getName());
+    }
+
+    public int doHashCode() {
+        return 13;
+    }
+
+    public boolean doEquals(Object value) {
+        if (!(value instanceof Annotation))
+            return false;
+
+        Annotation ann = (Annotation) value;
+
+        if (!_annType.equals(ann.annotationType()))
+            return false;
+
+        return true;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("@");
+        sb.append(_annType.getName());
+        sb.append("[");
+
+        boolean isFirst = true;
+        for (Map.Entry entry : _valueMap.entrySet()) {
+            if (!isFirst)
+                sb.append(", ");
+            isFirst = false;
+
+            sb.append(entry.getKey());
+            sb.append("=");
+
+            if (entry.getValue() instanceof String)
+                sb.append('"').append(entry.getValue()).append('"');
+            else
+                sb.append(entry.getValue());
+        }
+        sb.append("]");
+
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationSerializer.java
new file mode 100644
index 0000000..10cf3ef
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/AnnotationSerializer.java
@@ -0,0 +1,367 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import com.alibaba.com.caucho.hessian.HessianException;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Serializing a Java annotation
+ */
+public class AnnotationSerializer extends AbstractSerializer {
+    private static final Logger log
+            = Logger.getLogger(AnnotationSerializer.class.getName());
+
+    private static Object[] NULL_ARGS = new Object[0];
+
+    private Class _annType;
+    private Method[] _methods;
+    private MethodSerializer[] _methodSerializers;
+
+    public AnnotationSerializer(Class annType) {
+        if (!Annotation.class.isAssignableFrom(annType)) {
+            throw new IllegalStateException(annType.getName() + " is invalid because it is not a java.lang.annotation.Annotation");
+        }
+    }
+
+    private static MethodSerializer getMethodSerializer(Class type) {
+        if (int.class.equals(type)
+                || byte.class.equals(type)
+                || short.class.equals(type)
+                || int.class.equals(type)) {
+            return IntMethodSerializer.SER;
+        } else if (long.class.equals(type)) {
+            return LongMethodSerializer.SER;
+        } else if (double.class.equals(type) ||
+                float.class.equals(type)) {
+            return DoubleMethodSerializer.SER;
+        } else if (boolean.class.equals(type)) {
+            return BooleanMethodSerializer.SER;
+        } else if (String.class.equals(type)) {
+            return StringMethodSerializer.SER;
+        } else if (java.util.Date.class.equals(type)
+                || java.sql.Date.class.equals(type)
+                || java.sql.Timestamp.class.equals(type)
+                || java.sql.Time.class.equals(type)) {
+            return DateMethodSerializer.SER;
+        } else
+            return MethodSerializer.SER;
+    }
+
+    static HessianException error(Method method, Throwable cause) {
+        String msg = (method.getDeclaringClass().getSimpleName()
+                + "." + method.getName() + "(): " + cause);
+
+        throw new HessianMethodSerializationException(msg, cause);
+    }
+
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        if (out.addRef(obj)) {
+            return;
+        }
+
+        init(((Annotation) obj).annotationType());
+
+        int ref = out.writeObjectBegin(_annType.getName());
+
+        if (ref < -1) {
+            writeObject10(obj, out);
+        } else {
+            if (ref == -1) {
+                writeDefinition20(out);
+                out.writeObjectBegin(_annType.getName());
+            }
+
+            writeInstance(obj, out);
+        }
+    }
+
+    protected void writeObject10(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        for (int i = 0; i < _methods.length; i++) {
+            Method method = _methods[i];
+
+            out.writeString(method.getName());
+
+            _methodSerializers[i].serialize(out, obj, method);
+        }
+
+        out.writeMapEnd();
+    }
+
+    private void writeDefinition20(AbstractHessianOutput out)
+            throws IOException {
+        out.writeClassFieldLength(_methods.length);
+
+        for (int i = 0; i < _methods.length; i++) {
+            Method method = _methods[i];
+
+            out.writeString(method.getName());
+        }
+    }
+
+    public void writeInstance(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        for (int i = 0; i < _methods.length; i++) {
+            Method method = _methods[i];
+
+            _methodSerializers[i].serialize(out, obj, method);
+        }
+    }
+
+    private void init(Class cl) {
+        synchronized (this) {
+            if (_annType != null)
+                return;
+
+            _annType = cl;
+
+            ArrayList methods = new ArrayList();
+
+            for (Method method : _annType.getDeclaredMethods()) {
+                if (method.getName().equals("hashCode")
+                        || method.getName().equals("toString")
+                        || method.getName().equals("annotationType")) {
+                    continue;
+                }
+
+                if (method.getParameterTypes().length != 0)
+                    continue;
+
+                methods.add(method);
+
+                method.setAccessible(true);
+            }
+
+            if (_annType == null)
+                throw new IllegalStateException(cl.getName() + " is invalid because it does not have a valid annotationType()");
+
+            _methods = new Method[methods.size()];
+            methods.toArray(_methods);
+
+            _methodSerializers = new MethodSerializer[_methods.length];
+
+            for (int i = 0; i < _methods.length; i++) {
+                _methodSerializers[i] = getMethodSerializer(_methods[i].getReturnType());
+            }
+        }
+    }
+
+    private Class getAnnotationType(Class cl) {
+        if (cl == null)
+            return null;
+
+        if (Annotation.class.equals(cl.getSuperclass()))
+            return cl;
+
+        Class ifaces[] = cl.getInterfaces();
+
+        if (ifaces != null) {
+            for (Class iface : ifaces) {
+                if (iface.equals(Annotation.class))
+                    return cl;
+
+                Class annType = getAnnotationType(iface);
+
+                if (annType != null)
+                    return annType;
+            }
+        }
+
+        return getAnnotationType(cl.getSuperclass());
+    }
+
+    static class MethodSerializer {
+        static final MethodSerializer SER = new MethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            Object value = null;
+
+            try {
+                value = method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            try {
+                out.writeObject(value);
+            } catch (Exception e) {
+                throw error(method, e);
+            }
+        }
+    }
+
+    static class BooleanMethodSerializer extends MethodSerializer {
+        static final MethodSerializer SER = new BooleanMethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            boolean value = false;
+
+            try {
+                value = (Boolean) method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeBoolean(value);
+        }
+    }
+
+    static class IntMethodSerializer extends MethodSerializer {
+        static final MethodSerializer SER = new IntMethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = (Integer) method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeInt(value);
+        }
+    }
+
+    static class LongMethodSerializer extends MethodSerializer {
+        static final MethodSerializer SER = new LongMethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            long value = 0;
+
+            try {
+                value = (Long) method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeLong(value);
+        }
+    }
+
+    static class DoubleMethodSerializer extends MethodSerializer {
+        static final MethodSerializer SER = new DoubleMethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = (Double) method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeDouble(value);
+        }
+    }
+
+    static class StringMethodSerializer extends MethodSerializer {
+        static final MethodSerializer SER = new StringMethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            String value = null;
+
+            try {
+                value = (String) method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeString(value);
+        }
+    }
+
+    static class DateMethodSerializer extends MethodSerializer {
+        static final MethodSerializer SER = new DateMethodSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Method method)
+                throws IOException {
+            java.util.Date value = null;
+
+            try {
+                value = (java.util.Date) method.invoke(obj);
+            } catch (InvocationTargetException e) {
+                throw error(method, e.getCause());
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            if (value == null)
+                out.writeNull();
+            else
+                out.writeUTCDate(value.getTime());
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ArrayDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ArrayDeserializer.java
index e575411..8c7c715 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ArrayDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ArrayDeserializer.java
@@ -73,7 +73,6 @@
             _type = Object[].class;
     }
 
-    @Override
     public Class getType() {
         return _type;
     }
@@ -81,7 +80,6 @@
     /**
      * Reads the array.
      */
-    @Override
     public Object readList(AbstractHessianInput in, int length)
             throws IOException {
         if (length >= 0) {
@@ -126,7 +124,6 @@
     /**
      * Reads the array.
      */
-    @Override
     public Object readLengthList(AbstractHessianInput in, int length)
             throws IOException {
         Object[] data = createArray(length);
@@ -151,7 +148,6 @@
             return new Object[length];
     }
 
-    @Override
     public String toString() {
         return "ArrayDeserializer[" + _componentType + "]";
     }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ArraySerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ArraySerializer.java
index d6cfcc5..31d7ca5 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ArraySerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ArraySerializer.java
@@ -54,7 +54,6 @@
  * Serializing a Java array.
  */
 public class ArraySerializer extends AbstractSerializer {
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         if (out.addRef(obj))
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BasicDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/BasicDeserializer.java
index 81146ca..52589d6 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BasicDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/BasicDeserializer.java
@@ -88,7 +88,6 @@
         _code = code;
     }
 
-    @Override
     public Class getType() {
         switch (_code) {
             case NULL:
@@ -148,9 +147,6 @@
     @Override
     public Object readObject(AbstractHessianInput in)
             throws IOException {
-        if (in.checkAndReadNull()) {
-            return null;
-        }
         switch (_code) {
             case NULL:
                 // hessian/3490
@@ -515,7 +511,6 @@
         }
     }
 
-    @Override
     public Object readLengthList(AbstractHessianInput in, int length)
             throws IOException {
         switch (_code) {
@@ -608,8 +603,7 @@
         }
     }
 
-    public String toString()
-    {
+    public String toString() {
         return getClass().getSimpleName() + "[" + _code + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BasicSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/BasicSerializer.java
index 4b9f359..207c9d0 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BasicSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/BasicSerializer.java
@@ -54,7 +54,8 @@
 /**
  * Serializing an object for known object types.
  */
-public class BasicSerializer extends AbstractSerializer {
+public class BasicSerializer extends AbstractSerializer
+        implements ObjectSerializer {
     public static final int NULL = 0;
     public static final int BOOLEAN = NULL + 1;
     public static final int BYTE = BOOLEAN + 1;
@@ -66,7 +67,8 @@
     public static final int CHARACTER = DOUBLE + 1;
     public static final int CHARACTER_OBJECT = CHARACTER + 1;
     public static final int STRING = CHARACTER_OBJECT + 1;
-    public static final int DATE = STRING + 1;
+    public static final int STRING_BUILDER = STRING + 1;
+    public static final int DATE = STRING_BUILDER + 1;
     public static final int NUMBER = DATE + 1;
     public static final int OBJECT = NUMBER + 1;
 
@@ -81,16 +83,37 @@
     public static final int STRING_ARRAY = CHARACTER_ARRAY + 1;
     public static final int OBJECT_ARRAY = STRING_ARRAY + 1;
 
-    private int code;
+    public static final int BYTE_HANDLE = OBJECT_ARRAY + 1;
+    public static final int SHORT_HANDLE = BYTE_HANDLE + 1;
+    public static final int FLOAT_HANDLE = SHORT_HANDLE + 1;
+    private static final BasicSerializer FLOAT_HANDLE_SERIALIZER
+            = new BasicSerializer(FLOAT_HANDLE);
+    private static final BasicSerializer SHORT_HANDLE_SERIALIZER
+            = new BasicSerializer(SHORT_HANDLE);
+    private static final BasicSerializer BYTE_HANDLE_SERIALIZER
+            = new BasicSerializer(BYTE_HANDLE);
+    private int _code;
 
     public BasicSerializer(int code) {
-        this.code = code;
+        _code = code;
     }
 
-    @Override
+    public Serializer getObjectSerializer() {
+        switch (_code) {
+            case BYTE:
+                return BYTE_HANDLE_SERIALIZER;
+            case SHORT:
+                return SHORT_HANDLE_SERIALIZER;
+            case FLOAT:
+                return FLOAT_HANDLE_SERIALIZER;
+            default:
+                return this;
+        }
+    }
+
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        switch (code) {
+        switch (_code) {
             case BOOLEAN:
                 out.writeBoolean(((Boolean) obj).booleanValue());
                 break;
@@ -121,6 +144,10 @@
                 out.writeString((String) obj);
                 break;
 
+            case STRING_BUILDER:
+                out.writeString(((StringBuilder) obj).toString());
+                break;
+
             case DATE:
                 out.writeUTCDate(((Date) obj).getTime());
                 break;
@@ -269,8 +296,24 @@
                 out.writeNull();
                 break;
 
+            case OBJECT:
+                ObjectHandleSerializer.SER.writeObject(obj, out);
+                break;
+
+            case BYTE_HANDLE:
+                out.writeObject(new ByteHandle((Byte) obj));
+                break;
+
+            case SHORT_HANDLE:
+                out.writeObject(new ShortHandle((Short) obj));
+                break;
+
+            case FLOAT_HANDLE:
+                out.writeObject(new FloatHandle((Float) obj));
+                break;
+
             default:
-                throw new RuntimeException(code + " " + String.valueOf(obj.getClass()));
+                throw new RuntimeException(_code + " unknown code for " + obj.getClass());
         }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/BeanDeserializer.java
index a0f16c8..5bc4db2 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/BeanDeserializer.java
@@ -53,6 +53,7 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.HashMap;
+import java.util.Locale;
 
 /**
  * Serializing an object for known object types.
@@ -116,12 +117,10 @@
             throw new UnsupportedOperationException();
     }
 
-    @Override
     public Class getType() {
         return _type;
     }
 
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         try {
@@ -248,9 +247,9 @@
                 }
 
                 if (j == 1)
-                    name = name.substring(0, j).toLowerCase() + name.substring(j);
+                    name = name.substring(0, j).toLowerCase(Locale.ENGLISH) + name.substring(j);
                 else if (j > 1)
-                    name = name.substring(0, j - 1).toLowerCase() + name.substring(j - 1);
+                    name = name.substring(0, j - 1).toLowerCase(Locale.ENGLISH) + name.substring(j - 1);
 
 
                 methodMap.put(name, method);
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/BigDecimalDeserializer.java
similarity index 87%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/BigDecimalDeserializer.java
index be505fb..23ad53e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/BigDecimalDeserializer.java
@@ -48,9 +48,19 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.util.logging.Logger;
+import java.math.BigDecimal;
 
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Deserializing a BigDecimal
+ */
+public class BigDecimalDeserializer extends AbstractStringValueDeserializer {
+    @Override
+    public Class<?> getType() {
+        return BigDecimal.class;
+    }
+
+    @Override
+    protected Object create(String value) {
+        return new BigDecimal(value);
+    }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BitSetSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/BitSetSerializer.java
index 9b7c56e..bc5099a 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BitSetSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/BitSetSerializer.java
@@ -48,6 +48,7 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+
 import java.io.IOException;
 import java.util.BitSet;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ByteArraySerializer.java
similarity index 77%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/ByteArraySerializer.java
index 72a9822..6f15715 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ByteArraySerializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -51,15 +51,28 @@
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object for known object types.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class ByteArraySerializer extends AbstractSerializer
+        implements ObjectSerializer {
+    public static final ByteArraySerializer SER = new ByteArraySerializer();
+
+    private ByteArraySerializer() {
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public Serializer getObjectSerializer() {
+        return this;
+    }
+
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        byte[] data = (byte[]) obj;
+
+        if (data != null)
+            out.writeBytes(data, 0, data.length);
+        else
+            out.writeNull();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ByteHandle.java
similarity index 80%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/ByteHandle.java
index 72a9822..b171633 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ByteHandle.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -48,18 +48,30 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
+import java.io.Serializable;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Handle for Java Byte objects.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+public class ByteHandle implements Serializable {
+    private byte _value;
+
+    private ByteHandle() {
+    }
+
+    public ByteHandle(byte value) {
+        _value = value;
+    }
+
+    public byte getValue() {
+        return _value;
+    }
+
+    public Object readResolve() {
+        return new Byte(_value);
+    }
+
+    public String toString() {
+        return getClass().getSimpleName() + "[" + _value + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/CalendarSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/CalendarSerializer.java
index 0db6c14..9300ac1 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/CalendarSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/CalendarSerializer.java
@@ -48,29 +48,21 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
 import java.util.Calendar;
 
 /**
  * Serializing a calendar.
  */
 public class CalendarSerializer extends AbstractSerializer {
-    private static CalendarSerializer SERIALIZER = new CalendarSerializer();
+    public static final Serializer SER = new CalendarSerializer();
 
-    public static CalendarSerializer create() {
-        return SERIALIZER;
-    }
-
+    /**
+     * java.util.Calendar serializes to com.alibaba.com.caucho.hessian.io.CalendarHandle
+     */
     @Override
-    public void writeObject(Object obj, AbstractHessianOutput out)
-            throws IOException {
-        if (obj == null)
-            out.writeNull();
-        else {
-            Calendar cal = (Calendar) obj;
+    public Object writeReplace(Object obj) {
+        Calendar cal = (Calendar) obj;
 
-            out.writeObject(new CalendarHandle(cal.getClass(),
-                    cal.getTimeInMillis()));
-        }
+        return new CalendarHandle(cal.getClass(), cal.getTimeInMillis());
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ClassDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ClassDeserializer.java
index 422333c..137aca7 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ClassDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ClassDeserializer.java
@@ -85,12 +85,10 @@
         _loader = loader;
     }
 
-    @Override
     public Class getType() {
         return Class.class;
     }
 
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         int ref = in.addRef(null);
@@ -115,9 +113,10 @@
         return value;
     }
 
-    @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object readObject(AbstractHessianInput in, Object[] fields)
             throws IOException {
+        String[] fieldNames = (String[]) fields;
+
         int ref = in.addRef(null);
 
         String name = null;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ClassFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ClassFactory.java
index d64b6fe..d27080d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ClassFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ClassFactory.java
@@ -54,11 +54,9 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -67,28 +65,70 @@
 /**
  * Loads a class from the classloader.
  */
-public class ClassFactory
-{
+public class ClassFactory {
     protected static final Logger log
             = Logger.getLogger(ClassFactory.class.getName());
     private static final ArrayList<Allow> _staticAllowList;
     private static final Map<String, Object> _allowSubClassSet = new ConcurrentHashMap<>();
     private static final Map<String, Object> _allowClassSet = new ConcurrentHashMap<>();
 
+    static {
+        _staticAllowList = new ArrayList<Allow>();
+
+        ClassLoader classLoader = ClassFactory.class.getClassLoader();
+        try {
+            String[] denyClasses = readLines(classLoader.getResourceAsStream("DENY_CLASS"));
+            for (String denyClass : denyClasses) {
+                if (denyClass.startsWith("#")) {
+                    continue;
+                }
+                if (denyClass.endsWith(".")) {
+                    _staticAllowList.add(new AllowPrefix(denyClass, false));
+                } else {
+                    _staticAllowList.add(new Allow(toPattern(denyClass), false));
+                }
+            }
+        } catch (IOException ignore) {
+
+        }
+    }
+
     private ClassLoader _loader;
     private boolean _isWhitelist;
-
     private LinkedList<Allow> _allowList;
 
-    ClassFactory(ClassLoader loader)
-    {
+    ClassFactory(ClassLoader loader) {
         _loader = loader;
         initAllow();
     }
 
+    private static String toPattern(String pattern) {
+        pattern = pattern.replace(".", "\\.");
+        pattern = pattern.replace("*", ".*");
+
+        return pattern;
+    }
+
+    /**
+     * read lines.
+     *
+     * @param is input stream.
+     * @return lines.
+     * @throws IOException If an I/O error occurs
+     */
+    public static String[] readLines(InputStream is) throws IOException {
+        List<String> lines = new ArrayList<String>();
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                lines.add(line);
+            }
+            return lines.toArray(new String[0]);
+        }
+    }
+
     public Class<?> load(String className)
-            throws ClassNotFoundException
-    {
+            throws ClassNotFoundException {
         if (isAllow(className)) {
             Class<?> aClass = Class.forName(className, false, _loader);
 
@@ -98,7 +138,7 @@
 
             if (aClass.getInterfaces().length > 0) {
                 for (Class<?> anInterface : aClass.getInterfaces()) {
-                    if(!isAllow(anInterface.getName())) {
+                    if (!isAllow(anInterface.getName())) {
                         log.log(Level.SEVERE, className + "'s interfaces: " + anInterface.getName() + " in blacklist or not in whitelist, deserialization with type 'HashMap' instead.");
                         return HashMap.class;
                     }
@@ -115,7 +155,7 @@
             }
 
             for (Class<?> aSuperClass : allSuperClasses) {
-                if(!isAllow(aSuperClass.getName())) {
+                if (!isAllow(aSuperClass.getName())) {
                     log.log(Level.SEVERE, className + "'s superClass: " + aSuperClass.getName() + " in blacklist or not in whitelist, deserialization with type 'HashMap' instead.");
                     return HashMap.class;
                 }
@@ -124,15 +164,13 @@
 
             _allowClassSet.put(className, className);
             return aClass;
-        }
-        else {
+        } else {
             log.log(Level.SEVERE, className + " in blacklist or not in whitelist, deserialization with type 'HashMap' instead.");
             return HashMap.class;
         }
     }
 
-    private boolean isAllow(String className)
-    {
+    private boolean isAllow(String className) {
         LinkedList<Allow> allowList = _allowList;
 
         if (allowList == null) {
@@ -162,8 +200,7 @@
         return true;
     }
 
-    public void setWhitelist(boolean isWhitelist)
-    {
+    public void setWhitelist(boolean isWhitelist) {
         _allowClassSet.clear();
         _allowSubClassSet.clear();
         _isWhitelist = isWhitelist;
@@ -171,8 +208,7 @@
         initAllow();
     }
 
-    public void allow(String pattern)
-    {
+    public void allow(String pattern) {
         _allowClassSet.clear();
         _allowSubClassSet.clear();
         initAllow();
@@ -182,8 +218,7 @@
         }
     }
 
-    public void deny(String pattern)
-    {
+    public void deny(String pattern) {
         _allowClassSet.clear();
         _allowSubClassSet.clear();
         initAllow();
@@ -193,16 +228,7 @@
         }
     }
 
-    private static String toPattern(String pattern)
-    {
-        pattern = pattern.replace(".", "\\.");
-        pattern = pattern.replace("*", ".*");
-
-        return pattern;
-    }
-
-    private void initAllow()
-    {
+    private void initAllow() {
         synchronized (this) {
             if (_allowList == null) {
                 _allowList = new LinkedList<Allow>();
@@ -218,18 +244,15 @@
         public Allow() {
         }
 
-        private Allow(String pattern, boolean isAllow)
-        {
+        private Allow(String pattern, boolean isAllow) {
             _isAllow = isAllow;
             _pattern = Pattern.compile(pattern);
         }
 
-        Boolean allow(String className)
-        {
+        Boolean allow(String className) {
             if (_pattern.matcher(className).matches()) {
                 return _isAllow;
-            }
-            else {
+            } else {
                 return null;
             }
         }
@@ -239,61 +262,19 @@
         private Boolean _isAllow;
         private String _prefix;
 
-        private AllowPrefix(String prefix, boolean isAllow)
-        {
+        private AllowPrefix(String prefix, boolean isAllow) {
             super();
             _isAllow = isAllow;
             _prefix = prefix;
         }
 
         @Override
-        Boolean allow(String className)
-        {
+        Boolean allow(String className) {
             if (className.startsWith(_prefix)) {
                 return _isAllow;
-            }
-            else {
+            } else {
                 return null;
             }
         }
     }
-
-    static {
-        _staticAllowList = new ArrayList<Allow>();
-
-        ClassLoader classLoader = ClassFactory.class.getClassLoader();
-        try {
-            String[] denyClasses = readLines(classLoader.getResourceAsStream("DENY_CLASS"));
-            for (String denyClass : denyClasses) {
-                if (denyClass.startsWith("#")) {
-                    continue;
-                }
-                if (denyClass.endsWith(".")) {
-                    _staticAllowList.add(new AllowPrefix(denyClass, false));
-                } else {
-                    _staticAllowList.add(new Allow(toPattern(denyClass), false));
-                }
-            }
-        } catch (IOException ignore) {
-
-        }
-    }
-
-    /**
-     * read lines.
-     *
-     * @param is input stream.
-     * @return lines.
-     * @throws IOException If an I/O error occurs
-     */
-    public static String[] readLines(InputStream is) throws IOException {
-        List<String> lines = new ArrayList<String>();
-        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
-            String line;
-            while ((line = reader.readLine()) != null) {
-                lines.add(line);
-            }
-            return lines.toArray(new String[0]);
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ClassSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ClassSerializer.java
index 7529e17..b65411e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ClassSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ClassSerializer.java
@@ -54,7 +54,6 @@
  * Serializing a remote object.
  */
 public class ClassSerializer extends AbstractSerializer {
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         Class cl = (Class) obj;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionDeserializer.java
index c9465b6..1138fab 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionDeserializer.java
@@ -67,63 +67,32 @@
         _type = type;
     }
 
-    @Override
     public Class getType() {
         return _type;
     }
 
-    @Override
     public Object readList(AbstractHessianInput in, int length)
             throws IOException {
-        return readList(in, length, _type);
-    }
-
-    @Override
-    public Object readList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
         Collection list = createList();
 
         in.addRef(list);
 
-        Deserializer deserializer = null;
-
-        SerializerFactory factory = findSerializerFactory(in);
-        if (expectType != null) {
-            deserializer = factory.getDeserializer(expectType.getName());
-        }
-
-        while (!in.isEnd()) {
-            list.add(deserializer != null ? deserializer.readObject(in) : in.readObject());
-        }
-
+        while (!in.isEnd())
+            list.add(in.readObject());
 
         in.readEnd();
 
         return list;
     }
 
-    @Override
     public Object readLengthList(AbstractHessianInput in, int length)
             throws IOException {
-        return readList(in, length, null);
-    }
-
-    @Override
-    public Object readLengthList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
         Collection list = createList();
 
         in.addRef(list);
 
-        Deserializer deserializer = null;
-
-        SerializerFactory factory = findSerializerFactory(in);
-        if (expectType != null) {
-            deserializer = factory.getDeserializer(expectType.getName());
-        }
-
-        for (; length > 0; length--) {
-            list.add(deserializer != null ? deserializer.readObject(in) : in.readObject());
-        }
-
+        for (; length > 0; length--)
+            list.add(in.readObject());
 
         return list;
     }
@@ -161,8 +130,7 @@
         return list;
     }
 
-    public String toString()
-    {
+    public String toString() {
         return getClass().getSimpleName() + "[" + _type + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionSerializer.java
index abc92d4..9951ab0 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/CollectionSerializer.java
@@ -74,7 +74,6 @@
         _sendJavaType = sendJavaType;
     }
 
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         if (out.addRef(obj))
@@ -83,7 +82,27 @@
         Collection list = (Collection) obj;
 
         Class cl = obj.getClass();
-        boolean hasEnd = out.writeListBegin(list.size(), obj.getClass().getName());
+        boolean hasEnd;
+
+        if (cl.equals(ArrayList.class)
+                || !Serializable.class.isAssignableFrom(cl)) {
+            hasEnd = out.writeListBegin(list.size(), null);
+        } else if (!_sendJavaType) {
+            hasEnd = false;
+
+            // hessian/3a19
+            for (; cl != null; cl = cl.getSuperclass()) {
+                if (cl.getName().startsWith("java.")) {
+                    hasEnd = out.writeListBegin(list.size(), cl.getName());
+                    break;
+                }
+            }
+
+            if (cl == null)
+                hasEnd = out.writeListBegin(list.size(), null);
+        } else {
+            hasEnd = out.writeListBegin(list.size(), obj.getClass().getName());
+        }
 
         Iterator iter = list.iterator();
         while (iter.hasNext()) {
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ContextSerializerFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ContextSerializerFactory.java
new file mode 100644
index 0000000..b89d994
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ContextSerializerFactory.java
@@ -0,0 +1,481 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import com.alibaba.com.caucho.hessian.HessianException;
+
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The classloader-specific Factory for returning serialization
+ */
+public class ContextSerializerFactory {
+    private static final Logger log
+            = Logger.getLogger(ContextSerializerFactory.class.getName());
+    private static final WeakHashMap<ClassLoader, SoftReference<ContextSerializerFactory>>
+            _contextRefMap
+            = new WeakHashMap<ClassLoader, SoftReference<ContextSerializerFactory>>();
+    private static final ClassLoader _systemClassLoader;
+    private static Deserializer OBJECT_DESERIALIZER
+            = new BasicDeserializer(BasicDeserializer.OBJECT);
+    private static HashMap<String, Serializer> _staticSerializerMap;
+    private static HashMap<String, Deserializer> _staticDeserializerMap;
+    private static HashMap _staticClassNameMap;
+
+    static {
+        _staticSerializerMap = new HashMap();
+        _staticDeserializerMap = new HashMap();
+        _staticClassNameMap = new HashMap();
+
+        FieldDeserializer2Factory fieldFactory = FieldDeserializer2Factory.create();
+
+        addBasic(void.class, "void", BasicSerializer.NULL);
+
+        addBasic(Boolean.class, "boolean", BasicSerializer.BOOLEAN);
+        addBasic(Byte.class, "byte", BasicSerializer.BYTE);
+        addBasic(Short.class, "short", BasicSerializer.SHORT);
+        addBasic(Integer.class, "int", BasicSerializer.INTEGER);
+        addBasic(Long.class, "long", BasicSerializer.LONG);
+        addBasic(Float.class, "float", BasicSerializer.FLOAT);
+        addBasic(Double.class, "double", BasicSerializer.DOUBLE);
+        addBasic(Character.class, "char", BasicSerializer.CHARACTER_OBJECT);
+        addBasic(String.class, "string", BasicSerializer.STRING);
+        addBasic(Object.class, "object", BasicSerializer.OBJECT);
+        addBasic(java.util.Date.class, "date", BasicSerializer.DATE);
+
+        addBasic(boolean.class, "boolean", BasicSerializer.BOOLEAN);
+        addBasic(byte.class, "byte", BasicSerializer.BYTE);
+        addBasic(short.class, "short", BasicSerializer.SHORT);
+        addBasic(int.class, "int", BasicSerializer.INTEGER);
+        addBasic(long.class, "long", BasicSerializer.LONG);
+        addBasic(float.class, "float", BasicSerializer.FLOAT);
+        addBasic(double.class, "double", BasicSerializer.DOUBLE);
+        addBasic(char.class, "char", BasicSerializer.CHARACTER);
+
+        addBasic(boolean[].class, "[boolean", BasicSerializer.BOOLEAN_ARRAY);
+        addBasic(byte[].class, "[byte", BasicSerializer.BYTE_ARRAY);
+        _staticSerializerMap.put(byte[].class.getName(), ByteArraySerializer.SER);
+        addBasic(short[].class, "[short", BasicSerializer.SHORT_ARRAY);
+        addBasic(int[].class, "[int", BasicSerializer.INTEGER_ARRAY);
+        addBasic(long[].class, "[long", BasicSerializer.LONG_ARRAY);
+        addBasic(float[].class, "[float", BasicSerializer.FLOAT_ARRAY);
+        addBasic(double[].class, "[double", BasicSerializer.DOUBLE_ARRAY);
+        addBasic(char[].class, "[char", BasicSerializer.CHARACTER_ARRAY);
+        addBasic(String[].class, "[string", BasicSerializer.STRING_ARRAY);
+        addBasic(Object[].class, "[object", BasicSerializer.OBJECT_ARRAY);
+
+        Deserializer objectDeserializer = new JavaDeserializer(Object.class, fieldFactory);
+        _staticDeserializerMap.put("object", objectDeserializer);
+        _staticClassNameMap.put("object", objectDeserializer);
+
+        _staticSerializerMap.put(Class.class.getName(), new ClassSerializer());
+
+        _staticDeserializerMap.put(Number.class.getName(), new BasicDeserializer(BasicSerializer.NUMBER));
+
+    /*
+    for (Class cl : new Class[] { BigDecimal.class, File.class, ObjectName.class }) {
+      _staticSerializerMap.put(cl, StringValueSerializer.SER);
+      _staticDeserializerMap.put(cl, new StringValueDeserializer(cl));
+    }
+
+    _staticSerializerMap.put(ObjectName.class, StringValueSerializer.SER);
+    try {
+      _staticDeserializerMap.put(ObjectName.class,
+                           new StringValueDeserializer(ObjectName.class));
+    } catch (Throwable e) {
+    }
+    */
+
+        _staticSerializerMap.put(InetAddress.class.getName(),
+                InetAddressSerializer.create());
+
+        _staticSerializerMap.put(java.sql.Date.class.getName(),
+                new SqlDateSerializer());
+        _staticSerializerMap.put(java.sql.Time.class.getName(),
+                new SqlDateSerializer());
+        _staticSerializerMap.put(java.sql.Timestamp.class.getName(),
+                new SqlDateSerializer());
+
+        _staticDeserializerMap.put(java.sql.Date.class.getName(),
+                new SqlDateDeserializer(java.sql.Date.class));
+        _staticDeserializerMap.put(java.sql.Time.class.getName(),
+                new SqlDateDeserializer(java.sql.Time.class));
+        _staticDeserializerMap.put(java.sql.Timestamp.class.getName(),
+                new SqlDateDeserializer(java.sql.Timestamp.class));
+
+        // hessian/3bb5
+        _staticDeserializerMap.put(StackTraceElement.class.getName(),
+                new StackTraceElementDeserializer(fieldFactory));
+
+        ClassLoader systemClassLoader = null;
+        try {
+            systemClassLoader = ClassLoader.getSystemClassLoader();
+        } catch (Exception e) {
+        }
+
+        _systemClassLoader = systemClassLoader;
+    }
+
+    private final HashSet<String> _serializerFiles = new HashSet<String>();
+    private final HashSet<String> _deserializerFiles = new HashSet<String>();
+    private final HashMap<String, Serializer> _serializerClassMap
+            = new HashMap<String, Serializer>();
+    private final ConcurrentHashMap<String, Serializer> _customSerializerMap
+            = new ConcurrentHashMap<String, Serializer>();
+    private final HashMap<Class<?>, Serializer> _serializerInterfaceMap
+            = new HashMap<Class<?>, Serializer>();
+    private final HashMap<String, Deserializer> _deserializerClassMap
+            = new HashMap<String, Deserializer>();
+    private final HashMap<String, Deserializer> _deserializerClassNameMap
+            = new HashMap<String, Deserializer>();
+    private final ConcurrentHashMap<String, Deserializer> _customDeserializerMap
+            = new ConcurrentHashMap<String, Deserializer>();
+    private final HashMap<Class<?>, Deserializer> _deserializerInterfaceMap
+            = new HashMap<Class<?>, Deserializer>();
+    private ContextSerializerFactory _parent;
+    private WeakReference<ClassLoader> _loaderRef;
+
+    public ContextSerializerFactory(ContextSerializerFactory parent,
+                                    ClassLoader loader) {
+        if (loader == null)
+            loader = _systemClassLoader;
+
+        _loaderRef = new WeakReference<ClassLoader>(loader);
+
+        init();
+    }
+
+    public static ContextSerializerFactory create() {
+        return create(Thread.currentThread().getContextClassLoader());
+    }
+
+    public static ContextSerializerFactory create(ClassLoader loader) {
+        synchronized (_contextRefMap) {
+            SoftReference<ContextSerializerFactory> factoryRef
+                    = _contextRefMap.get(loader);
+
+            ContextSerializerFactory factory = null;
+
+            if (factoryRef != null)
+                factory = factoryRef.get();
+
+            if (factory == null) {
+                ContextSerializerFactory parent = null;
+
+                if (loader != null)
+                    parent = create(loader.getParent());
+
+                factory = new ContextSerializerFactory(parent, loader);
+                factoryRef = new SoftReference<ContextSerializerFactory>(factory);
+
+                _contextRefMap.put(loader, factoryRef);
+            }
+
+            return factory;
+        }
+    }
+
+    private static void addBasic(Class cl, String typeName, int type) {
+        _staticSerializerMap.put(cl.getName(), new BasicSerializer(type));
+
+        Deserializer deserializer = new BasicDeserializer(type);
+        _staticDeserializerMap.put(cl.getName(), deserializer);
+        _staticClassNameMap.put(typeName, deserializer);
+    }
+
+    public ClassLoader getClassLoader() {
+        WeakReference<ClassLoader> loaderRef = _loaderRef;
+
+        if (loaderRef != null)
+            return loaderRef.get();
+        else
+            return null;
+    }
+
+    /**
+     * Returns the serializer for a given class.
+     */
+    public Serializer getSerializer(String className) {
+        Serializer serializer = _serializerClassMap.get(className);
+
+        if (serializer == AbstractSerializer.NULL)
+            return null;
+        else
+            return serializer;
+    }
+
+    /**
+     * Returns a custom serializer the class
+     *
+     * @param cl the class of the object that needs to be serialized.
+     * @return a serializer object for the serialization.
+     */
+    public Serializer getCustomSerializer(Class cl) {
+        Serializer serializer = _customSerializerMap.get(cl.getName());
+
+        if (serializer == AbstractSerializer.NULL)
+            return null;
+        else if (serializer != null)
+            return serializer;
+
+        try {
+            Class serClass = Class.forName(cl.getName() + "HessianSerializer",
+                    false, cl.getClassLoader());
+
+            Serializer ser = (Serializer) serClass.newInstance();
+
+            _customSerializerMap.put(cl.getName(), ser);
+
+            return ser;
+        } catch (ClassNotFoundException e) {
+            log.log(Level.ALL, e.toString(), e);
+        } catch (Exception e) {
+            throw new HessianException(e);
+        }
+
+        _customSerializerMap.put(cl.getName(), AbstractSerializer.NULL);
+
+        return null;
+    }
+
+    /**
+     * Returns the deserializer for a given class.
+     */
+    public Deserializer getDeserializer(String className) {
+        Deserializer deserializer = _deserializerClassMap.get(className);
+
+        if (deserializer != null && deserializer != AbstractDeserializer.NULL) {
+            return deserializer;
+        }
+
+        deserializer = _deserializerInterfaceMap.get(className);
+
+        if (deserializer != null && deserializer != AbstractDeserializer.NULL) {
+            return deserializer;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a custom deserializer the class
+     *
+     * @param cl the class of the object that needs to be deserialized.
+     * @return a deserializer object for the deserialization.
+     */
+    public Deserializer getCustomDeserializer(Class cl) {
+        Deserializer deserializer = _customDeserializerMap.get(cl.getName());
+
+        if (deserializer == AbstractDeserializer.NULL)
+            return null;
+        else if (deserializer != null)
+            return deserializer;
+
+        try {
+            Class serClass = Class.forName(cl.getName() + "HessianDeserializer",
+                    false, cl.getClassLoader());
+
+            Deserializer ser = (Deserializer) serClass.newInstance();
+
+            _customDeserializerMap.put(cl.getName(), ser);
+
+            return ser;
+        } catch (ClassNotFoundException e) {
+            log.log(Level.ALL, e.toString(), e);
+        } catch (Exception e) {
+            throw new HessianException(e);
+        }
+
+        _customDeserializerMap.put(cl.getName(), AbstractDeserializer.NULL);
+
+        return null;
+    }
+
+    /**
+     * Initialize the factory
+     */
+    private void init() {
+        if (_parent != null) {
+            _serializerFiles.addAll(_parent._serializerFiles);
+            _deserializerFiles.addAll(_parent._deserializerFiles);
+
+            _serializerClassMap.putAll(_parent._serializerClassMap);
+            _deserializerClassMap.putAll(_parent._deserializerClassMap);
+        }
+
+        if (_parent == null) {
+            _serializerClassMap.putAll(_staticSerializerMap);
+            _deserializerClassMap.putAll(_staticDeserializerMap);
+            _deserializerClassNameMap.putAll(_staticClassNameMap);
+        }
+
+        HashMap<Class, Class> classMap;
+
+        classMap = new HashMap<Class, Class>();
+        initSerializerFiles("META-INF/dubbo/hessian/serializers",
+                _serializerFiles,
+                classMap,
+                Serializer.class);
+
+        for (Map.Entry<Class, Class> entry : classMap.entrySet()) {
+            try {
+                Serializer ser = (Serializer) entry.getValue().newInstance();
+
+                if (entry.getKey().isInterface())
+                    _serializerInterfaceMap.put(entry.getKey(), ser);
+                else
+                    _serializerClassMap.put(entry.getKey().getName(), ser);
+            } catch (Exception e) {
+                throw new HessianException(e);
+            }
+        }
+
+        classMap = new HashMap<Class, Class>();
+        initSerializerFiles("META-INF/dubbo/hessian/deserializers",
+                _deserializerFiles,
+                classMap,
+                Deserializer.class);
+
+        for (Map.Entry<Class, Class> entry : classMap.entrySet()) {
+            try {
+                Deserializer ser = (Deserializer) entry.getValue().newInstance();
+
+                if (entry.getKey().isInterface())
+                    _deserializerInterfaceMap.put(entry.getKey(), ser);
+                else {
+                    _deserializerClassMap.put(entry.getKey().getName(), ser);
+                }
+            } catch (Exception e) {
+                throw new HessianException(e);
+            }
+        }
+    }
+
+    private void initSerializerFiles(String fileName,
+                                     HashSet<String> fileList,
+                                     HashMap<Class, Class> classMap,
+                                     Class type) {
+        try {
+            ClassLoader classLoader = getClassLoader();
+
+            // on systems with the security manager enabled, the system classloader
+            // is null
+            if (classLoader == null)
+                return;
+
+            Enumeration iter;
+
+            iter = classLoader.getResources(fileName);
+            while (iter.hasMoreElements()) {
+                URL url = (URL) iter.nextElement();
+
+                if (fileList.contains(url.toString()))
+                    continue;
+
+                fileList.add(url.toString());
+
+                InputStream is = null;
+                try {
+                    is = url.openStream();
+
+                    Properties props = new Properties();
+                    props.load(is);
+
+                    for (Map.Entry entry : props.entrySet()) {
+                        String apiName = (String) entry.getKey();
+                        String serializerName = (String) entry.getValue();
+
+                        Class apiClass = null;
+                        Class serializerClass = null;
+
+                        try {
+                            apiClass = Class.forName(apiName, false, classLoader);
+                        } catch (ClassNotFoundException e) {
+                            log.fine(url + ": " + apiName + " is not available in this context: " + getClassLoader());
+                            continue;
+                        }
+
+                        try {
+                            serializerClass = Class.forName(serializerName, false, classLoader);
+                        } catch (ClassNotFoundException e) {
+                            log.fine(url + ": " + serializerName + " is not available in this context: " + getClassLoader());
+                            continue;
+                        }
+
+                        if (!type.isAssignableFrom(serializerClass))
+                            throw new HessianException(url + ": " + serializerClass.getName() + " is invalid because it does not implement " + type.getName());
+
+                        classMap.put(apiClass, serializerClass);
+                    }
+                } finally {
+                    if (is != null)
+                        is.close();
+                }
+            }
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HessianException(e);
+        }
+    }
+}
+
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/Deserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/Deserializer.java
index d8b6824..9008b8e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/Deserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/Deserializer.java
@@ -51,11 +51,14 @@
 import java.io.IOException;
 
 /**
- * Deserializing an object.
- *
+ * Deserializing an object. Custom deserializers should extend
+ * from AbstractDeserializer to avoid issues with signature
+ * changes.
  */
 public interface Deserializer {
-    public Class getType();
+    public Class<?> getType();
+
+    public boolean isReadResolve();
 
     public Object readObject(AbstractHessianInput in)
             throws IOException;
@@ -63,47 +66,43 @@
     public Object readList(AbstractHessianInput in, int length)
             throws IOException;
 
-    /**
-     * deserialize list object from expect type.
-     *
-     * @param in
-     * @param length
-     * @param expectType
-     * @return
-     * @throws IOException
-     */
-    public Object readList(AbstractHessianInput in, int length, Class<?> expectType)
-            throws IOException;
-
     public Object readLengthList(AbstractHessianInput in, int length)
             throws IOException;
 
-    /**
-     * deserialize list object from expect type.
-     *
-     * @param in
-     * @param length
-     * @param expectType
-     * @return
-     * @throws IOException
-     */
-    public Object readLengthList(AbstractHessianInput in, int length, Class<?> expectType)
-            throws IOException;
-
     public Object readMap(AbstractHessianInput in)
             throws IOException;
 
     /**
-     *  deserialize map object from expect key and value type.
-     * @param in
-     * @param expectKeyType
-     * @param expectValueType
-     * @return
+     * Creates an empty array for the deserializers field
+     * entries.
+     *
+     * @param len number of fields to be read
+     * @return empty array of the proper field type.
+     */
+    public Object[] createFields(int len);
+
+    /**
+     * Returns the deserializer's field reader for the given name.
+     *
+     * @param name the field name
+     * @return the deserializer's internal field reader
+     */
+    public Object createField(String name);
+
+    /**
+     * Reads the object from the input stream, given the field
+     * definition.
+     *
+     * @param in     the input stream
+     * @param fields the deserializer's own field marshal
+     * @return the new object
      * @throws IOException
      */
-    public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType)
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
             throws IOException;
 
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object readObject(AbstractHessianInput in,
+                             String[] fieldNames)
             throws IOException;
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumDeserializer.java
index d1876a8..235bb32 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumDeserializer.java
@@ -75,12 +75,10 @@
         }
     }
 
-    @Override
     public Class getType() {
         return _enumType;
     }
 
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         String name = null;
@@ -104,8 +102,9 @@
     }
 
     @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object readObject(AbstractHessianInput in, Object[] fields)
             throws IOException {
+        String[] fieldNames = (String[]) fields;
         String name = null;
 
         for (int i = 0; i < fieldNames.length; i++) {
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSerializer.java
index 6ebf138..0835cc8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSerializer.java
@@ -69,13 +69,12 @@
         }
     }
 
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         if (out.addRef(obj))
             return;
 
-        Class cl = obj.getClass();
+        Class<?> cl = obj.getClass();
 
         if (!cl.isEnum() && cl.getSuperclass().isEnum())
             cl = cl.getSuperclass();
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationDeserializer.java
index b912fa8..912e492 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationDeserializer.java
@@ -64,7 +64,6 @@
         return _deserializer;
     }
 
-    @Override
     public Object readList(AbstractHessianInput in, int length)
             throws IOException {
         Vector list = new Vector();
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationSerializer.java
index 0665132..aa8d25c 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/EnumerationSerializer.java
@@ -64,7 +64,6 @@
         return _serializer;
     }
 
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         Enumeration iter = (Enumeration) obj;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2.java
similarity index 85%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2.java
index be505fb..467ea9f 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -48,9 +48,12 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.util.logging.Logger;
+import java.io.IOException;
 
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Serializing an object for known object types.
+ */
+public interface FieldDeserializer2 {
+    void deserialize(AbstractHessianInput in, Object obj)
+            throws IOException;
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2Factory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2Factory.java
new file mode 100644
index 0000000..28c8660
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2Factory.java
@@ -0,0 +1,457 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class FieldDeserializer2Factory {
+    private static final Logger log
+            = Logger.getLogger(JavaDeserializer.class.getName());
+
+    public static FieldDeserializer2Factory create() {
+        boolean isEnableUnsafeSerializer = (UnsafeSerializer.isEnabled()
+                && UnsafeDeserializer.isEnabled());
+
+        if (isEnableUnsafeSerializer) {
+            return new FieldDeserializer2FactoryUnsafe();
+        } else {
+            return new FieldDeserializer2Factory();
+        }
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    protected static Object getParamArg(Class<?> cl) {
+        if (!cl.isPrimitive())
+            return null;
+        else if (boolean.class.equals(cl))
+            return Boolean.FALSE;
+        else if (byte.class.equals(cl))
+            return new Byte((byte) 0);
+        else if (short.class.equals(cl))
+            return new Short((short) 0);
+        else if (char.class.equals(cl))
+            return new Character((char) 0);
+        else if (int.class.equals(cl))
+            return Integer.valueOf(0);
+        else if (long.class.equals(cl))
+            return Long.valueOf(0);
+        else if (float.class.equals(cl))
+            return Float.valueOf(0);
+        else if (double.class.equals(cl))
+            return Double.valueOf(0);
+        else
+            throw new UnsupportedOperationException();
+    }
+
+    static void logDeserializeError(Field field, Object obj, Object value,
+                                    Throwable e)
+            throws IOException {
+        String fieldName = (field.getDeclaringClass().getName()
+                + "." + field.getName());
+
+        if (e instanceof HessianFieldException)
+            throw (HessianFieldException) e;
+        else if (e instanceof IOException)
+            throw new HessianFieldException(fieldName + ": " + e.getMessage(), e);
+
+        if (value != null)
+            throw new HessianFieldException(fieldName + ": " + value.getClass().getName()
+                    + " cannot be assigned to '" + field.getType().getName() + "'", e);
+        else
+            throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    FieldDeserializer2 create(Field field) {
+        if (Modifier.isTransient(field.getModifiers())
+                || Modifier.isStatic(field.getModifiers())) {
+            return NullFieldDeserializer.DESER;
+        }
+
+        // XXX: could parameterize the handler to only deal with public
+        try {
+            field.setAccessible(true);
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+
+        Class<?> type = field.getType();
+        FieldDeserializer2 deser;
+
+        if (String.class.equals(type))
+            deser = new StringFieldDeserializer(field);
+        else if (byte.class.equals(type)) {
+            deser = new ByteFieldDeserializer(field);
+        } else if (short.class.equals(type)) {
+            deser = new ShortFieldDeserializer(field);
+        } else if (int.class.equals(type)) {
+            deser = new IntFieldDeserializer(field);
+        } else if (long.class.equals(type)) {
+            deser = new LongFieldDeserializer(field);
+        } else if (float.class.equals(type)) {
+            deser = new FloatFieldDeserializer(field);
+        } else if (double.class.equals(type)) {
+            deser = new DoubleFieldDeserializer(field);
+        } else if (boolean.class.equals(type)) {
+            deser = new BooleanFieldDeserializer(field);
+        } else if (java.sql.Date.class.equals(type)) {
+            deser = new SqlDateFieldDeserializer(field);
+        } else if (java.sql.Timestamp.class.equals(type)) {
+            deser = new SqlTimestampFieldDeserializer(field);
+        } else if (java.sql.Time.class.equals(type)) {
+            deser = new SqlTimeFieldDeserializer(field);
+        } else {
+            deser = new ObjectFieldDeserializer(field);
+        }
+
+        return deser;
+    }
+
+    static class NullFieldDeserializer implements FieldDeserializer2 {
+        static NullFieldDeserializer DESER = new NullFieldDeserializer();
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            in.readObject();
+        }
+    }
+
+    static class ObjectFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        ObjectFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            Object value = null;
+
+            try {
+                value = in.readObject(_field.getType());
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class BooleanFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        BooleanFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            boolean value = false;
+
+            try {
+                value = in.readBoolean();
+
+                _field.setBoolean(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ByteFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        ByteFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _field.setByte(obj, (byte) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ShortFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        ShortFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _field.setShort(obj, (short) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class IntFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        IntFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _field.setInt(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class LongFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        LongFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            long value = 0;
+
+            try {
+                value = in.readLong();
+
+                _field.setLong(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class FloatFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        FloatFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = in.readDouble();
+
+                _field.setFloat(obj, (float) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class DoubleFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        DoubleFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = in.readDouble();
+
+                _field.setDouble(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class StringFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        StringFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            String value = null;
+
+            try {
+                value = in.readString();
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlDateFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        SqlDateFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Date value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+
+                if (date != null) {
+                    value = new java.sql.Date(date.getTime());
+
+                    _field.set(obj, value);
+                } else {
+                    _field.set(obj, null);
+                }
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlTimestampFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        SqlTimestampFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Timestamp value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+
+                if (date != null) {
+                    value = new java.sql.Timestamp(date.getTime());
+
+                    _field.set(obj, value);
+                } else {
+                    _field.set(obj, null);
+                }
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlTimeFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+
+        SqlTimeFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Time value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+
+                if (date != null) {
+                    value = new java.sql.Time(date.getTime());
+
+                    _field.set(obj, value);
+                } else {
+                    _field.set(obj, null);
+                }
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2FactoryUnsafe.java b/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2FactoryUnsafe.java
new file mode 100644
index 0000000..8aae28f
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/FieldDeserializer2FactoryUnsafe.java
@@ -0,0 +1,527 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import sun.misc.Unsafe;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class FieldDeserializer2FactoryUnsafe extends FieldDeserializer2Factory {
+    private static final Logger log
+            = Logger.getLogger(JavaDeserializer.class.getName());
+
+    private static boolean _isEnabled;
+    @SuppressWarnings("restriction")
+    private static Unsafe _unsafe;
+
+    static {
+        boolean isEnabled = false;
+
+        try {
+            Class<?> unsafe = Class.forName("sun.misc.Unsafe");
+            Field theUnsafe = null;
+            for (Field field : unsafe.getDeclaredFields()) {
+                if (field.getName().equals("theUnsafe"))
+                    theUnsafe = field;
+            }
+
+            if (theUnsafe != null) {
+                theUnsafe.setAccessible(true);
+                _unsafe = (Unsafe) theUnsafe.get(null);
+            }
+
+            isEnabled = _unsafe != null;
+
+            String unsafeProp = System.getProperty("com.caucho.hessian.unsafe");
+
+            if ("false".equals(unsafeProp))
+                isEnabled = false;
+        } catch (Throwable e) {
+            log.log(Level.FINER, e.toString(), e);
+        }
+
+        _isEnabled = isEnabled;
+    }
+
+    static void logDeserializeError(Field field, Object obj, Object value,
+                                    Throwable e)
+            throws IOException {
+        String fieldName = (field.getDeclaringClass().getName()
+                + "." + field.getName());
+
+        if (e instanceof HessianFieldException)
+            throw (HessianFieldException) e;
+        else if (e instanceof IOException)
+            throw new HessianFieldException(fieldName + ": " + e.getMessage(), e);
+
+        if (value != null)
+            throw new HessianFieldException(fieldName + ": " + value.getClass().getName()
+                    + " cannot be assigned to '" + field.getType().getName() + "'", e);
+        else
+            throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    @Override
+    public FieldDeserializer2 create(Field field) {
+        if (Modifier.isTransient(field.getModifiers())
+                || Modifier.isStatic(field.getModifiers())) {
+            return NullFieldDeserializer.DESER;
+        }
+
+    /*
+    // XXX: could parameterize the handler to only deal with public
+    try {
+      field.setAccessible(true);
+    } catch (Throwable e) {
+      e.printStackTrace();
+    }
+    */
+
+        Class<?> type = field.getType();
+        FieldDeserializer2 deser;
+
+        if (String.class.equals(type)) {
+            deser = new StringFieldDeserializer(field);
+        } else if (byte.class.equals(type)) {
+            deser = new ByteFieldDeserializer(field);
+        } else if (char.class.equals(type)) {
+            deser = new CharFieldDeserializer(field);
+        } else if (short.class.equals(type)) {
+            deser = new ShortFieldDeserializer(field);
+        } else if (int.class.equals(type)) {
+            deser = new IntFieldDeserializer(field);
+        } else if (long.class.equals(type)) {
+            deser = new LongFieldDeserializer(field);
+        } else if (float.class.equals(type)) {
+            deser = new FloatFieldDeserializer(field);
+        } else if (double.class.equals(type)) {
+            deser = new DoubleFieldDeserializer(field);
+        } else if (boolean.class.equals(type)) {
+            deser = new BooleanFieldDeserializer(field);
+        } else if (java.sql.Date.class.equals(type)) {
+            deser = new SqlDateFieldDeserializer(field);
+        } else if (java.sql.Timestamp.class.equals(type)) {
+            deser = new SqlTimestampFieldDeserializer(field);
+        } else if (java.sql.Time.class.equals(type)) {
+            deser = new SqlTimeFieldDeserializer(field);
+        } else {
+            deser = new ObjectFieldDeserializer(field);
+        }
+
+        return deser;
+    }
+
+    static class NullFieldDeserializer implements FieldDeserializer2 {
+        static NullFieldDeserializer DESER = new NullFieldDeserializer();
+
+        @Override
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            in.readObject();
+        }
+    }
+
+    static class ObjectFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        ObjectFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            Object value = null;
+
+            try {
+                value = in.readObject(_field.getType());
+
+                _unsafe.putObject(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class BooleanFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        BooleanFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            boolean value = false;
+
+            try {
+                value = in.readBoolean();
+
+                _unsafe.putBoolean(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ByteFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        ByteFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _unsafe.putByte(obj, _offset, (byte) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class CharFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        CharFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            String value = null;
+
+            try {
+                value = in.readString();
+
+                char ch;
+
+                if (value != null && value.length() > 0)
+                    ch = value.charAt(0);
+                else
+                    ch = 0;
+
+                _unsafe.putChar(obj, _offset, ch);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ShortFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        ShortFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _unsafe.putShort(obj, _offset, (short) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class IntFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        IntFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _unsafe.putInt(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class LongFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        LongFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            long value = 0;
+
+            try {
+                value = in.readLong();
+
+                _unsafe.putLong(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class FloatFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        FloatFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = in.readDouble();
+
+                _unsafe.putDouble(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class DoubleFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        DoubleFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = in.readDouble();
+
+                _unsafe.putDouble(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class StringFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        StringFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            String value = null;
+
+            try {
+                value = in.readString();
+
+                _unsafe.putObject(obj, _offset, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlDateFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        SqlDateFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Date value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+
+                if (date != null) {
+                    value = new java.sql.Date(date.getTime());
+
+                    _unsafe.putObject(obj, _offset, value);
+                } else {
+                    _unsafe.putObject(obj, _offset, null);
+                }
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlTimestampFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        SqlTimestampFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Timestamp value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+
+                if (date != null) {
+                    value = new java.sql.Timestamp(date.getTime());
+
+                    _unsafe.putObject(obj, _offset, value);
+                } else {
+                    _unsafe.putObject(obj, _offset, null);
+                }
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlTimeFieldDeserializer implements FieldDeserializer2 {
+        private final Field _field;
+        private final long _offset;
+
+        @SuppressWarnings("restriction")
+        SqlTimeFieldDeserializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(_field);
+        }
+
+        @SuppressWarnings("restriction")
+        public void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Time value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+
+                if (date != null) {
+                    value = new java.sql.Time(date.getTime());
+
+                    _unsafe.putObject(obj, _offset, value);
+                } else {
+                    _unsafe.putObject(obj, _offset, null);
+                }
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/FileDeserializer.java
similarity index 88%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/FileDeserializer.java
index be505fb..82bf8be 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/FileDeserializer.java
@@ -48,9 +48,19 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.util.logging.Logger;
+import java.io.File;
 
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Deserializing a File
+ */
+public class FileDeserializer extends AbstractStringValueDeserializer {
+    @Override
+    public Class getType() {
+        return File.class;
+    }
+
+    @Override
+    protected Object create(String value) {
+        return new File(value);
+    }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/FloatHandle.java
similarity index 78%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/FloatHandle.java
index 72a9822..a790285 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/FloatHandle.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -48,18 +48,30 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
+import java.io.Serializable;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Handle for Java Float objects.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+public class FloatHandle implements Serializable {
+    private double _value;
+
+    private FloatHandle() {
+    }
+
+    public FloatHandle(float value) {
+        _value = value;
+    }
+
+    public double getValue() {
+        return _value;
+    }
+
+    public Object readResolve() {
+        return Double.parseDouble(String.valueOf(((Number) _value).floatValue()));
+    }
+
+    public String toString() {
+        return getClass().getSimpleName() + "[" + _value + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Input.java b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Input.java
index 0466396..a09bbbf 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Input.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Input.java
@@ -57,17 +57,15 @@
 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);
@@ -84,38 +82,34 @@
     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 SIZE = 1024;
     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];
+    // standard, unmodified factory for deserializing objects
+    protected SerializerFactory _defaultSerializerFactory;
     // factory for deserializing objects in the input stream
     protected SerializerFactory _serializerFactory;
-    protected ArrayList _refs;
-    protected ArrayList _classDefs;
-    protected ArrayList _types;
+    protected ArrayList<Object> _refs
+            = new ArrayList<Object>();
+    protected ArrayList<ObjectDefinition> _classDefs
+            = new ArrayList<ObjectDefinition>();
+    protected ArrayList<String> _types
+            = new ArrayList<String>();
     // 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 Throwable _replyFault;
+
     private StringBuilder _sbuf = new StringBuilder();
+
     // true if this is the last chunk
     private boolean _isLastChunk;
     // the chunk length
@@ -128,36 +122,62 @@
      * @param is the underlying input stream.
      */
     public Hessian2Input(InputStream is) {
-        _is = is;
+        init(is);
+    }
+
+    private static Field getDetailMessageField() {
+        if (_detailMessageField == null) {
+            try {
+                _detailMessageField = Throwable.class.getDeclaredField("detailMessage");
+                _detailMessageField.setAccessible(true);
+            } catch (Throwable e) {
+            }
+        }
+
+        return _detailMessageField;
     }
 
     /**
      * Gets the serializer factory.
      */
     public SerializerFactory getSerializerFactory() {
+        // the default serializer factory cannot be modified by external
+        // callers
+        if (_serializerFactory == _defaultSerializerFactory) {
+            _serializerFactory = new SerializerFactory();
+        }
+
         return _serializerFactory;
     }
 
     /**
      * Sets the serializer factory.
      */
-    @Override
     public void setSerializerFactory(SerializerFactory factory) {
         _serializerFactory = factory;
     }
 
     /**
-     * Gets the serializer factory, creating a default if necessary.
+     * Gets the serializer factory.
      */
-    public final SerializerFactory findSerializerFactory() {
+    protected final SerializerFactory findSerializerFactory() {
         SerializerFactory factory = _serializerFactory;
 
-        if (factory == null)
-            _serializerFactory = factory = new SerializerFactory();
+        if (factory == null) {
+            factory = SerializerFactory.createDefault();
+            _defaultSerializerFactory = factory;
+            _serializerFactory = factory;
+        }
 
         return factory;
     }
 
+    public void allow(String pattern) {
+        ClassFactory factory = getSerializerFactory().getClassFactory();
+
+        factory.allow(pattern);
+    }
+
     public boolean isCloseStreamOnClose() {
         return _isCloseStreamOnClose;
     }
@@ -169,7 +189,6 @@
     /**
      * Returns the calls method
      */
-    @Override
     public String getMethod() {
         return _method;
     }
@@ -188,44 +207,19 @@
         reset();
     }
 
-    public void reset() {
+    public void initPacket(InputStream is) {
+        _is = is;
+
         resetReferences();
-
-        if (_classDefs != null) {
-            _classDefs.clear();
-        }
-
-        if (_types != null) {
-            _types.clear();
-        }
-
-        _offset = 0;
-        _length = 0;
-    }
-
-    @Override
-    public boolean checkAndReadNull() {
-        try {
-            int tag = read();
-            if ('N' == tag) {
-                return true;
-            }
-            if (-1 != tag) {
-                _offset--;
-            }
-        } catch (IOException ignored) {
-        }
-        return false;
     }
 
     /**
      * Starts reading the call
-     * <p>
+     *
      * <pre>
      * c major minor
      * </pre>
      */
-    @Override
     public int readCall()
             throws IOException {
         int tag = read();
@@ -238,7 +232,7 @@
 
     /**
      * Starts reading the envelope
-     * <p>
+     *
      * <pre>
      * E major minor
      * </pre>
@@ -265,9 +259,9 @@
 
     /**
      * Completes reading the envelope
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * Z
      * </pre>
@@ -282,14 +276,13 @@
 
     /**
      * 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();
@@ -299,7 +292,7 @@
 
     /**
      * Returns the number of method arguments
-     * <p>
+     *
      * <pre>
      * int
      * </pre>
@@ -312,15 +305,14 @@
 
     /**
      * 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();
@@ -328,15 +320,26 @@
         readMethod();
     }
 
+    public Object[] readArguments()
+            throws IOException {
+        int len = readInt();
+
+        Object[] args = new Object[len];
+
+        for (int i = 0; i < len; i++)
+            args[i] = readObject();
+
+        return args;
+    }
+
     /**
      * Completes reading the call
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * </pre>
      */
-    @Override
     public void completeCall()
             throws IOException {
     }
@@ -377,14 +380,13 @@
 
     /**
      * 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 (?)
@@ -403,9 +405,11 @@
         if (detail instanceof Throwable) {
             _replyFault = (Throwable) detail;
 
-            if (message != null && _detailMessageField != null) {
+            Field detailMessageField = getDetailMessageField();
+
+            if (message != null && detailMessageField != null) {
                 try {
-                    _detailMessageField.set(_replyFault, message);
+                    detailMessageField.set(_replyFault, message);
                 } catch (Throwable e) {
                 }
             }
@@ -422,23 +426,22 @@
 
     /**
      * 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>
@@ -453,20 +456,19 @@
 
     /**
      * 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>
+     * Starts reading a packet
+     *
      * <pre>
      * p major minor
      * </pre>
@@ -475,9 +477,10 @@
             throws IOException {
         int tag = read();
 
-        if (tag != 'p' && tag != 'P') {
+        if (tag == 'p') {
+        } else if (tag == 'P') {
+        } else
             throw error("expected Hessian message ('p') at " + codeName(tag));
-        }
 
         int major = read();
         int minor = read();
@@ -487,9 +490,9 @@
 
     /**
      * Completes reading the message
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * z
      * </pre>
@@ -504,12 +507,11 @@
 
     /**
      * Reads a null
-     * <p>
+     *
      * <pre>
      * N
      * </pre>
      */
-    @Override
     public void readNull()
             throws IOException {
         int tag = read();
@@ -525,13 +527,12 @@
 
     /**
      * Reads a boolean
-     * <p>
+     *
      * <pre>
      * T
      * F
      * </pre>
      */
-    @Override
     public boolean readBoolean()
             throws IOException {
         int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
@@ -761,7 +762,7 @@
 
     /**
      * Reads a short
-     * <p>
+     *
      * <pre>
      * I b32 b24 b16 b8
      * </pre>
@@ -773,12 +774,11 @@
 
     /**
      * 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();
@@ -993,12 +993,11 @@
 
     /**
      * Reads a long
-     * <p>
+     *
      * <pre>
      * L b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
      */
-    @Override
     public long readLong()
             throws IOException {
         int tag = read();
@@ -1209,7 +1208,7 @@
 
     /**
      * Reads a float
-     * <p>
+     *
      * <pre>
      * D b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
@@ -1221,12 +1220,11 @@
 
     /**
      * Reads a double
-     * <p>
+     *
      * <pre>
      * D b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
      */
-    @Override
     public double readDouble()
             throws IOException {
         int tag = read();
@@ -1434,12 +1432,11 @@
 
     /**
      * Reads a date.
-     * <p>
+     *
      * <pre>
      * T b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
      */
-    @Override
     public long readUTCDate()
             throws IOException {
         int tag = read();
@@ -1468,6 +1465,7 @@
             _chunkLength = 0;
             return -1;
         }
+
         int tag = read();
 
         switch (tag) {
@@ -1554,6 +1552,14 @@
                     _chunkLength = tag - 0x00;
                     break;
 
+                case 0x30:
+                case 0x31:
+                case 0x32:
+                case 0x33:
+                    _isLastChunk = true;
+                    _chunkLength = (tag - 0x30) * 256 + read();
+                    break;
+
                 default:
                     throw expect("string", tag);
             }
@@ -1582,6 +1588,51 @@
                         _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;
+
+                    case 0x30:
+                    case 0x31:
+                    case 0x32:
+                    case 0x33:
+                        _isLastChunk = true;
+                        _chunkLength = (tag - 0x30) * 256 + read();
+                        break;
+
                     default:
                         throw expect("string", tag);
                 }
@@ -1600,12 +1651,11 @@
 
     /**
      * Reads a string
-     * <p>
+     *
      * <pre>
      * S b16 b8 string value
      * </pre>
      */
-    @Override
     public String readString()
             throws IOException {
         int tag = read();
@@ -1860,8 +1910,9 @@
 
                 _sbuf.setLength(0);
 
-                while ((ch = parseChar()) >= 0)
+                while ((ch = parseChar()) >= 0) {
                     _sbuf.append((char) ch);
+                }
 
                 return _sbuf.toString();
 
@@ -1886,12 +1937,11 @@
 
     /**
      * Reads a byte array
-     * <p>
+     *
      * <pre>
      * B b16 b8 data value
      * </pre>
      */
-    @Override
     public byte[] readBytes()
             throws IOException {
         int tag = read();
@@ -1900,9 +1950,9 @@
             case 'N':
                 return null;
 
-            case 'B':
+            case BC_BINARY:
             case BC_BINARY_CHUNK:
-                _isLastChunk = tag == 'B';
+                _isLastChunk = tag == BC_BINARY;
                 _chunkLength = (read() << 8) + read();
 
                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -1934,9 +1984,15 @@
 
                 byte[] buffer = new byte[_chunkLength];
 
-                int k = 0;
-                while ((data = parseByte()) >= 0)
-                    buffer[k++] = (byte) data;
+                int offset = 0;
+                while (offset < _chunkLength) {
+                    int sublen = read(buffer, 0, _chunkLength - offset);
+
+                    if (sublen <= 0)
+                        break;
+
+                    offset += sublen;
+                }
 
                 return buffer;
             }
@@ -1949,10 +2005,15 @@
                 _chunkLength = (tag - 0x34) * 256 + read();
 
                 byte[] buffer = new byte[_chunkLength];
-                int k = 0;
 
-                while ((data = parseByte()) >= 0) {
-                    buffer[k++] = (byte) data;
+                int offset = 0;
+                while (offset < _chunkLength) {
+                    int sublen = read(buffer, 0, _chunkLength - offset);
+
+                    if (sublen <= 0)
+                        break;
+
+                    offset += sublen;
                 }
 
                 return buffer;
@@ -1986,7 +2047,7 @@
                 return -1;
 
             case 'B':
-            case BC_BINARY_CHUNK:
+            case BC_BINARY_CHUNK: {
                 _isLastChunk = tag == 'B';
                 _chunkLength = (read() << 8) + read();
 
@@ -1998,6 +2059,53 @@
                     _chunkLength = END_OF_DATA;
 
                 return value;
+            }
+
+            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;
+
+                int value = parseByte();
+
+                // special code so successive read byte won't
+                // be read as a single object.
+                if (_chunkLength == 0)
+                    _chunkLength = END_OF_DATA;
+
+                return value;
+            }
+
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37: {
+                _isLastChunk = true;
+                _chunkLength = (tag - 0x34) * 256 + read();
+
+                int value = parseByte();
+
+                // special code so successive read byte won't
+                // be read as a single object.
+                if (_chunkLength == 0)
+                    _chunkLength = END_OF_DATA;
+
+                return value;
+            }
 
             default:
                 throw expect("binary", tag);
@@ -2027,6 +2135,36 @@
                     _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;
+                }
+
+                case 0x34:
+                case 0x35:
+                case 0x36:
+                case 0x37: {
+                    _isLastChunk = true;
+                    _chunkLength = (tag - 0x34) * 256 + read();
+                    break;
+                }
+
                 default:
                     throw expect("binary", tag);
             }
@@ -2072,41 +2210,11 @@
     }
 
     /**
-     * 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)
+        if (cl == null || cl == Object.class)
             return readObject();
 
         int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
@@ -2116,13 +2224,9 @@
                 return null;
 
             case 'H': {
-                Deserializer reader = findSerializerFactory().getDeserializer(expectedClass);
+                Deserializer reader = findSerializerFactory().getDeserializer(cl);
 
-                boolean keyValuePair = expectedTypes != null && expectedTypes.length == 2;
-                // fix deserialize of short type
-                return reader.readMap(this
-                        , keyValuePair ? expectedTypes[0] : null
-                        , keyValuePair ? expectedTypes[1] : null);
+                return reader.readMap(this);
             }
 
             case 'M': {
@@ -2131,21 +2235,21 @@
                 // hessian/3bb3
                 if ("".equals(type)) {
                     Deserializer reader;
-                    reader = findSerializerFactory().getDeserializer(expectedClass);
+                    reader = findSerializerFactory().getDeserializer(cl);
 
                     return reader.readMap(this);
                 } else {
                     Deserializer reader;
-                    reader = findSerializerFactory().getObjectDeserializer(type, expectedClass);
+                    reader = findSerializerFactory().getObjectDeserializer(type, cl);
 
                     return reader.readMap(this);
                 }
             }
 
             case 'C': {
-                readObjectDefinition(expectedClass);
+                readObjectDefinition(cl);
 
-                return readObject(expectedClass);
+                return readObject(cl);
             }
 
             case 0x60:
@@ -2170,9 +2274,9 @@
                 if (ref < 0 || size <= ref)
                     throw new HessianProtocolException("'" + ref + "' is an unknown class definition");
 
-                ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
+                ObjectDefinition def = _classDefs.get(ref);
 
-                return readObjectInstance(expectedClass, def);
+                return readObjectInstance(cl, def);
             }
 
             case 'O': {
@@ -2182,16 +2286,16 @@
                 if (ref < 0 || size <= ref)
                     throw new HessianProtocolException("'" + ref + "' is an unknown class definition");
 
-                ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
+                ObjectDefinition def = _classDefs.get(ref);
 
-                return readObjectInstance(expectedClass, def);
+                return readObjectInstance(cl, def);
             }
 
             case BC_LIST_VARIABLE: {
                 String type = readType();
 
                 Deserializer reader;
-                reader = findSerializerFactory().getListDeserializer(type, expectedClass);
+                reader = findSerializerFactory().getListDeserializer(type, cl);
 
                 Object v = reader.readList(this, -1);
 
@@ -2203,11 +2307,9 @@
                 int length = readInt();
 
                 Deserializer reader;
-                reader = findSerializerFactory().getListDeserializer(type, expectedClass);
+                reader = findSerializerFactory().getListDeserializer(type, cl);
 
-                boolean valueType = expectedTypes != null && expectedTypes.length == 1;
-
-                Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
+                Object v = reader.readLengthList(this, length);
 
                 return v;
             }
@@ -2225,24 +2327,18 @@
                 String type = readType();
 
                 Deserializer reader;
-                reader = findSerializerFactory().getListDeserializer(null, expectedClass);
+                reader = findSerializerFactory().getListDeserializer(type, cl);
 
-                boolean valueType = expectedTypes != null && expectedTypes.length == 1;
-
-                // fix deserialize of short type
-                Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
+                Object v = reader.readLengthList(this, length);
 
                 return v;
             }
 
             case BC_LIST_VARIABLE_UNTYPED: {
                 Deserializer reader;
-                reader = findSerializerFactory().getListDeserializer(null, expectedClass);
+                reader = findSerializerFactory().getListDeserializer(null, cl);
 
-                boolean valueType = expectedTypes != null && expectedTypes.length == 1;
-
-                // fix deserialize of short type
-                Object v = reader.readList(this, -1, valueType ? expectedTypes[0] : null);
+                Object v = reader.readList(this, -1);
 
                 return v;
             }
@@ -2251,12 +2347,9 @@
                 int length = readInt();
 
                 Deserializer reader;
-                reader = findSerializerFactory().getListDeserializer(null, expectedClass);
+                reader = findSerializerFactory().getListDeserializer(null, cl);
 
-                boolean valueType = expectedTypes != null && expectedTypes.length == 1;
-
-                // fix deserialize of short type
-                Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
+                Object v = reader.readLengthList(this, length);
 
                 return v;
             }
@@ -2272,12 +2365,9 @@
                 int length = tag - 0x78;
 
                 Deserializer reader;
-                reader = findSerializerFactory().getListDeserializer(null, expectedClass);
+                reader = findSerializerFactory().getListDeserializer(null, cl);
 
-                boolean valueType = expectedTypes != null && expectedTypes.length == 1;
-
-                // fix deserialize of short type
-                Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);
+                Object v = reader.readLengthList(this, length);
 
                 return v;
             }
@@ -2294,7 +2384,7 @@
 
         // hessian/3b2i vs hessian/3406
         // return readObject();
-        Object value = findSerializerFactory().getDeserializer(expectedClass).readObject(this);
+        Object value = findSerializerFactory().getDeserializer(cl).readObject(this);
         return value;
     }
 
@@ -2302,14 +2392,8 @@
      * 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) {
@@ -2664,9 +2748,7 @@
                 Deserializer reader;
                 reader = findSerializerFactory().getListDeserializer(type, null);
 
-                boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
-
-                return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
+                return reader.readLengthList(this, length);
             }
 
             case BC_LIST_FIXED_UNTYPED: {
@@ -2676,9 +2758,7 @@
                 Deserializer reader;
                 reader = findSerializerFactory().getListDeserializer(null, null);
 
-                boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
-
-                return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
+                return reader.readLengthList(this, length);
             }
 
             // compact fixed list
@@ -2697,9 +2777,7 @@
                 Deserializer reader;
                 reader = findSerializerFactory().getListDeserializer(type, null);
 
-                boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
-
-                return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
+                return reader.readLengthList(this, length);
             }
 
             // compact fixed untyped list
@@ -2717,22 +2795,11 @@
                 Deserializer reader;
                 reader = findSerializerFactory().getListDeserializer(null, null);
 
-                boolean valueType = expectedTypes != null && expectedTypes.size() == 1;
-
-                return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null);
+                return reader.readLengthList(this, length);
             }
 
             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);
+                return findSerializerFactory().readMap(this, null);
             }
 
             case 'M': {
@@ -2765,10 +2832,11 @@
             case 0x6f: {
                 int ref = tag - 0x60;
 
-                if (_classDefs == null)
-                    throw error("No classes defined at reference '{0}'" + tag);
+                if (_classDefs.size() <= ref)
+                    throw error("No classes defined at reference '"
+                            + Integer.toHexString(tag) + "'");
 
-                ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
+                ObjectDefinition def = _classDefs.get(ref);
 
                 return readObjectInstance(null, def);
             }
@@ -2776,7 +2844,10 @@
             case 'O': {
                 int ref = readInt();
 
-                ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);
+                if (_classDefs.size() <= ref)
+                    throw error("Illegal object reference #" + ref);
+
+                ObjectDefinition def = _classDefs.get(ref);
 
                 return readObjectInstance(null, def);
             }
@@ -2795,94 +2866,59 @@
         }
     }
 
-    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)
+    private void readObjectDefinition(Class<?> cl)
             throws IOException {
         String type = readString();
         int len = readInt();
 
+        SerializerFactory factory = findSerializerFactory();
+
+        Deserializer reader = factory.getObjectDeserializer(type, null);
+
+        Object[] fields = reader.createFields(len);
         String[] fieldNames = new String[len];
-        for (int i = 0; i < len; i++)
-            fieldNames[i] = readString();
 
-        ObjectDefinition def = new ObjectDefinition(type, fieldNames);
+        for (int i = 0; i < len; i++) {
+            String name = readString();
 
-        if (_classDefs == null)
-            _classDefs = new ArrayList();
+            fields[i] = reader.createField(name);
+            fieldNames[i] = name;
+        }
+
+        ObjectDefinition def
+                = new ObjectDefinition(type, reader, fields, fieldNames);
 
         _classDefs.add(def);
     }
 
-    private Object readObjectInstance(Class cl, ObjectDefinition def)
+    private Object readObjectInstance(Class<?> cl,
+                                      ObjectDefinition def)
             throws IOException {
         String type = def.getType();
-        String[] fieldNames = def.getFieldNames();
+        Deserializer reader = def.getReader();
+        Object[] fields = def.getFields();
 
-        if (cl != null) {
-            Deserializer reader;
-            reader = findSerializerFactory().getObjectDeserializer(type, cl);
+        SerializerFactory factory = findSerializerFactory();
 
-            return reader.readObject(this, fieldNames);
+        if (cl != reader.getType() && cl != null) {
+            reader = factory.getObjectDeserializer(type, cl);
+
+            return reader.readObject(this, def.getFieldNames());
         } else {
-            return findSerializerFactory().readObject(this, type, fieldNames);
+            return reader.readObject(this, fields);
         }
     }
 
-    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();
@@ -2894,16 +2930,16 @@
     /**
      * Reads a reference.
      */
-    @Override
     public Object readRef()
             throws IOException {
-        return _refs.get(parseInt());
+        int value = parseInt();
+
+        return _refs.get(value);
     }
 
     /**
      * Reads the start of a list.
      */
-    @Override
     public int readListStart()
             throws IOException {
         return read();
@@ -2912,7 +2948,6 @@
     /**
      * Reads the start of a list.
      */
-    @Override
     public int readMapStart()
             throws IOException {
         return read();
@@ -2921,7 +2956,6 @@
     /**
      * Returns true if this is the end of a list or a map.
      */
-    @Override
     public boolean isEnd()
             throws IOException {
         int code;
@@ -2941,7 +2975,6 @@
     /**
      * Reads the end byte.
      */
-    @Override
     public void readEnd()
             throws IOException {
         int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
@@ -2957,7 +2990,6 @@
     /**
      * Reads the end byte.
      */
-    @Override
     public void readMapEnd()
             throws IOException {
         int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
@@ -2969,7 +3001,6 @@
     /**
      * Reads the end byte.
      */
-    @Override
     public void readListEnd()
             throws IOException {
         int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
@@ -2994,7 +3025,6 @@
     /**
      * Adds a list/map reference.
      */
-    @Override
     public void setRef(int i, Object ref) {
         _refs.set(i, ref);
     }
@@ -3004,8 +3034,25 @@
      */
     @Override
     public void resetReferences() {
-        if (_refs != null)
-            _refs.clear();
+        _refs.clear();
+    }
+
+    public void reset() {
+        resetReferences();
+
+        _classDefs.clear();
+        _types.clear();
+    }
+
+    public void resetBuffer() {
+        int offset = _offset;
+        _offset = 0;
+
+        int length = _length;
+        _length = 0;
+
+        if (length > 0 && offset != length)
+            throw new IllegalStateException("offset=" + offset + " length=" + length);
     }
 
     public Object readStreamingObject()
@@ -3031,13 +3078,12 @@
 
     /**
      * 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();
@@ -3107,12 +3153,11 @@
 
     /**
      * Parses the length for an array
-     * <p>
+     *
      * <pre>
      * l b32 b24 b16 b8
      * </pre>
      */
-    @Override
     public int readLength()
             throws IOException {
         throw new UnsupportedOperationException();
@@ -3120,7 +3165,7 @@
 
     /**
      * Parses a 32-bit integer value from the stream.
-     * <p>
+     *
      * <pre>
      * b32 b24 b16 b8
      * </pre>
@@ -3152,7 +3197,7 @@
 
     /**
      * Parses a 64-bit long value from the stream.
-     * <p>
+     *
      * <pre>
      * b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
@@ -3180,7 +3225,7 @@
 
     /**
      * Parses a 64-bit double value from the stream.
-     * <p>
+     *
      * <pre>
      * b64 b56 b48 b40 b32 b24 b16 b8
      * </pre>
@@ -3197,6 +3242,38 @@
         throw new UnsupportedOperationException();
     }
 
+    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 a character from the underlying stream.
+     */
+    private int parseChar()
+            throws IOException {
+        while (_chunkLength <= 0) {
+            if (!parseChunkLength())
+                return -1;
+        }
+
+        _chunkLength--;
+
+        return parseUTF8Char();
+    }
+
     private boolean parseChunkLength()
             throws IOException {
         if (_isLastChunk)
@@ -3270,21 +3347,6 @@
     }
 
     /**
-     * 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()
@@ -3375,7 +3437,6 @@
     /**
      * Reads bytes based on an input stream.
      */
-    @Override
     public InputStream readInputStream()
             throws IOException {
         int tag = read();
@@ -3387,7 +3448,7 @@
             case BC_BINARY:
             case BC_BINARY_CHUNK:
             case 'b': //maybe it's a mistype of BC_BINARY_CHUNK
-                _isLastChunk = tag == 'B';
+                _isLastChunk = tag == BC_BINARY;
                 _chunkLength = (read() << 8) + read();
                 break;
 
@@ -3410,6 +3471,7 @@
                 _isLastChunk = true;
                 _chunkLength = tag - 0x20;
                 break;
+
             case 0x34:
             case 0x35:
             case 0x36:
@@ -3417,6 +3479,7 @@
                 _isLastChunk = true;
                 _chunkLength = (tag - 0x34) * 256 + read();
                 break;
+
             default:
                 throw expect("binary", tag);
         }
@@ -3471,6 +3534,7 @@
                         _isLastChunk = true;
                         _chunkLength = code - 0x20;
                         break;
+
                     case 0x34:
                     case 0x35:
                     case 0x36:
@@ -3478,6 +3542,7 @@
                         _isLastChunk = true;
                         _chunkLength = (code - 0x34) * 256 + read();
                         break;
+
                     default:
                         throw expect("byte[]", code);
                 }
@@ -3518,6 +3583,13 @@
         return _buffer[_offset++] & 0xff;
     }
 
+    protected void unread() {
+        if (_offset <= 0)
+            throw new IllegalStateException();
+
+        _offset--;
+    }
+
     private final boolean readBuffer()
             throws IOException {
         byte[] buffer = _buffer;
@@ -3545,7 +3617,6 @@
         return true;
     }
 
-    @Override
     public Reader getReader() {
         return null;
     }
@@ -3558,16 +3629,21 @@
             _offset--;
 
             try {
+                int offset = _offset;
+                String context
+                        = buildDebugContext(_buffer, 0, _length, offset);
+
                 Object obj = readObject();
 
                 if (obj != null) {
                     return error("expected " + expect
                             + " at 0x" + Integer.toHexString(ch & 0xff)
-                            + " " + obj.getClass().getName());
+                            + " " + obj.getClass().getName()
+                            + "\n  " + context + "");
                 } else
                     return error("expected " + expect
                             + " at 0x" + Integer.toHexString(ch & 0xff) + " null");
-            } catch (IOException e) {
+            } catch (Exception e) {
                 log.log(Level.FINE, e.toString(), e);
 
                 return error("expected " + expect
@@ -3576,6 +3652,36 @@
         }
     }
 
+    private String buildDebugContext(byte[] buffer, int offset, int length,
+                                     int errorOffset) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("[");
+        for (int i = 0; i < errorOffset; i++) {
+            int ch = buffer[offset + i];
+            addDebugChar(sb, ch);
+        }
+        sb.append("] ");
+        addDebugChar(sb, buffer[offset + errorOffset]);
+        sb.append(" [");
+        for (int i = errorOffset + 1; i < length; i++) {
+            int ch = buffer[offset + i];
+            addDebugChar(sb, ch);
+        }
+        sb.append("]");
+
+        return sb.toString();
+    }
+
+    private void addDebugChar(StringBuilder sb, int ch) {
+        if (ch >= 0x20 && ch < 0x7f) {
+            sb.append((char) ch);
+        } else if (ch == '\n')
+            sb.append((char) ch);
+        else
+            sb.append(String.format("\\x%02x", ch & 0xff));
+    }
+
     protected String codeName(int ch) {
         if (ch < 0)
             return "end of file";
@@ -3590,6 +3696,10 @@
             return new HessianProtocolException(message);
     }
 
+    public void free() {
+        reset();
+    }
+
     @Override
     public void close()
             throws IOException {
@@ -3604,26 +3714,40 @@
 
     final static class ObjectDefinition {
         private final String _type;
-        private final String[] _fields;
+        private final Deserializer _reader;
+        private final Object[] _fields;
+        private final String[] _fieldNames;
 
-        ObjectDefinition(String type, String[] fields) {
+        ObjectDefinition(String type,
+                         Deserializer reader,
+                         Object[] fields,
+                         String[] fieldNames) {
             _type = type;
+            _reader = reader;
             _fields = fields;
+            _fieldNames = fieldNames;
         }
 
         String getType() {
             return _type;
         }
 
-        String[] getFieldNames() {
+        Deserializer getReader() {
+            return _reader;
+        }
+
+        Object[] getFields() {
             return _fields;
         }
+
+        String[] getFieldNames() {
+            return _fieldNames;
+        }
     }
 
     class ReadInputStream extends InputStream {
         boolean _isClosed = false;
 
-        @Override
         public int read()
                 throws IOException {
             if (_isClosed)
@@ -3636,7 +3760,6 @@
             return ch;
         }
 
-        @Override
         public int read(byte[] buffer, int offset, int length)
                 throws IOException {
             if (_isClosed)
@@ -3649,7 +3772,6 @@
             return len;
         }
 
-        @Override
         public void close()
                 throws IOException {
             while (read() >= 0) {
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Output.java b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Output.java
index c4a6850..ed83316 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Output.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Output.java
@@ -51,18 +51,19 @@
 import com.alibaba.com.caucho.hessian.util.IdentityIntMap;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.HashMap;
 
 /**
  * Output stream for Hessian 2 requests.
- * <p>
+ *
  * <p>Since HessianOutput does not depend on any classes other than
  * in the JDK, it can be extracted independently into a smaller package.
- * <p>
+ *
  * <p>HessianOutput is unbuffered, so any client needs to provide
  * its own buffering.
- * <p>
+ *
  * <pre>
  * OutputStream os = ...; // from http connection
  * Hessian2Output out = new Hessian2Output(os);
@@ -76,20 +77,35 @@
 public class Hessian2Output
         extends AbstractHessianOutput
         implements Hessian2Constants {
-    public final static int SIZE = 4096;
+    // should match Resin buffer size for perf
+    public final static int SIZE = 8 * 1024;
+    // map of references
+    private final IdentityIntMap _refs
+            = new IdentityIntMap(256);
+    // map of classes
+    private final IdentityIntMap _classRefs
+            = new IdentityIntMap(256);
     private final byte[] _buffer = new byte[SIZE];
     // the output stream/
     protected OutputStream _os;
-    // map of references
-    private IdentityIntMap _refs = new IdentityIntMap();
+    private int _refCount = 0;
     private boolean _isCloseStreamOnClose;
-    // map of classes
-    private HashMap _classRefs;
     // map of types
-    private HashMap _typeRefs;
+    private HashMap<String, Integer> _typeRefs;
     private int _offset;
 
-    private boolean _isStreaming;
+    private boolean _isPacket;
+
+    private boolean _isUnshared;
+
+    /**
+     * Creates a new Hessian output stream, initialized with an
+     * underlying output stream.
+     *
+     * @param os the underlying output stream.
+     */
+    public Hessian2Output() {
+    }
 
     /**
      * Creates a new Hessian output stream, initialized with an
@@ -98,7 +114,7 @@
      * @param os the underlying output stream.
      */
     public Hessian2Output(OutputStream os) {
-        _os = os;
+        init(os);
     }
 
     @Override
@@ -108,21 +124,10 @@
         _os = os;
     }
 
-    /**
-     * Resets all counters and references
-     */
-    public void reset() {
+    public void initPacket(OutputStream os) {
         resetReferences();
 
-        if (_classRefs != null) {
-            _classRefs.clear();
-        }
-
-        if (_typeRefs != null) {
-            _typeRefs.clear();
-        }
-
-        _offset = 0;
+        _os = os;
     }
 
     public boolean isCloseStreamOnClose() {
@@ -134,26 +139,44 @@
     }
 
     /**
+     * Sets hessian to be "unshared", meaning it will not detect
+     * duplicate or circular references.
+     */
+    @Override
+    public boolean setUnshared(boolean isUnshared) {
+        boolean oldIsUnshared = _isUnshared;
+
+        _isUnshared = isUnshared;
+
+        return oldIsUnshared;
+    }
+
+    /**
      * Writes a complete method call.
      */
     @Override
     public void call(String method, Object[] args)
             throws IOException {
+        writeVersion();
+
         int length = args != null ? args.length : 0;
 
         startCall(method, length);
 
-        for (int i = 0; i < args.length; i++)
+        for (int i = 0; i < length; i++) {
             writeObject(args[i]);
+        }
 
         completeCall();
+
+        flush();
     }
 
     /**
      * 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.
-     * <p>
+     *
      * <code><pre>
      * C
      * string # method name
@@ -168,7 +191,7 @@
         int offset = _offset;
 
         if (SIZE < offset + 32) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -183,7 +206,7 @@
     /**
      * Writes the call tag.  This would be followed by the
      * method and the arguments
-     * <p>
+     *
      * <code><pre>
      * C
      * </pre></code>
@@ -200,7 +223,7 @@
 
     /**
      * Starts an envelope.
-     * <p>
+     *
      * <code><pre>
      * E major minor
      * m b16 b8 method-name
@@ -213,7 +236,7 @@
         int offset = _offset;
 
         if (SIZE < offset + 32) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -224,9 +247,9 @@
 
     /**
      * Completes an envelope.
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * Z
      * </pre>
@@ -240,14 +263,13 @@
 
     /**
      * Writes the method tag.
-     * <p>
+     *
      * <code><pre>
      * string
      * </pre></code>
      *
      * @param method the method name to call.
      */
-    @Override
     public void writeMethod(String method)
             throws IOException {
         writeString(method);
@@ -255,7 +277,7 @@
 
     /**
      * Completes.
-     * <p>
+     *
      * <code><pre>
      * z
      * </pre></code>
@@ -265,16 +287,16 @@
             throws IOException {
     /*
     flushIfFull();
-    
+
     _buffer[_offset++] = (byte) 'Z';
     */
     }
 
     /**
      * Starts the reply
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * R
      * </pre>
@@ -292,6 +314,7 @@
     public void writeVersion()
             throws IOException {
         flushIfFull();
+
         _buffer[_offset++] = (byte) 'H';
         _buffer[_offset++] = (byte) 2;
         _buffer[_offset++] = (byte) 0;
@@ -299,9 +322,9 @@
 
     /**
      * Completes reading the reply
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * z
      * </pre>
@@ -313,9 +336,9 @@
 
     /**
      * Starts a packet
-     * <p>
+     *
      * <p>A message contains several objects encapsulated by a length</p>
-     * <p>
+     *
      * <pre>
      * p x02 x00
      * </pre>
@@ -331,9 +354,9 @@
 
     /**
      * Completes reading the message
-     * <p>
+     *
      * <p>A successful completion will have a single value:
-     * <p>
+     *
      * <pre>
      * z
      * </pre>
@@ -348,19 +371,19 @@
     /**
      * Writes a fault.  The fault will be written
      * as a descriptive string followed by an object:
-     * <p>
+     *
      * <code><pre>
      * F map
      * </pre></code>
-     * <p>
+     *
      * <code><pre>
      * F H
      * \x04code
      * \x10the fault code
-     * <p>
+     *
      * \x07message
      * \x11the fault message
-     * <p>
+     *
      * \x06detail
      * M\xnnjavax.ejb.FinderException
      *     ...
@@ -370,7 +393,6 @@
      *
      * @param code the fault code, a three digit
      */
-    @Override
     public void writeFault(String code, String message, Object detail)
             throws IOException {
         flushIfFull();
@@ -380,7 +402,7 @@
         _buffer[_offset++] = (byte) 'F';
         _buffer[_offset++] = (byte) 'H';
 
-        _refs.put(new HashMap(), _refs.size());
+        addRef(new Object(), _refCount++, false);
 
         writeString("code");
         writeString(code);
@@ -408,7 +430,9 @@
             return;
         }
 
-        Serializer serializer = findSerializerFactory().getSerializer(object.getClass());
+        Serializer serializer
+                = findSerializerFactory().getObjectSerializer(object.getClass());
+
         serializer.writeObject(object, this);
     }
 
@@ -416,7 +440,7 @@
      * 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>.
-     * <p>
+     *
      * <code><pre>
      * list ::= V type value* Z
      *      ::= v type int value*
@@ -424,7 +448,6 @@
      *
      * @return true for variable lists, false for fixed lists
      */
-    @Override
     public boolean writeListBegin(int length, String type)
             throws IOException {
         flushIfFull();
@@ -463,7 +486,6 @@
     /**
      * Writes the tail of the list to the stream for a variable-length list.
      */
-    @Override
     public void writeListEnd()
             throws IOException {
         flushIfFull();
@@ -475,17 +497,16 @@
      * 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>.
-     * <p>
+     *
      * <code><pre>
      * map ::= M type (<value> <value>)* Z
      *     ::= H (<value> <value>)* Z
      * </pre></code>
      */
-    @Override
     public void writeMapBegin(String type)
             throws IOException {
         if (SIZE < _offset + 32)
-            flush();
+            flushBuffer();
 
         if (type != null) {
             _buffer[_offset++] = BC_MAP;
@@ -498,18 +519,17 @@
     /**
      * Writes the tail of the map to the stream.
      */
-    @Override
     public void writeMapEnd()
             throws IOException {
         if (SIZE < _offset + 32)
-            flush();
+            flushBuffer();
 
         _buffer[_offset++] = (byte) BC_END;
     }
 
     /**
      * Writes the object definition
-     * <p>
+     *
      * <code><pre>
      * C &lt;string> &lt;int> &lt;string>*
      * </pre></code>
@@ -517,16 +537,12 @@
     @Override
     public int writeObjectBegin(String type)
             throws IOException {
-        if (_classRefs == null)
-            _classRefs = new HashMap();
+        int newRef = _classRefs.size();
+        int ref = _classRefs.put(type, newRef, false);
 
-        Integer refV = (Integer) _classRefs.get(type);
-
-        if (refV != null) {
-            int ref = refV.intValue();
-
+        if (newRef != ref) {
             if (SIZE < _offset + 32)
-                flush();
+                flushBuffer();
 
             if (ref <= OBJECT_DIRECT_MAX) {
                 _buffer[_offset++] = (byte) (BC_OBJECT_DIRECT + ref);
@@ -537,12 +553,8 @@
 
             return ref;
         } else {
-            int ref = _classRefs.size();
-
-            _classRefs.put(type, Integer.valueOf(ref));
-
             if (SIZE < _offset + 32)
-                flush();
+                flushBuffer();
 
             _buffer[_offset++] = (byte) 'C';
 
@@ -585,7 +597,7 @@
         }
 
         if (_typeRefs == null)
-            _typeRefs = new HashMap();
+            _typeRefs = new HashMap<String, Integer>();
 
         Integer typeRefV = (Integer) _typeRefs.get(type);
 
@@ -603,7 +615,7 @@
     /**
      * Writes a boolean value to the stream.  The boolean will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * T
      * F
@@ -615,7 +627,7 @@
     public void writeBoolean(boolean value)
             throws IOException {
         if (SIZE < _offset + 16)
-            flush();
+            flushBuffer();
 
         if (value)
             _buffer[_offset++] = (byte) 'T';
@@ -626,7 +638,7 @@
     /**
      * Writes an integer value to the stream.  The integer will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * I b32 b24 b16 b8
      * </pre></code>
@@ -640,7 +652,7 @@
         byte[] buffer = _buffer;
 
         if (SIZE <= offset + 16) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -667,21 +679,20 @@
     /**
      * Writes a long value to the stream.  The long will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * L b64 b56 b48 b40 b32 b24 b16 b8
      * </pre></code>
      *
      * @param value the long value to write.
      */
-    @Override
     public void writeLong(long value)
             throws IOException {
         int offset = _offset;
         byte[] buffer = _buffer;
 
         if (SIZE <= offset + 16) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -722,21 +733,20 @@
     /**
      * Writes a double value to the stream.  The double will be written
      * with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * D b64 b56 b48 b40 b32 b24 b16 b8
      * </pre></code>
      *
      * @param value the double value to write.
      */
-    @Override
     public void writeDouble(double value)
             throws IOException {
         int offset = _offset;
         byte[] buffer = _buffer;
 
         if (SIZE <= offset + 16) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -804,7 +814,7 @@
 
     /**
      * Writes a date to the stream.
-     * <p>
+     *
      * <code><pre>
      * date ::= d   b7 b6 b5 b4 b3 b2 b1 b0
      *      ::= x65 b3 b2 b1 b0
@@ -812,11 +822,10 @@
      *
      * @param time the date in milliseconds from the epoch in UTC
      */
-    @Override
     public void writeUTCDate(long time)
             throws IOException {
         if (SIZE < _offset + 32)
-            flush();
+            flushBuffer();
 
         int offset = _offset;
         byte[] buffer = _buffer;
@@ -854,21 +863,20 @@
     /**
      * Writes a null value to the stream.
      * The null will be written with the following syntax
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
      *
      * @param value the string value to write.
      */
-    @Override
     public void writeNull()
             throws IOException {
         int offset = _offset;
         byte[] buffer = _buffer;
 
         if (SIZE <= offset + 16) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -880,27 +888,26 @@
     /**
      * Writes a string value to the stream using UTF-8 encoding.
      * The string will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * S b16 b8 string-value
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
      *
      * @param value the string value to write.
      */
-    @Override
     public void writeString(String value)
             throws IOException {
         int offset = _offset;
         byte[] buffer = _buffer;
 
         if (SIZE <= offset + 16) {
-            flush();
+            flushBuffer();
             offset = _offset;
         }
 
@@ -918,7 +925,7 @@
                 offset = _offset;
 
                 if (SIZE <= offset + 16) {
-                    flush();
+                    flushBuffer();
                     offset = _offset;
                 }
 
@@ -943,7 +950,7 @@
             offset = _offset;
 
             if (SIZE <= offset + 16) {
-                flush();
+                flushBuffer();
                 offset = _offset;
             }
 
@@ -967,25 +974,24 @@
     /**
      * Writes a string value to the stream using UTF-8 encoding.
      * The string will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * S b16 b8 string-value
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
      *
      * @param value the string value to write.
      */
-    @Override
     public void writeString(char[] buffer, int offset, int length)
             throws IOException {
         if (buffer == null) {
             if (SIZE < _offset + 16)
-                flush();
+                flushBuffer();
 
             _buffer[_offset++] = (byte) ('N');
         } else {
@@ -993,7 +999,7 @@
                 int sublen = 0x8000;
 
                 if (SIZE < _offset + 16)
-                    flush();
+                    flushBuffer();
 
                 // chunk can't end in high surrogate
                 char tail = buffer[offset + sublen - 1];
@@ -1012,7 +1018,7 @@
             }
 
             if (SIZE < _offset + 16)
-                flush();
+                flushBuffer();
 
             if (length <= STRING_DIRECT_MAX) {
                 _buffer[_offset++] = (byte) (BC_STRING_DIRECT + length);
@@ -1032,25 +1038,24 @@
     /**
      * Writes a byte array to the stream.
      * The array will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * B b16 b18 bytes
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
      *
      * @param value the string value to write.
      */
-    @Override
     public void writeBytes(byte[] buffer)
             throws IOException {
         if (buffer == null) {
             if (SIZE < _offset + 16)
-                flush();
+                flushBuffer();
 
             _buffer[_offset++] = 'N';
         } else
@@ -1060,20 +1065,19 @@
     /**
      * Writes a byte array to the stream.
      * The array will be written with the following syntax:
-     * <p>
+     *
      * <code><pre>
      * B b16 b18 bytes
      * </pre></code>
      * <p>
      * If the value is null, it will be written as
-     * <p>
+     *
      * <code><pre>
      * N
      * </pre></code>
      *
      * @param value the string value to write.
      */
-    @Override
     public void writeBytes(byte[] buffer, int offset, int length)
             throws IOException {
         if (buffer == null) {
@@ -1082,8 +1086,6 @@
 
             _buffer[_offset++] = (byte) 'N';
         } else {
-            flush();
-
             while (SIZE - _offset - 3 < length) {
                 int sublen = SIZE - _offset - 3;
 
@@ -1131,39 +1133,38 @@
 
     /**
      * Writes a byte buffer to the stream.
-     * <p>
+     *
      * <code><pre>
      * </pre></code>
      */
-    @Override
     public void writeByteBufferStart()
             throws IOException {
     }
 
     /**
      * Writes a byte buffer to the stream.
-     * <p>
+     *
      * <code><pre>
      * b b16 b18 bytes
      * </pre></code>
      */
-    @Override
     public void writeByteBufferPart(byte[] buffer, int offset, int length)
             throws IOException {
         while (length > 0) {
-            int sublen = length;
+            flushIfFull();
 
-            if (0x8000 < sublen)
-                sublen = 0x8000;
+            int sublen = _buffer.length - _offset;
 
-            flush(); // bypass buffer
+            if (length < sublen)
+                sublen = length;
 
-            _os.write(BC_BINARY_CHUNK);
-            _os.write(sublen >> 8);
-            _os.write(sublen);
+            _buffer[_offset++] = BC_BINARY_CHUNK;
+            _buffer[_offset++] = (byte) (sublen >> 8);
+            _buffer[_offset++] = (byte) sublen;
 
-            _os.write(buffer, offset, sublen);
+            System.arraycopy(buffer, offset, _buffer, _offset, sublen);
 
+            _offset += sublen;
             length -= sublen;
             offset += sublen;
         }
@@ -1171,12 +1172,11 @@
 
     /**
      * Writes a byte buffer to the stream.
-     * <p>
+     *
      * <code><pre>
      * b b16 b18 bytes
      * </pre></code>
      */
-    @Override
     public void writeByteBufferEnd(byte[] buffer, int offset, int length)
             throws IOException {
         writeBytes(buffer, offset, length);
@@ -1191,8 +1191,37 @@
     }
 
     /**
+     * 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.
-     * <p>
+     *
      * <code><pre>
      * x51 &lt;int>
      * </pre></code>
@@ -1203,7 +1232,7 @@
     protected void writeRef(int value)
             throws IOException {
         if (SIZE < _offset + 16)
-            flush();
+            flushBuffer();
 
         _buffer[_offset++] = (byte) BC_REF;
 
@@ -1218,26 +1247,43 @@
     @Override
     public boolean addRef(Object object)
             throws IOException {
-        int ref = _refs.get(object);
+        if (_isUnshared) {
+            _refCount++;
+            return false;
+        }
 
-        if (ref >= 0) {
+        int newRef = _refCount;
+
+        int ref = addRef(object, newRef, false);
+
+        if (ref != newRef) {
             writeRef(ref);
 
             return true;
         } else {
-            _refs.put(object, _refs.size());
+            _refCount++;
 
             return false;
         }
     }
 
+    @Override
+    public int getRef(Object obj) {
+        if (_isUnshared)
+            return -1;
+
+        return _refs.get(obj);
+    }
+
     /**
      * Removes a reference.
      */
     @Override
     public boolean removeRef(Object obj)
             throws IOException {
-        if (_refs != null) {
+        if (_isUnshared) {
+            return false;
+        } else if (_refs != null) {
             _refs.remove(obj);
 
             return true;
@@ -1251,73 +1297,101 @@
     @Override
     public boolean replaceRef(Object oldRef, Object newRef)
             throws IOException {
-        Integer value = (Integer) _refs.remove(oldRef);
+        if (_isUnshared) {
+            return false;
+        }
 
-        if (value != null) {
-            _refs.put(newRef, value);
+        int value = _refs.get(oldRef);
+
+        if (value >= 0) {
+            addRef(newRef, value, true);
+
+            _refs.remove(oldRef);
+
             return true;
         } else
             return false;
     }
 
-    /**
-     * Resets the references for streaming.
-     */
-    @Override
-    public void resetReferences() {
-        if (_refs != null)
-            _refs.clear();
+    private int addRef(Object value, int newRef, boolean isReplace) {
+        int prevRef = _refs.put(value, newRef, isReplace);
+
+        return prevRef;
     }
 
     /**
      * Starts the streaming message
-     * <p>
+     *
      * <p>A streaming message starts with 'P'</p>
-     * <p>
+     *
      * <pre>
      * P x02 x00
      * </pre>
      */
     public void writeStreamingObject(Object obj)
             throws IOException {
-        startStreamingPacket();
+        startPacket();
 
         writeObject(obj);
 
-        endStreamingPacket();
+        endPacket();
     }
 
     /**
      * Starts a streaming packet
-     * <p>
-     * <p>A streaming message starts with 'P'</p>
-     * <p>
-     * <pre>
-     * P x02 x00
-     * </pre>
+     *
+     * <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 startStreamingPacket()
+    public void startPacket()
             throws IOException {
-        if (_refs != null)
+        if (_refs != null) {
             _refs.clear();
+            _refCount = 0;
+        }
 
-        flush();
+        flushBuffer();
 
-        _isStreaming = true;
-        _offset = 3;
+        _isPacket = true;
+        _offset = 4;
+        _buffer[0] = (byte) 0x05; // 0x05 = binary
+        _buffer[1] = (byte) 0x55;
+        _buffer[2] = (byte) 0x55;
+        _buffer[3] = (byte) 0x55;
     }
 
-    public void endStreamingPacket()
+    public void endPacket()
             throws IOException {
-        int len = _offset - 3;
+        int offset = _offset;
 
-        _buffer[0] = (byte) 'P';
-        _buffer[1] = (byte) (len >> 8);
-        _buffer[2] = (byte) len;
+        OutputStream os = _os;
 
-        _isStreaming = false;
+        if (os == null) {
+            _offset = 0;
+            return;
+        }
 
-        flush();
+        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);
+        }
     }
 
     /**
@@ -1328,7 +1402,7 @@
     public void printLenString(String v)
             throws IOException {
         if (SIZE < _offset + 16)
-            flush();
+            flushBuffer();
 
         if (v == null) {
             _buffer[_offset++] = (byte) (0);
@@ -1365,7 +1439,7 @@
         for (int i = 0; i < length; i++) {
             if (SIZE <= offset + 16) {
                 _offset = offset;
-                flush();
+                flushBuffer();
                 offset = _offset;
             }
 
@@ -1399,7 +1473,7 @@
         for (int i = 0; i < length; i++) {
             if (SIZE <= offset + 16) {
                 _offset = offset;
-                flush();
+                flushBuffer();
                 offset = _offset;
             }
 
@@ -1425,12 +1499,10 @@
         int offset = _offset;
 
         if (SIZE < offset + 32) {
-            _offset = 0;
-            _os.write(_buffer, 0, offset);
+            flushBuffer();
         }
     }
 
-    @Override
     public final void flush()
             throws IOException {
         flushBuffer();
@@ -1443,23 +1515,33 @@
             throws IOException {
         int offset = _offset;
 
-        if (!_isStreaming && offset > 0) {
+        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;
 
-            _os.write(_buffer, 0, offset);
-        } else if (_isStreaming && offset > 3) {
-            int len = offset - 3;
-            _buffer[0] = 'p';
-            _buffer[1] = (byte) (len >> 8);
-            _buffer[2] = (byte) len;
-            _offset = 3;
+            _buffer[0] |= (byte) 0x80;
+            _buffer[1] = (byte) (0x7e);
+            _buffer[2] = (byte) (len >> 8);
+            _buffer[3] = (byte) (len);
+            _offset = 4;
 
-            _os.write(_buffer, 0, offset);
+            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 final void close()
+    public void close()
             throws IOException {
         // hessian/3a8c
         flush();
@@ -1473,13 +1555,47 @@
         }
     }
 
+    public void free() {
+        reset();
+
+        _os = null;
+        _isCloseStreamOnClose = false;
+    }
+
+    /**
+     * Resets the references for streaming.
+     */
+    @Override
+    public void resetReferences() {
+        if (_refs != null) {
+            _refs.clear();
+            _refCount = 0;
+        }
+    }
+
+    /**
+     * Resets all counters and references
+     */
+    public void reset() {
+        if (_refs != null) {
+            _refs.clear();
+            _refCount = 0;
+        }
+
+        _classRefs.clear();
+        _typeRefs = null;
+        _offset = 0;
+        _isPacket = false;
+        _isUnshared = false;
+    }
+
     class BytesOutputStream extends OutputStream {
         private int _startOffset;
 
         BytesOutputStream()
                 throws IOException {
             if (SIZE < _offset + 16) {
-                Hessian2Output.this.flush();
+                Hessian2Output.this.flushBuffer();
             }
 
             _startOffset = _offset;
@@ -1496,7 +1612,7 @@
                 _buffer[_startOffset + 1] = (byte) (length >> 8);
                 _buffer[_startOffset + 2] = (byte) (length);
 
-                Hessian2Output.this.flush();
+                Hessian2Output.this.flushBuffer();
 
                 _startOffset = _offset;
                 _offset += 3;
@@ -1529,7 +1645,7 @@
                     _buffer[_startOffset + 1] = (byte) (chunkLength >> 8);
                     _buffer[_startOffset + 2] = (byte) (chunkLength);
 
-                    Hessian2Output.this.flush();
+                    Hessian2Output.this.flushBuffer();
 
                     _startOffset = _offset;
                     _offset += 3;
@@ -1552,7 +1668,7 @@
             _buffer[startOffset + 1] = (byte) (length >> 8);
             _buffer[startOffset + 2] = (byte) (length);
 
-            Hessian2Output.this.flush();
+            Hessian2Output.this.flushBuffer();
         }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingInput.java b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingInput.java
index ebf8b77..c986297 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingInput.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingInput.java
@@ -50,11 +50,24 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
- * Output stream for Hessian 2 streaming requests.
+ * Input stream for Hessian 2 streaming requests using WebSocket.
+ * <p>
+ * For best performance, use HessianFactory:
+ *
+ * <code><pre>
+ * HessianFactory factory = new HessianFactory();
+ * Hessian2StreamingInput hIn = factory.createHessian2StreamingInput(is);
+ * </pre></code>
  */
 public class Hessian2StreamingInput {
+    private static final Logger log
+            = Logger.getLogger(Hessian2StreamingInput.class.getName());
+
+    private StreamingInputStream _is;
     private Hessian2Input _in;
 
     /**
@@ -64,7 +77,38 @@
      * @param is the underlying output stream.
      */
     public Hessian2StreamingInput(InputStream is) {
-        _in = new Hessian2Input(new StreamingInputStream(is));
+        _is = new StreamingInputStream(is);
+        _in = new Hessian2Input(_is);
+    }
+
+    public void setSerializerFactory(SerializerFactory factory) {
+        _in.setSerializerFactory(factory);
+    }
+
+    public boolean isDataAvailable() {
+        StreamingInputStream is = _is;
+
+        return is != null && is.isDataAvailable();
+    }
+
+    public Hessian2Input startPacket()
+            throws IOException {
+        if (_is.startPacket()) {
+            _in.resetReferences();
+            _in.resetBuffer(); // XXX:
+            return _in;
+        } else
+            return null;
+    }
+
+    public void endPacket()
+            throws IOException {
+        _is.endPacket();
+        _in.resetBuffer(); // XXX:
+    }
+
+    public Hessian2Input getHessianInput() {
+        return _in;
     }
 
     /**
@@ -72,7 +116,13 @@
      */
     public Object readObject()
             throws IOException {
-        return _in.readStreamingObject();
+        _is.startPacket();
+
+        Object obj = _in.readStreamingObject();
+
+        _is.endPacket();
+
+        return obj;
     }
 
     /**
@@ -85,36 +135,68 @@
 
     static class StreamingInputStream extends InputStream {
         private InputStream _is;
+
         private int _length;
+        private boolean _isPacketEnd;
 
         StreamingInputStream(InputStream is) {
             _is = is;
         }
 
-        @Override
+        public boolean isDataAvailable() {
+            try {
+                return _is != null && _is.available() > 0;
+            } catch (IOException e) {
+                log.log(Level.FINER, e.toString(), e);
+
+                return true;
+            }
+        }
+
+        public boolean startPacket()
+                throws IOException {
+            // skip zero-length packets
+            do {
+                _isPacketEnd = false;
+            } while ((_length = readChunkLength(_is)) == 0);
+
+            return _length > 0;
+        }
+
+        public void endPacket()
+                throws IOException {
+            while (!_isPacketEnd) {
+                if (_length <= 0)
+                    _length = readChunkLength(_is);
+
+                if (_length > 0) {
+                    _is.skip(_length);
+                    _length = 0;
+                }
+            }
+
+            if (_length > 0) {
+                _is.skip(_length);
+                _length = 0;
+            }
+        }
+
         public int read()
                 throws IOException {
             InputStream is = _is;
 
-            while (_length == 0) {
-                int code = is.read();
-
-                if (code < 0)
-                    return -1;
-                else if (code != 'p' && code != 'P')
-                    throw new HessianProtocolException("expected streaming packet at 0x"
-                            + Integer.toHexString(code & 0xff));
-
-                int d1 = is.read();
-                int d2 = is.read();
-
-                if (d2 < 0)
+            if (_length == 0) {
+                if (_isPacketEnd)
                     return -1;
 
-                _length = (d1 << 8) + d2;
+                _length = readChunkLength(is);
+
+                if (_length <= 0)
+                    return -1;
             }
 
             _length--;
+
             return is.read();
         }
 
@@ -123,24 +205,14 @@
                 throws IOException {
             InputStream is = _is;
 
-            while (_length == 0) {
-                int code = is.read();
-
-                if (code < 0)
-                    return -1;
-                else if (code != 'p' && code != 'P') {
-                    throw new HessianProtocolException("expected streaming packet at 0x"
-                            + Integer.toHexString(code & 0xff)
-                            + " (" + (char) code + ")");
-                }
-
-                int d1 = is.read();
-                int d2 = is.read();
-
-                if (d2 < 0)
+            if (_length <= 0) {
+                if (_isPacketEnd)
                     return -1;
 
-                _length = (d1 << 8) + d2;
+                _length = readChunkLength(is);
+
+                if (_length <= 0)
+                    return -1;
             }
 
             int sublen = _length;
@@ -156,5 +228,42 @@
 
             return sublen;
         }
+
+        private int readChunkLength(InputStream is)
+                throws IOException {
+            if (_isPacketEnd)
+                return -1;
+
+            int length = 0;
+
+            int code = is.read();
+
+            if (code < 0) {
+                _isPacketEnd = true;
+                return -1;
+            }
+
+            _isPacketEnd = (code & 0x80) == 0;
+
+            int len = is.read() & 0x7f;
+
+            if (len < 0x7e) {
+                length = len;
+            } else if (len == 0x7e) {
+                length = (((is.read() & 0xff) << 8)
+                        + (is.read() & 0xff));
+            } else {
+                length = (((is.read() & 0xff) << 56)
+                        + ((is.read() & 0xff) << 48)
+                        + ((is.read() & 0xff) << 40)
+                        + ((is.read() & 0xff) << 32)
+                        + ((is.read() & 0xff) << 24)
+                        + ((is.read() & 0xff) << 16)
+                        + ((is.read() & 0xff) << 8)
+                        + ((is.read() & 0xff)));
+            }
+
+            return length;
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingOutput.java b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingOutput.java
index 19fb331..97514c3 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingOutput.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2StreamingOutput.java
@@ -67,6 +67,14 @@
         _out = new Hessian2Output(os);
     }
 
+    public Hessian2StreamingOutput(Hessian2Output out) {
+        _out = out;
+    }
+
+    public Hessian2Output getHessian2Output() {
+        return _out;
+    }
+
     public boolean isCloseStreamOnClose() {
         return _out.isCloseStreamOnClose();
     }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianFactory.java
new file mode 100644
index 0000000..64a2883
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianFactory.java
@@ -0,0 +1,217 @@
+/*
+ * 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 com.alibaba.com.caucho.hessian.util.HessianFreeList;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.logging.Logger;
+
+/**
+ * Factory for creating HessianInput and HessianOutput streams.
+ */
+public class HessianFactory {
+    public static final Logger log
+            = Logger.getLogger(HessianFactory.class.getName());
+    private final HessianFreeList<Hessian2Output> _freeHessian2Output
+            = new HessianFreeList<Hessian2Output>(32);
+    private final HessianFreeList<Hessian2Input> _freeHessian2Input
+            = new HessianFreeList<Hessian2Input>(32);
+    private SerializerFactory _serializerFactory;
+    private SerializerFactory _defaultSerializerFactory;
+
+    public HessianFactory() {
+        _defaultSerializerFactory = SerializerFactory.createDefault();
+        _serializerFactory = _defaultSerializerFactory;
+    }
+
+    public SerializerFactory getSerializerFactory() {
+        // the default serializer factory cannot be modified by external
+        // callers
+        if (_serializerFactory == _defaultSerializerFactory) {
+            _serializerFactory = new SerializerFactory();
+        }
+
+        return _serializerFactory;
+    }
+
+    public void setSerializerFactory(SerializerFactory factory) {
+        _serializerFactory = factory;
+    }
+
+    /**
+     * Enable whitelist deserialization mode. Only classes matching the whitelist
+     * will be allowed.
+     */
+    public void setWhitelist(boolean isWhitelist) {
+        getSerializerFactory().getClassFactory().setWhitelist(isWhitelist);
+    }
+
+    /**
+     * Allow a class or package based on a pattern.
+     * <p>
+     * Examples: "java.util.*", "com.foo.io.Bean"
+     */
+    public void allow(String pattern) {
+        getSerializerFactory().getClassFactory().allow(pattern);
+    }
+
+
+    /**
+     * Deny a class or package based on a pattern.
+     * <p>
+     * Examples: "java.util.*", "com.foo.io.Bean"
+     */
+    public void deny(String pattern) {
+        getSerializerFactory().getClassFactory().deny(pattern);
+    }
+
+    /**
+     * Creates a new Hessian 2.0 deserializer.
+     */
+    public Hessian2Input createHessian2Input(InputStream is) {
+        Hessian2Input in = _freeHessian2Input.allocate();
+
+        if (in == null) {
+            in = new Hessian2Input(is);
+            in.setSerializerFactory(getSerializerFactory());
+        } else {
+            in.init(is);
+        }
+
+        return in;
+    }
+
+    /**
+     * Frees a Hessian 2.0 deserializer
+     */
+    public void freeHessian2Input(Hessian2Input in) {
+        if (in == null)
+            return;
+
+        in.free();
+
+        _freeHessian2Input.free(in);
+    }
+
+    /**
+     * Creates a new Hessian 2.0 deserializer.
+     */
+    public Hessian2StreamingInput createHessian2StreamingInput(InputStream is) {
+        Hessian2StreamingInput in = new Hessian2StreamingInput(is);
+        in.setSerializerFactory(getSerializerFactory());
+
+        return in;
+    }
+
+    /**
+     * Frees a Hessian 2.0 deserializer
+     */
+    public void freeHessian2StreamingInput(Hessian2StreamingInput in) {
+    }
+
+    /**
+     * Creates a new Hessian 2.0 serializer.
+     */
+    public Hessian2Output createHessian2Output(OutputStream os) {
+        Hessian2Output out = createHessian2Output();
+
+        out.init(os);
+
+        return out;
+    }
+
+    /**
+     * Creates a new Hessian 2.0 serializer.
+     */
+    public Hessian2Output createHessian2Output() {
+        Hessian2Output out = _freeHessian2Output.allocate();
+
+        if (out == null) {
+            out = new Hessian2Output();
+
+            out.setSerializerFactory(getSerializerFactory());
+        }
+
+        return out;
+    }
+
+    /**
+     * Frees a Hessian 2.0 serializer
+     */
+    public void freeHessian2Output(Hessian2Output out) {
+        if (out == null)
+            return;
+
+        out.free();
+
+        _freeHessian2Output.free(out);
+    }
+
+    /**
+     * Creates a new Hessian 2.0 serializer.
+     */
+    public Hessian2StreamingOutput createHessian2StreamingOutput(OutputStream os) {
+        Hessian2Output out = createHessian2Output(os);
+
+        return new Hessian2StreamingOutput(out);
+    }
+
+    /**
+     * Frees a Hessian 2.0 serializer
+     */
+    public void freeHessian2StreamingOutput(Hessian2StreamingOutput out) {
+        if (out == null)
+            return;
+
+        freeHessian2Output(out.getHessian2Output());
+    }
+
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianMethodSerializationException.java
similarity index 70%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/HessianMethodSerializationException.java
index 72a9822..9419242 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianMethodSerializationException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -48,18 +48,37 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
+import com.alibaba.com.caucho.hessian.HessianException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Exception for faults when the fault doesn't return a java exception.
+ * This exception is required for MicroHessianInput.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class HessianMethodSerializationException extends HessianException {
     /**
-     * Looks up a proxy object.
+     * Zero-arg constructor.
      */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public HessianMethodSerializationException() {
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianMethodSerializationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianMethodSerializationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianMethodSerializationException(Throwable cause) {
+        super(cause);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianProtocolException.java b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianProtocolException.java
index 28aa320..6456c1c 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianProtocolException.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianProtocolException.java
@@ -98,7 +98,6 @@
     /**
      * Returns the underlying cause.
      */
-    @Override
     public Throwable getCause() {
         return getRootCause();
     }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianRemote.java b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianRemote.java
index 0f5755e..c3def9d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianRemote.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/HessianRemote.java
@@ -52,7 +52,7 @@
  * Encapsulates a remote address when no stub is available, e.g. for
  * Java MicroEdition.
  */
-public class HessianRemote {
+public class HessianRemote implements java.io.Serializable {
     private String type;
     private String url;
 
@@ -97,7 +97,6 @@
     /**
      * Defines the hashcode.
      */
-    @Override
     public int hashCode() {
         return url.hashCode();
     }
@@ -105,7 +104,6 @@
     /**
      * Defines equality
      */
-    @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof HessianRemote))
             return false;
@@ -118,8 +116,7 @@
     /**
      * Readable version of the remote.
      */
-    @Override
     public String toString() {
-        return "[HessianRemote " + url + "]";
+        return "HessianRemote[" + url + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/IOExceptionWrapper.java b/src/main/java/com/alibaba/com/caucho/hessian/io/IOExceptionWrapper.java
index 261d4be..9b66c80 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/IOExceptionWrapper.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/IOExceptionWrapper.java
@@ -68,7 +68,6 @@
         _cause = cause;
     }
 
-    @Override
     public Throwable getCause() {
         return _cause;
     }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/InetAddressHandle.java
similarity index 74%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/InetAddressHandle.java
index be505fb..dad139a 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/InetAddressHandle.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -48,9 +48,31 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import java.net.InetAddress;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Handle for an InetAddress object.
+ */
+public class InetAddressHandle implements java.io.Serializable, HessianHandle {
+    private static final Logger log = Logger.getLogger(InetAddressHandle.class.getName());
+
+    private String hostName;
+    private byte[] address;
+
+    public InetAddressHandle(String hostName, byte[] address) {
+        this.hostName = hostName;
+        this.address = address;
+    }
+
+    private Object readResolve() {
+        try {
+            return InetAddress.getByAddress(this.hostName, this.address);
+        } catch (Exception e) {
+            log.log(Level.FINE, e.toString(), e);
+
+            return null;
+        }
+    }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/InetAddressSerializer.java
similarity index 79%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/InetAddressSerializer.java
index 72a9822..c0785a6 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/InetAddressSerializer.java
@@ -49,17 +49,27 @@
 package com.alibaba.com.caucho.hessian.io;
 
 import java.io.IOException;
+import java.net.InetAddress;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a locale.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class InetAddressSerializer extends AbstractSerializer {
+    private static InetAddressSerializer SERIALIZER = new InetAddressSerializer();
+
+    public static InetAddressSerializer create() {
+        return SERIALIZER;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            InetAddress addr = (InetAddress) obj;
+            out.writeObject(new InetAddressHandle(addr.getHostName(),
+                    addr.getAddress()));
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamDeserializer.java
index 2dd0290..cbe92bf 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamDeserializer.java
@@ -54,10 +54,12 @@
  * Serializing a stream object.
  */
 public class InputStreamDeserializer extends AbstractDeserializer {
+    public static final InputStreamDeserializer DESER
+            = new InputStreamDeserializer();
+
     public InputStreamDeserializer() {
     }
 
-    @Override
     public Object readObject(AbstractHessianInput in)
             throws IOException {
         return in.readInputStream();
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamSerializer.java
index 9bf87e6..ecb5165 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/InputStreamSerializer.java
@@ -58,7 +58,6 @@
     public InputStreamSerializer() {
     }
 
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         InputStream is = (InputStream) obj;
@@ -66,14 +65,7 @@
         if (is == null)
             out.writeNull();
         else {
-            byte[] buf = new byte[1024];
-            int len;
-
-            while ((len = is.read(buf, 0, buf.length)) > 0) {
-                out.writeByteBufferPart(buf, 0, len);
-            }
-
-            out.writeByteBufferEnd(buf, 0, 0);
+            out.writeByteStream(is);
         }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/IteratorDeserializer.java
similarity index 78%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/IteratorDeserializer.java
index 72a9822..5a8a209 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/IteratorDeserializer.java
@@ -49,17 +49,35 @@
 package com.alibaba.com.caucho.hessian.io;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing a JDK 1.2 Collection.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class IteratorDeserializer extends AbstractListDeserializer {
+    private static IteratorDeserializer _deserializer;
+
+    public static IteratorDeserializer create() {
+        if (_deserializer == null)
+            _deserializer = new IteratorDeserializer();
+
+        return _deserializer;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public Object readList(AbstractHessianInput in, int length)
             throws IOException {
-        return new HessianRemote(type, url);
+        ArrayList list = new ArrayList();
+
+        in.addRef(list);
+
+        while (!in.isEnd())
+            list.add(in.readObject());
+
+        in.readEnd();
+
+        return list.iterator();
     }
 }
+
+
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/IteratorSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/IteratorSerializer.java
index 2d704d2..e09f0c9 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/IteratorSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/IteratorSerializer.java
@@ -64,7 +64,6 @@
         return _serializer;
     }
 
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         Iterator iter = (Iterator) obj;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/JavaDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/JavaDeserializer.java
index f20b31d..e9e16bf 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/JavaDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/JavaDeserializer.java
@@ -48,36 +48,29 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import com.alibaba.com.caucho.hessian.io.FieldDeserializer2Factory.NullFieldDeserializer;
+
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
 import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Logger;
 
 /**
  * Serializing an object for known object types.
  */
 public class JavaDeserializer extends AbstractMapDeserializer {
-    private static final Logger log
-            = Logger.getLogger(JavaDeserializer.class.getName());
-
-    private Class _type;
-    private HashMap _fieldMap;
+    private Class<?> _type;
+    private HashMap<?, FieldDeserializer2> _fieldMap;
     private Method _readResolve;
-    private Constructor _constructor;
+    private Constructor<?> _constructor;
     private Object[] _constructorArgs;
 
-    public JavaDeserializer(Class cl) {
+    public JavaDeserializer(Class<?> cl, FieldDeserializer2Factory fieldFactory) {
         _type = cl;
-        _fieldMap = getFieldMap(cl);
+        _fieldMap = getFieldMap(cl, fieldFactory);
 
         _readResolve = getReadResolve(cl);
 
@@ -85,55 +78,14 @@
             _readResolve.setAccessible(true);
         }
 
-        Constructor[] constructors = cl.getDeclaredConstructors();
-        long bestCost = Long.MAX_VALUE;
-
-        for (int i = 0; i < constructors.length; i++) {
-            Class[] param = constructors[i].getParameterTypes();
-            long cost = 0;
-
-            for (int j = 0; j < param.length; j++) {
-                cost = 4 * cost;
-
-                if (Object.class.equals(param[j]))
-                    cost += 1;
-                else if (String.class.equals(param[j]))
-                    cost += 2;
-                else if (int.class.equals(param[j]))
-                    cost += 3;
-                else if (long.class.equals(param[j]))
-                    cost += 4;
-                else if (param[j].isPrimitive())
-                    cost += 5;
-                else
-                    cost += 6;
-            }
-
-            if (cost < 0 || cost > (1 << 48))
-                cost = 1 << 48;
-
-            cost += (long) param.length << 48;
-
-            if (cost < bestCost) {
-                _constructor = constructors[i];
-                bestCost = cost;
-            }
-        }
-
-        if (_constructor != null) {
-            _constructor.setAccessible(true);
-            Class[] params = _constructor.getParameterTypes();
-            _constructorArgs = new Object[params.length];
-            for (int i = 0; i < params.length; i++) {
-                _constructorArgs[i] = getParamArg(params[i]);
-            }
-        }
+        _constructor = getConstructor(cl);
+        _constructorArgs = getConstructorArgs(_constructor);
     }
 
     /**
      * Creates a map of the classes fields.
      */
-    protected static Object getParamArg(Class cl) {
+    protected static Object getParamArg(Class<?> cl) {
         if (!cl.isPrimitive())
             return null;
         else if (boolean.class.equals(cl))
@@ -174,12 +126,75 @@
             throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);
     }
 
+    protected Constructor<?> getConstructor(Class<?> cl) {
+        Constructor<?>[] constructors = cl.getDeclaredConstructors();
+        long bestCost = Long.MAX_VALUE;
+
+        Constructor<?> constructor = null;
+
+        for (int i = 0; i < constructors.length; i++) {
+            Class<?>[] param = constructors[i].getParameterTypes();
+            long cost = 0;
+
+            for (int j = 0; j < param.length; j++) {
+                cost = 4 * cost;
+
+                if (Object.class.equals(param[j]))
+                    cost += 1;
+                else if (String.class.equals(param[j]))
+                    cost += 2;
+                else if (int.class.equals(param[j]))
+                    cost += 3;
+                else if (long.class.equals(param[j]))
+                    cost += 4;
+                else if (param[j].isPrimitive())
+                    cost += 5;
+                else
+                    cost += 6;
+            }
+
+            if (cost < 0 || cost > (1 << 48))
+                cost = 1 << 48;
+
+            cost += (long) param.length << 48;
+
+            if (cost < bestCost) {
+                constructor = constructors[i];
+                bestCost = cost;
+            }
+        }
+
+        if (constructor != null) {
+            constructor.setAccessible(true);
+        }
+
+        return constructor;
+    }
+
+    protected Object[] getConstructorArgs(Constructor<?> constructor) {
+        Object[] constructorArgs = null;
+
+        if (constructor != null) {
+            Class<?>[] params = constructor.getParameterTypes();
+            constructorArgs = new Object[params.length];
+            for (int i = 0; i < params.length; i++) {
+                constructorArgs[i] = getParamArg(params[i]);
+            }
+        }
+
+        return constructorArgs;
+    }
+
     @Override
-    public Class getType() {
+    public Class<?> getType() {
         return _type;
     }
 
     @Override
+    public boolean isReadResolve() {
+        return _readResolve != null;
+    }
+
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         try {
@@ -196,7 +211,40 @@
     }
 
     @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object[] createFields(int len) {
+        return new FieldDeserializer2[len];
+    }
+
+    @Override
+    public Object createField(String name) {
+        Object reader = _fieldMap.get(name);
+
+        if (reader == null)
+            reader = NullFieldDeserializer.DESER;
+
+        return reader;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readObject(in, obj, (FieldDeserializer2[]) fields);
+        } catch (IOException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             String[] fieldNames)
             throws IOException {
         try {
             Object obj = instantiate();
@@ -214,15 +262,15 @@
     /**
      * Returns the readResolve method
      */
-    protected Method getReadResolve(Class cl) {
+    protected Method getReadResolve(Class<?> cl) {
         for (; cl != null; cl = cl.getSuperclass()) {
             Method[] methods = cl.getDeclaredMethods();
 
             for (int i = 0; i < methods.length; i++) {
                 Method method = methods[i];
 
-                if (method.getName().equals("readResolve") &&
-                        method.getParameterTypes().length == 0)
+                if (method.getName().equals("readResolve")
+                        && method.getParameterTypes().length == 0)
                     return method;
             }
         }
@@ -238,7 +286,7 @@
             while (!in.isEnd()) {
                 Object key = in.readObject();
 
-                FieldDeserializer deser = (FieldDeserializer) _fieldMap.get(key);
+                FieldDeserializer2 deser = _fieldMap.get(key);
 
                 if (deser != null)
                     deser.deserialize(in, obj);
@@ -248,7 +296,7 @@
 
             in.readMapEnd();
 
-            Object resolve = resolve(obj);
+            Object resolve = resolve(in, obj);
 
             if (obj != resolve)
                 in.setRef(ref, resolve);
@@ -261,25 +309,18 @@
         }
     }
 
-    public Object readObject(AbstractHessianInput in,
-                             Object obj,
-                             String[] fieldNames)
+    private Object readObject(AbstractHessianInput in,
+                              Object obj,
+                              FieldDeserializer2[] fields)
             throws IOException {
         try {
             int ref = in.addRef(obj);
 
-            for (int i = 0; i < fieldNames.length; i++) {
-                String name = fieldNames[i];
-
-                FieldDeserializer deser = (FieldDeserializer) _fieldMap.get(name);
-
-                if (deser != null)
-                    deser.deserialize(in, obj);
-                else
-                    in.readObject();
+            for (FieldDeserializer2 reader : fields) {
+                reader.deserialize(in, obj);
             }
 
-            Object resolve = resolve(obj);
+            Object resolve = resolve(in, obj);
 
             if (obj != resolve)
                 in.setRef(ref, resolve);
@@ -292,14 +333,45 @@
         }
     }
 
-    private Object resolve(Object obj)
+    public Object readObject(AbstractHessianInput in,
+                             Object obj,
+                             String[] fieldNames)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            for (String fieldName : fieldNames) {
+                FieldDeserializer2 reader = _fieldMap.get(fieldName);
+
+                if (reader != null)
+                    reader.deserialize(in, obj);
+                else
+                    in.readObject();
+            }
+
+            Object resolve = resolve(in, obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e);
+        }
+    }
+
+    protected Object resolve(AbstractHessianInput in, Object obj)
             throws Exception {
         // if there's a readResolve method, call it
         try {
             if (_readResolve != null)
                 return _readResolve.invoke(obj, new Object[0]);
         } catch (InvocationTargetException e) {
-            if (e.getTargetException() != null)
+            if (e.getCause() instanceof Exception)
+                throw (Exception) e.getCause();
+            else
                 throw e;
         }
 
@@ -321,8 +393,10 @@
     /**
      * Creates a map of the classes fields.
      */
-    protected HashMap getFieldMap(Class cl) {
-        HashMap fieldMap = new HashMap();
+    protected HashMap<String, FieldDeserializer2>
+    getFieldMap(Class<?> cl, FieldDeserializer2Factory fieldFactory) {
+        HashMap<String, FieldDeserializer2> fieldMap
+                = new HashMap<String, FieldDeserializer2>();
 
         for (; cl != null; cl = cl.getSuperclass()) {
             Field[] fields = cl.getDeclaredFields();
@@ -335,53 +409,16 @@
                 else if (fieldMap.get(field.getName()) != null)
                     continue;
 
-                // XXX: could parameterize the handler to only deal with public
-                try {
-                    field.setAccessible(true);
-                } catch (Throwable e) {
-                    e.printStackTrace();
-                }
+	/*
+        // XXX: could parameterize the handler to only deal with public
+        try {
+          field.setAccessible(true);
+        } catch (Throwable e) {
+          e.printStackTrace();
+        }
+	*/
 
-                Class type = field.getType();
-                FieldDeserializer deser;
-
-                if (String.class.equals(type))
-                    deser = new StringFieldDeserializer(field);
-                else if (byte.class.equals(type)) {
-                    deser = new ByteFieldDeserializer(field);
-                } else if (short.class.equals(type)) {
-                    deser = new ShortFieldDeserializer(field);
-                } else if (int.class.equals(type)) {
-                    deser = new IntFieldDeserializer(field);
-                } else if (long.class.equals(type)) {
-                    deser = new LongFieldDeserializer(field);
-                } else if (float.class.equals(type)) {
-                    deser = new FloatFieldDeserializer(field);
-                } else if (double.class.equals(type)) {
-                    deser = new DoubleFieldDeserializer(field);
-                } else if (boolean.class.equals(type)) {
-                    deser = new BooleanFieldDeserializer(field);
-                } else if (java.sql.Date.class.equals(type)) {
-                    deser = new SqlDateFieldDeserializer(field);
-                } else if (java.sql.Timestamp.class.equals(type)) {
-                    deser = new SqlTimestampFieldDeserializer(field);
-                } else if (java.sql.Time.class.equals(type)) {
-                    deser = new SqlTimeFieldDeserializer(field);
-                }
-                // support generic type of map
-                else if (Map.class.equals(type)
-                        && field.getGenericType() != field.getType()) {
-                    deser = new ObjectMapFieldDeserializer(field);
-                } else if (List.class.equals(type)
-                        && field.getGenericType() != field.getType()) {
-                    deser = new ObjectListFieldDeserializer(field);
-                } else if (Set.class.equals(type)
-                        && field.getGenericType() != field.getType()) {
-                    deser = new ObjectSetFieldDeserializer(field);
-                }
-                else {
-                    deser = new ObjectFieldDeserializer(field);
-                }
+                FieldDeserializer2 deser = fieldFactory.create(field);
 
                 fieldMap.put(field.getName(), deser);
             }
@@ -389,398 +426,4 @@
 
         return fieldMap;
     }
-
-    abstract static class FieldDeserializer {
-        abstract void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException;
-    }
-
-    static class ObjectFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        ObjectFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            Object value = null;
-
-            try {
-                value = in.readObject(_field.getType());
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class BooleanFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        BooleanFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            boolean value = false;
-
-            try {
-                value = in.readBoolean();
-
-                _field.setBoolean(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class ByteFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        ByteFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            int value = 0;
-
-            try {
-                value = in.readInt();
-
-                _field.setByte(obj, (byte) value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class ShortFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        ShortFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            int value = 0;
-
-            try {
-                value = in.readInt();
-
-                _field.setShort(obj, (short) value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class ObjectMapFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        ObjectMapFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            Object value = null;
-
-            try {
-
-                Type[] types = ((ParameterizedType) _field.getGenericType()).getActualTypeArguments();
-                value = in.readObject(_field.getType(),
-                        isPrimitive(types[0]) ? (Class<?>) types[0] : null,
-                        isPrimitive(types[1]) ? (Class<?>) types[1] : null
-                );
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class ObjectListFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        ObjectListFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            Object value = null;
-
-            try {
-
-                Type[] types = ((ParameterizedType) _field.getGenericType()).getActualTypeArguments();
-                value = in.readObject(_field.getType(),
-                        isPrimitive(types[0]) ? (Class<?>) types[0] : null
-                );
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class ObjectSetFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        ObjectSetFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            Object value = null;
-
-            try {
-
-                Type[] types = ((ParameterizedType) _field.getGenericType()).getActualTypeArguments();
-                value = in.readObject(_field.getType(),
-                        isPrimitive(types[0]) ? (Class<?>) types[0] : null
-                );
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-
-    static class IntFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        IntFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            int value = 0;
-
-            try {
-                value = in.readInt();
-
-                _field.setInt(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class LongFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        LongFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            long value = 0;
-
-            try {
-                value = in.readLong();
-
-                _field.setLong(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class FloatFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        FloatFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            double value = 0;
-
-            try {
-                value = in.readDouble();
-
-                _field.setFloat(obj, (float) value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class DoubleFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        DoubleFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            double value = 0;
-
-            try {
-                value = in.readDouble();
-
-                _field.setDouble(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class StringFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        StringFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            String value = null;
-
-            try {
-                value = in.readString();
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class SqlDateFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        SqlDateFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            java.sql.Date value = null;
-
-            try {
-                java.util.Date date = (java.util.Date) in.readObject();
-                if (date != null)
-                    value = new java.sql.Date(date.getTime());
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class SqlTimestampFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        SqlTimestampFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            java.sql.Timestamp value = null;
-
-            try {
-                java.util.Date date = (java.util.Date) in.readObject();
-                if (date != null)
-                    value = new java.sql.Timestamp(date.getTime());
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    static class SqlTimeFieldDeserializer extends FieldDeserializer {
-        private final Field _field;
-
-        SqlTimeFieldDeserializer(Field field) {
-            _field = field;
-        }
-
-        @Override
-        void deserialize(AbstractHessianInput in, Object obj)
-                throws IOException {
-            java.sql.Time value = null;
-
-            try {
-                java.util.Date date = (java.util.Date) in.readObject();
-                if (date != null) value = new java.sql.Time(date.getTime());
-
-                _field.set(obj, value);
-            } catch (Exception e) {
-                logDeserializeError(_field, obj, value, e);
-            }
-        }
-    }
-
-    /**
-     * @see java.lang.Boolean#TYPE
-     * @see java.lang.Character#TYPE
-     * @see java.lang.Byte#TYPE
-     * @see java.lang.Short#TYPE
-     * @see java.lang.Integer#TYPE
-     * @see java.lang.Long#TYPE
-     * @see java.lang.Float#TYPE
-     * @see java.lang.Double#TYPE
-     * @see java.lang.Void#TYPE
-     */
-    private static boolean isPrimitive(Type type) {
-        try {
-            if (type != null) {
-                if (type instanceof Class<?>) {
-                    Class<?> clazz = (Class<?>) type;
-                    return clazz.isPrimitive()
-                            || PRIMITIVE_TYPE.containsKey(clazz.getName());
-                }
-            }
-        } catch (Exception e) {
-            // ignore exception
-        }
-        return false;
-    }
-
-    static final Map<String, Boolean> PRIMITIVE_TYPE = new HashMap<String, Boolean>() {
-        {
-            put(Boolean.class.getName(), true);
-            put(Character.class.getName(), true);
-            put(Byte.class.getName(), true);
-            put(Short.class.getName(), true);
-            put(Integer.class.getName(), true);
-            put(Long.class.getName(), true);
-            put(Float.class.getName(), true);
-            put(Double.class.getName(), true);
-            put(Void.class.getName(), true);
-        }
-    };
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/JavaSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/JavaSerializer.java
index 3fe18a7..ab89f94 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/JavaSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/JavaSerializer.java
@@ -48,13 +48,16 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import com.alibaba.com.caucho.hessian.HessianUnshared;
+
 import java.io.IOException;
+import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
+import java.util.WeakHashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -65,7 +68,8 @@
     private static final Logger log
             = Logger.getLogger(JavaSerializer.class.getName());
 
-    private static Object[] NULL_ARGS = new Object[0];
+    private static final WeakHashMap<Class<?>, SoftReference<JavaSerializer>> _serializerMap
+            = new WeakHashMap<Class<?>, SoftReference<JavaSerializer>>();
 
     private Field[] _fields;
     private FieldSerializer[] _fieldSerializers;
@@ -73,63 +77,48 @@
     private Object _writeReplaceFactory;
     private Method _writeReplace;
 
-    public JavaSerializer(Class cl, ClassLoader loader) {
-        introspectWriteReplace(cl, loader);
+    public JavaSerializer(Class<?> cl) {
+        introspect(cl);
+
+        _writeReplace = getWriteReplace(cl);
 
         if (_writeReplace != null)
             _writeReplace.setAccessible(true);
+    }
 
-        List primitiveFields = new ArrayList();
-        List compoundFields = new ArrayList();
+    public static Serializer create(Class<?> cl) {
+        synchronized (_serializerMap) {
+            SoftReference<JavaSerializer> baseRef
+                    = _serializerMap.get(cl);
 
-        for (; cl != null; cl = cl.getSuperclass()) {
-            Field[] fields = cl.getDeclaredFields();
-            for (int i = 0; i < fields.length; i++) {
-                Field field = fields[i];
+            JavaSerializer base = baseRef != null ? baseRef.get() : null;
 
-                if (Modifier.isTransient(field.getModifiers())
-                        || Modifier.isStatic(field.getModifiers()))
-                    continue;
-
-                // XXX: could parameterize the handler to only deal with public
-                field.setAccessible(true);
-
-                if (field.getType().isPrimitive()
-                        || (field.getType().getName().startsWith("java.lang.")
-                        && !field.getType().equals(Object.class)))
-                    primitiveFields.add(field);
+            if (base == null) {
+                if (cl.isAnnotationPresent(HessianUnshared.class))
+                    base = new JavaUnsharedSerializer(cl);
                 else
-                    compoundFields.add(field);
+                    base = new JavaSerializer(cl);
+
+                baseRef = new SoftReference<JavaSerializer>(base);
+                _serializerMap.put(cl, baseRef);
             }
-        }
 
-        List fields = new ArrayList();
-        fields.addAll(primitiveFields);
-        fields.addAll(compoundFields);
-        Collections.reverse(fields);
-
-        _fields = new Field[fields.size()];
-        fields.toArray(_fields);
-
-        _fieldSerializers = new FieldSerializer[_fields.length];
-
-        for (int i = 0; i < _fields.length; i++) {
-            _fieldSerializers[i] = getFieldSerializer(_fields[i].getType());
+            return base;
         }
     }
 
     /**
      * Returns the writeReplace method
      */
-    protected static Method getWriteReplace(Class cl) {
+    public static Method getWriteReplace(Class<?> cl) {
         for (; cl != null; cl = cl.getSuperclass()) {
             Method[] methods = cl.getDeclaredMethods();
 
             for (int i = 0; i < methods.length; i++) {
                 Method method = methods[i];
 
-                if (method.getName().equals("writeReplace") &&
-                        method.getParameterTypes().length == 0)
+                if (method.getName().equals("writeReplace")
+                        && method.getParameterTypes().length == 0)
                     return method;
             }
         }
@@ -137,7 +126,7 @@
         return null;
     }
 
-    private static FieldSerializer getFieldSerializer(Class type) {
+    private static FieldSerializer getFieldSerializer(Class<?> type) {
         if (int.class.equals(type)
                 || byte.class.equals(type)
                 || short.class.equals(type)
@@ -161,34 +150,53 @@
             return FieldSerializer.SER;
     }
 
-    private void introspectWriteReplace(Class cl, ClassLoader loader) {
-        try {
-            String className = cl.getName() + "HessianSerializer";
+    protected void introspect(Class<?> cl) {
+        if (_writeReplace != null)
+            _writeReplace.setAccessible(true);
 
-            Class serializerClass = Class.forName(className, false, loader);
+        ArrayList<Field> primitiveFields = new ArrayList<Field>();
+        ArrayList<Field> compoundFields = new ArrayList<Field>();
 
-            Object serializerObject = serializerClass.newInstance();
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Field[] fields = cl.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
 
-            Method writeReplace = getWriteReplace(serializerClass, cl);
+                if (Modifier.isTransient(field.getModifiers())
+                        || Modifier.isStatic(field.getModifiers()))
+                    continue;
 
-            if (writeReplace != null) {
-                _writeReplaceFactory = serializerObject;
-                _writeReplace = writeReplace;
+                // XXX: could parameterize the handler to only deal with public
+                field.setAccessible(true);
 
-                return;
+                if (field.getType().isPrimitive()
+                        || (field.getType().getName().startsWith("java.lang.")
+                        && !field.getType().equals(Object.class)))
+                    primitiveFields.add(field);
+                else
+                    compoundFields.add(field);
             }
-        } catch (ClassNotFoundException e) {
-        } catch (Exception e) {
-            log.log(Level.FINER, e.toString(), e);
         }
 
-        _writeReplace = getWriteReplace(cl);
+        ArrayList<Field> fields = new ArrayList<Field>();
+        fields.addAll(primitiveFields);
+        fields.addAll(compoundFields);
+        Collections.reverse(fields);
+
+        _fields = new Field[fields.size()];
+        fields.toArray(_fields);
+
+        _fieldSerializers = new FieldSerializer[_fields.length];
+
+        for (int i = 0; i < _fields.length; i++) {
+            _fieldSerializers[i] = getFieldSerializer(_fields[i].getType());
+        }
     }
 
     /**
      * Returns the writeReplace method
      */
-    protected Method getWriteReplace(Class cl, Class param) {
+    protected Method getWriteReplace(Class<?> cl, Class<?> param) {
         for (; cl != null; cl = cl.getSuperclass()) {
             for (Method method : cl.getDeclaredMethods()) {
                 if (method.getName().equals("writeReplace")
@@ -208,7 +216,7 @@
             return;
         }
 
-        Class cl = obj.getClass();
+        Class<?> cl = obj.getClass();
 
         try {
             if (_writeReplace != null) {
@@ -219,17 +227,29 @@
                 else
                     repl = _writeReplace.invoke(obj);
 
-                //Some class would return itself for wrapReplace, which would cause infinite recursion
-                //In this case, we could write the object just like normal cases
-                if (repl != obj) {
-                    out.removeRef(obj);
+                // out.removeRef(obj);
 
-                    out.writeObject(repl);
+        /*
+        out.writeObject(repl);
 
-                    out.replaceRef(repl, obj);
+        out.replaceRef(repl, obj);
+        */
 
-                    return;
+                //hessian/3a5a
+                int ref = out.writeObjectBegin(cl.getName());
+
+                if (ref < -1) {
+                    writeObject10(repl, out);
+                } else {
+                    if (ref == -1) {
+                        writeDefinition20(out);
+                        out.writeObjectBegin(cl.getName());
+                    }
+
+                    writeInstance(repl, out);
                 }
+
+                return;
             }
         } catch (RuntimeException e) {
             throw e;
@@ -252,7 +272,7 @@
         }
     }
 
-    private void writeObject10(Object obj, AbstractHessianOutput out)
+    protected void writeObject10(Object obj, AbstractHessianOutput out)
             throws IOException {
         for (int i = 0; i < _fields.length; i++) {
             Field field = _fields[i];
@@ -276,12 +296,23 @@
         }
     }
 
+    @Override
     public void writeInstance(Object obj, AbstractHessianOutput out)
             throws IOException {
-        for (int i = 0; i < _fields.length; i++) {
-            Field field = _fields[i];
+        try {
+            for (int i = 0; i < _fields.length; i++) {
+                Field field = _fields[i];
 
-            _fieldSerializers[i].serialize(out, obj, field);
+                _fieldSerializers[i].serialize(out, obj, field);
+            }
+        } catch (RuntimeException e) {
+            throw new RuntimeException(e.getMessage() + "\n class: "
+                    + obj.getClass().getName(),
+                    e);
+        } catch (IOException e) {
+            throw new IOExceptionWrapper(e.getMessage() + "\n class: "
+                    + obj.getClass().getName(),
+                    e);
         }
     }
 
@@ -301,10 +332,14 @@
             try {
                 out.writeObject(value);
             } catch (RuntimeException e) {
-                throw new RuntimeException(e.getMessage() + "\n Java field: " + field,
+                throw new RuntimeException(e.getMessage() + "\n field: "
+                        + field.getDeclaringClass().getName()
+                        + '.' + field.getName(),
                         e);
             } catch (IOException e) {
-                throw new IOExceptionWrapper(e.getMessage() + "\n Java field: " + field,
+                throw new IOExceptionWrapper(e.getMessage() + "\n field: "
+                        + field.getDeclaringClass().getName()
+                        + '.' + field.getName(),
                         e);
             }
         }
@@ -313,7 +348,6 @@
     static class BooleanFieldSerializer extends FieldSerializer {
         static final FieldSerializer SER = new BooleanFieldSerializer();
 
-        @Override
         void serialize(AbstractHessianOutput out, Object obj, Field field)
                 throws IOException {
             boolean value = false;
@@ -331,7 +365,6 @@
     static class IntFieldSerializer extends FieldSerializer {
         static final FieldSerializer SER = new IntFieldSerializer();
 
-        @Override
         void serialize(AbstractHessianOutput out, Object obj, Field field)
                 throws IOException {
             int value = 0;
@@ -349,7 +382,6 @@
     static class LongFieldSerializer extends FieldSerializer {
         static final FieldSerializer SER = new LongFieldSerializer();
 
-        @Override
         void serialize(AbstractHessianOutput out, Object obj, Field field)
                 throws IOException {
             long value = 0;
@@ -367,7 +399,6 @@
     static class DoubleFieldSerializer extends FieldSerializer {
         static final FieldSerializer SER = new DoubleFieldSerializer();
 
-        @Override
         void serialize(AbstractHessianOutput out, Object obj, Field field)
                 throws IOException {
             double value = 0;
@@ -385,7 +416,6 @@
     static class StringFieldSerializer extends FieldSerializer {
         static final FieldSerializer SER = new StringFieldSerializer();
 
-        @Override
         void serialize(AbstractHessianOutput out, Object obj, Field field)
                 throws IOException {
             String value = null;
@@ -403,7 +433,6 @@
     static class DateFieldSerializer extends FieldSerializer {
         static final FieldSerializer SER = new DateFieldSerializer();
 
-        @Override
         void serialize(AbstractHessianOutput out, Object obj, Field field)
                 throws IOException {
             java.util.Date value = null;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/JavaUnsharedSerializer.java
similarity index 75%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/JavaUnsharedSerializer.java
index 72a9822..ff95fa2 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/JavaUnsharedSerializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -49,17 +49,28 @@
 package com.alibaba.com.caucho.hessian.io;
 
 import java.io.IOException;
+import java.util.logging.Logger;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object for known object types.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class JavaUnsharedSerializer extends JavaSerializer {
+    private static final Logger log
+            = Logger.getLogger(JavaUnsharedSerializer.class.getName());
+
+    public JavaUnsharedSerializer(Class<?> cl) {
+        super(cl);
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        boolean oldUnshared = out.setUnshared(true);
+
+        try {
+            super.writeObject(obj, out);
+        } finally {
+            out.setUnshared(oldUnshared);
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleHandle.java b/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleHandle.java
index 751eee6..880f0a5 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleHandle.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleHandle.java
@@ -48,6 +48,7 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+
 import java.util.Locale;
 
 /**
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleSerializer.java
index 2ac9393..c757288 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/LocaleSerializer.java
@@ -48,6 +48,7 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+
 import java.io.IOException;
 import java.util.Locale;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/MapDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/MapDeserializer.java
index 9720fb6..bfe4cb5 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/MapDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/MapDeserializer.java
@@ -50,7 +50,6 @@
 
 import java.io.IOException;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Type;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.SortedMap;
@@ -60,16 +59,16 @@
  * Deserializing a JDK 1.2 Map.
  */
 public class MapDeserializer extends AbstractMapDeserializer {
-    private Class _type;
-    private Constructor _ctor;
+    private Class<?> _type;
+    private Constructor<?> _ctor;
 
-    public MapDeserializer(Class type) {
+    public MapDeserializer(Class<?> type) {
         if (type == null)
             type = HashMap.class;
 
         _type = type;
 
-        Constructor[] ctors = type.getConstructors();
+        Constructor<?>[] ctors = type.getConstructors();
         for (int i = 0; i < ctors.length; i++) {
             if (ctors[i].getParameterTypes().length == 0)
                 _ctor = ctors[i];
@@ -84,27 +83,15 @@
         }
     }
 
-    @Override
-    public Class getType() {
+    public Class<?> getType() {
         if (_type != null)
             return _type;
         else
             return HashMap.class;
     }
 
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
-        return readMap(in, null, null);
-    }
-
-    /**
-     *  support generic type of map, fix the type of short serialization <p>
-     *  eg: Map<String, Short> serialize & deserialize
-     *
-     */
-    @Override
-    public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {
         Map map;
 
         if (_type == null)
@@ -123,35 +110,21 @@
 
         in.addRef(map);
 
-        doReadMap(in, map, expectKeyType, expectValueType);
+        while (!in.isEnd()) {
+            map.put(in.readObject(), in.readObject());
+        }
 
         in.readEnd();
 
         return map;
     }
 
-    protected void doReadMap(AbstractHessianInput in, Map map, Class<?> keyType, Class<?> valueType) throws IOException {
-        Deserializer keyDeserializer = null, valueDeserializer = null;
-
-        SerializerFactory factory = findSerializerFactory(in);
-        if(keyType != null){
-            keyDeserializer = factory.getDeserializer(keyType.getName());
-        }
-        if(valueType != null){
-            valueDeserializer = factory.getDeserializer(valueType.getName());
-        }
-
-        while (!in.isEnd()) {
-            map.put(keyDeserializer != null ? keyDeserializer.readObject(in) : in.readObject(),
-                    valueDeserializer != null? valueDeserializer.readObject(in) : in.readObject());
-        }
-    }
-
     @Override
     public Object readObject(AbstractHessianInput in,
-                             String[] fieldNames)
+                             Object[] fields)
             throws IOException {
-        Map map = createMap();
+        String[] fieldNames = (String[]) fields;
+        Map<Object, Object> map = createMap();
 
         int ref = in.addRef(map);
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/MapSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/MapSerializer.java
index 5d48eaa..95d4869 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/MapSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/MapSerializer.java
@@ -73,7 +73,6 @@
         _isSendJavaType = sendJavaType;
     }
 
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         if (out.addRef(obj))
@@ -81,14 +80,28 @@
 
         Map map = (Map) obj;
 
-        Class cl = obj.getClass();
+        Class<?> cl = obj.getClass();
 
         if (cl.equals(HashMap.class)
-                || !_isSendJavaType
                 || !(obj instanceof java.io.Serializable))
             out.writeMapBegin(null);
-        else
-            out.writeMapBegin(obj.getClass().getName());
+        else if (!_isSendJavaType) {
+            // hessian/3a19
+            for (; cl != null; cl = cl.getSuperclass()) {
+                if (cl.equals(HashMap.class)) {
+                    out.writeMapBegin(null);
+                    break;
+                } else if (cl.getName().startsWith("java.")) {
+                    out.writeMapBegin(cl.getName());
+                    break;
+                }
+            }
+
+            if (cl == null)
+                out.writeMapBegin(null);
+        } else {
+            out.writeMapBegin(cl.getName());
+        }
 
         Iterator iter = map.entrySet().iterator();
         while (iter.hasNext()) {
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectDeserializer.java
index c4e69f1..03230e8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectDeserializer.java
@@ -54,14 +54,13 @@
  * Serializing an object for known object types.
  */
 public class ObjectDeserializer extends AbstractDeserializer {
-    private Class _cl;
+    private Class<?> _cl;
 
-    public ObjectDeserializer(Class cl) {
+    public ObjectDeserializer(Class<?> cl) {
         _cl = cl;
     }
 
-    @Override
-    public Class getType() {
+    public Class<?> getType() {
         return _cl;
     }
 
@@ -72,7 +71,7 @@
     }
 
     @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object readObject(AbstractHessianInput in, Object[] fields)
             throws IOException {
         throw new UnsupportedOperationException(String.valueOf(this));
     }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectHandleSerializer.java
similarity index 77%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/ObjectHandleSerializer.java
index 72a9822..b135aa3 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectHandleSerializer.java
@@ -51,15 +51,29 @@
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a remote object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
+public class ObjectHandleSerializer extends AbstractSerializer {
+    public static final Serializer SER = new ObjectHandleSerializer();
+
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            if (out.addRef(obj))
+                return;
+
+            int ref = out.writeObjectBegin("object");
+
+            if (ref < -1) {
+                out.writeMapEnd();
+            } else {
+                if (ref == -1) {
+                    out.writeInt(0);
+                    out.writeObjectBegin("object");
+                }
+            }
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectNameDeserializer.java
similarity index 81%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/ObjectNameDeserializer.java
index 72a9822..b708ea6 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectNameDeserializer.java
@@ -48,18 +48,27 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
+import com.alibaba.com.caucho.hessian.HessianException;
+
+import javax.management.ObjectName;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing an ObjectName
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class ObjectNameDeserializer extends AbstractStringValueDeserializer {
     @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public Class<?> getType() {
+        return ObjectName.class;
+    }
+
+    @Override
+    protected Object create(String value) {
+        try {
+            return new ObjectName(value);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HessianException(e);
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectSerializer.java
similarity index 90%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/ObjectSerializer.java
index be505fb..bfdf67a 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ObjectSerializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -48,9 +48,9 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.util.logging.Logger;
-
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Serializing an object.
+ */
+public interface ObjectSerializer {
+    public Serializer getObjectSerializer();
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/RemoteDeserializer.java
similarity index 68%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/RemoteDeserializer.java
index be505fb..07680ed 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/RemoteDeserializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -50,7 +50,34 @@
 
 import java.util.logging.Logger;
 
-public class EnvelopeFactory {
+/**
+ * Serializing an object for known object types.
+ */
+public class RemoteDeserializer extends JavaDeserializer {
+    public static final Deserializer DESER = new RemoteDeserializer();
     private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+            = Logger.getLogger(RemoteDeserializer.class.getName());
+
+    public RemoteDeserializer() {
+        super(HessianRemote.class, FieldDeserializer2Factory.create());
+    }
+
+    @Override
+    public boolean isReadResolve() {
+        return true;
+    }
+
+    @Override
+    protected Object resolve(AbstractHessianInput in, Object obj)
+            throws Exception {
+        HessianRemote remote = (HessianRemote) obj;
+        HessianRemoteResolver resolver = in.getRemoteResolver();
+
+        if (resolver != null) {
+            Object proxy = resolver.lookup(remote.getType(), remote.getURL());
+
+            return proxy;
+        } else
+            return remote;
+    }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/RemoteSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/RemoteSerializer.java
index 2914688..ae35809 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/RemoteSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/RemoteSerializer.java
@@ -54,10 +54,11 @@
  * Serializing a remote object.
  */
 public class RemoteSerializer extends AbstractSerializer {
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        // XXX: needs to be handled as a separate class
-        throw new UnsupportedOperationException(getClass().getName());
+        HessianRemoteObject remoteObject = (HessianRemoteObject) obj;
+
+        out.writeObject(new HessianRemote(remoteObject.getHessianType(),
+                remoteObject.getHessianURL()));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/alibaba/com/caucho/hessian/io/SerializerFactory.java
index ac3bd18..45df1cf 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/SerializerFactory.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/SerializerFactory.java
@@ -48,36 +48,29 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import com.alibaba.com.caucho.hessian.io.java8.DurationHandle;
-import com.alibaba.com.caucho.hessian.io.java8.InstantHandle;
-import com.alibaba.com.caucho.hessian.io.java8.LocalDateHandle;
-import com.alibaba.com.caucho.hessian.io.java8.LocalDateTimeHandle;
-import com.alibaba.com.caucho.hessian.io.java8.LocalTimeHandle;
-import com.alibaba.com.caucho.hessian.io.java8.MonthDayHandle;
-import com.alibaba.com.caucho.hessian.io.java8.OffsetDateTimeHandle;
-import com.alibaba.com.caucho.hessian.io.java8.OffsetTimeHandle;
-import com.alibaba.com.caucho.hessian.io.java8.PeriodHandle;
-import com.alibaba.com.caucho.hessian.io.java8.YearHandle;
-import com.alibaba.com.caucho.hessian.io.java8.YearMonthHandle;
 import com.alibaba.com.caucho.hessian.io.java8.ZoneIdSerializer;
-import com.alibaba.com.caucho.hessian.io.java8.ZoneOffsetHandle;
-import com.alibaba.com.caucho.hessian.io.java8.ZonedDateTimeHandle;
 
-import javax.management.ObjectName;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.*;
+import java.lang.annotation.Annotation;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import static com.alibaba.com.caucho.hessian.io.java8.Java8TimeSerializer.create;
-
 /**
  * Factory for returning serialization methods.
  */
@@ -85,16 +78,18 @@
     private static final Logger log
             = Logger.getLogger(SerializerFactory.class.getName());
 
-    private static Deserializer OBJECT_DESERIALIZER
+    private static final Deserializer OBJECT_DESERIALIZER
             = new BasicDeserializer(BasicDeserializer.OBJECT);
+    private static final ClassLoader _systemClassLoader;
+    private static final HashMap _staticTypeMap;
+    private static final
+    WeakHashMap<ClassLoader, SoftReference<SerializerFactory>>
+            _defaultFactoryRefMap
+            = new WeakHashMap<ClassLoader, SoftReference<SerializerFactory>>();
+    private static final Object PRESENT = new Object();
     private static ConcurrentHashMap _unrecognizedTypeCache = new ConcurrentHashMap();
-    private static HashMap _staticSerializerMap;
-    private static HashMap _staticDeserializerMap;
-    private static HashMap _staticTypeMap;
 
     static {
-        _staticSerializerMap = new HashMap();
-        _staticDeserializerMap = new HashMap();
         _staticTypeMap = new HashMap();
 
         addBasic(void.class, "void", BasicSerializer.NULL);
@@ -108,6 +103,7 @@
         addBasic(Double.class, "double", BasicSerializer.DOUBLE);
         addBasic(Character.class, "char", BasicSerializer.CHARACTER_OBJECT);
         addBasic(String.class, "string", BasicSerializer.STRING);
+        addBasic(StringBuilder.class, "string", BasicSerializer.STRING_BUILDER);
         addBasic(Object.class, "object", BasicSerializer.OBJECT);
         addBasic(java.util.Date.class, "date", BasicSerializer.DATE);
 
@@ -131,87 +127,19 @@
         addBasic(String[].class, "[string", BasicSerializer.STRING_ARRAY);
         addBasic(Object[].class, "[object", BasicSerializer.OBJECT_ARRAY);
 
-        _staticSerializerMap.put(Class.class, new ClassSerializer());
+        Deserializer objectDeserializer = new JavaDeserializer(Object.class, new FieldDeserializer2Factory());
+        _staticTypeMap.put("object", objectDeserializer);
+        _staticTypeMap.put(HessianRemote.class.getName(),
+                RemoteDeserializer.DESER);
 
-        _staticDeserializerMap.put(Number.class, new BasicDeserializer(BasicSerializer.NUMBER));
 
-        _staticSerializerMap.put(BigDecimal.class, new StringValueSerializer());
+        ClassLoader systemClassLoader = null;
         try {
-            _staticDeserializerMap.put(BigDecimal.class,
-                    new StringValueDeserializer(BigDecimal.class));
-            _staticDeserializerMap.put(BigInteger.class,
-                    new BigIntegerDeserializer());
-        } catch (Throwable e) {
+            systemClassLoader = ClassLoader.getSystemClassLoader();
+        } catch (Exception e) {
         }
 
-
-        _staticSerializerMap.put(UUID.class, new StringValueSerializer());
-        _staticDeserializerMap.put(UUID.class, new UUIDDeserializer());
-
-        _staticSerializerMap.put(File.class, new StringValueSerializer());
-        try {
-            _staticDeserializerMap.put(File.class,
-                    new StringValueDeserializer(File.class));
-        } catch (Throwable e) {
-        }
-
-        _staticSerializerMap.put(ObjectName.class, new StringValueSerializer());
-        try {
-            _staticDeserializerMap.put(ObjectName.class,
-                    new StringValueDeserializer(ObjectName.class));
-        } catch (Throwable e) {
-        }
-
-        _staticSerializerMap.put(java.sql.Date.class, new SqlDateSerializer());
-        _staticSerializerMap.put(java.sql.Time.class, new SqlDateSerializer());
-        _staticSerializerMap.put(java.sql.Timestamp.class, new SqlDateSerializer());
-
-        _staticSerializerMap.put(java.io.InputStream.class,
-                new InputStreamSerializer());
-        _staticDeserializerMap.put(java.io.InputStream.class,
-                new InputStreamDeserializer());
-
-        try {
-            _staticDeserializerMap.put(java.sql.Date.class,
-                    new SqlDateDeserializer(java.sql.Date.class));
-            _staticDeserializerMap.put(java.sql.Time.class,
-                    new SqlDateDeserializer(java.sql.Time.class));
-            _staticDeserializerMap.put(java.sql.Timestamp.class,
-                    new SqlDateDeserializer(java.sql.Timestamp.class));
-        } catch (Throwable e) {
-            e.printStackTrace();
-        }
-
-        // hessian/3bb5
-        try {
-            Class stackTrace = StackTraceElement.class;
-
-            _staticDeserializerMap.put(stackTrace, new StackTraceElementDeserializer());
-        } catch (Throwable e) {
-        }
-
-        try {
-            if (isJava8()) {
-                _staticSerializerMap.put(Class.forName("java.time.LocalTime"), create(LocalTimeHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.LocalDate"), create(LocalDateHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.LocalDateTime"), create(LocalDateTimeHandle.class));
-
-                _staticSerializerMap.put(Class.forName("java.time.Instant"), create(InstantHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.Duration"), create(DurationHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.Period"), create(PeriodHandle.class));
-
-                _staticSerializerMap.put(Class.forName("java.time.Year"), create(YearHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.YearMonth"), create(YearMonthHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.MonthDay"), create(MonthDayHandle.class));
-
-                _staticSerializerMap.put(Class.forName("java.time.OffsetDateTime"), create(OffsetDateTimeHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.ZoneOffset"), create(ZoneOffsetHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.OffsetTime"), create(OffsetTimeHandle.class));
-                _staticSerializerMap.put(Class.forName("java.time.ZonedDateTime"), create(ZonedDateTimeHandle.class));
-            }
-        } catch (Throwable t) {
-            log.warning(String.valueOf(t.getCause()));
-        }
+        _systemClassLoader = systemClassLoader;
     }
 
     protected Serializer _defaultSerializer;
@@ -221,20 +149,23 @@
 
     protected CollectionSerializer _collectionSerializer;
     protected MapSerializer _mapSerializer;
-    private ClassLoader _loader;
+    private ContextSerializerFactory _contextFactory;
+    private WeakReference<ClassLoader> _loaderRef;
     private Deserializer _hashMapDeserializer;
     private Deserializer _arrayListDeserializer;
-    private ConcurrentHashMap _cachedSerializerMap;
-    private ConcurrentHashMap _cachedDeserializerMap;
-    private ConcurrentHashMap _cachedTypeDeserializerMap;
+    private Map _cachedSerializerMap;
+    private Map _cachedDeserializerMap;
+    private HashMap _cachedTypeDeserializerMap;
     private boolean _isAllowNonSerializable;
+    private boolean _isEnableUnsafeSerializer
+            = (UnsafeSerializer.isEnabled()
+            && UnsafeDeserializer.isEnabled());
     /**
      * For those classes are unknown in current classloader, record them in this set to avoid
      * frequently class loading and to reduce performance overhead.
      */
     private Map<String, Object> _typeNotFoundDeserializerMap = new ConcurrentHashMap<>(8);
-    private static final Object PRESENT = new Object();
-
+    private FieldDeserializer2Factory _fieldDeserializerFactory;
     private ClassFactory _classFactory;
 
     public SerializerFactory() {
@@ -242,36 +173,68 @@
     }
 
     public SerializerFactory(ClassLoader loader) {
-        _loader = loader;
-    }
+        _loaderRef = new WeakReference<ClassLoader>(loader);
 
-    public Class<?> loadSerializedClass(String className)
-            throws ClassNotFoundException
-    {
-        return getClassFactory().load(className);
-    }
+        _contextFactory = ContextSerializerFactory.create(loader);
 
-    public ClassFactory getClassFactory()
-    {
-        synchronized (this) {
-            if (_classFactory == null) {
-                _classFactory = new ClassFactory(getClassLoader());
-            }
-
-            return _classFactory;
+        if (_isEnableUnsafeSerializer) {
+            _fieldDeserializerFactory = new FieldDeserializer2FactoryUnsafe();
+        } else {
+            _fieldDeserializerFactory = new FieldDeserializer2Factory();
         }
     }
 
-    private static void addBasic(Class cl, String typeName, int type) {
-        _staticSerializerMap.put(cl, new BasicSerializer(type));
+    public static SerializerFactory createDefault() {
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
 
+        synchronized (_defaultFactoryRefMap) {
+            SoftReference<SerializerFactory> factoryRef
+                    = _defaultFactoryRefMap.get(loader);
+
+            SerializerFactory factory = null;
+
+            if (factoryRef != null)
+                factory = factoryRef.get();
+
+            if (factory == null) {
+                factory = new SerializerFactory();
+
+                factoryRef = new SoftReference<SerializerFactory>(factory);
+
+                _defaultFactoryRefMap.put(loader, factoryRef);
+            }
+
+            return factory;
+        }
+    }
+
+    private static void addBasic(Class<?> cl, String typeName, int type) {
         Deserializer deserializer = new BasicDeserializer(type);
-        _staticDeserializerMap.put(cl, deserializer);
+
         _staticTypeMap.put(typeName, deserializer);
     }
 
+    private static boolean isZoneId(Class cl) {
+        try {
+            return isJava8() && Class.forName("java.time.ZoneId").isAssignableFrom(cl);
+        } catch (ClassNotFoundException e) {
+            // ignore
+        }
+        return false;
+    }
+
+    /**
+     * check if the environment is java 8 or beyond
+     *
+     * @return if on java 8
+     */
+    private static boolean isJava8() {
+        String javaVersion = System.getProperty("java.specification.version");
+        return Double.valueOf(javaVersion) >= 1.8;
+    }
+
     public ClassLoader getClassLoader() {
-        return _loader;
+        return _loaderRef.get();
     }
 
     /**
@@ -316,90 +279,152 @@
      * @param cl the class of the object that needs to be serialized.
      * @return a serializer object for the serialization.
      */
-    @Override
+    public Serializer getObjectSerializer(Class<?> cl)
+            throws HessianProtocolException {
+        Serializer serializer = getSerializer(cl);
+
+        if (serializer instanceof ObjectSerializer)
+            return ((ObjectSerializer) serializer).getObjectSerializer();
+        else
+            return serializer;
+    }
+
+    public Class<?> loadSerializedClass(String className)
+            throws ClassNotFoundException {
+        return getClassFactory().load(className);
+    }
+
+    public ClassFactory getClassFactory() {
+        synchronized (this) {
+            if (_classFactory == null) {
+                _classFactory = new ClassFactory(getClassLoader());
+            }
+
+            return _classFactory;
+        }
+    }
+
+    public FieldDeserializer2Factory getFieldDeserializerFactory() {
+        return _fieldDeserializerFactory;
+    }
+
+    public boolean isEnableUnsafeSerializer() {
+        return _isEnableUnsafeSerializer;
+    }
+
+    /**
+     * Returns the serializer for a class.
+     *
+     * @param cl the class of the object that needs to be serialized.
+     * @return a serializer object for the serialization.
+     */
     public Serializer getSerializer(Class cl)
             throws HessianProtocolException {
         Serializer serializer;
 
-        serializer = (Serializer) _staticSerializerMap.get(cl);
-        if (serializer != null) {
-            return serializer;
-        }
-
         if (_cachedSerializerMap != null) {
             serializer = (Serializer) _cachedSerializerMap.get(cl);
+
             if (serializer != null) {
                 return serializer;
             }
         }
 
+        serializer = loadSerializer(cl);
+
+        if (_cachedSerializerMap == null)
+            _cachedSerializerMap = new ConcurrentHashMap(8);
+
+        _cachedSerializerMap.put(cl, serializer);
+
+        return serializer;
+    }
+
+    protected Serializer loadSerializer(Class<?> cl)
+            throws HessianProtocolException {
+        Serializer serializer = null;
+
         for (int i = 0;
-             serializer == null && _factories != null && i < _factories.size();
+             _factories != null && i < _factories.size();
              i++) {
             AbstractSerializerFactory factory;
 
             factory = (AbstractSerializerFactory) _factories.get(i);
 
             serializer = factory.getSerializer(cl);
+
+            if (serializer != null)
+                return serializer;
         }
 
+        serializer = _contextFactory.getSerializer(cl.getName());
+
+        if (serializer != null)
+            return serializer;
+
+        ClassLoader loader = cl.getClassLoader();
+
+        if (loader == null)
+            loader = _systemClassLoader;
+
+        ContextSerializerFactory factory = null;
+
+        factory = ContextSerializerFactory.create(loader);
+
+        serializer = factory.getCustomSerializer(cl);
+
         if (serializer != null) {
+            return serializer;
+        }
 
+        if (HessianRemoteObject.class.isAssignableFrom(cl)) {
+            return new RemoteSerializer();
+        } else if (InetAddress.class.isAssignableFrom(cl)) {
+            return InetAddressSerializer.create();
         } else if (isZoneId(cl)) //must before "else if (JavaSerializer.getWriteReplace(cl) != null)"
-            serializer = ZoneIdSerializer.getInstance();
-        else if (isEnumSet(cl))
-            serializer = EnumSetSerializer.getInstance();
-        else if (JavaSerializer.getWriteReplace(cl) != null)
-            serializer = new JavaSerializer(cl, _loader);
+            return ZoneIdSerializer.getInstance();
+        else if (JavaSerializer.getWriteReplace(cl) != null) {
+            Serializer baseSerializer = getDefaultSerializer(cl);
 
-        else if (HessianRemoteObject.class.isAssignableFrom(cl))
-            serializer = new RemoteSerializer();
-
-//    else if (BurlapRemoteObject.class.isAssignableFrom(cl))
-//      serializer = new RemoteSerializer();
-
-        else if (Map.class.isAssignableFrom(cl)) {
+            return new WriteReplaceSerializer(cl, getClassLoader(), baseSerializer);
+        } else if (Map.class.isAssignableFrom(cl)) {
             if (_mapSerializer == null)
                 _mapSerializer = new MapSerializer();
 
-            serializer = _mapSerializer;
+            return _mapSerializer;
         } else if (Collection.class.isAssignableFrom(cl)) {
             if (_collectionSerializer == null) {
                 _collectionSerializer = new CollectionSerializer();
             }
 
-            serializer = _collectionSerializer;
+            return _collectionSerializer;
         } else if (cl.isArray()) {
-            serializer = new ArraySerializer();
-        } else if (Throwable.class.isAssignableFrom(cl)) {
-            serializer = new ThrowableSerializer(cl, getClassLoader());
-        } else if (InputStream.class.isAssignableFrom(cl)) {
-            serializer = new InputStreamSerializer();
-        } else if (Iterator.class.isAssignableFrom(cl)) {
-            serializer = IteratorSerializer.create();
-        } else if (Enumeration.class.isAssignableFrom(cl)) {
-            serializer = EnumerationSerializer.create();
-        } else if (Calendar.class.isAssignableFrom(cl)) {
-            serializer = CalendarSerializer.create();
-        } else if (Locale.class.isAssignableFrom(cl)) {
-            serializer = LocaleSerializer.create();
-        } else if (Enum.class.isAssignableFrom(cl)) {
-            serializer = new EnumSerializer(cl);
-        } else if (BitSet.class.isAssignableFrom(cl)) {
-            serializer = BitSetSerializer.create();
-        }
+            return new ArraySerializer();
+        } else if (Throwable.class.isAssignableFrom(cl))
+            return new ThrowableSerializer(getDefaultSerializer(cl));
 
-        if (serializer == null) {
-            serializer = getDefaultSerializer(cl);
-        }
+        else if (InputStream.class.isAssignableFrom(cl))
+            return new InputStreamSerializer();
 
-        if (_cachedSerializerMap == null) {
-            _cachedSerializerMap = new ConcurrentHashMap(8);
-        }
+        else if (Iterator.class.isAssignableFrom(cl))
+            return IteratorSerializer.create();
 
-        _cachedSerializerMap.put(cl, serializer);
+        else if (Calendar.class.isAssignableFrom(cl))
+            return CalendarSerializer.SER;
 
-        return serializer;
+        else if (Enumeration.class.isAssignableFrom(cl))
+            return EnumerationSerializer.create();
+
+        else if (Enum.class.isAssignableFrom(cl))
+            return new EnumSerializer(cl);
+
+        else if (Annotation.class.isAssignableFrom(cl))
+            return new AnnotationSerializer(cl);
+
+        else if (BitSet.class.isAssignableFrom(cl))
+            return BitSetSerializer.create();
+
+        return getDefaultSerializer(cl);
     }
 
     /**
@@ -419,7 +444,11 @@
             throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable");
         }
 
-        return new JavaSerializer(cl, _loader);
+        if (_isEnableUnsafeSerializer
+                && JavaSerializer.getWriteReplace(cl) == null) {
+            return UnsafeSerializer.create(cl);
+        } else
+            return JavaSerializer.create(cl);
     }
 
     /**
@@ -428,21 +457,30 @@
      * @param cl the class of the object that needs to be deserialized.
      * @return a deserializer object for the serialization.
      */
-    @Override
     public Deserializer getDeserializer(Class cl)
             throws HessianProtocolException {
         Deserializer deserializer;
 
-        deserializer = (Deserializer) _staticDeserializerMap.get(cl);
-        if (deserializer != null)
-            return deserializer;
-
         if (_cachedDeserializerMap != null) {
             deserializer = (Deserializer) _cachedDeserializerMap.get(cl);
+
             if (deserializer != null)
                 return deserializer;
         }
 
+        deserializer = loadDeserializer(cl);
+
+        if (_cachedDeserializerMap == null)
+            _cachedDeserializerMap = new ConcurrentHashMap(8);
+
+        _cachedDeserializerMap.put(cl, deserializer);
+
+        return deserializer;
+    }
+
+    protected Deserializer loadDeserializer(Class cl)
+            throws HessianProtocolException {
+        Deserializer deserializer = null;
 
         for (int i = 0;
              deserializer == null && _factories != null && i < _factories.size();
@@ -453,40 +491,85 @@
             deserializer = factory.getDeserializer(cl);
         }
 
-        if (deserializer != null) {
-        } else if (Collection.class.isAssignableFrom(cl))
+        if (deserializer != null)
+            return deserializer;
+
+        // XXX: need test
+        deserializer = _contextFactory.getDeserializer(cl.getName());
+
+        if (deserializer != null)
+            return deserializer;
+
+        ContextSerializerFactory factory = null;
+
+        if (cl.getClassLoader() != null)
+            factory = ContextSerializerFactory.create(cl.getClassLoader());
+        else
+            factory = ContextSerializerFactory.create(_systemClassLoader);
+
+        deserializer = factory.getDeserializer(cl.getName());
+
+        if (deserializer != null)
+            return deserializer;
+
+        deserializer = factory.getCustomDeserializer(cl);
+
+        if (deserializer != null)
+            return deserializer;
+
+        if (Collection.class.isAssignableFrom(cl))
             deserializer = new CollectionDeserializer(cl);
 
-        else if (Map.class.isAssignableFrom(cl))
+        else if (Map.class.isAssignableFrom(cl)) {
             deserializer = new MapDeserializer(cl);
-
-        else if (cl.isInterface())
+        } else if (Iterator.class.isAssignableFrom(cl)) {
+            deserializer = IteratorDeserializer.create();
+        } else if (Annotation.class.isAssignableFrom(cl)) {
+            deserializer = new AnnotationDeserializer(cl);
+        } else if (cl.isInterface()) {
             deserializer = new ObjectDeserializer(cl);
-
-        else if (cl.isArray())
+        } else if (cl.isArray()) {
             deserializer = new ArrayDeserializer(cl.getComponentType());
-
-        else if (Enumeration.class.isAssignableFrom(cl))
+        } else if (Enumeration.class.isAssignableFrom(cl)) {
             deserializer = EnumerationDeserializer.create();
-
-        else if (Enum.class.isAssignableFrom(cl))
+        } else if (Enum.class.isAssignableFrom(cl))
             deserializer = new EnumDeserializer(cl);
 
         else if (Class.class.equals(cl))
-            deserializer = new ClassDeserializer(_loader);
+            deserializer = new ClassDeserializer(getClassLoader());
 
         else
             deserializer = getDefaultDeserializer(cl);
 
-        if (_cachedDeserializerMap == null)
-            _cachedDeserializerMap = new ConcurrentHashMap(8);
-
-        _cachedDeserializerMap.put(cl, deserializer);
-
         return deserializer;
     }
 
     /**
+     * Returns a custom serializer the class
+     *
+     * @param cl the class of the object that needs to be serialized.
+     * @return a serializer object for the serialization.
+     */
+    protected Deserializer getCustomDeserializer(Class cl) {
+        try {
+            Class serClass = Class.forName(cl.getName() + "HessianDeserializer",
+                    false, cl.getClassLoader());
+
+            Deserializer ser = (Deserializer) serClass.newInstance();
+
+            return ser;
+        } catch (ClassNotFoundException e) {
+            log.log(Level.FINEST, e.toString(), e);
+
+            return null;
+        } catch (Exception e) {
+            log.log(Level.FINE, e.toString(), e);
+
+            return null;
+        }
+    }
+
+    /**
      * Returns the default serializer for a class that isn't matched
      * directly.  Application can override this method to produce
      * bean-style serialization instead of field serialization.
@@ -495,12 +578,18 @@
      * @return a serializer object for the serialization.
      */
     protected Deserializer getDefaultDeserializer(Class cl) {
+        if (InputStream.class.equals(cl))
+            return InputStreamDeserializer.DESER;
+
         if (!Serializable.class.isAssignableFrom(cl)
                 && !_isAllowNonSerializable) {
             throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable");
         }
 
-        return new JavaDeserializer(cl);
+        if (_isEnableUnsafeSerializer) {
+            return new UnsafeDeserializer(cl, _fieldDeserializerFactory);
+        } else
+            return new JavaDeserializer(cl, _fieldDeserializerFactory);
     }
 
     /**
@@ -521,24 +610,16 @@
      */
     public Object readMap(AbstractHessianInput in, String type)
             throws HessianProtocolException, IOException {
-        return readMap(in, type, null, null);
-    }
-
-    /**
-     * Reads the object as a map.
-     */
-    public Object readMap(AbstractHessianInput in, String type, Class<?> expectKeyType, Class<?> expectValueType)
-            throws HessianProtocolException, IOException {
         Deserializer deserializer = getDeserializer(type);
 
         if (deserializer != null)
             return deserializer.readMap(in);
         else if (_hashMapDeserializer != null)
-            return _hashMapDeserializer.readMap(in, expectKeyType, expectValueType);
+            return _hashMapDeserializer.readMap(in);
         else {
             _hashMapDeserializer = new MapDeserializer(HashMap.class);
 
-            return _hashMapDeserializer.readMap(in, expectKeyType, expectValueType);
+            return _hashMapDeserializer.readMap(in);
         }
     }
 
@@ -572,12 +653,13 @@
         if (cl == null
                 || cl.equals(reader.getType())
                 || cl.isAssignableFrom(reader.getType())
+                || reader.isReadResolve()
                 || HessianHandle.class.isAssignableFrom(reader.getType())) {
             return reader;
         }
 
         if (log.isLoggable(Level.FINE)) {
-            log.fine("hessian: expected '" + cl.getName() + "' at '" + type + "' ("
+            log.fine("hessian: expected deserializer '" + cl.getName() + "' at '" + type + "' ("
                     + reader.getType().getName() + ")");
         }
 
@@ -652,7 +734,9 @@
         Deserializer deserializer;
 
         if (_cachedTypeDeserializerMap != null) {
-            deserializer = (Deserializer) _cachedTypeDeserializerMap.get(type);
+            synchronized (_cachedTypeDeserializerMap) {
+                deserializer = (Deserializer) _cachedTypeDeserializerMap.get(type);
+            }
 
             if (deserializer != null)
                 return deserializer;
@@ -672,11 +756,13 @@
                 deserializer = new ArrayDeserializer(Object.class);
         } else if (_unrecognizedTypeCache.get(type) == null) {
             try {
-//                Class cl = Class.forName(type, false, _loader);
+                //Class cl = Class.forName(type, false, getClassLoader());
+
                 Class cl = loadSerializedClass(type);
+
                 deserializer = getDeserializer(cl);
             } catch (Exception e) {
-                log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e);
+                log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + getClassLoader() + ":\n" + e);
                 _typeNotFoundDeserializerMap.put(type, PRESENT);
                 log.log(Level.FINER, e.toString(), e);
                 _unrecognizedTypeCache.put(type, new AtomicLong(1L));
@@ -689,34 +775,13 @@
 
         if (deserializer != null) {
             if (_cachedTypeDeserializerMap == null)
-                _cachedTypeDeserializerMap = new ConcurrentHashMap(8);
+                _cachedTypeDeserializerMap = new HashMap(8);
 
-            _cachedTypeDeserializerMap.put(type, deserializer);
+            synchronized (_cachedTypeDeserializerMap) {
+                _cachedTypeDeserializerMap.put(type, deserializer);
+            }
         }
 
         return deserializer;
     }
-
-    private static boolean isZoneId(Class cl) {
-        try {
-            return isJava8() && Class.forName("java.time.ZoneId").isAssignableFrom(cl);
-        } catch (ClassNotFoundException e) {
-            // ignore
-        }
-        return false;
-    }
-
-    private static boolean isEnumSet(Class cl) {
-        return EnumSet.class.isAssignableFrom(cl);
-    }
-
-    /**
-     * check if the environment is java 8 or beyond
-     *
-     * @return if on java 8
-     */
-    private static boolean isJava8() {
-        String javaVersion = System.getProperty("java.specification.version");
-        return Double.valueOf(javaVersion) >= 1.8;
-    }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ShortHandle.java
similarity index 80%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/ShortHandle.java
index 72a9822..d496822 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ShortHandle.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -48,18 +48,30 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
-import java.io.IOException;
+import java.io.Serializable;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Handle for Java Short objects.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+public class ShortHandle implements Serializable {
+    private short _value;
+
+    private ShortHandle() {
+    }
+
+    public ShortHandle(short value) {
+        _value = value;
+    }
+
+    public short getValue() {
+        return _value;
+    }
+
+    public Object readResolve() {
+        return new Short(_value);
+    }
+
+    public String toString() {
+        return getClass().getSimpleName() + "[" + _value + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateDeserializer.java
index df809d7..2f0f8d8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateDeserializer.java
@@ -48,6 +48,8 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import com.alibaba.com.caucho.hessian.HessianException;
+
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 
@@ -58,18 +60,19 @@
     private Class _cl;
     private Constructor _constructor;
 
-    public SqlDateDeserializer(Class cl)
-            throws NoSuchMethodException {
-        _cl = cl;
-        _constructor = cl.getConstructor(new Class[]{long.class});
+    public SqlDateDeserializer(Class cl) {
+        try {
+            _cl = cl;
+            _constructor = cl.getConstructor(new Class[]{long.class});
+        } catch (NoSuchMethodException e) {
+            throw new HessianException(e);
+        }
     }
 
-    @Override
     public Class getType() {
         return _cl;
     }
 
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         int ref = in.addRef(null);
@@ -94,9 +97,11 @@
         return value;
     }
 
-    @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
             throws IOException {
+        String[] fieldNames = (String[]) fields;
+
         int ref = in.addRef(null);
 
         long initValue = Long.MIN_VALUE;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateSerializer.java
index 5e6c8f4..c812c7e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/SqlDateSerializer.java
@@ -55,7 +55,6 @@
  * Serializing a sql date object.
  */
 public class SqlDateSerializer extends AbstractSerializer {
-    @Override
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         if (obj == null)
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/StackTraceElementDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/StackTraceElementDeserializer.java
index 4f64859..0dbaa2c 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/StackTraceElementDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/StackTraceElementDeserializer.java
@@ -48,12 +48,19 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import java.lang.reflect.Constructor;
+
 /**
  * Deserializing a JDK 1.4 StackTraceElement
  */
 public class StackTraceElementDeserializer extends JavaDeserializer {
-    public StackTraceElementDeserializer() {
-        super(StackTraceElement.class);
+    public StackTraceElementDeserializer(FieldDeserializer2Factory fieldFactory) {
+        super(StackTraceElement.class, fieldFactory);
+    }
+
+    @Override
+    protected Constructor<?> getConstructor(Class<?> cl) {
+        return null;
     }
 
     @Override
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueDeserializer.java
index 90ceef1..5c01436 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueDeserializer.java
@@ -48,13 +48,15 @@
 
 package com.alibaba.com.caucho.hessian.io;
 
+import com.alibaba.com.caucho.hessian.HessianException;
+
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 
 /**
  * Deserializing a string valued object
  */
-public class StringValueDeserializer extends AbstractDeserializer {
+public class StringValueDeserializer extends AbstractStringValueDeserializer {
     private Class _cl;
     private Constructor _constructor;
 
@@ -73,48 +75,7 @@
     }
 
     @Override
-    public Object readMap(AbstractHessianInput in)
-            throws IOException {
-        String value = null;
-
-        while (!in.isEnd()) {
-            String key = in.readString();
-
-            if (key.equals("value"))
-                value = in.readString();
-            else
-                in.readObject();
-        }
-
-        in.readMapEnd();
-
-        Object object = create(value);
-
-        in.addRef(object);
-
-        return object;
-    }
-
-    @Override
-    public Object readObject(AbstractHessianInput in, String[] fieldNames)
-            throws IOException {
-        String value = null;
-
-        for (int i = 0; i < fieldNames.length; i++) {
-            if ("value".equals(fieldNames[i]))
-                value = in.readString();
-            else
-                in.readObject();
-        }
-
-        Object object = create(value);
-
-        in.addRef(object);
-
-        return object;
-    }
-
-    private Object create(String value)
+    protected Object create(String value)
             throws IOException {
         if (value == null)
             throw new IOException(_cl.getName() + " expects name.");
@@ -122,7 +83,8 @@
         try {
             return _constructor.newInstance(new Object[]{value});
         } catch (Exception e) {
-            throw new IOExceptionWrapper(e);
+            throw new HessianException(_cl.getName() + ": value=" + value + "\n" + e,
+                    e);
         }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueSerializer.java
index 16a00b4..42ac410 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/StringValueSerializer.java
@@ -54,7 +54,8 @@
  * Serializing a remote object.
  */
 public class StringValueSerializer extends AbstractSerializer {
-    @Override
+    public static final Serializer SER = new StringValueSerializer();
+
     public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
         if (obj == null)
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ThrowableSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ThrowableSerializer.java
index 1f65e7f..42c0e21 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ThrowableSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ThrowableSerializer.java
@@ -53,9 +53,16 @@
 /**
  * Serializing an object for known object types.
  */
-public class ThrowableSerializer extends JavaSerializer {
-    public ThrowableSerializer(Class cl, ClassLoader loader) {
-        super(cl, loader);
+public class ThrowableSerializer extends AbstractSerializerWrapper {
+    private final Serializer _ser;
+
+    public ThrowableSerializer(Serializer ser) {
+        _ser = ser;
+    }
+
+    @Override
+    protected Serializer getDelegate() {
+        return _ser;
     }
 
     @Override
@@ -65,6 +72,6 @@
 
         e.getStackTrace();
 
-        super.writeObject(obj, out);
+        _ser.writeObject(obj, out);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeDeserializer.java
new file mode 100644
index 0000000..6b73277
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeDeserializer.java
@@ -0,0 +1,374 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import com.alibaba.com.caucho.hessian.io.FieldDeserializer2FactoryUnsafe.NullFieldDeserializer;
+import sun.misc.Unsafe;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class UnsafeDeserializer extends AbstractMapDeserializer {
+    private static final Logger log
+            = Logger.getLogger(JavaDeserializer.class.getName());
+
+    private static boolean _isEnabled;
+    @SuppressWarnings("restriction")
+    private static Unsafe _unsafe;
+
+    static {
+        boolean isEnabled = false;
+
+        try {
+            Class<?> unsafe = Class.forName("sun.misc.Unsafe");
+            Field theUnsafe = null;
+            for (Field field : unsafe.getDeclaredFields()) {
+                if (field.getName().equals("theUnsafe"))
+                    theUnsafe = field;
+            }
+
+            if (theUnsafe != null) {
+                theUnsafe.setAccessible(true);
+                _unsafe = (Unsafe) theUnsafe.get(null);
+            }
+
+            isEnabled = _unsafe != null;
+
+            String unsafeProp = System.getProperty("com.caucho.hessian.unsafe");
+
+            if ("false".equals(unsafeProp))
+                isEnabled = false;
+        } catch (Throwable e) {
+            log.log(Level.FINER, e.toString(), e);
+        }
+
+        _isEnabled = isEnabled;
+    }
+
+    private Class<?> _type;
+    private HashMap<String, FieldDeserializer2> _fieldMap;
+    private Method _readResolve;
+
+    public UnsafeDeserializer(Class<?> cl, FieldDeserializer2Factory fieldFactory) {
+        _type = cl;
+        _fieldMap = getFieldMap(cl, fieldFactory);
+
+        _readResolve = getReadResolve(cl);
+
+        if (_readResolve != null) {
+            _readResolve.setAccessible(true);
+        }
+    }
+
+    public static boolean isEnabled() {
+        return _isEnabled;
+    }
+
+    static void logDeserializeError(Field field, Object obj, Object value,
+                                    Throwable e)
+            throws IOException {
+        String fieldName = (field.getDeclaringClass().getName()
+                + "." + field.getName());
+
+        if (e instanceof HessianFieldException)
+            throw (HessianFieldException) e;
+        else if (e instanceof IOException)
+            throw new HessianFieldException(fieldName + ": " + e.getMessage(), e);
+
+        if (value != null)
+            throw new HessianFieldException(fieldName + ": " + value.getClass().getName()
+                    + " cannot be assigned to '" + field.getType().getName() + "'", e);
+        else
+            throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);
+    }
+
+    @Override
+    public Class<?> getType() {
+        return _type;
+    }
+
+    @Override
+    public boolean isReadResolve() {
+        return _readResolve != null;
+    }
+
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readMap(in, obj);
+        } catch (IOException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public Object[] createFields(int len) {
+        return new FieldDeserializer2[len];
+    }
+
+    public Object createField(String name) {
+        Object reader = _fieldMap.get(name);
+
+        if (reader == null)
+            reader = NullFieldDeserializer.DESER;
+
+        return reader;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             Object[] fields)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readObject(in, obj, (FieldDeserializer2[]) fields);
+        } catch (IOException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             String[] fieldNames)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readObject(in, obj, fieldNames);
+        } catch (IOException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Returns the readResolve method
+     */
+    protected Method getReadResolve(Class<?> cl) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Method[] methods = cl.getDeclaredMethods();
+
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+
+                if (method.getName().equals("readResolve")
+                        && method.getParameterTypes().length == 0)
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    public Object readMap(AbstractHessianInput in, Object obj)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            while (!in.isEnd()) {
+                Object key = in.readObject();
+
+                FieldDeserializer2 deser = (FieldDeserializer2) _fieldMap.get(key);
+
+                if (deser != null)
+                    deser.deserialize(in, obj);
+                else
+                    in.readObject();
+            }
+
+            in.readMapEnd();
+
+            Object resolve = resolve(in, obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+
+    public Object readObject(AbstractHessianInput in,
+                             Object obj,
+                             FieldDeserializer2[] fields)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            for (FieldDeserializer2 reader : fields) {
+                reader.deserialize(in, obj);
+            }
+
+            Object resolve = resolve(in, obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e);
+        }
+    }
+
+    public Object readObject(AbstractHessianInput in,
+                             Object obj,
+                             String[] fieldNames)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            for (String fieldName : fieldNames) {
+                FieldDeserializer2 reader = _fieldMap.get(fieldName);
+
+                if (reader != null)
+                    reader.deserialize(in, obj);
+                else
+                    in.readObject();
+            }
+
+            Object resolve = resolve(in, obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e);
+        }
+    }
+
+    protected Object resolve(AbstractHessianInput in, Object obj)
+            throws Exception {
+        // if there's a readResolve method, call it
+        try {
+            if (_readResolve != null)
+                return _readResolve.invoke(obj, new Object[0]);
+        } catch (InvocationTargetException e) {
+            if (e.getCause() instanceof Exception)
+                throw (Exception) e.getCause();
+            else
+                throw e;
+        }
+
+        return obj;
+    }
+
+    @SuppressWarnings("restriction")
+    protected Object instantiate()
+            throws Exception {
+        return _unsafe.allocateInstance(_type);
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    protected HashMap<String, FieldDeserializer2>
+    getFieldMap(Class<?> cl, FieldDeserializer2Factory fieldFactory) {
+        HashMap<String, FieldDeserializer2> fieldMap
+                = new HashMap<String, FieldDeserializer2>();
+
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Field[] fields = cl.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+
+                if (Modifier.isTransient(field.getModifiers())
+                        || Modifier.isStatic(field.getModifiers()))
+                    continue;
+                else if (fieldMap.get(field.getName()) != null)
+                    continue;
+
+        /*
+        // XXX: could parameterize the handler to only deal with public
+        try {
+          field.setAccessible(true);
+        } catch (Throwable e) {
+          e.printStackTrace();
+        }
+        */
+
+                FieldDeserializer2 deser = fieldFactory.create(field);
+
+                fieldMap.put(field.getName(), deser);
+            }
+        }
+
+        return fieldMap;
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeSerializer.java
new file mode 100644
index 0000000..1668567
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeSerializer.java
@@ -0,0 +1,522 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import com.alibaba.com.caucho.hessian.HessianUnshared;
+import sun.misc.Unsafe;
+
+import java.io.IOException;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class UnsafeSerializer extends AbstractSerializer {
+    private static final Logger log
+            = Logger.getLogger(UnsafeSerializer.class.getName());
+    private static final Unsafe _unsafe;
+    private static final WeakHashMap<Class<?>, SoftReference<UnsafeSerializer>> _serializerMap
+            = new WeakHashMap<Class<?>, SoftReference<UnsafeSerializer>>();
+    private static boolean _isEnabled;
+
+    static {
+        boolean isEnabled = false;
+        Unsafe unsafe = null;
+
+        try {
+            Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+            Field theUnsafe = null;
+            for (Field field : unsafeClass.getDeclaredFields()) {
+                if (field.getName().equals("theUnsafe"))
+                    theUnsafe = field;
+            }
+
+            if (theUnsafe != null) {
+                theUnsafe.setAccessible(true);
+                unsafe = (Unsafe) theUnsafe.get(null);
+            }
+
+            isEnabled = unsafe != null;
+
+            String unsafeProp = System.getProperty("com.caucho.hessian.unsafe");
+
+            if ("false".equals(unsafeProp))
+                isEnabled = false;
+        } catch (Throwable e) {
+            log.log(Level.ALL, e.toString(), e);
+        }
+
+        _unsafe = unsafe;
+        _isEnabled = isEnabled;
+    }
+
+    private Field[] _fields;
+    private FieldSerializer[] _fieldSerializers;
+
+    public UnsafeSerializer(Class<?> cl) {
+        introspect(cl);
+    }
+
+    public static boolean isEnabled() {
+        return _isEnabled;
+    }
+
+    public static UnsafeSerializer create(Class<?> cl) {
+        synchronized (_serializerMap) {
+            SoftReference<UnsafeSerializer> baseRef
+                    = _serializerMap.get(cl);
+
+            UnsafeSerializer base = baseRef != null ? baseRef.get() : null;
+
+            if (base == null) {
+                if (cl.isAnnotationPresent(HessianUnshared.class))
+                    base = new UnsafeUnsharedSerializer(cl);
+                else
+                    base = new UnsafeSerializer(cl);
+
+                baseRef = new SoftReference<UnsafeSerializer>(base);
+                _serializerMap.put(cl, baseRef);
+            }
+
+            return base;
+        }
+    }
+
+    private static FieldSerializer getFieldSerializer(Field field) {
+        Class<?> type = field.getType();
+
+        if (boolean.class.equals(type)) {
+            return new BooleanFieldSerializer(field);
+        } else if (byte.class.equals(type)) {
+            return new ByteFieldSerializer(field);
+        } else if (char.class.equals(type)) {
+            return new CharFieldSerializer(field);
+        } else if (short.class.equals(type)) {
+            return new ShortFieldSerializer(field);
+        } else if (int.class.equals(type)) {
+            return new IntFieldSerializer(field);
+        } else if (long.class.equals(type)) {
+            return new LongFieldSerializer(field);
+        } else if (double.class.equals(type)) {
+            return new DoubleFieldSerializer(field);
+        } else if (float.class.equals(type)) {
+            return new FloatFieldSerializer(field);
+        } else if (String.class.equals(type)) {
+            return new StringFieldSerializer(field);
+        } else if (java.util.Date.class.equals(type)
+                || java.sql.Date.class.equals(type)
+                || java.sql.Timestamp.class.equals(type)
+                || java.sql.Time.class.equals(type)) {
+            return new DateFieldSerializer(field);
+        } else
+            return new ObjectFieldSerializer(field);
+    }
+
+    protected void introspect(Class<?> cl) {
+        ArrayList<Field> primitiveFields = new ArrayList<Field>();
+        ArrayList<Field> compoundFields = new ArrayList<Field>();
+
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Field[] fields = cl.getDeclaredFields();
+
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+
+                if (Modifier.isTransient(field.getModifiers())
+                        || Modifier.isStatic(field.getModifiers())) {
+                    continue;
+                }
+
+        /*
+        // XXX: could parameterize the handler to only deal with public
+        field.setAccessible(true);
+        */
+
+                if (field.getType().isPrimitive()
+                        || (field.getType().getName().startsWith("java.lang.")
+                        && !field.getType().equals(Object.class))) {
+                    primitiveFields.add(field);
+                } else {
+                    compoundFields.add(field);
+                }
+            }
+        }
+
+        ArrayList<Field> fields = new ArrayList<Field>();
+        fields.addAll(primitiveFields);
+        fields.addAll(compoundFields);
+        Collections.reverse(fields);
+
+        _fields = new Field[fields.size()];
+        fields.toArray(_fields);
+
+        _fieldSerializers = new FieldSerializer[_fields.length];
+
+        for (int i = 0; i < _fields.length; i++) {
+            _fieldSerializers[i] = getFieldSerializer(_fields[i]);
+        }
+    }
+
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        if (out.addRef(obj)) {
+            return;
+        }
+
+        Class<?> cl = obj.getClass();
+
+        int ref = out.writeObjectBegin(cl.getName());
+
+        if (ref >= 0) {
+            writeInstance(obj, out);
+        } else if (ref == -1) {
+            writeDefinition20(out);
+            out.writeObjectBegin(cl.getName());
+            writeInstance(obj, out);
+        } else {
+            writeObject10(obj, out);
+        }
+    }
+
+    protected void writeObject10(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        for (int i = 0; i < _fields.length; i++) {
+            Field field = _fields[i];
+
+            out.writeString(field.getName());
+
+            _fieldSerializers[i].serialize(out, obj);
+        }
+
+        out.writeMapEnd();
+    }
+
+    private void writeDefinition20(AbstractHessianOutput out)
+            throws IOException {
+        out.writeClassFieldLength(_fields.length);
+
+        for (int i = 0; i < _fields.length; i++) {
+            Field field = _fields[i];
+
+            out.writeString(field.getName());
+        }
+    }
+
+    final public void writeInstance(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        try {
+            FieldSerializer[] fieldSerializers = _fieldSerializers;
+            int length = fieldSerializers.length;
+
+            for (int i = 0; i < length; i++) {
+                fieldSerializers[i].serialize(out, obj);
+            }
+        } catch (RuntimeException e) {
+            throw new RuntimeException(e.getMessage() + "\n class: "
+                    + obj.getClass().getName(),
+                    e);
+        } catch (IOException e) {
+            throw new IOExceptionWrapper(e.getMessage() + "\n class: "
+                    + obj.getClass().getName(),
+                    e);
+        }
+    }
+
+    abstract static class FieldSerializer {
+        abstract void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException;
+    }
+
+    final static class ObjectFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        ObjectFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        @Override
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            try {
+                Object value = _unsafe.getObject(obj, _offset);
+
+                out.writeObject(value);
+            } catch (RuntimeException e) {
+                throw new RuntimeException(e.getMessage() + "\n field: "
+                        + _field.getDeclaringClass().getName()
+                        + '.' + _field.getName(),
+                        e);
+            } catch (IOException e) {
+                throw new IOExceptionWrapper(e.getMessage() + "\n field: "
+                        + _field.getDeclaringClass().getName()
+                        + '.' + _field.getName(),
+                        e);
+            }
+        }
+    }
+
+    final static class BooleanFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        BooleanFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            boolean value = _unsafe.getBoolean(obj, _offset);
+
+            out.writeBoolean(value);
+        }
+    }
+
+    final static class ByteFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        ByteFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            int value = _unsafe.getByte(obj, _offset);
+
+            out.writeInt(value);
+        }
+    }
+
+    final static class CharFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        CharFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            char value = _unsafe.getChar(obj, _offset);
+
+            out.writeString(String.valueOf(value));
+        }
+    }
+
+    final static class ShortFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        ShortFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            int value = _unsafe.getShort(obj, _offset);
+
+            out.writeInt(value);
+        }
+    }
+
+    final static class IntFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        IntFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            int value = _unsafe.getInt(obj, _offset);
+
+            out.writeInt(value);
+        }
+    }
+
+    final static class LongFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        LongFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            long value = _unsafe.getLong(obj, _offset);
+
+            out.writeLong(value);
+        }
+    }
+
+    final static class FloatFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        FloatFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            double value = _unsafe.getFloat(obj, _offset);
+
+            out.writeDouble(value);
+        }
+    }
+
+    final static class DoubleFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        DoubleFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            double value = _unsafe.getDouble(obj, _offset);
+
+            out.writeDouble(value);
+        }
+    }
+
+    final static class StringFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        StringFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        @Override
+        final void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            String value = (String) _unsafe.getObject(obj, _offset);
+
+            out.writeString(value);
+        }
+    }
+
+    final static class DateFieldSerializer extends FieldSerializer {
+        private final Field _field;
+        private final long _offset;
+
+        DateFieldSerializer(Field field) {
+            _field = field;
+            _offset = _unsafe.objectFieldOffset(field);
+
+            if (_offset == Unsafe.INVALID_FIELD_OFFSET)
+                throw new IllegalStateException();
+        }
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj)
+                throws IOException {
+            java.util.Date value
+                    = (java.util.Date) _unsafe.getObject(obj, _offset);
+
+            if (value == null)
+                out.writeNull();
+            else
+                out.writeUTCDate(value.getTime());
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeUnsharedSerializer.java
similarity index 75%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeUnsharedSerializer.java
index 72a9822..1710b6d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/UnsafeUnsharedSerializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -49,17 +49,28 @@
 package com.alibaba.com.caucho.hessian.io;
 
 import java.io.IOException;
+import java.util.logging.Logger;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object for known object types.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class UnsafeUnsharedSerializer extends UnsafeSerializer {
+    private static final Logger log
+            = Logger.getLogger(UnsafeUnsharedSerializer.class.getName());
+
+    public UnsafeUnsharedSerializer(Class<?> cl) {
+        super(cl);
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        boolean oldUnshared = out.setUnshared(true);
+
+        try {
+            super.writeObject(obj, out);
+        } finally {
+            out.setUnshared(oldUnshared);
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ValueDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/ValueDeserializer.java
index af548ac..ea99666 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ValueDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/ValueDeserializer.java
@@ -54,7 +54,6 @@
  * Deserializing a string valued object
  */
 abstract public class ValueDeserializer extends AbstractDeserializer {
-    @Override
     public Object readMap(AbstractHessianInput in)
             throws IOException {
         String initValue = null;
@@ -73,7 +72,6 @@
         return create(initValue);
     }
 
-    @Override
     public Object readObject(AbstractHessianInput in, String[] fieldNames)
             throws IOException {
         String initValue = null;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/WriteReplaceSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/WriteReplaceSerializer.java
new file mode 100644
index 0000000..9e3c26a
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/WriteReplaceSerializer.java
@@ -0,0 +1,186 @@
+/*
+ * 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.alibaba.com.caucho.hessian.io;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class WriteReplaceSerializer extends AbstractSerializer {
+    private static final Logger log
+            = Logger.getLogger(WriteReplaceSerializer.class.getName());
+
+    private Object _writeReplaceFactory;
+    private Method _writeReplace;
+    private Serializer _baseSerializer;
+
+    public WriteReplaceSerializer(Class<?> cl,
+                                  ClassLoader loader,
+                                  Serializer baseSerializer) {
+        introspectWriteReplace(cl, loader);
+
+        _baseSerializer = baseSerializer;
+    }
+
+    /**
+     * Returns the writeReplace method
+     */
+    protected static Method getWriteReplace(Class<?> cl, Class<?> param) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            for (Method method : cl.getDeclaredMethods()) {
+                if (method.getName().equals("writeReplace")
+                        && method.getParameterTypes().length == 1
+                        && param.equals(method.getParameterTypes()[0]))
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the writeReplace method
+     */
+    protected static Method getWriteReplace(Class<?> cl) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Method[] methods = cl.getDeclaredMethods();
+
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+
+                if (method.getName().equals("writeReplace") &&
+                        method.getParameterTypes().length == 0)
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    private void introspectWriteReplace(Class<?> cl, ClassLoader loader) {
+        try {
+            String className = cl.getName() + "HessianSerializer";
+
+            Class<?> serializerClass = Class.forName(className, false, loader);
+
+            Object serializerObject = serializerClass.newInstance();
+
+            Method writeReplace = getWriteReplace(serializerClass, cl);
+
+            if (writeReplace != null) {
+                _writeReplaceFactory = serializerObject;
+                _writeReplace = writeReplace;
+            }
+        } catch (ClassNotFoundException e) {
+        } catch (Exception e) {
+            log.log(Level.FINER, e.toString(), e);
+        }
+
+        _writeReplace = getWriteReplace(cl);
+        if (_writeReplace != null)
+            _writeReplace.setAccessible(true);
+    }
+
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        int ref = out.getRef(obj);
+
+        if (ref >= 0) {
+            out.writeRef(ref);
+
+            return;
+        }
+
+        try {
+            Object repl;
+
+            repl = writeReplace(obj);
+
+            if (obj == repl) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.fine(this + ": Hessian writeReplace error.  The writeReplace method (" + _writeReplace + ") must not return the same object");
+                }
+
+                _baseSerializer.writeObject(obj, out);
+
+                return;
+            }
+
+            out.writeObject(repl);
+
+            out.replaceRef(repl, obj);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    protected Object writeReplace(Object obj) {
+        try {
+            if (_writeReplaceFactory != null)
+                return _writeReplace.invoke(_writeReplaceFactory, obj);
+            else
+                return _writeReplace.invoke(obj);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e.getCause());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/DurationSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/DurationSerializer.java
index 5cd0498..ba30a6e
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/DurationSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class DurationSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new DurationHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/InstantSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/InstantSerializer.java
index 5cd0498..d31c485
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/InstantSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class InstantSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new InstantHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalDateSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalDateSerializer.java
index 5cd0498..6956d9d
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalDateSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class LocalDateSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new LocalDateHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalDateTimeSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalDateTimeSerializer.java
index 5cd0498..3c67e8f
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalDateTimeSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class LocalDateTimeSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new LocalDateTimeHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalTimeSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalTimeSerializer.java
index 5cd0498..226a0e3
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/LocalTimeSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class LocalTimeSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new LocalTimeHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/MonthDaySerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/MonthDaySerializer.java
index 5cd0498..708cd04
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/MonthDaySerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class MonthDaySerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new MonthDayHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/OffsetDateTimeSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/OffsetDateTimeSerializer.java
index 5cd0498..63726d6
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/OffsetDateTimeSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class OffsetDateTimeSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new OffsetDateTimeHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/OffsetTimeSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/OffsetTimeSerializer.java
index 5cd0498..2531732
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/OffsetTimeSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class OffsetTimeSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new OffsetTimeHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/PeriodSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/PeriodSerializer.java
index 5cd0498..b7edfec
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/PeriodSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class PeriodSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new PeriodHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/YearMonthSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/YearMonthSerializer.java
index 5cd0498..dc12092
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/YearMonthSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class YearMonthSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new YearMonthHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/YearSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/YearSerializer.java
index 5cd0498..2973fc9
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/YearSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class YearSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new YearHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneIdSerializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneIdSerializer.java
index 7217be4..18a03d3 100755
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneIdSerializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneIdSerializer.java
@@ -17,6 +17,7 @@
 
 package com.alibaba.com.caucho.hessian.io.java8;
 
+
 import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
 import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneOffsetSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneOffsetSerializer.java
index 5cd0498..72a28c2
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZoneOffsetSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class ZoneOffsetSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new ZoneOffsetHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZonedDateTimeSerializer.java
old mode 100644
new mode 100755
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
copy to src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZonedDateTimeSerializer.java
index 5cd0498..32dd460
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/io/java8/ZonedDateTimeSerializer.java
@@ -15,18 +15,23 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian.io.java8;
 
-import java.math.BigInteger;
 
-public class BigIntegerDeserializer extends JavaDeserializer {
+import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
 
-    public BigIntegerDeserializer() {
-        super(BigInteger.class);
-    }
+import java.io.IOException;
+
+public class ZonedDateTimeSerializer<T> extends AbstractSerializer {
 
     @Override
-    protected Object instantiate() throws Exception {
-        return new BigInteger("0");
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+            return;
+        }
+
+        out.writeObject(new ZonedDateTimeHandle(obj));
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/util/HessianFreeList.java b/src/main/java/com/alibaba/com/caucho/hessian/util/HessianFreeList.java
new file mode 100644
index 0000000..245dbb6
--- /dev/null
+++ b/src/main/java/com/alibaba/com/caucho/hessian/util/HessianFreeList.java
@@ -0,0 +1,138 @@
+/*
+ * 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.alibaba.com.caucho.hessian.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+/**
+ * FreeList provides a simple class to manage free objects.  This is useful
+ * for large data structures that otherwise would gobble up huge GC time.
+ *
+ * <p>The free list is bounded.  Freeing an object when the list is full will
+ * do nothing.
+ */
+public final class HessianFreeList<T> {
+    private final AtomicReferenceArray<T> _freeStack;
+    private final AtomicInteger _top = new AtomicInteger();
+
+    /**
+     * Create a new free list.
+     *
+     * @param initialSize maximum number of free objects to store.
+     */
+    public HessianFreeList(int size) {
+        _freeStack = new AtomicReferenceArray(size);
+    }
+
+    /**
+     * Try to get an object from the free list.  Returns null if the free list
+     * is empty.
+     *
+     * @return the new object or null.
+     */
+    public T allocate() {
+        int top = _top.get();
+
+        if (top > 0 && _top.compareAndSet(top, top - 1))
+            return _freeStack.getAndSet(top - 1, null);
+        else
+            return null;
+    }
+
+    /**
+     * Frees the object.  If the free list is full, the object will be garbage
+     * collected.
+     *
+     * @param obj the object to be freed.
+     */
+    public boolean free(T obj) {
+        int top = _top.get();
+
+        if (top < _freeStack.length()) {
+            boolean isFree = _freeStack.compareAndSet(top, null, obj);
+
+            _top.compareAndSet(top, top + 1);
+
+            return isFree;
+        } else
+            return false;
+    }
+
+    public boolean allowFree(T obj) {
+        return _top.get() < _freeStack.length();
+    }
+
+    /**
+     * Frees the object.  If the free list is full, the object will be garbage
+     * collected.
+     *
+     * @param obj the object to be freed.
+     */
+    public void freeCareful(T obj) {
+        if (checkDuplicate(obj))
+            throw new IllegalStateException("tried to free object twice");
+
+        free(obj);
+    }
+
+    /**
+     * Debugging to see if the object has already been freed.
+     */
+    public boolean checkDuplicate(T obj) {
+        int top = _top.get();
+
+        for (int i = top - 1; i >= 0; i--) {
+            if (_freeStack.get(i) == obj)
+                return true;
+        }
+
+        return false;
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/util/IdentityIntMap.java b/src/main/java/com/alibaba/com/caucho/hessian/util/IdentityIntMap.java
index afa3a9f..b604d5f 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/util/IdentityIntMap.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/util/IdentityIntMap.java
@@ -51,7 +51,7 @@
 /**
  * The IntMap provides a simple hashmap from keys to integers.  The API is
  * an abbreviation of the HashMap collection API.
- * <p>
+ *
  * <p>The convenience of IntMap is avoiding all the silly wrapping of
  * integers.
  */
@@ -61,32 +61,69 @@
      * it's impossible to distinguish between the two.
      */
     public final static int NULL = 0xdeadbeef; // Integer.MIN_VALUE + 1;
-
-    private static final Object DELETED = new Object();
-
+    public static final int[] PRIMES =
+            {
+                    1,       /* 1<< 0 = 1 */
+                    2,       /* 1<< 1 = 2 */
+                    3,       /* 1<< 2 = 4 */
+                    7,       /* 1<< 3 = 8 */
+                    13,      /* 1<< 4 = 16 */
+                    31,      /* 1<< 5 = 32 */
+                    61,      /* 1<< 6 = 64 */
+                    127,     /* 1<< 7 = 128 */
+                    251,     /* 1<< 8 = 256 */
+                    509,     /* 1<< 9 = 512 */
+                    1021,    /* 1<<10 = 1024 */
+                    2039,    /* 1<<11 = 2048 */
+                    4093,    /* 1<<12 = 4096 */
+                    8191,    /* 1<<13 = 8192 */
+                    16381,   /* 1<<14 = 16384 */
+                    32749,   /* 1<<15 = 32768 */
+                    65521,   /* 1<<16 = 65536 */
+                    131071,  /* 1<<17 = 131072 */
+                    262139,  /* 1<<18 = 262144 */
+                    524287,  /* 1<<19 = 524288 */
+                    1048573, /* 1<<20 = 1048576 */
+                    2097143, /* 1<<21 = 2097152 */
+                    4194301, /* 1<<22 = 4194304 */
+                    8388593, /* 1<<23 = 8388608 */
+                    16777213, /* 1<<24 = 16777216 */
+                    33554393, /* 1<<25 = 33554432 */
+                    67108859, /* 1<<26 = 67108864 */
+                    134217689, /* 1<<27 = 134217728 */
+                    268435399, /* 1<<28 = 268435456 */
+            };
     private Object[] _keys;
     private int[] _values;
-
     private int _size;
-    private int _mask;
+    private int _prime;
 
     /**
      * Create a new IntMap.  Default size is 16.
      */
-    public IdentityIntMap() {
-        _keys = new Object[256];
-        _values = new int[256];
+    public IdentityIntMap(int capacity) {
+        _keys = new Object[capacity];
+        _values = new int[capacity];
 
-        _mask = _keys.length - 1;
+        _prime = getBiggestPrime(_keys.length);
         _size = 0;
     }
 
+    public static int getBiggestPrime(int value) {
+        for (int i = PRIMES.length - 1; i >= 0; i--) {
+            if (PRIMES[i] <= value)
+                return PRIMES[i];
+        }
+
+        return 2;
+    }
+
     /**
      * Clear the hashmap.
      */
     public void clear() {
-        Object[] keys = _keys;
-        int[] values = _values;
+        final Object[] keys = _keys;
+        final int[] values = _values;
 
         for (int i = keys.length - 1; i >= 0; i--) {
             keys[i] = null;
@@ -99,18 +136,19 @@
     /**
      * Returns the current number of entries in the map.
      */
-    public int size() {
+    public final int size() {
         return _size;
     }
 
     /**
      * Puts a new value in the property table with the appropriate flags
      */
-    public int get(Object key) {
-        int mask = _mask;
-        int hash = System.identityHashCode(key) % mask & mask;
+    public final int get(Object key) {
+        int prime = _prime;
+        int hash = System.identityHashCode(key) % prime;
+        // int hash = key.hashCode() & mask;
 
-        Object[] keys = _keys;
+        final Object[] keys = _keys;
 
         while (true) {
             Object mapKey = keys[hash];
@@ -120,58 +158,24 @@
             else if (mapKey == key)
                 return _values[hash];
 
-            hash = (hash + 1) % mask;
+            hash = (hash + 1) % prime;
         }
     }
 
     /**
-     * Expands the property table
-     */
-    private void resize(int newSize) {
-        Object[] newKeys = new Object[newSize];
-        int[] newValues = new int[newSize];
-
-        int mask = _mask = newKeys.length - 1;
-
-        Object[] keys = _keys;
-        int values[] = _values;
-
-        for (int i = keys.length - 1; i >= 0; i--) {
-            Object key = keys[i];
-
-            if (key == null || key == DELETED)
-                continue;
-
-            int hash = System.identityHashCode(key) % mask & mask;
-
-            while (true) {
-                if (newKeys[hash] == null) {
-                    newKeys[hash] = key;
-                    newValues[hash] = values[i];
-                    break;
-                }
-
-                hash = (hash + 1) % mask;
-            }
-        }
-
-        _keys = newKeys;
-        _values = newValues;
-    }
-
-    /**
      * Puts a new value in the property table with the appropriate flags
      */
-    public int put(Object key, int value) {
-        int mask = _mask;
-        int hash = System.identityHashCode(key) % mask & mask;
+    public final int put(Object key, int value, boolean isReplace) {
+        int prime = _prime;
+        int hash = Math.abs(System.identityHashCode(key) % prime);
+        // int hash = key.hashCode() % prime;
 
         Object[] keys = _keys;
 
         while (true) {
             Object testKey = keys[hash];
 
-            if (testKey == null || testKey == DELETED) {
+            if (testKey == null) {
                 keys[hash] = key;
                 _values[hash] = value;
 
@@ -180,54 +184,67 @@
                 if (keys.length <= 4 * _size)
                     resize(4 * keys.length);
 
-                return NULL;
+                return value;
             } else if (key != testKey) {
-                hash = (hash + 1) % mask;
+                hash = (hash + 1) % prime;
 
                 continue;
-            } else {
+            } else if (isReplace) {
                 int old = _values[hash];
 
                 _values[hash] = value;
 
                 return old;
+            } else {
+                return _values[hash];
             }
         }
     }
 
     /**
-     * Deletes the entry.  Returns true if successful.
+     * Removes a value in the property table.
      */
-    public int remove(Object key) {
-        int mask = _mask;
-        int hash = System.identityHashCode(key) % mask & mask;
-
-        while (true) {
-            Object mapKey = _keys[hash];
-
-            if (mapKey == null)
-                return NULL;
-            else if (mapKey == key) {
-                _keys[hash] = DELETED;
-
-                _size--;
-
-                return _values[hash];
-            }
-
-            hash = (hash + 1) % mask;
+    public final void remove(Object key) {
+        if (put(key, NULL, true) != NULL) {
+            _size--;
         }
     }
 
-    @Override
+    /**
+     * Expands the property table
+     */
+    private void resize(int newSize) {
+        Object[] keys = _keys;
+        int values[] = _values;
+
+        _keys = new Object[newSize];
+        _values = new int[newSize];
+        _size = 0;
+
+        _prime = getBiggestPrime(_keys.length);
+
+        for (int i = keys.length - 1; i >= 0; i--) {
+            Object key = keys[i];
+            int value = values[i];
+
+            if (key != null && value != NULL) {
+                put(key, value, true);
+            }
+        }
+    }
+
+    protected int hashCode(Object value) {
+        return System.identityHashCode(value);
+    }
+
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
 
         sbuf.append("IntMap[");
         boolean isFirst = true;
 
-        for (int i = 0; i <= _mask; i++) {
-            if (_keys[i] != null && _keys[i] != DELETED) {
+        for (int i = 0; i <= _keys.length; i++) {
+            if (_keys[i] != null) {
                 if (!isFirst)
                     sbuf.append(", ");
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/util/IntMap.java b/src/main/java/com/alibaba/com/caucho/hessian/util/IntMap.java
index 8711570..5fbdb6c 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/util/IntMap.java
+++ b/src/main/java/com/alibaba/com/caucho/hessian/util/IntMap.java
@@ -51,7 +51,7 @@
 /**
  * The IntMap provides a simple hashmap from keys to integers.  The API is
  * an abbreviation of the HashMap collection API.
- * <p>
+ *
  * <p>The convenience of IntMap is avoiding all the silly wrapping of
  * integers.
  */
@@ -61,32 +61,71 @@
      * it's impossible to distinguish between the two.
      */
     public final static int NULL = 0xdeadbeef; // Integer.MIN_VALUE + 1;
-
-    private static final Object DELETED = new Object();
-
+    public static final int[] PRIMES =
+            {
+                    1,       /* 1<< 0 = 1 */
+                    2,       /* 1<< 1 = 2 */
+                    3,       /* 1<< 2 = 4 */
+                    7,       /* 1<< 3 = 8 */
+                    13,      /* 1<< 4 = 16 */
+                    31,      /* 1<< 5 = 32 */
+                    61,      /* 1<< 6 = 64 */
+                    127,     /* 1<< 7 = 128 */
+                    251,     /* 1<< 8 = 256 */
+                    509,     /* 1<< 9 = 512 */
+                    1021,    /* 1<<10 = 1024 */
+                    2039,    /* 1<<11 = 2048 */
+                    4093,    /* 1<<12 = 4096 */
+                    8191,    /* 1<<13 = 8192 */
+                    16381,   /* 1<<14 = 16384 */
+                    32749,   /* 1<<15 = 32768 */
+                    65521,   /* 1<<16 = 65536 */
+                    131071,  /* 1<<17 = 131072 */
+                    262139,  /* 1<<18 = 262144 */
+                    524287,  /* 1<<19 = 524288 */
+                    1048573, /* 1<<20 = 1048576 */
+                    2097143, /* 1<<21 = 2097152 */
+                    4194301, /* 1<<22 = 4194304 */
+                    8388593, /* 1<<23 = 8388608 */
+                    16777213, /* 1<<24 = 16777216 */
+                    33554393, /* 1<<25 = 33554432 */
+                    67108859, /* 1<<26 = 67108864 */
+                    134217689, /* 1<<27 = 134217728 */
+                    268435399, /* 1<<28 = 268435456 */
+            };
     private Object[] _keys;
     private int[] _values;
-
     private int _size;
-    private int _mask;
+    private int _prime;
 
     /**
      * Create a new IntMap.  Default size is 16.
      */
     public IntMap() {
-        _keys = new Object[256];
-        _values = new int[256];
+        int capacity = 1024;
 
-        _mask = _keys.length - 1;
+        _keys = new Object[capacity];
+        _values = new int[capacity];
+
+        _prime = getBiggestPrime(_keys.length);
         _size = 0;
     }
 
+    public static int getBiggestPrime(int value) {
+        for (int i = PRIMES.length - 1; i >= 0; i--) {
+            if (PRIMES[i] <= value)
+                return PRIMES[i];
+        }
+
+        return 2;
+    }
+
     /**
      * Clear the hashmap.
      */
     public void clear() {
-        Object[] keys = _keys;
-        int[] values = _values;
+        final Object[] keys = _keys;
+        final int[] values = _values;
 
         for (int i = keys.length - 1; i >= 0; i--) {
             keys[i] = null;
@@ -99,79 +138,46 @@
     /**
      * Returns the current number of entries in the map.
      */
-    public int size() {
+    public final int size() {
         return _size;
     }
 
     /**
      * Puts a new value in the property table with the appropriate flags
      */
-    public int get(Object key) {
-        int mask = _mask;
-        int hash = key.hashCode() % mask & mask;
+    public final int get(Object key) {
+        int prime = _prime;
+        int hash = hashCode(key) % prime;
+        // int hash = key.hashCode() & mask;
 
-        Object[] keys = _keys;
+        final Object[] keys = _keys;
 
         while (true) {
             Object mapKey = keys[hash];
 
             if (mapKey == null)
                 return NULL;
-            else if (mapKey == key || mapKey.equals(key))
+            else if (mapKey == key)
                 return _values[hash];
 
-            hash = (hash + 1) % mask;
+            hash = (hash + 1) % prime;
         }
     }
 
     /**
-     * Expands the property table
-     */
-    private void resize(int newSize) {
-        Object[] newKeys = new Object[newSize];
-        int[] newValues = new int[newSize];
-
-        int mask = _mask = newKeys.length - 1;
-
-        Object[] keys = _keys;
-        int values[] = _values;
-
-        for (int i = keys.length - 1; i >= 0; i--) {
-            Object key = keys[i];
-
-            if (key == null || key == DELETED)
-                continue;
-
-            int hash = key.hashCode() % mask & mask;
-
-            while (true) {
-                if (newKeys[hash] == null) {
-                    newKeys[hash] = key;
-                    newValues[hash] = values[i];
-                    break;
-                }
-
-                hash = (hash + 1) % mask;
-            }
-        }
-
-        _keys = newKeys;
-        _values = newValues;
-    }
-
-    /**
      * Puts a new value in the property table with the appropriate flags
      */
-    public int put(Object key, int value) {
-        int mask = _mask;
-        int hash = key.hashCode() % mask & mask;
+    public final int put(Object key, int value, boolean isReplace) {
+        int prime = _prime;
+        int hash = hashCode(key) % prime;
+        // int hash = key.hashCode() % prime;
 
         Object[] keys = _keys;
 
         while (true) {
             Object testKey = keys[hash];
 
-            if (testKey == null || testKey == DELETED) {
+            if (testKey == null) {
                 keys[hash] = key;
                 _values[hash] = value;
 
@@ -181,53 +187,56 @@
                     resize(4 * keys.length);
 
                 return NULL;
-            } else if (key != testKey && !key.equals(testKey)) {
-                hash = (hash + 1) % mask;
+            } else if (key != testKey) {
+                hash = (hash + 1) % prime;
 
                 continue;
-            } else {
+            } else if (isReplace) {
                 int old = _values[hash];
 
                 _values[hash] = value;
 
                 return old;
+            } else {
+                return _values[hash];
             }
         }
     }
 
     /**
-     * Deletes the entry.  Returns true if successful.
+     * Expands the property table
      */
-    public int remove(Object key) {
-        int mask = _mask;
-        int hash = key.hashCode() % mask & mask;
+    private void resize(int newSize) {
+        Object[] keys = _keys;
+        int values[] = _values;
 
-        while (true) {
-            Object mapKey = _keys[hash];
+        _keys = new Object[newSize];
+        _values = new int[newSize];
+        _size = 0;
 
-            if (mapKey == null)
-                return NULL;
-            else if (mapKey == key) {
-                _keys[hash] = DELETED;
+        _prime = getBiggestPrime(_keys.length);
 
-                _size--;
+        for (int i = keys.length - 1; i >= 0; i--) {
+            Object key = keys[i];
 
-                return _values[hash];
+            if (key != null) {
+                put(key, values[i], true);
             }
-
-            hash = (hash + 1) % mask;
         }
     }
 
-    @Override
+    protected int hashCode(Object value) {
+        return value.hashCode();
+    }
+
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
 
         sbuf.append("IntMap[");
         boolean isFirst = true;
 
-        for (int i = 0; i <= _mask; i++) {
-            if (_keys[i] != null && _keys[i] != DELETED) {
+        for (int i = 0; i <= _keys.length; i++) {
+            if (_keys[i] != null) {
                 if (!isFirst)
                     sbuf.append(", ");
 
diff --git a/src/main/resources/META-INF/dubbo/hessian/deserializers b/src/main/resources/META-INF/dubbo/hessian/deserializers
new file mode 100644
index 0000000..ee6b6b1
--- /dev/null
+++ b/src/main/resources/META-INF/dubbo/hessian/deserializers
@@ -0,0 +1,3 @@
+java.io.File=com.alibaba.com.caucho.hessian.io.FileDeserializer
+java.math.BigDecimal=com.alibaba.com.caucho.hessian.io.BigDecimalDeserializer
+javax.management.ObjectName=com.alibaba.com.caucho.hessian.io.ObjectNameDeserializer
diff --git a/src/main/resources/META-INF/dubbo/hessian/serializers b/src/main/resources/META-INF/dubbo/hessian/serializers
new file mode 100644
index 0000000..3423280
--- /dev/null
+++ b/src/main/resources/META-INF/dubbo/hessian/serializers
@@ -0,0 +1,18 @@
+java.io.File=com.alibaba.com.caucho.hessian.io.StringValueSerializer
+java.math.BigDecimal=com.alibaba.com.caucho.hessian.io.StringValueSerializer
+java.util.Locale=com.alibaba.com.caucho.hessian.io.LocaleSerializer
+javax.management.ObjectName=com.alibaba.com.caucho.hessian.io.StringValueSerializer
+java.time.Duration=com.alibaba.com.caucho.hessian.io.java8.DurationSerializer
+java.time.Instant=com.alibaba.com.caucho.hessian.io.java8.InstantSerializer
+java.time.LocalDate=com.alibaba.com.caucho.hessian.io.java8.LocalDateSerializer
+java.time.LocalDateTime=com.alibaba.com.caucho.hessian.io.java8.LocalDateTimeSerializer
+java.time.LocalTime=com.alibaba.com.caucho.hessian.io.java8.LocalTimeSerializer
+java.time.MonthDay=com.alibaba.com.caucho.hessian.io.java8.MonthDaySerializer
+java.time.OffsetDateTime=com.alibaba.com.caucho.hessian.io.java8.OffsetDateTimeSerializer
+java.time.OffsetTime=com.alibaba.com.caucho.hessian.io.java8.OffsetTimeSerializer
+java.time.Period=com.alibaba.com.caucho.hessian.io.java8.PeriodSerializer
+java.time.Year=com.alibaba.com.caucho.hessian.io.java8.YearSerializer
+java.time.YearMonth=com.alibaba.com.caucho.hessian.io.java8.YearMonthSerializer
+java.time.ZonedDateTime=com.alibaba.com.caucho.hessian.io.java8.ZonedDateTimeSerializer
+java.time.ZoneId=com.alibaba.com.caucho.hessian.io.java8.ZoneIdSerializer
+java.time.ZoneOffset=com.alibaba.com.caucho.hessian.io.java8.ZoneOffsetSerializer
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/BitSetSerializerTest.java b/src/test/java/com/alibaba/com/caucho/hessian/io/BitSetSerializerTest.java
index 94a5fd9..64e97a0 100644
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/BitSetSerializerTest.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian/io/BitSetSerializerTest.java
@@ -43,6 +43,5 @@
 
     private void assertBitSet(BitSet bitSet) throws IOException {
         TestCase.assertEquals(bitSet, baseHessian2Serialize(bitSet));
-        TestCase.assertEquals(bitSet, baseHessianSerialize(bitSet));
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/CollectionSerializerTest.java b/src/test/java/com/alibaba/com/caucho/hessian/io/CollectionSerializerTest.java
index 75aa608..09970e6 100644
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/CollectionSerializerTest.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian/io/CollectionSerializerTest.java
@@ -38,7 +38,7 @@
         set.add(1111);
         set.add(2222);
 
-        Set deserialize = baseHessianSerialize(set);
+        Set deserialize = baseHessian2Serialize(set);
         Assert.assertTrue(deserialize.equals(set));
     }
 
@@ -89,7 +89,7 @@
         list.add(1111);
         list.add(2222);
 
-        List deserialize = baseHessianSerialize(list);
+        List deserialize = baseHessian2Serialize(list);
         Assert.assertTrue(deserialize.equals(list));
     }
 
@@ -101,7 +101,7 @@
         vector.add(1111);
         vector.add(2222);
 
-        List deserialize = baseHessianSerialize(vector);
+        List deserialize = baseHessian2Serialize(vector);
         Assert.assertTrue(deserialize.equals(vector));
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian1StringShortTest.java b/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian1StringShortTest.java
deleted file mode 100644
index c3ead15..0000000
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian1StringShortTest.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.com.caucho.hessian.io;
-
-import com.alibaba.com.caucho.hessian.io.base.SerializeTestBase;
-import com.alibaba.com.caucho.hessian.io.beans.Hessian2StringShortType;
-import com.alibaba.com.caucho.hessian.io.beans.PersonType;
-
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
-
-public class Hessian1StringShortTest extends SerializeTestBase {
-
-    @Test
-    public void serialize_string_short_map_then_deserialize() throws Exception {
-
-        Hessian2StringShortType stringShort = new Hessian2StringShortType();
-        Map<String, Short> stringShortMap = new HashMap<String, Short>();
-        stringShortMap.put("first", (short)0);
-        stringShortMap.put("last", (short)60);
-        stringShort.stringShortMap = stringShortMap;
-
-        Hessian2StringShortType deserialize = baseHessianSerialize(stringShort);
-        assertTrue(deserialize.stringShortMap != null);
-        assertTrue(deserialize.stringShortMap.size() == 2);
-        assertTrue(deserialize.stringShortMap.get("last") instanceof Short);
-        assertEquals(Short.valueOf((short)0), deserialize.stringShortMap.get("first"));
-        assertEquals(Short.valueOf((short)60), deserialize.stringShortMap.get("last"));
-    }
-
-    @Test
-    public void serialize_string_byte_map_then_deserialize() throws Exception {
-
-        Hessian2StringShortType stringShort = new Hessian2StringShortType();
-        Map<String, Byte> stringByteMap = new HashMap<String, Byte>();
-        stringByteMap.put("first", (byte)0);
-        stringByteMap.put("last", (byte)60);
-        stringShort.stringByteMap = stringByteMap;
-
-        Hessian2StringShortType deserialize = baseHessianSerialize(stringShort);
-        assertTrue(deserialize.stringByteMap != null);
-        assertTrue(deserialize.stringByteMap.size() == 2);
-        assertTrue(deserialize.stringByteMap.get("last") instanceof Byte);
-        assertEquals(Byte.valueOf((byte)0), deserialize.stringByteMap.get("first"));
-        assertEquals(Byte.valueOf((byte) 60), deserialize.stringByteMap.get("last"));
-    }
-
-    @Test
-    public void serialize_map_then_deserialize() throws Exception {
-
-        Map<String, Short> stringShortMap = new HashMap<String, Short>();
-        stringShortMap.put("first", (short)0);
-        stringShortMap.put("last", (short)60);
-
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        HessianOutput out = new HessianOutput(bout);
-
-        out.writeObject(stringShortMap);
-        out.flush();
-
-        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-        HessianInput input = new HessianInput(bin);
-        Map deserialize = (Map) input.readObject(HashMap.class, String.class, Short.class);
-        assertTrue(deserialize != null);
-        assertTrue(deserialize.size() == 2);
-        assertTrue(deserialize.get("last") instanceof Short);
-        assertEquals(Short.valueOf((short)0), deserialize.get("first"));
-        assertEquals(Short.valueOf((short)60), deserialize.get("last"));
-    }
-
-    @Test
-    public void serialize_map_then_deserialize0() throws Exception {
-
-        Map<String, Short> stringShortMap = new HashMap<String, Short>();
-        stringShortMap.put("first", (short)0);
-        stringShortMap.put("last", (short)60);
-
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        HessianOutput out = new HessianOutput(bout);
-
-        out.writeObject(stringShortMap);
-        out.flush();
-
-        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-        HessianInput input = new HessianInput(bin);
-        List<Class<?>> keyValueType = new ArrayList<Class<?>>();
-        keyValueType.add(String.class);
-        keyValueType.add(short.class);
-
-        Map deserialize = (Map) input.readObject(keyValueType);
-        assertTrue(deserialize != null);
-        assertTrue(deserialize.size() == 2);
-        assertTrue(deserialize.get("last") instanceof Short);
-        assertEquals(Short.valueOf((short)0), deserialize.get("first"));
-        assertEquals(Short.valueOf((short)60), deserialize.get("last"));
-    }
-
-    @Test
-    public void serialize_string_person_map_then_deserialize() throws Exception {
-
-        Hessian2StringShortType stringShort = new Hessian2StringShortType();
-        Map<String, PersonType> stringPersonTypeMap = new HashMap<String, PersonType>();
-        stringPersonTypeMap.put("first", new PersonType(
-                "jason.shang", 26, (double) 0.1, (short)1, (byte)2, Arrays.asList((short)1,(short)1)
-        ));
-        stringPersonTypeMap.put("last", new PersonType(
-                "jason.shang2", 52, (double) 0.2, (short)2, (byte)4, Arrays.asList((short)2,(short)2)
-        ));
-        stringShort.stringPersonTypeMap = stringPersonTypeMap;
-
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        HessianOutput out = new HessianOutput(bout);
-
-        out.writeObject(stringShort);
-        out.flush();
-
-        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-        HessianInput input = new HessianInput(bin);
-
-        Hessian2StringShortType deserialize = (Hessian2StringShortType) input.readObject();
-        assertTrue(deserialize.stringPersonTypeMap != null);
-        assertTrue(deserialize.stringPersonTypeMap.size() == 2);
-        assertTrue(deserialize.stringPersonTypeMap.get("last") instanceof PersonType);
-
-
-        assertEquals(new PersonType(
-                "jason.shang", 26, (double) 0.1, (short)1, (byte)2, Arrays.asList((short)1,(short)1)
-        ), deserialize.stringPersonTypeMap.get("first"));
-
-        assertEquals(new PersonType(
-                "jason.shang2", 52, (double) 0.2, (short)2, (byte)4, Arrays.asList((short)2,(short)2)
-        ), deserialize.stringPersonTypeMap.get("last"));
-
-    }
-
-    @Test
-    public void serialize_list_then_deserialize() throws Exception {
-
-        List<Short> shortList = new ArrayList<Short>();
-        shortList.add((short)0);
-        shortList.add((short)60);
-
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        HessianOutput out = new HessianOutput(bout);
-
-        out.writeObject(shortList);
-        out.flush();
-
-        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-        HessianInput input = new HessianInput(bin);
-        List<Short> deserialize = (List) input.readObject(ArrayList.class, Short.class);
-        assertTrue(deserialize != null);
-        assertTrue(deserialize.size() == 2);
-        assertTrue(deserialize.get(1) instanceof Short);
-        assertEquals(Short.valueOf((short)0), deserialize.get(0));
-        assertEquals(Short.valueOf((short)60), deserialize.get(1));
-    }
-
-    @Test
-    public void serialize_list_then_deserialize0() throws Exception {
-
-        List<Short> shortList = new ArrayList<Short>();
-        shortList.add((short)0);
-        shortList.add((short)60);
-
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        HessianOutput out = new HessianOutput(bout);
-
-        out.writeObject(shortList);
-        out.flush();
-
-        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-        HessianInput input = new HessianInput(bin);
-
-        List<Class<?>> valueType = new ArrayList<Class<?>>();
-        valueType.add(short.class);
-
-        List<Short> deserialize = (List) input.readObject(valueType);
-        assertTrue(deserialize != null);
-        assertTrue(deserialize.size() == 2);
-        assertTrue(deserialize.get(1) instanceof Short);
-        assertEquals(Short.valueOf((short)0), deserialize.get(0));
-        assertEquals(Short.valueOf((short)60), deserialize.get(1));
-    }
-
-}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian2StringShortTest.java b/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian2StringShortTest.java
index 30991f0..1e6f921 100644
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian2StringShortTest.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian/io/Hessian2StringShortTest.java
@@ -82,7 +82,8 @@
 
         ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
         Hessian2Input input = new Hessian2Input(bin);
-        Map deserialize = (Map) input.readObject(HashMap.class, String.class, Short.class);
+//        Map deserialize = (Map) input.readObject(HashMap.class, String.class, Short.class);
+        Map deserialize = (Map) input.readObject(HashMap.class);
         assertTrue(deserialize != null);
         assertTrue(deserialize.size() == 2);
         assertTrue(deserialize.get("last") instanceof Short);
@@ -110,7 +111,7 @@
         keyValueType.add(String.class);
         keyValueType.add(short.class);
 
-        Map deserialize = (Map) input.readObject(keyValueType);
+        Map deserialize = (Map) input.readObject(Map.class);
         assertTrue(deserialize != null);
         assertTrue(deserialize.size() == 2);
         assertTrue(deserialize.get("last") instanceof Short);
@@ -171,7 +172,7 @@
 
         ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
         Hessian2Input input = new Hessian2Input(bin);
-        List<Short> deserialize = (List) input.readObject(ArrayList.class, Short.class);
+        List<Short> deserialize = (List) input.readObject(ArrayList.class);
         assertTrue(deserialize != null);
         assertTrue(deserialize.size() == 2);
         assertTrue(deserialize.get(1) instanceof Short);
@@ -198,7 +199,7 @@
         List<Class<?>> valueType = new ArrayList<Class<?>>();
         valueType.add(short.class);
 
-        List<Short> deserialize = (List) input.readObject(valueType);
+        List<Short> deserialize = (List) input.readObject(List.class);
         assertTrue(deserialize != null);
         assertTrue(deserialize.size() == 2);
         assertTrue(deserialize.get(1) instanceof Short);
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/HessianJavaSerializeTest.java b/src/test/java/com/alibaba/com/caucho/hessian/io/HessianJavaSerializeTest.java
index 000e5dc..95b4447 100644
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/HessianJavaSerializeTest.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian/io/HessianJavaSerializeTest.java
@@ -39,7 +39,7 @@
         baseUser.setUserId(1);
         baseUser.setUserName("tom");
 
-        BaseUser serializedUser = baseHessianSerialize(baseUser);
+        BaseUser serializedUser = baseHessian2Serialize(baseUser);
         Assert.assertEquals("tom", serializedUser.getUserName());
     }
 
@@ -50,7 +50,7 @@
         subUser.setUserId(1);
         subUser.setUserName("tom");
 
-        SubUser serializedUser = baseHessianSerialize(subUser);
+        SubUser serializedUser = baseHessian2Serialize(subUser);
         Assert.assertEquals("tom", serializedUser.getUserName());
 
     }
@@ -78,7 +78,7 @@
         grandsonUser.setUserId(1);
         grandsonUser.setUserName("tom");
 
-        GrandsonUser serializedUser = baseHessianSerialize(grandsonUser);
+        GrandsonUser serializedUser = baseHessian2Serialize(grandsonUser);
         Assert.assertEquals("tom", serializedUser.getUserName());
     }
 
@@ -86,7 +86,6 @@
     public void testFloat() throws Exception {
         Float fData = 99.8F;
         Double dData = 99.8D;
-        Assert.assertEquals(dData, baseHessianSerialize(fData));
         Assert.assertEquals(dData, baseHessian2Serialize(fData));
     }
 
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/LocaleSerializerTest.java b/src/test/java/com/alibaba/com/caucho/hessian/io/LocaleSerializerTest.java
index e751438..cd8d85a 100644
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/LocaleSerializerTest.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian/io/LocaleSerializerTest.java
@@ -40,6 +40,5 @@
 
     private void assertLocale(Locale locale) throws IOException {
         TestCase.assertEquals(locale, baseHessian2Serialize(locale));
-        TestCase.assertEquals(locale, baseHessianSerialize(locale));
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian/io/base/SerializeTestBase.java b/src/test/java/com/alibaba/com/caucho/hessian/io/base/SerializeTestBase.java
index 45478dc..140e6f2 100644
--- a/src/test/java/com/alibaba/com/caucho/hessian/io/base/SerializeTestBase.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian/io/base/SerializeTestBase.java
@@ -18,8 +18,6 @@
 
 import com.alibaba.com.caucho.hessian.io.Hessian2Input;
 import com.alibaba.com.caucho.hessian.io.Hessian2Output;
-import com.alibaba.com.caucho.hessian.io.HessianInput;
-import com.alibaba.com.caucho.hessian.io.HessianOutput;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -30,25 +28,6 @@
  *
  */
 public class SerializeTestBase {
-    /**
-     * hessian serialize util
-     *
-     * @param data
-     * @param <T>
-     * @return
-     * @throws IOException
-     */
-    protected <T> T baseHessianSerialize(T data) throws IOException {
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        HessianOutput out = new HessianOutput(bout);
-
-        out.writeObject(data);
-        out.flush();
-
-        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
-        HessianInput input = new HessianInput(bin);
-        return (T) input.readObject();
-    }
 
     /**
      * hessian2 serialize util
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/HessianException.java
similarity index 75%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/HessianException.java
index 72a9822..8908c0d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/HessianException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,20 +46,36 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
-
-import java.io.IOException;
+package com.alibaba.com.caucho.hessian3;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Base runtime exception for Hessian exceptions.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class HessianException extends RuntimeException {
     /**
-     * Looks up a proxy object.
+     * Zero-arg constructor.
      */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public HessianException() {
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianException(Throwable rootCause) {
+        super(rootCause);
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractDeserializer.java
new file mode 100644
index 0000000..a1ea126
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractDeserializer.java
@@ -0,0 +1,150 @@
+/*
+ * 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 "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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+
+/**
+ * Deserializing an object.
+ */
+abstract public class AbstractDeserializer implements Deserializer {
+    @Override
+    public Class getType() {
+        return Object.class;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in)
+            throws IOException {
+        Object obj = in.readObject();
+
+        String className = getClass().getName();
+
+        if (obj != null)
+            throw error(className + ": unexpected object " + obj.getClass().getName());
+        else
+            throw error(className + ": unexpected null value");
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
+        if (expectType == null) {
+            return readList(in, length);
+        }
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
+        if (expectType == null) {
+            return readLengthList(in, length);
+        }
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        Object obj = in.readObject();
+
+        String className = getClass().getName();
+
+        if (obj != null)
+            throw error(className + ": unexpected object " + obj.getClass().getName());
+        else
+            throw error(className + ": unexpected null value");
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {
+        if (expectKeyType == null && expectValueType == null) {
+            return readMap(in);
+        }
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    protected HessianProtocolException error(String msg) {
+        return new HessianProtocolException(msg);
+    }
+
+    protected String codeName(int ch) {
+        if (ch < 0)
+            return "end of file";
+        else
+            return "0x" + Integer.toHexString(ch & 0xff);
+    }
+
+    protected SerializerFactory findSerializerFactory(AbstractHessianInput in) {
+        SerializerFactory serializerFactory = null;
+        if (in instanceof Hessian2Input) {
+            serializerFactory = ((Hessian2Input) in).findSerializerFactory();
+        } else if (in instanceof HessianInput) {
+            serializerFactory = ((HessianInput) in).getSerializerFactory();
+        }
+        return serializerFactory == null ? new SerializerFactory() : serializerFactory;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianInput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianInput.java
new file mode 100644
index 0000000..c32a3dd
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianInput.java
@@ -0,0 +1,459 @@
+/*
+ * 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 "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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.List;
+
+/**
+ * Abstract base class for Hessian requests.  Hessian users should only
+ * need to use the methods in this class.
+ * <p>
+ * <pre>
+ * AbstractHessianInput in = ...; // get input
+ * String value;
+ *
+ * in.startReply();         // read reply header
+ * value = in.readString(); // read string value
+ * in.completeReply();      // read reply footer
+ * </pre>
+ */
+abstract public class AbstractHessianInput {
+    private HessianRemoteResolver resolver;
+
+    /**
+     * Initialize the Hessian stream with the underlying input stream.
+     */
+    public void init(InputStream is) {
+    }
+
+    /**
+     * Returns the call's method
+     */
+    abstract public String getMethod();
+
+    /**
+     * Sets the resolver used to lookup remote objects.
+     */
+    public HessianRemoteResolver getRemoteResolver() {
+        return resolver;
+    }
+
+    /**
+     * Sets the resolver used to lookup remote objects.
+     */
+    public void setRemoteResolver(HessianRemoteResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    /**
+     * Sets the serializer factory.
+     */
+    public void setSerializerFactory(SerializerFactory ser) {
+    }
+
+    public abstract boolean checkAndReadNull();
+
+    /**
+     * Reads the call
+     * <p>
+     * <pre>
+     * c major minor
+     * </pre>
+     */
+    abstract public int readCall()
+            throws IOException;
+
+    /**
+     * For backward compatibility with HessianSkeleton
+     */
+    public void skipOptionalCall()
+            throws IOException {
+    }
+
+    /**
+     * Reads a header, returning null if there are no headers.
+     * <p>
+     * <pre>
+     * H b16 b8 value
+     * </pre>
+     */
+    abstract public String readHeader()
+            throws IOException;
+
+    /**
+     * Starts reading the call
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <pre>
+     * m b16 b8 method
+     * </pre>
+     */
+    abstract public String readMethod()
+            throws IOException;
+
+    /**
+     * Reads the number of method arguments
+     *
+     * @return -1 for a variable length (hessian 1.0)
+     */
+    public int readMethodArgLength()
+            throws IOException {
+        return -1;
+    }
+
+    /**
+     * 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>
+     */
+    abstract public void startCall()
+            throws IOException;
+
+    /**
+     * Completes reading the call
+     * <p>
+     * <p>The call expects the following protocol data
+     * <p>
+     * <pre>
+     * Z
+     * </pre>
+     */
+    abstract public void completeCall()
+            throws IOException;
+
+    /**
+     * Reads a reply as an object.
+     * If the reply has a fault, throws the exception.
+     */
+    abstract public Object readReply(Class expectedClass)
+            throws Throwable;
+
+    /**
+     * Starts reading the reply
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <pre>
+     * r
+     * v
+     * </pre>
+     */
+    abstract public void startReply()
+            throws Throwable;
+
+    /**
+     * Completes reading the call
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <pre>
+     * z
+     * </pre>
+     */
+    abstract public void completeReply()
+            throws IOException;
+
+    /**
+     * Reads a boolean
+     * <p>
+     * <pre>
+     * T
+     * F
+     * </pre>
+     */
+    abstract public boolean readBoolean()
+            throws IOException;
+
+    /**
+     * Reads a null
+     * <p>
+     * <pre>
+     * N
+     * </pre>
+     */
+    abstract public void readNull()
+            throws IOException;
+
+    /**
+     * Reads an integer
+     * <p>
+     * <pre>
+     * I b32 b24 b16 b8
+     * </pre>
+     */
+    abstract public int readInt()
+            throws IOException;
+
+    /**
+     * Reads a long
+     * <p>
+     * <pre>
+     * L b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre>
+     */
+    abstract public long readLong()
+            throws IOException;
+
+    /**
+     * Reads a double.
+     * <p>
+     * <pre>
+     * D b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre>
+     */
+    abstract public double readDouble()
+            throws IOException;
+
+    /**
+     * Reads a date.
+     * <p>
+     * <pre>
+     * T b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre>
+     */
+    abstract public long readUTCDate()
+            throws IOException;
+
+    /**
+     * Reads a string encoded in UTF-8
+     * <p>
+     * <pre>
+     * s b16 b8 non-final string chunk
+     * S b16 b8 final string chunk
+     * </pre>
+     */
+    abstract public String readString()
+            throws IOException;
+
+    /**
+     * Reads an XML node encoded in UTF-8
+     * <p>
+     * <pre>
+     * x b16 b8 non-final xml chunk
+     * X b16 b8 final xml chunk
+     * </pre>
+     */
+    public org.w3c.dom.Node readNode()
+            throws IOException {
+        throw new UnsupportedOperationException(getClass().getSimpleName());
+    }
+
+    /**
+     * Starts reading a string.  All the characters must be read before
+     * calling the next method.  The actual characters will be read with
+     * the reader's read() or read(char [], int, int).
+     * <p>
+     * <pre>
+     * s b16 b8 non-final string chunk
+     * S b16 b8 final string chunk
+     * </pre>
+     */
+    abstract public Reader getReader()
+            throws IOException;
+
+    /**
+     * Starts reading a byte array using an input stream.  All the bytes
+     * must be read before calling the following method.
+     * <p>
+     * <pre>
+     * b b16 b8 non-final binary chunk
+     * B b16 b8 final binary chunk
+     * </pre>
+     */
+    abstract public InputStream readInputStream()
+            throws IOException;
+
+    /**
+     * Reads a byte array.
+     * <p>
+     * <pre>
+     * b b16 b8 non-final binary chunk
+     * B b16 b8 final binary chunk
+     * </pre>
+     */
+    abstract public byte[] readBytes()
+            throws IOException;
+
+    /**
+     * Reads an arbitrary object from the input stream.
+     *
+     * @param expectedClass the expected class if the protocol doesn't supply it.
+     */
+    abstract public Object readObject(Class expectedClass)
+            throws IOException;
+
+    /**
+     * Reads an arbitrary object from the input stream.
+     *
+     * @param expectedClass the expected class if the protocol doesn't supply it.
+     * @param expectedTypes the runtime type hints, eg: expectedClass equals Map, expectedTypes can
+     *                      equals String.class, Short.class
+     */
+    public Object readObject(Class expectedClass, Class<?>... expectedTypes)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    /**
+     * Reads an arbitrary object from the input stream.
+     */
+    abstract public Object readObject()
+            throws IOException;
+
+    /**
+     * Reads an arbitrary object from the input stream.
+     *
+     * @param expectedTypes the runtime type hints, eg: expectedTypes can
+     *                      equals String.class, Short.class for HashMap
+     */
+    public Object readObject(List<Class<?>> expectedTypes)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    /**
+     * Reads a remote object reference to the stream.  The type is the
+     * type of the remote interface.
+     * <p>
+     * <code><pre>
+     * 'r' 't' b16 b8 type url
+     * </pre></code>
+     */
+    abstract public Object readRemote()
+            throws IOException;
+
+    /**
+     * Reads a reference
+     * <p>
+     * <pre>
+     * R b32 b24 b16 b8
+     * </pre>
+     */
+    abstract public Object readRef()
+            throws IOException;
+
+    /**
+     * Adds an object reference.
+     */
+    abstract public int addRef(Object obj)
+            throws IOException;
+
+    /**
+     * Sets an object reference.
+     */
+    abstract public void setRef(int i, Object obj)
+            throws IOException;
+
+    /**
+     * Resets the references for streaming.
+     */
+    public void resetReferences() {
+    }
+
+    /**
+     * Reads the start of a list
+     */
+    abstract public int readListStart()
+            throws IOException;
+
+    /**
+     * Reads the length of a list.
+     */
+    abstract public int readLength()
+            throws IOException;
+
+    /**
+     * Reads the start of a map
+     */
+    abstract public int readMapStart()
+            throws IOException;
+
+    /**
+     * Reads an object type.
+     */
+    abstract public String readType()
+            throws IOException;
+
+    /**
+     * Returns true if the data has ended.
+     */
+    abstract public boolean isEnd()
+            throws IOException;
+
+    /**
+     * Read the end byte
+     */
+    abstract public void readEnd()
+            throws IOException;
+
+    /**
+     * Read the end byte
+     */
+    abstract public void readMapEnd()
+            throws IOException;
+
+    /**
+     * Read the end byte
+     */
+    abstract public void readListEnd()
+            throws IOException;
+
+    public void close()
+            throws IOException {
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianOutput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianOutput.java
new file mode 100644
index 0000000..3592ec1
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianOutput.java
@@ -0,0 +1,515 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Abstract output stream for Hessian requests.
+ * <p>
+ * <pre>
+ * OutputStream os = ...; // from http connection
+ * AbstractOutput out = new HessianSerializerOutput(os);
+ * String value;
+ *
+ * out.startCall("hello");  // start hello call
+ * out.writeString("arg1"); // write a string argument
+ * out.completeCall();      // complete the call
+ * </pre>
+ */
+abstract public class AbstractHessianOutput {
+    // serializer factory
+    protected SerializerFactory _serializerFactory;
+
+    /**
+     * Gets the serializer factory.
+     */
+    public SerializerFactory getSerializerFactory() {
+        return _serializerFactory;
+    }
+
+    /**
+     * Sets the serializer factory.
+     */
+    public void setSerializerFactory(SerializerFactory factory) {
+        _serializerFactory = factory;
+    }
+
+    /**
+     * Gets the serializer factory.
+     */
+    public final SerializerFactory findSerializerFactory() {
+        SerializerFactory factory = _serializerFactory;
+
+        if (factory == null)
+            _serializerFactory = factory = new SerializerFactory();
+
+        return factory;
+    }
+
+    /**
+     * Initialize the output with a new underlying stream.
+     */
+    public void init(OutputStream os) {
+    }
+
+    /**
+     * Writes a complete method call.
+     */
+    public void call(String method, Object[] args)
+            throws IOException {
+        int length = args != null ? args.length : 0;
+
+        startCall(method, length);
+
+        for (int i = 0; i < length; i++)
+            writeObject(args[i]);
+
+        completeCall();
+    }
+
+    /**
+     * Starts the method call:
+     * <p>
+     * <code><pre>
+     * C
+     * </pre></code>
+     *
+     * @param method the method name to call.
+     */
+    abstract public void startCall()
+            throws IOException;
+
+    /**
+     * Starts the method call:
+     * <p>
+     * <code><pre>
+     * C string int
+     * </pre></code>
+     *
+     * @param method the method name to call.
+     */
+    abstract public void startCall(String method, int length)
+            throws IOException;
+
+    /**
+     * For Hessian 2.0, use the Header envelope instead
+     *
+     * @deprecated
+     */
+    public void writeHeader(String name)
+            throws IOException {
+        throw new UnsupportedOperationException(getClass().getSimpleName());
+    }
+
+    /**
+     * Writes the method tag.
+     * <p>
+     * <code><pre>
+     * string
+     * </pre></code>
+     *
+     * @param method the method name to call.
+     */
+    abstract public void writeMethod(String method)
+            throws IOException;
+
+    /**
+     * Completes the method call:
+     * <p>
+     * <code><pre>
+     * </pre></code>
+     */
+    abstract public void completeCall()
+            throws IOException;
+
+    /**
+     * Writes a boolean value to the stream.  The boolean will be written
+     * with the following syntax:
+     * <p>
+     * <code><pre>
+     * T
+     * F
+     * </pre></code>
+     *
+     * @param value the boolean value to write.
+     */
+    abstract public void writeBoolean(boolean value)
+            throws IOException;
+
+    /**
+     * Writes an integer value to the stream.  The integer will be written
+     * with the following syntax:
+     * <p>
+     * <code><pre>
+     * I b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param value the integer value to write.
+     */
+    abstract public void writeInt(int value)
+            throws IOException;
+
+    /**
+     * Writes a long value to the stream.  The long will be written
+     * with the following syntax:
+     * <p>
+     * <code><pre>
+     * L b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param value the long value to write.
+     */
+    abstract public void writeLong(long value)
+            throws IOException;
+
+    /**
+     * Writes a double value to the stream.  The double will be written
+     * with the following syntax:
+     * <p>
+     * <code><pre>
+     * D b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param value the double value to write.
+     */
+    abstract public void writeDouble(double value)
+            throws IOException;
+
+    /**
+     * Writes a date to the stream.
+     * <p>
+     * <code><pre>
+     * T  b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param time the date in milliseconds from the epoch in UTC
+     */
+    abstract public void writeUTCDate(long time)
+            throws IOException;
+
+    /**
+     * Writes a null value to the stream.
+     * The null will be written with the following syntax
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeNull()
+            throws IOException;
+
+    /**
+     * Writes a string value to the stream using UTF-8 encoding.
+     * The string will be written with the following syntax:
+     * <p>
+     * <code><pre>
+     * S b16 b8 string-value
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeString(String value)
+            throws IOException;
+
+    /**
+     * Writes a string value to the stream using UTF-8 encoding.
+     * The string will be written with the following syntax:
+     * <p>
+     * <code><pre>
+     * S b16 b8 string-value
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeString(char[] buffer, int offset, int length)
+            throws IOException;
+
+    /**
+     * Writes a byte array to the stream.
+     * The array will be written with the following syntax:
+     * <p>
+     * <code><pre>
+     * B b16 b18 bytes
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeBytes(byte[] buffer)
+            throws IOException;
+
+    /**
+     * Writes a byte array to the stream.
+     * The array will be written with the following syntax:
+     * <p>
+     * <code><pre>
+     * B b16 b18 bytes
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeBytes(byte[] buffer, int offset, int length)
+            throws IOException;
+
+    /**
+     * Writes a byte buffer to the stream.
+     */
+    abstract public void writeByteBufferStart()
+            throws IOException;
+
+    /**
+     * Writes a byte buffer to the stream.
+     * <p>
+     * <code><pre>
+     * b b16 b18 bytes
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeByteBufferPart(byte[] buffer,
+                                             int offset,
+                                             int length)
+            throws IOException;
+
+    /**
+     * Writes the last chunk of a byte buffer to the stream.
+     * <p>
+     * <code><pre>
+     * b b16 b18 bytes
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    abstract public void writeByteBufferEnd(byte[] buffer,
+                                            int offset,
+                                            int length)
+            throws IOException;
+
+    /**
+     * Writes a reference.
+     * <p>
+     * <code><pre>
+     * Q int
+     * </pre></code>
+     *
+     * @param value the integer value to write.
+     */
+    abstract protected void writeRef(int value)
+            throws IOException;
+
+    /**
+     * Removes a reference.
+     */
+    abstract public boolean removeRef(Object obj)
+            throws IOException;
+
+    /**
+     * Replaces a reference from one object to another.
+     */
+    abstract public boolean replaceRef(Object oldRef, Object newRef)
+            throws IOException;
+
+    /**
+     * Adds an object to the reference list.  If the object already exists,
+     * writes the reference, otherwise, the caller is responsible for
+     * the serialization.
+     * <p>
+     * <code><pre>
+     * R b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param object the object to add as a reference.
+     * @return true if the object has already been written.
+     */
+    abstract public boolean addRef(Object object)
+            throws IOException;
+
+    /**
+     * Resets the references for streaming.
+     */
+    public void resetReferences() {
+    }
+
+    /**
+     * Writes a generic object to the output stream.
+     */
+    abstract public void writeObject(Object object)
+            throws IOException;
+
+    /**
+     * 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>.
+     * <p>
+     * <code><pre>
+     * V
+     *   x13 java.util.ArrayList   # type
+     *   x93                       # length=3
+     *   x91                       # 1
+     *   x92                       # 2
+     *   x93                       # 3
+     * &lt;/list>
+     * </pre></code>
+     */
+    abstract public boolean writeListBegin(int length, String type)
+            throws IOException;
+
+    /**
+     * Writes the tail of the list to the stream.
+     */
+    abstract public void writeListEnd()
+            throws IOException;
+
+    /**
+     * 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>.
+     * <p>
+     * <code><pre>
+     * M type (<key> <value>)* Z
+     * </pre></code>
+     */
+    abstract public void writeMapBegin(String type)
+            throws IOException;
+
+    /**
+     * Writes the tail of the map to the stream.
+     */
+    abstract public void writeMapEnd()
+            throws IOException;
+
+    /**
+     * Writes the object header to the stream (for Hessian 2.0), or a
+     * Map for Hessian 1.0.  Object writers will call
+     * <code>writeObjectBegin</code> followed by the map contents and then
+     * call <code>writeObjectEnd</code>.
+     * <p>
+     * <code><pre>
+     * C type int <key>*
+     * C int <value>*
+     * </pre></code>
+     *
+     * @return true if the object has already been defined.
+     */
+    public int writeObjectBegin(String type)
+            throws IOException {
+        writeMapBegin(type);
+
+        return -2;
+    }
+
+    /**
+     * Writes the end of the class.
+     */
+    public void writeClassFieldLength(int len)
+            throws IOException {
+    }
+
+    /**
+     * Writes the tail of the object to the stream.
+     */
+    public void writeObjectEnd()
+            throws IOException {
+    }
+
+    public void writeReply(Object o)
+            throws IOException {
+        startReply();
+        writeObject(o);
+        completeReply();
+    }
+
+
+    public void startReply()
+            throws IOException {
+    }
+
+    public void completeReply()
+            throws IOException {
+    }
+
+    public void writeFault(String code, String message, Object detail)
+            throws IOException {
+    }
+
+    public void flush()
+            throws IOException {
+    }
+
+    public void close()
+            throws IOException {
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianResolver.java
similarity index 97%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianResolver.java
index 72a9822..a833ee1 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractHessianResolver.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractListDeserializer.java
similarity index 84%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractListDeserializer.java
index 72a9822..6131978 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractListDeserializer.java
@@ -46,20 +46,22 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing a JDK 1.2 Collection.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class AbstractListDeserializer extends AbstractDeserializer {
     @Override
-    public Object lookup(String type, String url)
+    public Object readObject(AbstractHessianInput in)
             throws IOException {
-        return new HessianRemote(type, url);
+        Object obj = in.readObject();
+
+        if (obj != null)
+            throw error("expected list at " + obj.getClass().getName());
+        else
+            throw error("expected list at null");
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractMapDeserializer.java
similarity index 78%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractMapDeserializer.java
index 72a9822..88973ff 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractMapDeserializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,29 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.HashMap;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object for known object types.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class AbstractMapDeserializer extends AbstractDeserializer {
+
     @Override
-    public Object lookup(String type, String url)
+    public Class getType() {
+        return HashMap.class;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in)
             throws IOException {
-        return new HessianRemote(type, url);
+        Object obj = in.readObject();
+
+        if (obj != null)
+            throw error("expected map/object at " + obj.getClass().getName());
+        else
+            throw error("expected map/object at null");
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractSerializer.java
similarity index 82%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractSerializer.java
index 72a9822..2afa4a9 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractSerializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,19 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.logging.Logger;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+abstract public class AbstractSerializer implements Serializer {
+    protected static final Logger log
+            = Logger.getLogger(AbstractSerializer.class.getName());
+
     @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
-    }
+    abstract public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException;
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractSerializerFactory.java
similarity index 73%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractSerializerFactory.java
index 7c8e328..5754dea 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/AbstractSerializerFactory.java
@@ -46,35 +46,27 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 /**
  * Factory for returning serialization methods.
  */
-public class BeanSerializerFactory extends SerializerFactory {
+abstract public class AbstractSerializerFactory {
     /**
-     * Returns the default serializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
+     * Returns the serializer for a class.
      *
      * @param cl the class of the object that needs to be serialized.
      * @return a serializer object for the serialization.
      */
-    @Override
-    protected Serializer getDefaultSerializer(Class cl) {
-        return new BeanSerializer(cl, getClassLoader());
-    }
+    abstract public Serializer getSerializer(Class cl)
+            throws HessianProtocolException;
 
     /**
-     * Returns the default deserializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
+     * Returns the deserializer for a class.
      *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
+     * @param cl the class of the object that needs to be deserialized.
+     * @return a deserializer object for the serialization.
      */
-    @Override
-    protected Deserializer getDefaultDeserializer(Class cl) {
-        return new BeanDeserializer(cl);
-    }
+    abstract public Deserializer getDeserializer(Class cl)
+            throws HessianProtocolException;
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/ArrayDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ArrayDeserializer.java
new file mode 100644
index 0000000..459a36b
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ArrayDeserializer.java
@@ -0,0 +1,158 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+
+/**
+ * Deserializing a Java array
+ */
+public class ArrayDeserializer extends AbstractListDeserializer {
+    private Class _componentType;
+    private Class _type;
+
+    public ArrayDeserializer(Class componentType) {
+        _componentType = componentType;
+
+        if (_componentType != null) {
+            try {
+                _type = Array.newInstance(_componentType, 0).getClass();
+            } catch (Exception e) {
+            }
+        }
+
+        if (_type == null)
+            _type = Object[].class;
+    }
+
+    @Override
+    public Class getType() {
+        return _type;
+    }
+
+    /**
+     * Reads the array.
+     */
+    @Override
+    public Object readList(AbstractHessianInput in, int length)
+            throws IOException {
+        if (length >= 0) {
+            Object[] data = createArray(length);
+
+            in.addRef(data);
+
+            if (_componentType != null) {
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readObject(_componentType);
+            } else {
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readObject();
+            }
+
+            in.readListEnd();
+
+            return data;
+        } else {
+            ArrayList list = new ArrayList();
+
+            in.addRef(list);
+
+            if (_componentType != null) {
+                while (!in.isEnd())
+                    list.add(in.readObject(_componentType));
+            } else {
+                while (!in.isEnd())
+                    list.add(in.readObject());
+            }
+
+            in.readListEnd();
+
+            Object[] data = createArray(list.size());
+            for (int i = 0; i < data.length; i++)
+                data[i] = list.get(i);
+
+            return data;
+        }
+    }
+
+    /**
+     * Reads the array.
+     */
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length)
+            throws IOException {
+        Object[] data = createArray(length);
+
+        in.addRef(data);
+
+        if (_componentType != null) {
+            for (int i = 0; i < data.length; i++)
+                data[i] = in.readObject(_componentType);
+        } else {
+            for (int i = 0; i < data.length; i++)
+                data[i] = in.readObject();
+        }
+
+        return data;
+    }
+
+    protected Object[] createArray(int length) {
+        if (_componentType != null)
+            return (Object[]) Array.newInstance(_componentType, length);
+        else
+            return new Object[length];
+    }
+
+    @Override
+    public String toString() {
+        return "ArrayDeserializer[" + _componentType + "]";
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ArraySerializer.java
similarity index 68%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/ArraySerializer.java
index 72a9822..c50a08e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ArraySerializer.java
@@ -46,20 +46,48 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a Java array.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class ArraySerializer extends AbstractSerializer {
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (out.addRef(obj))
+            return;
+
+        Object[] array = (Object[]) obj;
+
+        boolean hasEnd = out.writeListBegin(array.length,
+                getArrayType(obj.getClass()));
+
+        for (int i = 0; i < array.length; i++)
+            out.writeObject(array[i]);
+
+        if (hasEnd)
+            out.writeListEnd();
+    }
+
+    /**
+     * Returns the &lt;type> name for a &lt;list>.
+     */
+    private String getArrayType(Class cl) {
+        if (cl.isArray())
+            return '[' + getArrayType(cl.getComponentType());
+
+        String name = cl.getName();
+
+        if (name.equals("java.lang.String"))
+            return "string";
+        else if (name.equals("java.lang.Object"))
+            return "object";
+        else if (name.equals("java.util.Date"))
+            return "date";
+        else
+            return name;
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/BasicDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BasicDeserializer.java
new file mode 100644
index 0000000..626e226
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BasicDeserializer.java
@@ -0,0 +1,615 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class BasicDeserializer extends AbstractDeserializer {
+    public static final int NULL = BasicSerializer.NULL;
+    public static final int BOOLEAN = BasicSerializer.BOOLEAN;
+    public static final int BYTE = BasicSerializer.BYTE;
+    public static final int SHORT = BasicSerializer.SHORT;
+    public static final int INTEGER = BasicSerializer.INTEGER;
+    public static final int LONG = BasicSerializer.LONG;
+    public static final int FLOAT = BasicSerializer.FLOAT;
+    public static final int DOUBLE = BasicSerializer.DOUBLE;
+    public static final int CHARACTER = BasicSerializer.CHARACTER;
+    public static final int CHARACTER_OBJECT = BasicSerializer.CHARACTER_OBJECT;
+    public static final int STRING = BasicSerializer.STRING;
+    public static final int DATE = BasicSerializer.DATE;
+    public static final int NUMBER = BasicSerializer.NUMBER;
+    public static final int OBJECT = BasicSerializer.OBJECT;
+
+    public static final int BOOLEAN_ARRAY = BasicSerializer.BOOLEAN_ARRAY;
+    public static final int BYTE_ARRAY = BasicSerializer.BYTE_ARRAY;
+    public static final int SHORT_ARRAY = BasicSerializer.SHORT_ARRAY;
+    public static final int INTEGER_ARRAY = BasicSerializer.INTEGER_ARRAY;
+    public static final int LONG_ARRAY = BasicSerializer.LONG_ARRAY;
+    public static final int FLOAT_ARRAY = BasicSerializer.FLOAT_ARRAY;
+    public static final int DOUBLE_ARRAY = BasicSerializer.DOUBLE_ARRAY;
+    public static final int CHARACTER_ARRAY = BasicSerializer.CHARACTER_ARRAY;
+    public static final int STRING_ARRAY = BasicSerializer.STRING_ARRAY;
+    public static final int OBJECT_ARRAY = BasicSerializer.OBJECT_ARRAY;
+
+    private int _code;
+
+    public BasicDeserializer(int code) {
+        _code = code;
+    }
+
+    @Override
+    public Class getType() {
+        switch (_code) {
+            case NULL:
+                return void.class;
+            case BOOLEAN:
+                return Boolean.class;
+            case BYTE:
+                return Byte.class;
+            case SHORT:
+                return Short.class;
+            case INTEGER:
+                return Integer.class;
+            case LONG:
+                return Long.class;
+            case FLOAT:
+                return Float.class;
+            case DOUBLE:
+                return Double.class;
+            case CHARACTER:
+                return Character.class;
+            case CHARACTER_OBJECT:
+                return Character.class;
+            case STRING:
+                return String.class;
+            case DATE:
+                return Date.class;
+            case NUMBER:
+                return Number.class;
+            case OBJECT:
+                return Object.class;
+
+            case BOOLEAN_ARRAY:
+                return boolean[].class;
+            case BYTE_ARRAY:
+                return byte[].class;
+            case SHORT_ARRAY:
+                return short[].class;
+            case INTEGER_ARRAY:
+                return int[].class;
+            case LONG_ARRAY:
+                return long[].class;
+            case FLOAT_ARRAY:
+                return float[].class;
+            case DOUBLE_ARRAY:
+                return double[].class;
+            case CHARACTER_ARRAY:
+                return char[].class;
+            case STRING_ARRAY:
+                return String[].class;
+            case OBJECT_ARRAY:
+                return Object[].class;
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in)
+            throws IOException {
+        if (in.checkAndReadNull()) {
+            return null;
+        }
+        switch (_code) {
+            case NULL:
+                // hessian/3490
+                in.readObject();
+
+                return null;
+
+            case BOOLEAN:
+                return Boolean.valueOf(in.readBoolean());
+
+            case BYTE:
+                return Byte.valueOf((byte) in.readInt());
+
+            case SHORT:
+                return Short.valueOf((short) in.readInt());
+
+            case INTEGER:
+                return Integer.valueOf(in.readInt());
+
+            case LONG:
+                return Long.valueOf(in.readLong());
+
+            case FLOAT:
+                return Float.valueOf((float) in.readDouble());
+
+            case DOUBLE:
+                return Double.valueOf(in.readDouble());
+
+            case STRING:
+                return in.readString();
+
+            case OBJECT:
+                return in.readObject();
+
+            case CHARACTER: {
+                String s = in.readString();
+                if (s == null || s.equals(""))
+                    return Character.valueOf((char) 0);
+                else
+                    return Character.valueOf(s.charAt(0));
+            }
+
+            case CHARACTER_OBJECT: {
+                String s = in.readString();
+                if (s == null || s.equals(""))
+                    return null;
+                else
+                    return Character.valueOf(s.charAt(0));
+            }
+
+            case DATE:
+                return new Date(in.readUTCDate());
+
+            case NUMBER:
+                return in.readObject();
+
+            case BYTE_ARRAY:
+                return in.readBytes();
+
+            case CHARACTER_ARRAY: {
+                String s = in.readString();
+
+                if (s == null)
+                    return null;
+                else {
+                    int len = s.length();
+                    char[] chars = new char[len];
+                    s.getChars(0, len, chars, 0);
+                    return chars;
+                }
+            }
+
+            case BOOLEAN_ARRAY:
+            case SHORT_ARRAY:
+            case INTEGER_ARRAY:
+            case LONG_ARRAY:
+            case FLOAT_ARRAY:
+            case DOUBLE_ARRAY:
+            case STRING_ARRAY: {
+                int code = in.readListStart();
+
+                switch (code) {
+                    case 'N':
+                        return null;
+
+                    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:
+                        int length = code - 0x10;
+                        in.readInt();
+
+                        return readLengthList(in, length);
+
+                    default:
+                        String type = in.readType();
+                        length = in.readLength();
+
+                        return readList(in, length);
+                }
+            }
+
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length)
+            throws IOException {
+        switch (_code) {
+            case BOOLEAN_ARRAY: {
+                if (length >= 0) {
+                    boolean[] data = new boolean[length];
+
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = in.readBoolean();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(Boolean.valueOf(in.readBoolean()));
+
+                    in.readEnd();
+
+                    boolean[] data = new boolean[list.size()];
+
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = ((Boolean) list.get(i)).booleanValue();
+
+                    return data;
+                }
+            }
+
+            case SHORT_ARRAY: {
+                if (length >= 0) {
+                    short[] data = new short[length];
+
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = (short) in.readInt();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(Short.valueOf((short) in.readInt()));
+
+                    in.readEnd();
+
+                    short[] data = new short[list.size()];
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = ((Short) list.get(i)).shortValue();
+
+                    in.addRef(data);
+
+                    return data;
+                }
+            }
+
+            case INTEGER_ARRAY: {
+                if (length >= 0) {
+                    int[] data = new int[length];
+
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = in.readInt();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(Integer.valueOf(in.readInt()));
+
+
+                    in.readEnd();
+
+                    int[] data = new int[list.size()];
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = ((Integer) list.get(i)).intValue();
+
+                    in.addRef(data);
+
+                    return data;
+                }
+            }
+
+            case LONG_ARRAY: {
+                if (length >= 0) {
+                    long[] data = new long[length];
+
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = in.readLong();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(Long.valueOf(in.readLong()));
+
+                    in.readEnd();
+
+                    long[] data = new long[list.size()];
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = ((Long) list.get(i)).longValue();
+
+                    in.addRef(data);
+
+                    return data;
+                }
+            }
+
+            case FLOAT_ARRAY: {
+                if (length >= 0) {
+                    float[] data = new float[length];
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = (float) in.readDouble();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(new Float(in.readDouble()));
+
+                    in.readEnd();
+
+                    float[] data = new float[list.size()];
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = ((Float) list.get(i)).floatValue();
+
+                    in.addRef(data);
+
+                    return data;
+                }
+            }
+
+            case DOUBLE_ARRAY: {
+                if (length >= 0) {
+                    double[] data = new double[length];
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = in.readDouble();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(new Double(in.readDouble()));
+
+                    in.readEnd();
+
+                    double[] data = new double[list.size()];
+                    in.addRef(data);
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = ((Double) list.get(i)).doubleValue();
+
+                    return data;
+                }
+            }
+
+            case STRING_ARRAY: {
+                if (length >= 0) {
+                    String[] data = new String[length];
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = in.readString();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    while (!in.isEnd())
+                        list.add(in.readString());
+
+                    in.readEnd();
+
+                    String[] data = new String[list.size()];
+                    in.addRef(data);
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = (String) list.get(i);
+
+                    return data;
+                }
+            }
+
+            case OBJECT_ARRAY: {
+                if (length >= 0) {
+                    Object[] data = new Object[length];
+                    in.addRef(data);
+
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = in.readObject();
+
+                    in.readEnd();
+
+                    return data;
+                } else {
+                    ArrayList list = new ArrayList();
+
+                    in.addRef(list); // XXX: potential issues here
+
+                    while (!in.isEnd())
+                        list.add(in.readObject());
+
+                    in.readEnd();
+
+                    Object[] data = new Object[list.size()];
+                    for (int i = 0; i < data.length; i++)
+                        data[i] = (Object) list.get(i);
+
+                    return data;
+                }
+            }
+
+            default:
+                throw new UnsupportedOperationException(String.valueOf(this));
+        }
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length)
+            throws IOException {
+        switch (_code) {
+            case BOOLEAN_ARRAY: {
+                boolean[] data = new boolean[length];
+
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readBoolean();
+
+                return data;
+            }
+
+            case SHORT_ARRAY: {
+                short[] data = new short[length];
+
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = (short) in.readInt();
+
+                return data;
+            }
+
+            case INTEGER_ARRAY: {
+                int[] data = new int[length];
+
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readInt();
+
+                return data;
+            }
+
+            case LONG_ARRAY: {
+                long[] data = new long[length];
+
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readLong();
+
+                return data;
+            }
+
+            case FLOAT_ARRAY: {
+                float[] data = new float[length];
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = (float) in.readDouble();
+
+                return data;
+            }
+
+            case DOUBLE_ARRAY: {
+                double[] data = new double[length];
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readDouble();
+
+                return data;
+            }
+
+            case STRING_ARRAY: {
+                String[] data = new String[length];
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readString();
+
+                return data;
+            }
+
+            case OBJECT_ARRAY: {
+                Object[] data = new Object[length];
+                in.addRef(data);
+
+                for (int i = 0; i < data.length; i++)
+                    data[i] = in.readObject();
+
+                return data;
+            }
+
+            default:
+                throw new UnsupportedOperationException(String.valueOf(this));
+        }
+    }
+
+    public String toString()
+    {
+        return getClass().getSimpleName() + "[" + _code + "]";
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/BasicSerializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BasicSerializer.java
new file mode 100644
index 0000000..d9a68ff
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BasicSerializer.java
@@ -0,0 +1,276 @@
+/*
+ * 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 "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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class BasicSerializer extends AbstractSerializer {
+    public static final int NULL = 0;
+    public static final int BOOLEAN = NULL + 1;
+    public static final int BYTE = BOOLEAN + 1;
+    public static final int SHORT = BYTE + 1;
+    public static final int INTEGER = SHORT + 1;
+    public static final int LONG = INTEGER + 1;
+    public static final int FLOAT = LONG + 1;
+    public static final int DOUBLE = FLOAT + 1;
+    public static final int CHARACTER = DOUBLE + 1;
+    public static final int CHARACTER_OBJECT = CHARACTER + 1;
+    public static final int STRING = CHARACTER_OBJECT + 1;
+    public static final int DATE = STRING + 1;
+    public static final int NUMBER = DATE + 1;
+    public static final int OBJECT = NUMBER + 1;
+
+    public static final int BOOLEAN_ARRAY = OBJECT + 1;
+    public static final int BYTE_ARRAY = BOOLEAN_ARRAY + 1;
+    public static final int SHORT_ARRAY = BYTE_ARRAY + 1;
+    public static final int INTEGER_ARRAY = SHORT_ARRAY + 1;
+    public static final int LONG_ARRAY = INTEGER_ARRAY + 1;
+    public static final int FLOAT_ARRAY = LONG_ARRAY + 1;
+    public static final int DOUBLE_ARRAY = FLOAT_ARRAY + 1;
+    public static final int CHARACTER_ARRAY = DOUBLE_ARRAY + 1;
+    public static final int STRING_ARRAY = CHARACTER_ARRAY + 1;
+    public static final int OBJECT_ARRAY = STRING_ARRAY + 1;
+
+    private int code;
+
+    public BasicSerializer(int code) {
+        this.code = code;
+    }
+
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        switch (code) {
+            case BOOLEAN:
+                out.writeBoolean(((Boolean) obj).booleanValue());
+                break;
+
+            case BYTE:
+            case SHORT:
+            case INTEGER:
+                out.writeInt(((Number) obj).intValue());
+                break;
+
+            case LONG:
+                out.writeLong(((Number) obj).longValue());
+                break;
+
+            case FLOAT:
+                out.writeDouble(Double.parseDouble(String.valueOf(((Number) obj).floatValue())));
+                break;
+            case DOUBLE:
+                out.writeDouble(((Number) obj).doubleValue());
+                break;
+
+            case CHARACTER:
+            case CHARACTER_OBJECT:
+                out.writeString(String.valueOf(obj));
+                break;
+
+            case STRING:
+                out.writeString((String) obj);
+                break;
+
+            case DATE:
+                out.writeUTCDate(((Date) obj).getTime());
+                break;
+
+            case BOOLEAN_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                boolean[] data = (boolean[]) obj;
+                boolean hasEnd = out.writeListBegin(data.length, "[boolean");
+                for (int i = 0; i < data.length; i++)
+                    out.writeBoolean(data[i]);
+
+                if (hasEnd)
+                    out.writeListEnd();
+
+                break;
+            }
+
+            case BYTE_ARRAY: {
+                byte[] data = (byte[]) obj;
+                out.writeBytes(data, 0, data.length);
+                break;
+            }
+
+            case SHORT_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                short[] data = (short[]) obj;
+                boolean hasEnd = out.writeListBegin(data.length, "[short");
+
+                for (int i = 0; i < data.length; i++)
+                    out.writeInt(data[i]);
+
+                if (hasEnd)
+                    out.writeListEnd();
+                break;
+            }
+
+            case INTEGER_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                int[] data = (int[]) obj;
+
+                boolean hasEnd = out.writeListBegin(data.length, "[int");
+
+                for (int i = 0; i < data.length; i++)
+                    out.writeInt(data[i]);
+
+                if (hasEnd)
+                    out.writeListEnd();
+
+                break;
+            }
+
+            case LONG_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                long[] data = (long[]) obj;
+
+                boolean hasEnd = out.writeListBegin(data.length, "[long");
+
+                for (int i = 0; i < data.length; i++)
+                    out.writeLong(data[i]);
+
+                if (hasEnd)
+                    out.writeListEnd();
+                break;
+            }
+
+            case FLOAT_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                float[] data = (float[]) obj;
+
+                boolean hasEnd = out.writeListBegin(data.length, "[float");
+
+                for (int i = 0; i < data.length; i++)
+                    out.writeDouble(data[i]);
+
+                if (hasEnd)
+                    out.writeListEnd();
+                break;
+            }
+
+            case DOUBLE_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                double[] data = (double[]) obj;
+                boolean hasEnd = out.writeListBegin(data.length, "[double");
+
+                for (int i = 0; i < data.length; i++)
+                    out.writeDouble(data[i]);
+
+                if (hasEnd)
+                    out.writeListEnd();
+                break;
+            }
+
+            case STRING_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                String[] data = (String[]) obj;
+
+                boolean hasEnd = out.writeListBegin(data.length, "[string");
+
+                for (int i = 0; i < data.length; i++) {
+                    out.writeString(data[i]);
+                }
+
+                if (hasEnd)
+                    out.writeListEnd();
+                break;
+            }
+
+            case CHARACTER_ARRAY: {
+                char[] data = (char[]) obj;
+                out.writeString(data, 0, data.length);
+                break;
+            }
+
+            case OBJECT_ARRAY: {
+                if (out.addRef(obj))
+                    return;
+
+                Object[] data = (Object[]) obj;
+
+                boolean hasEnd = out.writeListBegin(data.length, "[object");
+
+                for (int i = 0; i < data.length; i++) {
+                    out.writeObject(data[i]);
+                }
+
+                if (hasEnd)
+                    out.writeListEnd();
+                break;
+            }
+
+            case NULL:
+                out.writeNull();
+                break;
+
+            default:
+                throw new RuntimeException(code + " " + String.valueOf(obj.getClass()));
+        }
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanDeserializer.java
new file mode 100644
index 0000000..db412cd
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanDeserializer.java
@@ -0,0 +1,286 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class BeanDeserializer extends AbstractMapDeserializer {
+    private Class _type;
+    private HashMap _methodMap;
+    private Method _readResolve;
+    private Constructor _constructor;
+    private Object[] _constructorArgs;
+
+    public BeanDeserializer(Class cl) {
+        _type = cl;
+        _methodMap = getMethodMap(cl);
+
+        _readResolve = getReadResolve(cl);
+
+        Constructor[] constructors = cl.getConstructors();
+        int bestLength = Integer.MAX_VALUE;
+
+        for (int i = 0; i < constructors.length; i++) {
+            if (constructors[i].getParameterTypes().length < bestLength) {
+                _constructor = constructors[i];
+                bestLength = _constructor.getParameterTypes().length;
+            }
+        }
+
+        if (_constructor != null) {
+            _constructor.setAccessible(true);
+            Class[] params = _constructor.getParameterTypes();
+            _constructorArgs = new Object[params.length];
+            for (int i = 0; i < params.length; i++) {
+                _constructorArgs[i] = getParamArg(params[i]);
+            }
+        }
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    protected static Object getParamArg(Class cl) {
+        if (!cl.isPrimitive())
+            return null;
+        else if (boolean.class.equals(cl))
+            return Boolean.FALSE;
+        else if (byte.class.equals(cl))
+            return Byte.valueOf((byte) 0);
+        else if (short.class.equals(cl))
+            return Short.valueOf((short) 0);
+        else if (char.class.equals(cl))
+            return Character.valueOf((char) 0);
+        else if (int.class.equals(cl))
+            return Integer.valueOf(0);
+        else if (long.class.equals(cl))
+            return Long.valueOf(0);
+        else if (float.class.equals(cl))
+            return Double.valueOf(0);
+        else if (double.class.equals(cl))
+            return Double.valueOf(0);
+        else
+            throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Class getType() {
+        return _type;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readMap(in, obj);
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+
+    public Object readMap(AbstractHessianInput in, Object obj)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            while (!in.isEnd()) {
+                Object key = in.readObject();
+
+                Method method = (Method) _methodMap.get(key);
+
+                if (method != null) {
+                    Object value = in.readObject(method.getParameterTypes()[0]);
+
+                    method.invoke(obj, new Object[]{value});
+                } else {
+                    Object value = in.readObject();
+                }
+            }
+
+            in.readMapEnd();
+
+            Object resolve = resolve(obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+
+    private Object resolve(Object obj) {
+        // if there's a readResolve method, call it
+        try {
+            if (_readResolve != null)
+                return _readResolve.invoke(obj, new Object[0]);
+        } catch (Exception e) {
+        }
+
+        return obj;
+    }
+
+    protected Object instantiate()
+            throws Exception {
+        return _constructor.newInstance(_constructorArgs);
+    }
+
+    /**
+     * Returns the readResolve method
+     */
+    protected Method getReadResolve(Class cl) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Method[] methods = cl.getDeclaredMethods();
+
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+
+                if (method.getName().equals("readResolve") &&
+                        method.getParameterTypes().length == 0)
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    protected HashMap getMethodMap(Class cl) {
+        HashMap methodMap = new HashMap();
+
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Method[] methods = cl.getDeclaredMethods();
+
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+
+                if (Modifier.isStatic(method.getModifiers()))
+                    continue;
+
+                String name = method.getName();
+
+                if (!name.startsWith("set"))
+                    continue;
+
+                Class[] paramTypes = method.getParameterTypes();
+                if (paramTypes.length != 1)
+                    continue;
+
+                if (!method.getReturnType().equals(void.class))
+                    continue;
+
+                if (findGetter(methods, name, paramTypes[0]) == null)
+                    continue;
+
+                // XXX: could parameterize the handler to only deal with public
+                try {
+                    method.setAccessible(true);
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+
+                name = name.substring(3);
+
+                int j = 0;
+                for (; j < name.length() && Character.isUpperCase(name.charAt(j)); j++) {
+                }
+
+                if (j == 1)
+                    name = name.substring(0, j).toLowerCase() + name.substring(j);
+                else if (j > 1)
+                    name = name.substring(0, j - 1).toLowerCase() + name.substring(j - 1);
+
+
+                methodMap.put(name, method);
+            }
+        }
+
+        return methodMap;
+    }
+
+    /**
+     * Finds any matching setter.
+     */
+    private Method findGetter(Method[] methods, String setterName, Class arg) {
+        String getterName = "get" + setterName.substring(3);
+
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+
+            if (!method.getName().equals(getterName))
+                continue;
+
+            if (!method.getReturnType().equals(arg))
+                continue;
+
+            Class[] params = method.getParameterTypes();
+
+            if (params.length == 0)
+                return method;
+        }
+
+        return null;
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanSerializer.java
similarity index 99%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializer.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/BeanSerializer.java
index a3a8ba5..6da2f1e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializer.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanSerializer.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.lang.reflect.Method;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanSerializerFactory.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/BeanSerializerFactory.java
index 7c8e328..b0f9d2f 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BeanSerializerFactory.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 /**
  * Factory for returning serialization methods.
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BigIntegerDeserializer.java
similarity index 95%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/BigIntegerDeserializer.java
index 5cd0498..4592621 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BigIntegerDeserializer.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BigIntegerDeserializer.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.math.BigInteger;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BitSetHandle.java
similarity index 81%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/BitSetHandle.java
index 72a9822..92665c6 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BitSetHandle.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,20 +46,25 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
-import java.io.IOException;
+import java.util.BitSet;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Handle for a BitSet object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+public class BitSetHandle implements java.io.Serializable, HessianHandle {
+    private long[] words;
+
+    public BitSetHandle(long[] words) {
+        this.words = words;
+    }
+
+    private Object readResolve() {
+        if (words == null) {
+            return null;
+        }
+
+        return BitSet.valueOf(words);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/BitSetSerializer.java
similarity index 79%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/BitSetSerializer.java
index 72a9822..5754128 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/BitSetSerializer.java
@@ -46,20 +46,30 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.BitSet;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a BitSet.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class BitSetSerializer extends AbstractSerializer {
+    private static BitSetSerializer SERIALIZER = new BitSetSerializer();
+
+    public static BitSetSerializer create() {
+        return SERIALIZER;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            BitSet bitSet = (BitSet) obj;
+
+            out.writeObject(new BitSetHandle(bitSet.toLongArray()));
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/CalendarHandle.java
similarity index 69%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/CalendarHandle.java
index 72a9822..42f4347 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/CalendarHandle.java
@@ -46,20 +46,45 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
-import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Handle for a calendar object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+public class CalendarHandle implements java.io.Serializable, HessianHandle {
+    private Class type;
+    private Date date;
+
+    public CalendarHandle() {
+    }
+
+    public CalendarHandle(Class type, long time) {
+        if (!GregorianCalendar.class.equals(type))
+            this.type = type;
+
+        this.date = new Date(time);
+    }
+
+    private Object readResolve() {
+        try {
+            Calendar cal;
+
+            if (this.type != null)
+                cal = (Calendar) this.type.newInstance();
+            else
+                cal = new GregorianCalendar();
+
+            cal.setTimeInMillis(this.date.getTime());
+
+            return cal;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/CalendarSerializer.java
similarity index 78%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/CalendarSerializer.java
index 72a9822..b8e4187 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/CalendarSerializer.java
@@ -46,20 +46,31 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.Calendar;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a calendar.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class CalendarSerializer extends AbstractSerializer {
+    private static CalendarSerializer SERIALIZER = new CalendarSerializer();
+
+    public static CalendarSerializer create() {
+        return SERIALIZER;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            Calendar cal = (Calendar) obj;
+
+            out.writeObject(new CalendarHandle(cal.getClass(),
+                    cal.getTimeInMillis()));
+        }
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassDeserializer.java
new file mode 100644
index 0000000..9c5d151
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassDeserializer.java
@@ -0,0 +1,158 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Deserializing a JDK 1.2 Class.
+ */
+public class ClassDeserializer extends AbstractMapDeserializer {
+    private static final HashMap<String, Class> _primClasses
+            = new HashMap<String, Class>();
+
+    static {
+        _primClasses.put("void", void.class);
+        _primClasses.put("boolean", boolean.class);
+        _primClasses.put("java.lang.Boolean", Boolean.class);
+        _primClasses.put("byte", byte.class);
+        _primClasses.put("java.lang.Byte", Byte.class);
+        _primClasses.put("char", char.class);
+        _primClasses.put("java.lang.Character", Character.class);
+        _primClasses.put("short", short.class);
+        _primClasses.put("java.lang.Short", Short.class);
+        _primClasses.put("int", int.class);
+        _primClasses.put("java.lang.Integer", Integer.class);
+        _primClasses.put("long", long.class);
+        _primClasses.put("java.lang.Long", Long.class);
+        _primClasses.put("float", float.class);
+        _primClasses.put("java.lang.Float", Float.class);
+        _primClasses.put("double", double.class);
+        _primClasses.put("java.lang.Double", Double.class);
+        _primClasses.put("java.lang.String", String.class);
+    }
+
+    private ClassLoader _loader;
+
+    public ClassDeserializer(ClassLoader loader) {
+        _loader = loader;
+    }
+
+    @Override
+    public Class getType() {
+        return Class.class;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        int ref = in.addRef(null);
+
+        String name = null;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("name"))
+                name = in.readString();
+            else
+                in.readObject();
+        }
+
+        in.readMapEnd();
+
+        Object value = create(name);
+
+        in.setRef(ref, value);
+
+        return value;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        int ref = in.addRef(null);
+
+        String name = null;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            if ("name".equals(fieldNames[i]))
+                name = in.readString();
+            else
+                in.readObject();
+        }
+
+        Object value = create(name);
+
+        in.setRef(ref, value);
+
+        return value;
+    }
+
+    Object create(String name)
+            throws IOException {
+        if (name == null)
+            throw new IOException("Serialized Class expects name.");
+
+        Class cl = _primClasses.get(name);
+
+        if (cl != null)
+            return cl;
+
+        try {
+            if (_loader != null)
+                return Class.forName(name, false, _loader);
+            else
+                return Class.forName(name);
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassFactory.java
new file mode 100644
index 0000000..e63863c
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassFactory.java
@@ -0,0 +1,299 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+/**
+ * Loads a class from the classloader.
+ */
+public class ClassFactory
+{
+    protected static final Logger log
+            = Logger.getLogger(ClassFactory.class.getName());
+    private static final ArrayList<Allow> _staticAllowList;
+    private static final Map<String, Object> _allowSubClassSet = new ConcurrentHashMap<>();
+    private static final Map<String, Object> _allowClassSet = new ConcurrentHashMap<>();
+
+    private ClassLoader _loader;
+    private boolean _isWhitelist;
+
+    private LinkedList<Allow> _allowList;
+
+    ClassFactory(ClassLoader loader)
+    {
+        _loader = loader;
+        initAllow();
+    }
+
+    public Class<?> load(String className)
+            throws ClassNotFoundException
+    {
+        if (isAllow(className)) {
+            Class<?> aClass = Class.forName(className, false, _loader);
+
+            if (_allowClassSet.containsKey(className)) {
+                return aClass;
+            }
+
+            if (aClass.getInterfaces().length > 0) {
+                for (Class<?> anInterface : aClass.getInterfaces()) {
+                    if(!isAllow(anInterface.getName())) {
+                        log.log(Level.SEVERE, className + "'s interfaces: " + anInterface.getName() + " in blacklist or not in whitelist, deserialization with type 'HashMap' instead.");
+                        return HashMap.class;
+                    }
+                }
+            }
+
+            List<Class<?>> allSuperClasses = new LinkedList<>();
+
+            Class<?> superClass = aClass.getSuperclass();
+            while (superClass != null) {
+                // add current super class
+                allSuperClasses.add(superClass);
+                superClass = superClass.getSuperclass();
+            }
+
+            for (Class<?> aSuperClass : allSuperClasses) {
+                if(!isAllow(aSuperClass.getName())) {
+                    log.log(Level.SEVERE, className + "'s superClass: " + aSuperClass.getName() + " in blacklist or not in whitelist, deserialization with type 'HashMap' instead.");
+                    return HashMap.class;
+                }
+
+            }
+
+            _allowClassSet.put(className, className);
+            return aClass;
+        }
+        else {
+            log.log(Level.SEVERE, className + " in blacklist or not in whitelist, deserialization with type 'HashMap' instead.");
+            return HashMap.class;
+        }
+    }
+
+    private boolean isAllow(String className)
+    {
+        LinkedList<Allow> allowList = _allowList;
+
+        if (allowList == null) {
+            return true;
+        }
+
+        if (_allowSubClassSet.containsKey(className)) {
+            return true;
+        }
+
+        for (Allow allow : allowList) {
+            Boolean isAllow = allow.allow(className);
+
+            if (isAllow != null) {
+                if (isAllow) {
+                    _allowSubClassSet.put(className, className);
+                }
+                return isAllow;
+            }
+        }
+
+        if (_isWhitelist) {
+            return false;
+        }
+
+        _allowSubClassSet.put(className, className);
+        return true;
+    }
+
+    public void setWhitelist(boolean isWhitelist)
+    {
+        _allowClassSet.clear();
+        _allowSubClassSet.clear();
+        _isWhitelist = isWhitelist;
+
+        initAllow();
+    }
+
+    public void allow(String pattern)
+    {
+        _allowClassSet.clear();
+        _allowSubClassSet.clear();
+        initAllow();
+
+        synchronized (this) {
+            _allowList.addFirst(new Allow(toPattern(pattern), true));
+        }
+    }
+
+    public void deny(String pattern)
+    {
+        _allowClassSet.clear();
+        _allowSubClassSet.clear();
+        initAllow();
+
+        synchronized (this) {
+            _allowList.addFirst(new Allow(toPattern(pattern), false));
+        }
+    }
+
+    private static String toPattern(String pattern)
+    {
+        pattern = pattern.replace(".", "\\.");
+        pattern = pattern.replace("*", ".*");
+
+        return pattern;
+    }
+
+    private void initAllow()
+    {
+        synchronized (this) {
+            if (_allowList == null) {
+                _allowList = new LinkedList<Allow>();
+                _allowList.addAll(_staticAllowList);
+            }
+        }
+    }
+
+    static class Allow {
+        private Boolean _isAllow;
+        private Pattern _pattern;
+
+        public Allow() {
+        }
+
+        private Allow(String pattern, boolean isAllow)
+        {
+            _isAllow = isAllow;
+            _pattern = Pattern.compile(pattern);
+        }
+
+        Boolean allow(String className)
+        {
+            if (_pattern.matcher(className).matches()) {
+                return _isAllow;
+            }
+            else {
+                return null;
+            }
+        }
+    }
+
+    static class AllowPrefix extends Allow {
+        private Boolean _isAllow;
+        private String _prefix;
+
+        private AllowPrefix(String prefix, boolean isAllow)
+        {
+            super();
+            _isAllow = isAllow;
+            _prefix = prefix;
+        }
+
+        @Override
+        Boolean allow(String className)
+        {
+            if (className.startsWith(_prefix)) {
+                return _isAllow;
+            }
+            else {
+                return null;
+            }
+        }
+    }
+
+    static {
+        _staticAllowList = new ArrayList<Allow>();
+
+        ClassLoader classLoader = ClassFactory.class.getClassLoader();
+        try {
+            String[] denyClasses = readLines(classLoader.getResourceAsStream("DENY_CLASS"));
+            for (String denyClass : denyClasses) {
+                if (denyClass.startsWith("#")) {
+                    continue;
+                }
+                if (denyClass.endsWith(".")) {
+                    _staticAllowList.add(new AllowPrefix(denyClass, false));
+                } else {
+                    _staticAllowList.add(new Allow(toPattern(denyClass), false));
+                }
+            }
+        } catch (IOException ignore) {
+
+        }
+    }
+
+    /**
+     * read lines.
+     *
+     * @param is input stream.
+     * @return lines.
+     * @throws IOException If an I/O error occurs
+     */
+    public static String[] readLines(InputStream is) throws IOException {
+        List<String> lines = new ArrayList<String>();
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                lines.add(line);
+            }
+            return lines.toArray(new String[0]);
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassSerializer.java
similarity index 72%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/ClassSerializer.java
index 72a9822..0c0d10f 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ClassSerializer.java
@@ -46,20 +46,39 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a remote object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class ClassSerializer extends AbstractSerializer {
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        Class cl = (Class) obj;
+
+        if (cl == null) {
+            out.writeNull();
+        } else if (out.addRef(obj)) {
+            return;
+        } else {
+            int ref = out.writeObjectBegin("java.lang.Class");
+
+            if (ref < -1) {
+                out.writeString("name");
+                out.writeString(cl.getName());
+                out.writeMapEnd();
+            } else {
+                if (ref == -1) {
+                    out.writeInt(1);
+                    out.writeString("name");
+                    out.writeObjectBegin("java.lang.Class");
+                }
+
+                out.writeString(cl.getName());
+            }
+        }
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/CollectionDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/CollectionDeserializer.java
new file mode 100644
index 0000000..b652528
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/CollectionDeserializer.java
@@ -0,0 +1,170 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Deserializing a JDK 1.2 Collection.
+ */
+public class CollectionDeserializer extends AbstractListDeserializer {
+    private Class _type;
+
+    public CollectionDeserializer(Class type) {
+        _type = type;
+    }
+
+    @Override
+    public Class getType() {
+        return _type;
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length)
+            throws IOException {
+        return readList(in, length, _type);
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
+        Collection list = createList();
+
+        in.addRef(list);
+
+        Deserializer deserializer = null;
+
+        SerializerFactory factory = findSerializerFactory(in);
+        if (expectType != null) {
+            deserializer = factory.getDeserializer(expectType.getName());
+        }
+
+        while (!in.isEnd()) {
+            list.add(deserializer != null ? deserializer.readObject(in) : in.readObject());
+        }
+
+
+        in.readEnd();
+
+        return list;
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length)
+            throws IOException {
+        return readList(in, length, null);
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length, Class<?> expectType) throws IOException {
+        Collection list = createList();
+
+        in.addRef(list);
+
+        Deserializer deserializer = null;
+
+        SerializerFactory factory = findSerializerFactory(in);
+        if (expectType != null) {
+            deserializer = factory.getDeserializer(expectType.getName());
+        }
+
+        for (; length > 0; length--) {
+            list.add(deserializer != null ? deserializer.readObject(in) : in.readObject());
+        }
+
+
+        return list;
+    }
+
+    private Collection createList()
+            throws IOException {
+        Collection list = null;
+
+        if (_type == null)
+            list = new ArrayList();
+        else if (!_type.isInterface()) {
+            try {
+                list = (Collection) _type.newInstance();
+            } catch (Exception e) {
+            }
+        }
+
+        if (list != null) {
+        } else if (SortedSet.class.isAssignableFrom(_type))
+            list = new TreeSet();
+        else if (Set.class.isAssignableFrom(_type))
+            list = new HashSet();
+        else if (List.class.isAssignableFrom(_type))
+            list = new ArrayList();
+        else if (Collection.class.isAssignableFrom(_type))
+            list = new ArrayList();
+        else {
+            try {
+                list = (Collection) _type.newInstance();
+            } catch (Exception e) {
+                throw new IOExceptionWrapper(e);
+            }
+        }
+
+        return list;
+    }
+
+    public String toString()
+    {
+        return getClass().getSimpleName() + "[" + _type + "]";
+    }
+}
+
+
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/CollectionSerializer.java
similarity index 67%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/CollectionSerializer.java
index 72a9822..5740fd0 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/CollectionSerializer.java
@@ -46,20 +46,53 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a JDK 1.2 Collection.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class CollectionSerializer extends AbstractSerializer {
+    private boolean _sendJavaType = true;
+
     /**
-     * Looks up a proxy object.
+     * Return true if the java type of the collection should be sent.
      */
+    public boolean getSendJavaType() {
+        return _sendJavaType;
+    }
+
+    /**
+     * Set true if the java type of the collection should be sent.
+     */
+    public void setSendJavaType(boolean sendJavaType) {
+        _sendJavaType = sendJavaType;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (out.addRef(obj))
+            return;
+
+        Collection list = (Collection) obj;
+
+        Class cl = obj.getClass();
+        boolean hasEnd = out.writeListBegin(list.size(), obj.getClass().getName());
+
+        Iterator iter = list.iterator();
+        while (iter.hasNext()) {
+            Object value = iter.next();
+
+            out.writeObject(value);
+        }
+
+        if (hasEnd)
+            out.writeListEnd();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/Deflation.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Deflation.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/Deflation.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/Deflation.java
index 11380ad..8638d82 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/Deflation.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Deflation.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianEnvelope.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Deserializer.java
similarity index 61%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/HessianEnvelope.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/Deserializer.java
index 21bd3a7..84d60c8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianEnvelope.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Deserializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,33 +46,64 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Factory class for wrapping and unwrapping hessian streams.
+ * Deserializing an object.
+ *
  */
-abstract public class HessianEnvelope {
-    /**
-     * Wrap the Hessian output stream in an envelope.
-     */
-    abstract public Hessian2Output wrap(Hessian2Output out)
+public interface Deserializer {
+    public Class getType();
+
+    public Object readObject(AbstractHessianInput in)
+            throws IOException;
+
+    public Object readList(AbstractHessianInput in, int length)
             throws IOException;
 
     /**
-     * Unwrap the Hessian input stream with this envelope.  It is an
-     * error if the actual envelope does not match the expected envelope
-     * class.
+     * deserialize list object from expect type.
+     *
+     * @param in
+     * @param length
+     * @param expectType
+     * @return
+     * @throws IOException
      */
-    abstract public Hessian2Input unwrap(Hessian2Input in)
+    public Object readList(AbstractHessianInput in, int length, Class<?> expectType)
+            throws IOException;
+
+    public Object readLengthList(AbstractHessianInput in, int length)
             throws IOException;
 
     /**
-     * Unwrap the envelope after having read the envelope code ('E') and
-     * the envelope method.  Called by the EnvelopeFactory for dynamic
-     * reading of the envelopes.
+     * deserialize list object from expect type.
+     *
+     * @param in
+     * @param length
+     * @param expectType
+     * @return
+     * @throws IOException
      */
-    abstract public Hessian2Input unwrapHeaders(Hessian2Input in)
+    public Object readLengthList(AbstractHessianInput in, int length, Class<?> expectType)
+            throws IOException;
+
+    public Object readMap(AbstractHessianInput in)
+            throws IOException;
+
+    /**
+     *  deserialize map object from expect key and value type.
+     * @param in
+     * @param expectKeyType
+     * @param expectValueType
+     * @return
+     * @throws IOException
+     */
+    public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType)
+            throws IOException;
+
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
             throws IOException;
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumDeserializer.java
new file mode 100644
index 0000000..331d324
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumDeserializer.java
@@ -0,0 +1,136 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Deserializing an enum valued object
+ */
+public class EnumDeserializer extends AbstractDeserializer {
+    private Class _enumType;
+    private Method _valueOf;
+
+    public EnumDeserializer(Class cl) {
+        // hessian/33b[34], hessian/3bb[78]
+        if (cl.isEnum())
+            _enumType = cl;
+        else if (cl.getSuperclass().isEnum())
+            _enumType = cl.getSuperclass();
+        else
+            throw new RuntimeException("Class " + cl.getName() + " is not an enum");
+
+        try {
+            _valueOf = _enumType.getMethod("valueOf",
+                    new Class[]{Class.class, String.class});
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Class getType() {
+        return _enumType;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        String name = null;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("name"))
+                name = in.readString();
+            else
+                in.readObject();
+        }
+
+        in.readMapEnd();
+
+        Object obj = create(name);
+
+        in.addRef(obj);
+
+        return obj;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        String name = null;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            if ("name".equals(fieldNames[i]))
+                name = in.readString();
+            else
+                in.readObject();
+        }
+
+        Object obj = create(name);
+
+        in.addRef(obj);
+
+        return obj;
+    }
+
+    private Object create(String name)
+            throws IOException {
+        if (name == null)
+            throw new IOException(_enumType.getName() + " expects name.");
+
+        try {
+            return _valueOf.invoke(null, _enumType, name);
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSerializer.java
similarity index 60%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSerializer.java
index 7c8e328..23bf4a3 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSerializer.java
@@ -46,35 +46,61 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
 
 /**
- * Factory for returning serialization methods.
+ * Serializing an object for known object types.
  */
-public class BeanSerializerFactory extends SerializerFactory {
-    /**
-     * Returns the default serializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
-     */
-    @Override
-    protected Serializer getDefaultSerializer(Class cl) {
-        return new BeanSerializer(cl, getClassLoader());
+public class EnumSerializer extends AbstractSerializer {
+    private Method _name;
+
+    public EnumSerializer(Class cl) {
+        // hessian/32b[12], hessian/3ab[23]
+        if (!cl.isEnum() && cl.getSuperclass().isEnum())
+            cl = cl.getSuperclass();
+
+        try {
+            _name = cl.getMethod("name", new Class[0]);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
     }
 
-    /**
-     * Returns the default deserializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
-     */
     @Override
-    protected Deserializer getDefaultDeserializer(Class cl) {
-        return new BeanDeserializer(cl);
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        if (out.addRef(obj))
+            return;
+
+        Class cl = obj.getClass();
+
+        if (!cl.isEnum() && cl.getSuperclass().isEnum())
+            cl = cl.getSuperclass();
+
+        String name = null;
+        try {
+            name = (String) _name.invoke(obj, (Object[]) null);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        int ref = out.writeObjectBegin(cl.getName());
+
+        if (ref < -1) {
+            out.writeString("name");
+            out.writeString(name);
+            out.writeMapEnd();
+        } else {
+            if (ref == -1) {
+                out.writeClassFieldLength(1);
+                out.writeString("name");
+                out.writeObjectBegin(cl.getName());
+            }
+
+            out.writeString(name);
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSetHandler.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSetHandler.java
similarity index 96%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/EnumSetHandler.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSetHandler.java
index 3c0a623..4150ad1 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSetHandler.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSetHandler.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.Serializable;
 import java.util.Arrays;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSetSerializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSetSerializer.java
similarity index 97%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/EnumSetSerializer.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSetSerializer.java
index 9fa4535..b277c16 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnumSetSerializer.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumSetSerializer.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumerationDeserializer.java
similarity index 76%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/EnumerationDeserializer.java
index 72a9822..7841a72 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumerationDeserializer.java
@@ -46,20 +46,38 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.Vector;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing a JDK 1.2 Collection.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class EnumerationDeserializer extends AbstractListDeserializer {
+    private static EnumerationDeserializer _deserializer;
+
+    public static EnumerationDeserializer create() {
+        if (_deserializer == null)
+            _deserializer = new EnumerationDeserializer();
+
+        return _deserializer;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public Object readList(AbstractHessianInput in, int length)
             throws IOException {
-        return new HessianRemote(type, url);
+        Vector list = new Vector();
+
+        in.addRef(list);
+
+        while (!in.isEnd())
+            list.add(in.readObject());
+
+        in.readEnd();
+
+        return list.elements();
     }
 }
+
+
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumerationSerializer.java
similarity index 74%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/EnumerationSerializer.java
index 72a9822..040b126 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnumerationSerializer.java
@@ -46,20 +46,38 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.Enumeration;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a JDK 1.2 Enumeration.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class EnumerationSerializer extends AbstractSerializer {
+    private static EnumerationSerializer _serializer;
+
+    public static EnumerationSerializer create() {
+        if (_serializer == null)
+            _serializer = new EnumerationSerializer();
+
+        return _serializer;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        Enumeration iter = (Enumeration) obj;
+
+        boolean hasEnd = out.writeListBegin(-1, null);
+
+        while (iter.hasMoreElements()) {
+            Object value = iter.nextElement();
+
+            out.writeObject(value);
+        }
+
+        if (hasEnd)
+            out.writeListEnd();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnvelopeFactory.java
similarity index 97%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/EnvelopeFactory.java
index be505fb..764a7b3 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/EnvelopeFactory.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.util.logging.Logger;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/ExtSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ExtSerializerFactory.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/ExtSerializerFactory.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/ExtSerializerFactory.java
index 1b11a44..8b1168a 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/ExtSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ExtSerializerFactory.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.util.HashMap;
 
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Constants.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Constants.java
new file mode 100644
index 0000000..cdf39c3
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Constants.java
@@ -0,0 +1,145 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+public interface Hessian2Constants {
+    public static final int BC_BINARY = 'B'; // final chunk
+    public static final int BC_BINARY_CHUNK = 'A'; // non-final chunk
+    public static final int BC_BINARY_DIRECT = 0x20; // 1-byte length binary
+    public static final int BINARY_DIRECT_MAX = 0x0f;
+    public static final int BC_BINARY_SHORT = 0x34; // 2-byte length binary
+    public static final int BINARY_SHORT_MAX = 0x3ff; // 0-1023 binary
+
+    public static final int BC_CLASS_DEF = 'C'; // object/class definition
+
+    public static final int BC_DATE = 0x4a; // 64-bit millisecond UTC date
+    public static final int BC_DATE_MINUTE = 0x4b; // 32-bit minute UTC date
+
+    public static final int BC_DOUBLE = 'D'; // IEEE 64-bit double
+
+    public static final int BC_DOUBLE_ZERO = 0x5b;
+    public static final int BC_DOUBLE_ONE = 0x5c;
+    public static final int BC_DOUBLE_BYTE = 0x5d;
+    public static final int BC_DOUBLE_SHORT = 0x5e;
+    public static final int BC_DOUBLE_MILL = 0x5f;
+
+    public static final int BC_FALSE = 'F'; // boolean false
+
+    public static final int BC_INT = 'I'; // 32-bit int
+
+    public static final int INT_DIRECT_MIN = -0x10;
+    public static final int INT_DIRECT_MAX = 0x2f;
+    public static final int BC_INT_ZERO = 0x90;
+
+    public static final int INT_BYTE_MIN = -0x800;
+    public static final int INT_BYTE_MAX = 0x7ff;
+    public static final int BC_INT_BYTE_ZERO = 0xc8;
+
+    public static final int BC_END = 'Z';
+
+    public static final int INT_SHORT_MIN = -0x40000;
+    public static final int INT_SHORT_MAX = 0x3ffff;
+    public static final int BC_INT_SHORT_ZERO = 0xd4;
+
+    public static final int BC_LIST_VARIABLE = 0x55;
+    public static final int BC_LIST_FIXED = 'V';
+    public static final int BC_LIST_VARIABLE_UNTYPED = 0x57;
+    public static final int BC_LIST_FIXED_UNTYPED = 0x58;
+
+    public static final int BC_LIST_DIRECT = 0x70;
+    public static final int BC_LIST_DIRECT_UNTYPED = 0x78;
+    public static final int LIST_DIRECT_MAX = 0x7;
+
+    public static final int BC_LONG = 'L'; // 64-bit signed integer
+    public static final long LONG_DIRECT_MIN = -0x08;
+    public static final long LONG_DIRECT_MAX = 0x0f;
+    public static final int BC_LONG_ZERO = 0xe0;
+
+    public static final long LONG_BYTE_MIN = -0x800;
+    public static final long LONG_BYTE_MAX = 0x7ff;
+    public static final int BC_LONG_BYTE_ZERO = 0xf8;
+
+    public static final int LONG_SHORT_MIN = -0x40000;
+    public static final int LONG_SHORT_MAX = 0x3ffff;
+    public static final int BC_LONG_SHORT_ZERO = 0x3c;
+
+    public static final int BC_LONG_INT = 0x59;
+
+    public static final int BC_MAP = 'M';
+    public static final int BC_MAP_UNTYPED = 'H';
+
+    public static final int BC_NULL = 'N';
+
+    public static final int BC_OBJECT = 'O';
+    public static final int BC_OBJECT_DEF = 'C';
+
+    public static final int BC_OBJECT_DIRECT = 0x60;
+    public static final int OBJECT_DIRECT_MAX = 0x0f;
+
+    public static final int BC_REF = 0x51;
+
+    public static final int BC_STRING = 'S'; // final string
+    public static final int BC_STRING_CHUNK = 'R'; // non-final string
+
+    public static final int BC_STRING_DIRECT = 0x00;
+    public static final int STRING_DIRECT_MAX = 0x1f;
+    public static final int BC_STRING_SHORT = 0x30;
+    public static final int STRING_SHORT_MAX = 0x3ff;
+
+    public static final int BC_TRUE = 'T';
+
+    public static final int P_PACKET_CHUNK = 0x4f;
+    public static final int P_PACKET = 'P';
+
+    public static final int P_PACKET_DIRECT = 0x80;
+    public static final int PACKET_DIRECT_MAX = 0x7f;
+
+    public static final int P_PACKET_SHORT = 0x70;
+    public static final int PACKET_SHORT_MAX = 0xfff;
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Input.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Input.java
new file mode 100644
index 0000000..995ad9d
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Input.java
@@ -0,0 +1,3659 @@
+/*
+ * 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.hessian3.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 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;
+    }
+
+    @Override
+    public void init(InputStream is) {
+        _is = is;
+
+        reset();
+    }
+
+    public void reset() {
+        resetReferences();
+
+        if (_classDefs != null) {
+            _classDefs.clear();
+        }
+
+        if (_types != null) {
+            _types.clear();
+        }
+
+        _offset = 0;
+        _length = 0;
+    }
+
+    @Override
+    public boolean checkAndReadNull() {
+        try {
+            int tag = read();
+            if ('N' == tag) {
+                return true;
+            }
+            if (-1 != tag) {
+                _offset--;
+            }
+        } catch (IOException ignored) {
+        }
+        return false;
+    }
+
+    /**
+     * 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' && tag != 'P') {
+            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 BC_BINARY:
+            case BC_BINARY_CHUNK:
+            case 'b': //maybe it's a mistype of BC_BINARY_CHUNK
+                _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;
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+                _isLastChunk = true;
+                _chunkLength = (tag - 0x34) * 256 + read();
+                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 BC_BINARY_CHUNK:
+                    case 'b': //maybe it's a mistype of BC_BINARY_CHUNK
+                        _isLastChunk = false;
+
+                        _chunkLength = (read() << 8) + read();
+                        break;
+
+                    case BC_BINARY:
+                        _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);
+                }
+            }
+
+            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());
+                } 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) {
+            }
+        }
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Output.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Output.java
new file mode 100644
index 0000000..2bb575c
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2Output.java
@@ -0,0 +1,1558 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import com.alibaba.com.caucho.hessian3.util.IdentityIntMap;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+
+/**
+ * Output stream for Hessian 2 requests.
+ * <p>
+ * <p>Since HessianOutput does not depend on any classes other than
+ * in the JDK, it can be extracted independently into a smaller package.
+ * <p>
+ * <p>HessianOutput is unbuffered, so any client needs to provide
+ * its own buffering.
+ * <p>
+ * <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 {
+    public final static int SIZE = 4096;
+    private final byte[] _buffer = new byte[SIZE];
+    // the output stream/
+    protected OutputStream _os;
+    // map of references
+    private IdentityIntMap _refs = new IdentityIntMap();
+    private boolean _isCloseStreamOnClose;
+    // map of classes
+    private HashMap _classRefs;
+    // map of types
+    private HashMap _typeRefs;
+    private int _offset;
+
+    private boolean _isStreaming;
+
+    /**
+     * Creates a new Hessian output stream, initialized with an
+     * underlying output stream.
+     *
+     * @param os the underlying output stream.
+     */
+    public Hessian2Output(OutputStream os) {
+        _os = os;
+    }
+
+    @Override
+    public void init(OutputStream os) {
+        reset();
+
+        _os = os;
+    }
+
+    /**
+     * Resets all counters and references
+     */
+    public void reset() {
+        resetReferences();
+
+        if (_classRefs != null) {
+            _classRefs.clear();
+        }
+
+        if (_typeRefs != null) {
+            _typeRefs.clear();
+        }
+
+        _offset = 0;
+    }
+
+    public boolean isCloseStreamOnClose() {
+        return _isCloseStreamOnClose;
+    }
+
+    public void setCloseStreamOnClose(boolean isClose) {
+        _isCloseStreamOnClose = isClose;
+    }
+
+    /**
+     * Writes a complete method call.
+     */
+    @Override
+    public void call(String method, Object[] args)
+            throws IOException {
+        int length = args != null ? args.length : 0;
+
+        startCall(method, length);
+
+        for (int i = 0; i < args.length; i++)
+            writeObject(args[i]);
+
+        completeCall();
+    }
+
+    /**
+     * 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.
+     * <p>
+     * <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) {
+            flush();
+            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
+     * <p>
+     * <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.
+     * <p>
+     * <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) {
+            flush();
+            offset = _offset;
+        }
+
+        _buffer[_offset++] = (byte) 'E';
+
+        writeString(method);
+    }
+
+    /**
+     * Completes an envelope.
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <pre>
+     * Z
+     * </pre>
+     */
+    public void completeEnvelope()
+            throws IOException {
+        flushIfFull();
+
+        _buffer[_offset++] = (byte) 'Z';
+    }
+
+    /**
+     * Writes the method tag.
+     * <p>
+     * <code><pre>
+     * string
+     * </pre></code>
+     *
+     * @param method the method name to call.
+     */
+    @Override
+    public void writeMethod(String method)
+            throws IOException {
+        writeString(method);
+    }
+
+    /**
+     * Completes.
+     * <p>
+     * <code><pre>
+     * z
+     * </pre></code>
+     */
+    @Override
+    public void completeCall()
+            throws IOException {
+    /*
+    flushIfFull();
+    
+    _buffer[_offset++] = (byte) 'Z';
+    */
+    }
+
+    /**
+     * Starts the reply
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <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
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <pre>
+     * z
+     * </pre>
+     */
+    @Override
+    public void completeReply()
+            throws IOException {
+    }
+
+    /**
+     * Starts a packet
+     * <p>
+     * <p>A message contains several objects encapsulated by a length</p>
+     * <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
+     * <p>
+     * <p>A successful completion will have a single value:
+     * <p>
+     * <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:
+     * <p>
+     * <code><pre>
+     * F map
+     * </pre></code>
+     * <p>
+     * <code><pre>
+     * F H
+     * \x04code
+     * \x10the fault code
+     * <p>
+     * \x07message
+     * \x11the fault message
+     * <p>
+     * \x06detail
+     * M\xnnjavax.ejb.FinderException
+     *     ...
+     * Z
+     * Z
+     * </pre></code>
+     *
+     * @param code the fault code, a three digit
+     */
+    @Override
+    public void writeFault(String code, String message, Object detail)
+            throws IOException {
+        flushIfFull();
+
+        writeVersion();
+
+        _buffer[_offset++] = (byte) 'F';
+        _buffer[_offset++] = (byte) 'H';
+
+        _refs.put(new HashMap(), _refs.size());
+
+        writeString("code");
+        writeString(code);
+
+        writeString("message");
+        writeString(message);
+
+        if (detail != null) {
+            writeString("detail");
+            writeObject(detail);
+        }
+
+        flushIfFull();
+        _buffer[_offset++] = (byte) 'Z';
+    }
+
+    /**
+     * Writes any object to the output stream.
+     */
+    @Override
+    public void writeObject(Object object)
+            throws IOException {
+        if (object == null) {
+            writeNull();
+            return;
+        }
+
+        Serializer serializer = findSerializerFactory().getSerializer(object.getClass());
+        serializer.writeObject(object, this);
+    }
+
+    /**
+     * 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>.
+     * <p>
+     * <code><pre>
+     * list ::= V type value* Z
+     *      ::= v type int value*
+     * </pre></code>
+     *
+     * @return true for variable lists, false for fixed lists
+     */
+    @Override
+    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.
+     */
+    @Override
+    public void writeListEnd()
+            throws IOException {
+        flushIfFull();
+
+        _buffer[_offset++] = (byte) BC_END;
+    }
+
+    /**
+     * 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>.
+     * <p>
+     * <code><pre>
+     * map ::= M type (<value> <value>)* Z
+     *     ::= H (<value> <value>)* Z
+     * </pre></code>
+     */
+    @Override
+    public void writeMapBegin(String type)
+            throws IOException {
+        if (SIZE < _offset + 32)
+            flush();
+
+        if (type != null) {
+            _buffer[_offset++] = BC_MAP;
+
+            writeType(type);
+        } else
+            _buffer[_offset++] = BC_MAP_UNTYPED;
+    }
+
+    /**
+     * Writes the tail of the map to the stream.
+     */
+    @Override
+    public void writeMapEnd()
+            throws IOException {
+        if (SIZE < _offset + 32)
+            flush();
+
+        _buffer[_offset++] = (byte) BC_END;
+    }
+
+    /**
+     * Writes the object definition
+     * <p>
+     * <code><pre>
+     * C &lt;string> &lt;int> &lt;string>*
+     * </pre></code>
+     */
+    @Override
+    public int writeObjectBegin(String type)
+            throws IOException {
+        if (_classRefs == null)
+            _classRefs = new HashMap();
+
+        Integer refV = (Integer) _classRefs.get(type);
+
+        if (refV != null) {
+            int ref = refV.intValue();
+
+            if (SIZE < _offset + 32)
+                flush();
+
+            if (ref <= OBJECT_DIRECT_MAX) {
+                _buffer[_offset++] = (byte) (BC_OBJECT_DIRECT + ref);
+            } else {
+                _buffer[_offset++] = (byte) 'O';
+                writeInt(ref);
+            }
+
+            return ref;
+        } else {
+            int ref = _classRefs.size();
+
+            _classRefs.put(type, Integer.valueOf(ref));
+
+            if (SIZE < _offset + 32)
+                flush();
+
+            _buffer[_offset++] = (byte) 'C';
+
+            writeString(type);
+
+            return -1;
+        }
+    }
+
+    /**
+     * Writes the tail of the class definition to the stream.
+     */
+    @Override
+    public void writeClassFieldLength(int len)
+            throws IOException {
+        writeInt(len);
+    }
+
+    /**
+     * Writes the tail of the object definition to the stream.
+     */
+    @Override
+    public void writeObjectEnd()
+            throws IOException {
+    }
+
+    /**
+     * <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();
+
+        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:
+     * <p>
+     * <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)
+            flush();
+
+        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:
+     * <p>
+     * <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) {
+            flush();
+            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:
+     * <p>
+     * <code><pre>
+     * L b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param value the long value to write.
+     */
+    @Override
+    public void writeLong(long value)
+            throws IOException {
+        int offset = _offset;
+        byte[] buffer = _buffer;
+
+        if (SIZE <= offset + 16) {
+            flush();
+            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:
+     * <p>
+     * <code><pre>
+     * D b64 b56 b48 b40 b32 b24 b16 b8
+     * </pre></code>
+     *
+     * @param value the double value to write.
+     */
+    @Override
+    public void writeDouble(double value)
+            throws IOException {
+        int offset = _offset;
+        byte[] buffer = _buffer;
+
+        if (SIZE <= offset + 16) {
+            flush();
+            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.
+     * <p>
+     * <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
+     */
+    @Override
+    public void writeUTCDate(long time)
+            throws IOException {
+        if (SIZE < _offset + 32)
+            flush();
+
+        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
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    @Override
+    public void writeNull()
+            throws IOException {
+        int offset = _offset;
+        byte[] buffer = _buffer;
+
+        if (SIZE <= offset + 16) {
+            flush();
+            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:
+     * <p>
+     * <code><pre>
+     * S b16 b8 string-value
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    @Override
+    public void writeString(String value)
+            throws IOException {
+        int offset = _offset;
+        byte[] buffer = _buffer;
+
+        if (SIZE <= offset + 16) {
+            flush();
+            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) {
+                    flush();
+                    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) {
+                flush();
+                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:
+     * <p>
+     * <code><pre>
+     * S b16 b8 string-value
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    @Override
+    public void writeString(char[] buffer, int offset, int length)
+            throws IOException {
+        if (buffer == null) {
+            if (SIZE < _offset + 16)
+                flush();
+
+            _buffer[_offset++] = (byte) ('N');
+        } else {
+            while (length > 0x8000) {
+                int sublen = 0x8000;
+
+                if (SIZE < _offset + 16)
+                    flush();
+
+                // 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)
+                flush();
+
+            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:
+     * <p>
+     * <code><pre>
+     * B b16 b18 bytes
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    @Override
+    public void writeBytes(byte[] buffer)
+            throws IOException {
+        if (buffer == null) {
+            if (SIZE < _offset + 16)
+                flush();
+
+            _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:
+     * <p>
+     * <code><pre>
+     * B b16 b18 bytes
+     * </pre></code>
+     * <p>
+     * If the value is null, it will be written as
+     * <p>
+     * <code><pre>
+     * N
+     * </pre></code>
+     *
+     * @param value the string value to write.
+     */
+    @Override
+    public void writeBytes(byte[] buffer, int offset, int length)
+            throws IOException {
+        if (buffer == null) {
+            if (SIZE < _offset + 16)
+                flushBuffer();
+
+            _buffer[_offset++] = (byte) 'N';
+        } else {
+            flush();
+
+            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.
+     * <p>
+     * <code><pre>
+     * </pre></code>
+     */
+    @Override
+    public void writeByteBufferStart()
+            throws IOException {
+    }
+
+    /**
+     * Writes a byte buffer to the stream.
+     * <p>
+     * <code><pre>
+     * b b16 b18 bytes
+     * </pre></code>
+     */
+    @Override
+    public void writeByteBufferPart(byte[] buffer, int offset, int length)
+            throws IOException {
+        while (length > 0) {
+            int sublen = length;
+
+            if (0x8000 < sublen)
+                sublen = 0x8000;
+
+            flush(); // bypass buffer
+
+            _os.write(BC_BINARY_CHUNK);
+            _os.write(sublen >> 8);
+            _os.write(sublen);
+
+            _os.write(buffer, offset, sublen);
+
+            length -= sublen;
+            offset += sublen;
+        }
+    }
+
+    /**
+     * Writes a byte buffer to the stream.
+     * <p>
+     * <code><pre>
+     * b b16 b18 bytes
+     * </pre></code>
+     */
+    @Override
+    public void writeByteBufferEnd(byte[] buffer, int offset, int length)
+            throws IOException {
+        writeBytes(buffer, offset, length);
+    }
+
+    /**
+     * Returns an output stream to write binary data.
+     */
+    public OutputStream getBytesOutputStream()
+            throws IOException {
+        return new BytesOutputStream();
+    }
+
+    /**
+     * Writes a reference.
+     * <p>
+     * <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)
+            flush();
+
+        _buffer[_offset++] = (byte) BC_REF;
+
+        writeInt(value);
+    }
+
+    /**
+     * If the object has already been written, just write its ref.
+     *
+     * @return true if we're writing a ref.
+     */
+    @Override
+    public boolean addRef(Object object)
+            throws IOException {
+        int ref = _refs.get(object);
+
+        if (ref >= 0) {
+            writeRef(ref);
+
+            return true;
+        } else {
+            _refs.put(object, _refs.size());
+
+            return false;
+        }
+    }
+
+    /**
+     * Removes a reference.
+     */
+    @Override
+    public boolean removeRef(Object obj)
+            throws IOException {
+        if (_refs != null) {
+            _refs.remove(obj);
+
+            return true;
+        } else
+            return false;
+    }
+
+    /**
+     * Replaces a reference from one object to another.
+     */
+    @Override
+    public boolean replaceRef(Object oldRef, Object newRef)
+            throws IOException {
+        Integer value = (Integer) _refs.remove(oldRef);
+
+        if (value != null) {
+            _refs.put(newRef, value);
+            return true;
+        } else
+            return false;
+    }
+
+    /**
+     * Resets the references for streaming.
+     */
+    @Override
+    public void resetReferences() {
+        if (_refs != null)
+            _refs.clear();
+    }
+
+    /**
+     * Starts the streaming message
+     * <p>
+     * <p>A streaming message starts with 'P'</p>
+     * <p>
+     * <pre>
+     * P x02 x00
+     * </pre>
+     */
+    public void writeStreamingObject(Object obj)
+            throws IOException {
+        startStreamingPacket();
+
+        writeObject(obj);
+
+        endStreamingPacket();
+    }
+
+    /**
+     * Starts a streaming packet
+     * <p>
+     * <p>A streaming message starts with 'P'</p>
+     * <p>
+     * <pre>
+     * P x02 x00
+     * </pre>
+     */
+    public void startStreamingPacket()
+            throws IOException {
+        if (_refs != null)
+            _refs.clear();
+
+        flush();
+
+        _isStreaming = true;
+        _offset = 3;
+    }
+
+    public void endStreamingPacket()
+            throws IOException {
+        int len = _offset - 3;
+
+        _buffer[0] = (byte) 'P';
+        _buffer[1] = (byte) (len >> 8);
+        _buffer[2] = (byte) len;
+
+        _isStreaming = false;
+
+        flush();
+    }
+
+    /**
+     * 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)
+            flush();
+
+        if (v == null) {
+            _buffer[_offset++] = (byte) (0);
+            _buffer[_offset++] = (byte) (0);
+        } else {
+            int len = v.length();
+            _buffer[_offset++] = (byte) (len >> 8);
+            _buffer[_offset++] = (byte) (len);
+
+            printString(v, 0, len);
+        }
+    }
+
+    /**
+     * Prints a string to the stream, encoded as UTF-8
+     *
+     * @param v the string to print.
+     */
+    public void printString(String v)
+            throws IOException {
+        printString(v, 0, v.length());
+    }
+
+    /**
+     * Prints a string to the stream, encoded as UTF-8
+     *
+     * @param v the string to print.
+     */
+    public void printString(String v, int strOffset, int length)
+            throws IOException {
+        int offset = _offset;
+        byte[] buffer = _buffer;
+
+        for (int i = 0; i < length; i++) {
+            if (SIZE <= offset + 16) {
+                _offset = offset;
+                flush();
+                offset = _offset;
+            }
+
+            char ch = v.charAt(i + strOffset);
+
+            if (ch < 0x80)
+                buffer[offset++] = (byte) (ch);
+            else if (ch < 0x800) {
+                buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
+                buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
+            } else {
+                buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf));
+                buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
+                buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
+            }
+        }
+
+        _offset = offset;
+    }
+
+    /**
+     * Prints a string to the stream, encoded as UTF-8
+     *
+     * @param v the string to print.
+     */
+    public void printString(char[] v, int strOffset, int length)
+            throws IOException {
+        int offset = _offset;
+        byte[] buffer = _buffer;
+
+        for (int i = 0; i < length; i++) {
+            if (SIZE <= offset + 16) {
+                _offset = offset;
+                flush();
+                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) {
+            _offset = 0;
+            _os.write(_buffer, 0, offset);
+        }
+    }
+
+    @Override
+    public final void flush()
+            throws IOException {
+        flushBuffer();
+
+        if (_os != null)
+            _os.flush();
+    }
+
+    public final void flushBuffer()
+            throws IOException {
+        int offset = _offset;
+
+        if (!_isStreaming && offset > 0) {
+            _offset = 0;
+
+            _os.write(_buffer, 0, offset);
+        } else if (_isStreaming && offset > 3) {
+            int len = offset - 3;
+            _buffer[0] = 'p';
+            _buffer[1] = (byte) (len >> 8);
+            _buffer[2] = (byte) len;
+            _offset = 3;
+
+            _os.write(_buffer, 0, offset);
+        }
+    }
+
+    @Override
+    public final void close()
+            throws IOException {
+        // hessian/3a8c
+        flush();
+
+        OutputStream os = _os;
+        _os = null;
+
+        if (os != null) {
+            if (_isCloseStreamOnClose)
+                os.close();
+        }
+    }
+
+    class BytesOutputStream extends OutputStream {
+        private int _startOffset;
+
+        BytesOutputStream()
+                throws IOException {
+            if (SIZE < _offset + 16) {
+                Hessian2Output.this.flush();
+            }
+
+            _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.flush();
+
+                _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.flush();
+
+                    _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.flush();
+        }
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2StreamingInput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2StreamingInput.java
new file mode 100644
index 0000000..35316cc
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2StreamingInput.java
@@ -0,0 +1,160 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Output stream for Hessian 2 streaming requests.
+ */
+public class Hessian2StreamingInput {
+    private Hessian2Input _in;
+
+    /**
+     * Creates a new Hessian input stream, initialized with an
+     * underlying input stream.
+     *
+     * @param is the underlying output stream.
+     */
+    public Hessian2StreamingInput(InputStream is) {
+        _in = new Hessian2Input(new StreamingInputStream(is));
+    }
+
+    /**
+     * Read the next object
+     */
+    public Object readObject()
+            throws IOException {
+        return _in.readStreamingObject();
+    }
+
+    /**
+     * Close the output.
+     */
+    public void close()
+            throws IOException {
+        _in.close();
+    }
+
+    static class StreamingInputStream extends InputStream {
+        private InputStream _is;
+        private int _length;
+
+        StreamingInputStream(InputStream is) {
+            _is = is;
+        }
+
+        @Override
+        public int read()
+                throws IOException {
+            InputStream is = _is;
+
+            while (_length == 0) {
+                int code = is.read();
+
+                if (code < 0)
+                    return -1;
+                else if (code != 'p' && code != 'P')
+                    throw new HessianProtocolException("expected streaming packet at 0x"
+                            + Integer.toHexString(code & 0xff));
+
+                int d1 = is.read();
+                int d2 = is.read();
+
+                if (d2 < 0)
+                    return -1;
+
+                _length = (d1 << 8) + d2;
+            }
+
+            _length--;
+            return is.read();
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int length)
+                throws IOException {
+            InputStream is = _is;
+
+            while (_length == 0) {
+                int code = is.read();
+
+                if (code < 0)
+                    return -1;
+                else if (code != 'p' && code != 'P') {
+                    throw new HessianProtocolException("expected streaming packet at 0x"
+                            + Integer.toHexString(code & 0xff)
+                            + " (" + (char) code + ")");
+                }
+
+                int d1 = is.read();
+                int d2 = is.read();
+
+                if (d2 < 0)
+                    return -1;
+
+                _length = (d1 << 8) + d2;
+            }
+
+            int sublen = _length;
+            if (length < sublen)
+                sublen = length;
+
+            sublen = is.read(buffer, offset, sublen);
+
+            if (sublen < 0)
+                return -1;
+
+            _length -= sublen;
+
+            return sublen;
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2StreamingOutput.java
similarity index 65%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2StreamingOutput.java
index 7c8e328..a2edf24 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Hessian2StreamingOutput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,35 +46,56 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
 
 /**
- * Factory for returning serialization methods.
+ * Output stream for Hessian 2 streaming requests.
  */
-public class BeanSerializerFactory extends SerializerFactory {
+public class Hessian2StreamingOutput {
+    private Hessian2Output _out;
+
     /**
-     * Returns the default serializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
+     * Creates a new Hessian output stream, initialized with an
+     * underlying output stream.
      *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
+     * @param os the underlying output stream.
      */
-    @Override
-    protected Serializer getDefaultSerializer(Class cl) {
-        return new BeanSerializer(cl, getClassLoader());
+    public Hessian2StreamingOutput(OutputStream os) {
+        _out = new Hessian2Output(os);
+    }
+
+    public boolean isCloseStreamOnClose() {
+        return _out.isCloseStreamOnClose();
+    }
+
+    public void setCloseStreamOnClose(boolean isClose) {
+        _out.setCloseStreamOnClose(isClose);
     }
 
     /**
-     * Returns the default deserializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
+     * Writes any object to the output stream.
      */
-    @Override
-    protected Deserializer getDefaultDeserializer(Class cl) {
-        return new BeanDeserializer(cl);
+    public void writeObject(Object object)
+            throws IOException {
+        _out.writeStreamingObject(object);
+    }
+
+    /**
+     * Flushes the output.
+     */
+    public void flush()
+            throws IOException {
+        _out.flush();
+    }
+
+    /**
+     * Close the output.
+     */
+    public void close()
+            throws IOException {
+        _out.close();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugInputStream.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugInputStream.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugInputStream.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugInputStream.java
index 092fa22..2cdc1b0 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugInputStream.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugInputStream.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugOutputStream.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugOutputStream.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugOutputStream.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugOutputStream.java
index 5d45d93..0d145f4 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugOutputStream.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugOutputStream.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugState.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugState.java
similarity index 99%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugState.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugState.java
index 0798289..7c4f7b5 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianDebugState.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianDebugState.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.PrintWriter;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianEnvelope.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianEnvelope.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianEnvelope.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianEnvelope.java
index 21bd3a7..1e4e6de 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianEnvelope.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianEnvelope.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianFieldException.java
similarity index 75%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianFieldException.java
index 72a9822..7f8309e 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianFieldException.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,36 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
-
-import java.io.IOException;
+package com.alibaba.com.caucho.hessian3.io;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Exception during field reading.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class HessianFieldException extends HessianProtocolException {
     /**
-     * Looks up a proxy object.
+     * Zero-arg constructor.
      */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public HessianFieldException() {
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianFieldException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianFieldException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianFieldException(Throwable cause) {
+        super(cause);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianHandle.java
similarity index 90%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianHandle.java
index be505fb..ac447d2 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianHandle.java
@@ -46,11 +46,10 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
-import java.util.logging.Logger;
-
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Marks a type as a handle
+ */
+public interface HessianHandle {
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianInput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianInput.java
similarity index 99%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianInput.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianInput.java
index 14b3096..8d0ebd8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianInput.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianInput.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianInputFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianInputFactory.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianInputFactory.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianInputFactory.java
index 2c0cf69..02b2580 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianInputFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianInputFactory.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianOutput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianOutput.java
similarity index 99%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianOutput.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianOutput.java
index aad614b..c07ace9 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianOutput.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianOutput.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianProtocolException.java
similarity index 65%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianProtocolException.java
index 7c8e328..16d98d3 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianProtocolException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,35 +46,60 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
 
 /**
- * Factory for returning serialization methods.
+ * Exception for faults when the fault doesn't return a java exception.
+ * This exception is required for MicroHessianInput.
  */
-public class BeanSerializerFactory extends SerializerFactory {
+public class HessianProtocolException extends IOException {
+    private Throwable rootCause;
+
     /**
-     * Returns the default serializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
+     * Zero-arg constructor.
      */
-    @Override
-    protected Serializer getDefaultSerializer(Class cl) {
-        return new BeanSerializer(cl, getClassLoader());
+    public HessianProtocolException() {
     }
 
     /**
-     * Returns the default deserializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
+     * Create the exception.
+     */
+    public HessianProtocolException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianProtocolException(String message, Throwable rootCause) {
+        super(message);
+
+        this.rootCause = rootCause;
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianProtocolException(Throwable rootCause) {
+        super(String.valueOf(rootCause));
+
+        this.rootCause = rootCause;
+    }
+
+    /**
+     * Returns the underlying cause.
+     */
+    public Throwable getRootCause() {
+        return rootCause;
+    }
+
+    /**
+     * Returns the underlying cause.
      */
     @Override
-    protected Deserializer getDefaultDeserializer(Class cl) {
-        return new BeanDeserializer(cl);
+    public Throwable getCause() {
+        return getRootCause();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemote.java
similarity index 61%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemote.java
index 72a9822..d43527d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemote.java
@@ -46,20 +46,80 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
-
-import java.io.IOException;
+package com.alibaba.com.caucho.hessian3.io;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Encapsulates a remote address when no stub is available, e.g. for
+ * Java MicroEdition.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class HessianRemote {
+    private String type;
+    private String url;
+
     /**
-     * Looks up a proxy object.
+     * Creates a new Hessian remote object.
+     *
+     * @param type the remote stub interface
+     * @param url  the remote url
+     */
+    public HessianRemote(String type, String url) {
+        this.type = type;
+        this.url = url;
+    }
+
+    /**
+     * Creates an uninitialized Hessian remote.
+     */
+    public HessianRemote() {
+    }
+
+    /**
+     * Returns the remote api class name.
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Returns the remote URL.
+     */
+    public String getURL() {
+        return url;
+    }
+
+    /**
+     * Sets the remote URL.
+     */
+    public void setURL(String url) {
+        this.url = url;
+    }
+
+    /**
+     * Defines the hashcode.
      */
     @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public int hashCode() {
+        return url.hashCode();
+    }
+
+    /**
+     * Defines equality
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof HessianRemote))
+            return false;
+
+        HessianRemote remote = (HessianRemote) obj;
+
+        return url.equals(remote.url);
+    }
+
+    /**
+     * Readable version of the remote.
+     */
+    @Override
+    public String toString() {
+        return "[HessianRemote " + url + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemoteObject.java
similarity index 90%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemoteObject.java
index be505fb..a36e583 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemoteObject.java
@@ -46,11 +46,13 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
-import java.util.logging.Logger;
+/**
+ * Interface for any hessian remote object.
+ */
+public interface HessianRemoteObject {
+    public String getHessianType();
 
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+    public String getHessianURL();
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemoteResolver.java
similarity index 91%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemoteResolver.java
index 72a9822..0e2b947 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianRemoteResolver.java
@@ -46,20 +46,17 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
  * Looks up remote objects.  The default just returns a HessianRemote object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public interface HessianRemoteResolver {
     /**
      * Looks up a proxy object.
      */
-    @Override
     public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
-    }
+            throws IOException;
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianSerializerInput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianSerializerInput.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianSerializerInput.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianSerializerInput.java
index ceed54b..e87904f 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianSerializerInput.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianSerializerInput.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianSerializerOutput.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianSerializerOutput.java
similarity index 98%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/HessianSerializerOutput.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianSerializerOutput.java
index 56a8772..f359807 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/HessianSerializerOutput.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianSerializerOutput.java
@@ -46,7 +46,7 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianServiceException.java
similarity index 74%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/HessianServiceException.java
index 72a9822..b2ccc1b 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/HessianServiceException.java
@@ -46,20 +46,42 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
-
-import java.io.IOException;
+package com.alibaba.com.caucho.hessian3.io;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Exception for faults when the fault doesn't return a java exception.
+ * This exception is required for MicroHessianInput.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class HessianServiceException extends Exception {
+    private String code;
+    private Object detail;
+
     /**
-     * Looks up a proxy object.
+     * Zero-arg constructor.
      */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public HessianServiceException() {
+    }
+
+    /**
+     * Create the exception.
+     */
+    public HessianServiceException(String message, String code, Object detail) {
+        super(message);
+        this.code = code;
+        this.detail = detail;
+    }
+
+    /**
+     * Returns the code.
+     */
+    public String getCode() {
+        return code;
+    }
+
+    /**
+     * Returns the detail.
+     */
+    public Object getDetail() {
+        return detail;
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/IOExceptionWrapper.java
similarity index 83%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/IOExceptionWrapper.java
index 72a9822..c35cbc1 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/IOExceptionWrapper.java
@@ -46,20 +46,30 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Exception wrapper for IO.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class IOExceptionWrapper extends IOException {
+    private Throwable _cause;
+
+    public IOExceptionWrapper(Throwable cause) {
+        super(cause.toString());
+
+        _cause = cause;
+    }
+
+    public IOExceptionWrapper(String msg, Throwable cause) {
+        super(msg);
+
+        _cause = cause;
+    }
+
     @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    public Throwable getCause() {
+        return _cause;
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/InputStreamDeserializer.java
similarity index 84%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/InputStreamDeserializer.java
index 72a9822..d659dac 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/InputStreamDeserializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,20 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a stream object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class InputStreamDeserializer extends AbstractDeserializer {
+    public InputStreamDeserializer() {
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public Object readObject(AbstractHessianInput in)
             throws IOException {
-        return new HessianRemote(type, url);
+        return in.readInputStream();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/InputStreamSerializer.java
similarity index 75%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/InputStreamSerializer.java
index 72a9822..bbc036d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/InputStreamSerializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,34 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.io.InputStream;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a stream object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class InputStreamSerializer extends AbstractSerializer {
+    public InputStreamSerializer() {
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        InputStream is = (InputStream) obj;
+
+        if (is == null)
+            out.writeNull();
+        else {
+            byte[] buf = new byte[1024];
+            int len;
+
+            while ((len = is.read(buf, 0, buf.length)) > 0) {
+                out.writeByteBufferPart(buf, 0, len);
+            }
+
+            out.writeByteBufferEnd(buf, 0, 0);
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/IteratorSerializer.java
similarity index 75%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/IteratorSerializer.java
index 72a9822..ed729fb 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/IteratorSerializer.java
@@ -46,20 +46,38 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.Iterator;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a JDK 1.2 Iterator.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class IteratorSerializer extends AbstractSerializer {
+    private static IteratorSerializer _serializer;
+
+    public static IteratorSerializer create() {
+        if (_serializer == null)
+            _serializer = new IteratorSerializer();
+
+        return _serializer;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        Iterator iter = (Iterator) obj;
+
+        boolean hasEnd = out.writeListBegin(-1, null);
+
+        while (iter.hasNext()) {
+            Object value = iter.next();
+
+            out.writeObject(value);
+        }
+
+        if (hasEnd)
+            out.writeListEnd();
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/JavaDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/JavaDeserializer.java
new file mode 100644
index 0000000..f24debf
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/JavaDeserializer.java
@@ -0,0 +1,786 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class JavaDeserializer extends AbstractMapDeserializer {
+    private static final Logger log
+            = Logger.getLogger(JavaDeserializer.class.getName());
+
+    private Class _type;
+    private HashMap _fieldMap;
+    private Method _readResolve;
+    private Constructor _constructor;
+    private Object[] _constructorArgs;
+
+    public JavaDeserializer(Class cl) {
+        _type = cl;
+        _fieldMap = getFieldMap(cl);
+
+        _readResolve = getReadResolve(cl);
+
+        if (_readResolve != null) {
+            _readResolve.setAccessible(true);
+        }
+
+        Constructor[] constructors = cl.getDeclaredConstructors();
+        long bestCost = Long.MAX_VALUE;
+
+        for (int i = 0; i < constructors.length; i++) {
+            Class[] param = constructors[i].getParameterTypes();
+            long cost = 0;
+
+            for (int j = 0; j < param.length; j++) {
+                cost = 4 * cost;
+
+                if (Object.class.equals(param[j]))
+                    cost += 1;
+                else if (String.class.equals(param[j]))
+                    cost += 2;
+                else if (int.class.equals(param[j]))
+                    cost += 3;
+                else if (long.class.equals(param[j]))
+                    cost += 4;
+                else if (param[j].isPrimitive())
+                    cost += 5;
+                else
+                    cost += 6;
+            }
+
+            if (cost < 0 || cost > (1 << 48))
+                cost = 1 << 48;
+
+            cost += (long) param.length << 48;
+
+            if (cost < bestCost) {
+                _constructor = constructors[i];
+                bestCost = cost;
+            }
+        }
+
+        if (_constructor != null) {
+            _constructor.setAccessible(true);
+            Class[] params = _constructor.getParameterTypes();
+            _constructorArgs = new Object[params.length];
+            for (int i = 0; i < params.length; i++) {
+                _constructorArgs[i] = getParamArg(params[i]);
+            }
+        }
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    protected static Object getParamArg(Class cl) {
+        if (!cl.isPrimitive())
+            return null;
+        else if (boolean.class.equals(cl))
+            return Boolean.FALSE;
+        else if (byte.class.equals(cl))
+            return new Byte((byte) 0);
+        else if (short.class.equals(cl))
+            return new Short((short) 0);
+        else if (char.class.equals(cl))
+            return new Character((char) 0);
+        else if (int.class.equals(cl))
+            return Integer.valueOf(0);
+        else if (long.class.equals(cl))
+            return Long.valueOf(0);
+        else if (float.class.equals(cl))
+            return Float.valueOf(0);
+        else if (double.class.equals(cl))
+            return Double.valueOf(0);
+        else
+            throw new UnsupportedOperationException();
+    }
+
+    static void logDeserializeError(Field field, Object obj, Object value,
+                                    Throwable e)
+            throws IOException {
+        String fieldName = (field.getDeclaringClass().getName()
+                + "." + field.getName());
+
+        if (e instanceof HessianFieldException)
+            throw (HessianFieldException) e;
+        else if (e instanceof IOException)
+            throw new HessianFieldException(fieldName + ": " + e.getMessage(), e);
+
+        if (value != null)
+            throw new HessianFieldException(fieldName + ": " + value.getClass().getName()
+                    + " cannot be assigned to '" + field.getType().getName() + "'", e);
+        else
+            throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);
+    }
+
+    @Override
+    public Class getType() {
+        return _type;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readMap(in, obj);
+        } catch (IOException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        try {
+            Object obj = instantiate();
+
+            return readObject(in, obj, fieldNames);
+        } catch (IOException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Returns the readResolve method
+     */
+    protected Method getReadResolve(Class cl) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Method[] methods = cl.getDeclaredMethods();
+
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+
+                if (method.getName().equals("readResolve") &&
+                        method.getParameterTypes().length == 0)
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    public Object readMap(AbstractHessianInput in, Object obj)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            while (!in.isEnd()) {
+                Object key = in.readObject();
+
+                FieldDeserializer deser = (FieldDeserializer) _fieldMap.get(key);
+
+                if (deser != null)
+                    deser.deserialize(in, obj);
+                else
+                    in.readObject();
+            }
+
+            in.readMapEnd();
+
+            Object resolve = resolve(obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+
+    public Object readObject(AbstractHessianInput in,
+                             Object obj,
+                             String[] fieldNames)
+            throws IOException {
+        try {
+            int ref = in.addRef(obj);
+
+            for (int i = 0; i < fieldNames.length; i++) {
+                String name = fieldNames[i];
+
+                FieldDeserializer deser = (FieldDeserializer) _fieldMap.get(name);
+
+                if (deser != null)
+                    deser.deserialize(in, obj);
+                else
+                    in.readObject();
+            }
+
+            Object resolve = resolve(obj);
+
+            if (obj != resolve)
+                in.setRef(ref, resolve);
+
+            return resolve;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e);
+        }
+    }
+
+    private Object resolve(Object obj)
+            throws Exception {
+        // if there's a readResolve method, call it
+        try {
+            if (_readResolve != null)
+                return _readResolve.invoke(obj, new Object[0]);
+        } catch (InvocationTargetException e) {
+            if (e.getTargetException() != null)
+                throw e;
+        }
+
+        return obj;
+    }
+
+    protected Object instantiate()
+            throws Exception {
+        try {
+            if (_constructor != null)
+                return _constructor.newInstance(_constructorArgs);
+            else
+                return _type.newInstance();
+        } catch (Exception e) {
+            throw new HessianProtocolException("'" + _type.getName() + "' could not be instantiated", e);
+        }
+    }
+
+    /**
+     * Creates a map of the classes fields.
+     */
+    protected HashMap getFieldMap(Class cl) {
+        HashMap fieldMap = new HashMap();
+
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Field[] fields = cl.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+
+                if (Modifier.isTransient(field.getModifiers())
+                        || Modifier.isStatic(field.getModifiers()))
+                    continue;
+                else if (fieldMap.get(field.getName()) != null)
+                    continue;
+
+                // XXX: could parameterize the handler to only deal with public
+                try {
+                    field.setAccessible(true);
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+
+                Class type = field.getType();
+                FieldDeserializer deser;
+
+                if (String.class.equals(type))
+                    deser = new StringFieldDeserializer(field);
+                else if (byte.class.equals(type)) {
+                    deser = new ByteFieldDeserializer(field);
+                } else if (short.class.equals(type)) {
+                    deser = new ShortFieldDeserializer(field);
+                } else if (int.class.equals(type)) {
+                    deser = new IntFieldDeserializer(field);
+                } else if (long.class.equals(type)) {
+                    deser = new LongFieldDeserializer(field);
+                } else if (float.class.equals(type)) {
+                    deser = new FloatFieldDeserializer(field);
+                } else if (double.class.equals(type)) {
+                    deser = new DoubleFieldDeserializer(field);
+                } else if (boolean.class.equals(type)) {
+                    deser = new BooleanFieldDeserializer(field);
+                } else if (java.sql.Date.class.equals(type)) {
+                    deser = new SqlDateFieldDeserializer(field);
+                } else if (java.sql.Timestamp.class.equals(type)) {
+                    deser = new SqlTimestampFieldDeserializer(field);
+                } else if (java.sql.Time.class.equals(type)) {
+                    deser = new SqlTimeFieldDeserializer(field);
+                }
+                // support generic type of map
+                else if (Map.class.equals(type)
+                        && field.getGenericType() != field.getType()) {
+                    deser = new ObjectMapFieldDeserializer(field);
+                } else if (List.class.equals(type)
+                        && field.getGenericType() != field.getType()) {
+                    deser = new ObjectListFieldDeserializer(field);
+                } else if (Set.class.equals(type)
+                        && field.getGenericType() != field.getType()) {
+                    deser = new ObjectSetFieldDeserializer(field);
+                }
+                else {
+                    deser = new ObjectFieldDeserializer(field);
+                }
+
+                fieldMap.put(field.getName(), deser);
+            }
+        }
+
+        return fieldMap;
+    }
+
+    abstract static class FieldDeserializer {
+        abstract void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException;
+    }
+
+    static class ObjectFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        ObjectFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            Object value = null;
+
+            try {
+                value = in.readObject(_field.getType());
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class BooleanFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        BooleanFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            boolean value = false;
+
+            try {
+                value = in.readBoolean();
+
+                _field.setBoolean(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ByteFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        ByteFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _field.setByte(obj, (byte) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ShortFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        ShortFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _field.setShort(obj, (short) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ObjectMapFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        ObjectMapFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            Object value = null;
+
+            try {
+
+                Type[] types = ((ParameterizedType) _field.getGenericType()).getActualTypeArguments();
+                value = in.readObject(_field.getType(),
+                        isPrimitive(types[0]) ? (Class<?>) types[0] : null,
+                        isPrimitive(types[1]) ? (Class<?>) types[1] : null
+                );
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ObjectListFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        ObjectListFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            Object value = null;
+
+            try {
+
+                Type[] types = ((ParameterizedType) _field.getGenericType()).getActualTypeArguments();
+                value = in.readObject(_field.getType(),
+                        isPrimitive(types[0]) ? (Class<?>) types[0] : null
+                );
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class ObjectSetFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        ObjectSetFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            Object value = null;
+
+            try {
+
+                Type[] types = ((ParameterizedType) _field.getGenericType()).getActualTypeArguments();
+                value = in.readObject(_field.getType(),
+                        isPrimitive(types[0]) ? (Class<?>) types[0] : null
+                );
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+
+    static class IntFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        IntFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = in.readInt();
+
+                _field.setInt(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class LongFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        LongFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            long value = 0;
+
+            try {
+                value = in.readLong();
+
+                _field.setLong(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class FloatFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        FloatFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = in.readDouble();
+
+                _field.setFloat(obj, (float) value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class DoubleFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        DoubleFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = in.readDouble();
+
+                _field.setDouble(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class StringFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        StringFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            String value = null;
+
+            try {
+                value = in.readString();
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlDateFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        SqlDateFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Date value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+                if (date != null)
+                    value = new java.sql.Date(date.getTime());
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlTimestampFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        SqlTimestampFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Timestamp value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+                if (date != null)
+                    value = new java.sql.Timestamp(date.getTime());
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    static class SqlTimeFieldDeserializer extends FieldDeserializer {
+        private final Field _field;
+
+        SqlTimeFieldDeserializer(Field field) {
+            _field = field;
+        }
+
+        @Override
+        void deserialize(AbstractHessianInput in, Object obj)
+                throws IOException {
+            java.sql.Time value = null;
+
+            try {
+                java.util.Date date = (java.util.Date) in.readObject();
+                if (date != null) value = new java.sql.Time(date.getTime());
+
+                _field.set(obj, value);
+            } catch (Exception e) {
+                logDeserializeError(_field, obj, value, e);
+            }
+        }
+    }
+
+    /**
+     * @see java.lang.Boolean#TYPE
+     * @see java.lang.Character#TYPE
+     * @see java.lang.Byte#TYPE
+     * @see java.lang.Short#TYPE
+     * @see java.lang.Integer#TYPE
+     * @see java.lang.Long#TYPE
+     * @see java.lang.Float#TYPE
+     * @see java.lang.Double#TYPE
+     * @see java.lang.Void#TYPE
+     */
+    private static boolean isPrimitive(Type type) {
+        try {
+            if (type != null) {
+                if (type instanceof Class<?>) {
+                    Class<?> clazz = (Class<?>) type;
+                    return clazz.isPrimitive()
+                            || PRIMITIVE_TYPE.containsKey(clazz.getName());
+                }
+            }
+        } catch (Exception e) {
+            // ignore exception
+        }
+        return false;
+    }
+
+    static final Map<String, Boolean> PRIMITIVE_TYPE = new HashMap<String, Boolean>() {
+        {
+            put(Boolean.class.getName(), true);
+            put(Character.class.getName(), true);
+            put(Byte.class.getName(), true);
+            put(Short.class.getName(), true);
+            put(Integer.class.getName(), true);
+            put(Long.class.getName(), true);
+            put(Float.class.getName(), true);
+            put(Double.class.getName(), true);
+            put(Void.class.getName(), true);
+        }
+    };
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/JavaSerializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/JavaSerializer.java
new file mode 100644
index 0000000..4e53245
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/JavaSerializer.java
@@ -0,0 +1,423 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class JavaSerializer extends AbstractSerializer {
+    private static final Logger log
+            = Logger.getLogger(JavaSerializer.class.getName());
+
+    private static Object[] NULL_ARGS = new Object[0];
+
+    private Field[] _fields;
+    private FieldSerializer[] _fieldSerializers;
+
+    private Object _writeReplaceFactory;
+    private Method _writeReplace;
+
+    public JavaSerializer(Class cl, ClassLoader loader) {
+        introspectWriteReplace(cl, loader);
+
+        if (_writeReplace != null)
+            _writeReplace.setAccessible(true);
+
+        List primitiveFields = new ArrayList();
+        List compoundFields = new ArrayList();
+
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Field[] fields = cl.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+
+                if (Modifier.isTransient(field.getModifiers())
+                        || Modifier.isStatic(field.getModifiers()))
+                    continue;
+
+                // XXX: could parameterize the handler to only deal with public
+                field.setAccessible(true);
+
+                if (field.getType().isPrimitive()
+                        || (field.getType().getName().startsWith("java.lang.")
+                        && !field.getType().equals(Object.class)))
+                    primitiveFields.add(field);
+                else
+                    compoundFields.add(field);
+            }
+        }
+
+        List fields = new ArrayList();
+        fields.addAll(primitiveFields);
+        fields.addAll(compoundFields);
+        Collections.reverse(fields);
+
+        _fields = new Field[fields.size()];
+        fields.toArray(_fields);
+
+        _fieldSerializers = new FieldSerializer[_fields.length];
+
+        for (int i = 0; i < _fields.length; i++) {
+            _fieldSerializers[i] = getFieldSerializer(_fields[i].getType());
+        }
+    }
+
+    /**
+     * Returns the writeReplace method
+     */
+    protected static Method getWriteReplace(Class cl) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            Method[] methods = cl.getDeclaredMethods();
+
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+
+                if (method.getName().equals("writeReplace") &&
+                        method.getParameterTypes().length == 0)
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    private static FieldSerializer getFieldSerializer(Class type) {
+        if (int.class.equals(type)
+                || byte.class.equals(type)
+                || short.class.equals(type)
+                || int.class.equals(type)) {
+            return IntFieldSerializer.SER;
+        } else if (long.class.equals(type)) {
+            return LongFieldSerializer.SER;
+        } else if (double.class.equals(type) ||
+                float.class.equals(type)) {
+            return DoubleFieldSerializer.SER;
+        } else if (boolean.class.equals(type)) {
+            return BooleanFieldSerializer.SER;
+        } else if (String.class.equals(type)) {
+            return StringFieldSerializer.SER;
+        } else if (java.util.Date.class.equals(type)
+                || java.sql.Date.class.equals(type)
+                || java.sql.Timestamp.class.equals(type)
+                || java.sql.Time.class.equals(type)) {
+            return DateFieldSerializer.SER;
+        } else
+            return FieldSerializer.SER;
+    }
+
+    private void introspectWriteReplace(Class cl, ClassLoader loader) {
+        try {
+            String className = cl.getName() + "HessianSerializer";
+
+            Class serializerClass = Class.forName(className, false, loader);
+
+            Object serializerObject = serializerClass.newInstance();
+
+            Method writeReplace = getWriteReplace(serializerClass, cl);
+
+            if (writeReplace != null) {
+                _writeReplaceFactory = serializerObject;
+                _writeReplace = writeReplace;
+
+                return;
+            }
+        } catch (ClassNotFoundException e) {
+        } catch (Exception e) {
+            log.log(Level.FINER, e.toString(), e);
+        }
+
+        _writeReplace = getWriteReplace(cl);
+    }
+
+    /**
+     * Returns the writeReplace method
+     */
+    protected Method getWriteReplace(Class cl, Class param) {
+        for (; cl != null; cl = cl.getSuperclass()) {
+            for (Method method : cl.getDeclaredMethods()) {
+                if (method.getName().equals("writeReplace")
+                        && method.getParameterTypes().length == 1
+                        && param.equals(method.getParameterTypes()[0]))
+                    return method;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        if (out.addRef(obj)) {
+            return;
+        }
+
+        Class cl = obj.getClass();
+
+        try {
+            if (_writeReplace != null) {
+                Object repl;
+
+                if (_writeReplaceFactory != null)
+                    repl = _writeReplace.invoke(_writeReplaceFactory, obj);
+                else
+                    repl = _writeReplace.invoke(obj);
+
+                //Some class would return itself for wrapReplace, which would cause infinite recursion
+                //In this case, we could write the object just like normal cases
+                if (repl != obj) {
+                    out.removeRef(obj);
+
+                    out.writeObject(repl);
+
+                    out.replaceRef(repl, obj);
+
+                    return;
+                }
+            }
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            // log.log(Level.FINE, e.toString(), e);
+            throw new RuntimeException(e);
+        }
+
+        int ref = out.writeObjectBegin(cl.getName());
+
+        if (ref < -1) {
+            writeObject10(obj, out);
+        } else {
+            if (ref == -1) {
+                writeDefinition20(out);
+                out.writeObjectBegin(cl.getName());
+            }
+
+            writeInstance(obj, out);
+        }
+    }
+
+    private void writeObject10(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        for (int i = 0; i < _fields.length; i++) {
+            Field field = _fields[i];
+
+            out.writeString(field.getName());
+
+            _fieldSerializers[i].serialize(out, obj, field);
+        }
+
+        out.writeMapEnd();
+    }
+
+    private void writeDefinition20(AbstractHessianOutput out)
+            throws IOException {
+        out.writeClassFieldLength(_fields.length);
+
+        for (int i = 0; i < _fields.length; i++) {
+            Field field = _fields[i];
+
+            out.writeString(field.getName());
+        }
+    }
+
+    public void writeInstance(Object obj, AbstractHessianOutput out)
+            throws IOException {
+        for (int i = 0; i < _fields.length; i++) {
+            Field field = _fields[i];
+
+            _fieldSerializers[i].serialize(out, obj, field);
+        }
+    }
+
+    static class FieldSerializer {
+        static final FieldSerializer SER = new FieldSerializer();
+
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            Object value = null;
+
+            try {
+                value = field.get(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            try {
+                out.writeObject(value);
+            } catch (RuntimeException e) {
+                throw new RuntimeException(e.getMessage() + "\n Java field: " + field,
+                        e);
+            } catch (IOException e) {
+                throw new IOExceptionWrapper(e.getMessage() + "\n Java field: " + field,
+                        e);
+            }
+        }
+    }
+
+    static class BooleanFieldSerializer extends FieldSerializer {
+        static final FieldSerializer SER = new BooleanFieldSerializer();
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            boolean value = false;
+
+            try {
+                value = field.getBoolean(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeBoolean(value);
+        }
+    }
+
+    static class IntFieldSerializer extends FieldSerializer {
+        static final FieldSerializer SER = new IntFieldSerializer();
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            int value = 0;
+
+            try {
+                value = field.getInt(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeInt(value);
+        }
+    }
+
+    static class LongFieldSerializer extends FieldSerializer {
+        static final FieldSerializer SER = new LongFieldSerializer();
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            long value = 0;
+
+            try {
+                value = field.getLong(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeLong(value);
+        }
+    }
+
+    static class DoubleFieldSerializer extends FieldSerializer {
+        static final FieldSerializer SER = new DoubleFieldSerializer();
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            double value = 0;
+
+            try {
+                value = field.getDouble(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeDouble(value);
+        }
+    }
+
+    static class StringFieldSerializer extends FieldSerializer {
+        static final FieldSerializer SER = new StringFieldSerializer();
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            String value = null;
+
+            try {
+                value = (String) field.get(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            out.writeString(value);
+        }
+    }
+
+    static class DateFieldSerializer extends FieldSerializer {
+        static final FieldSerializer SER = new DateFieldSerializer();
+
+        @Override
+        void serialize(AbstractHessianOutput out, Object obj, Field field)
+                throws IOException {
+            java.util.Date value = null;
+
+            try {
+                value = (java.util.Date) field.get(obj);
+            } catch (IllegalAccessException e) {
+                log.log(Level.FINE, e.toString(), e);
+            }
+
+            if (value == null)
+                out.writeNull();
+            else
+                out.writeUTCDate(value.getTime());
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/LocaleHandle.java
similarity index 65%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/LocaleHandle.java
index 72a9822..322e9d4 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/LocaleHandle.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,20 +46,45 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
-import java.io.IOException;
+import java.util.Locale;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Handle for a locale object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
-    @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+public class LocaleHandle implements java.io.Serializable, HessianHandle {
+    private String value;
+
+    public LocaleHandle(String locale) {
+        this.value = locale;
+    }
+
+    private Object readResolve() {
+        if (value == null) {
+            return null;
+        }
+
+        if (value.length() == 0) {
+            return new Locale("");
+        }
+
+        int extStart = value.indexOf("_#");
+        if (extStart != -1) value = value.substring(0, extStart);
+
+        String language = value, country = "", variant = "";
+        int pos1 = value.indexOf('_');
+        if (pos1 != -1) {
+            language = value.substring(0, pos1++);
+
+            int pos2 = value.indexOf('_', pos1);
+            if (pos2 == -1) {
+                country = value.substring(pos1);
+            } else {
+                country = value.substring(pos1, pos2);
+                variant = value.substring(pos2 + 1);
+            }
+        }
+        return new Locale(language, country, variant);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/LocaleSerializer.java
similarity index 79%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/LocaleSerializer.java
index 72a9822..70142fa 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/LocaleSerializer.java
@@ -46,20 +46,30 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.Locale;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a locale.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class LocaleSerializer extends AbstractSerializer {
+    private static LocaleSerializer SERIALIZER = new LocaleSerializer();
+
+    public static LocaleSerializer create() {
+        return SERIALIZER;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            Locale locale = (Locale) obj;
+
+            out.writeObject(new LocaleHandle(locale.toString()));
+        }
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/MapDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/MapDeserializer.java
new file mode 100644
index 0000000..4c0a88d
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/MapDeserializer.java
@@ -0,0 +1,184 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Deserializing a JDK 1.2 Map.
+ */
+public class MapDeserializer extends AbstractMapDeserializer {
+    private Class _type;
+    private Constructor _ctor;
+
+    public MapDeserializer(Class type) {
+        if (type == null)
+            type = HashMap.class;
+
+        _type = type;
+
+        Constructor[] ctors = type.getConstructors();
+        for (int i = 0; i < ctors.length; i++) {
+            if (ctors[i].getParameterTypes().length == 0)
+                _ctor = ctors[i];
+        }
+
+        if (_ctor == null) {
+            try {
+                _ctor = HashMap.class.getConstructor(new Class[0]);
+            } catch (Exception e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+
+    @Override
+    public Class getType() {
+        if (_type != null)
+            return _type;
+        else
+            return HashMap.class;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        return readMap(in, null, null);
+    }
+
+    /**
+     *  support generic type of map, fix the type of short serialization <p>
+     *  eg: Map<String, Short> serialize & deserialize
+     *
+     */
+    @Override
+    public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {
+        Map map;
+
+        if (_type == null)
+            map = new HashMap();
+        else if (_type.equals(Map.class))
+            map = new HashMap();
+        else if (_type.equals(SortedMap.class))
+            map = new TreeMap();
+        else {
+            try {
+                map = (Map) _ctor.newInstance();
+            } catch (Exception e) {
+                throw new IOExceptionWrapper(e);
+            }
+        }
+
+        in.addRef(map);
+
+        doReadMap(in, map, expectKeyType, expectValueType);
+
+        in.readEnd();
+
+        return map;
+    }
+
+    protected void doReadMap(AbstractHessianInput in, Map map, Class<?> keyType, Class<?> valueType) throws IOException {
+        Deserializer keyDeserializer = null, valueDeserializer = null;
+
+        SerializerFactory factory = findSerializerFactory(in);
+        if(keyType != null){
+            keyDeserializer = factory.getDeserializer(keyType.getName());
+        }
+        if(valueType != null){
+            valueDeserializer = factory.getDeserializer(valueType.getName());
+        }
+
+        while (!in.isEnd()) {
+            map.put(keyDeserializer != null ? keyDeserializer.readObject(in) : in.readObject(),
+                    valueDeserializer != null? valueDeserializer.readObject(in) : in.readObject());
+        }
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in,
+                             String[] fieldNames)
+            throws IOException {
+        Map map = createMap();
+
+        int ref = in.addRef(map);
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            String name = fieldNames[i];
+
+            map.put(name, in.readObject());
+        }
+
+        return map;
+    }
+
+    private Map createMap()
+            throws IOException {
+
+        if (_type == null)
+            return new HashMap();
+        else if (_type.equals(Map.class))
+            return new HashMap();
+        else if (_type.equals(SortedMap.class))
+            return new TreeMap();
+        else {
+            try {
+                return (Map) _ctor.newInstance();
+            } catch (Exception e) {
+                throw new IOExceptionWrapper(e);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/MapSerializer.java
similarity index 62%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/MapSerializer.java
index 72a9822..7c2e4e8 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/MapSerializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,57 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a JDK 1.2 java.util.Map.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
+public class MapSerializer extends AbstractSerializer {
+    private boolean _isSendJavaType = true;
+
     /**
-     * Looks up a proxy object.
+     * Return true if the java type of the collection should be sent.
      */
+    public boolean getSendJavaType() {
+        return _isSendJavaType;
+    }
+
+    /**
+     * Set true if the java type of the collection should be sent.
+     */
+    public void setSendJavaType(boolean sendJavaType) {
+        _isSendJavaType = sendJavaType;
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (out.addRef(obj))
+            return;
+
+        Map map = (Map) obj;
+
+        Class cl = obj.getClass();
+
+        if (cl.equals(HashMap.class)
+                || !_isSendJavaType
+                || !(obj instanceof java.io.Serializable))
+            out.writeMapBegin(null);
+        else
+            out.writeMapBegin(obj.getClass().getName());
+
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+
+            out.writeObject(entry.getKey());
+            out.writeObject(entry.getValue());
+        }
+        out.writeMapEnd();
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ObjectDeserializer.java
similarity index 64%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/ObjectDeserializer.java
index 7c8e328..4a75bb7 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/BeanSerializerFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ObjectDeserializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
+ * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
  *
  * The Apache Software License, Version 1.1
  *
@@ -46,35 +46,51 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
+
+import java.io.IOException;
 
 /**
- * Factory for returning serialization methods.
+ * Serializing an object for known object types.
  */
-public class BeanSerializerFactory extends SerializerFactory {
-    /**
-     * Returns the default serializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
-     */
-    @Override
-    protected Serializer getDefaultSerializer(Class cl) {
-        return new BeanSerializer(cl, getClassLoader());
+public class ObjectDeserializer extends AbstractDeserializer {
+    private Class _cl;
+
+    public ObjectDeserializer(Class cl) {
+        _cl = cl;
     }
 
-    /**
-     * Returns the default deserializer for a class that isn't matched
-     * directly.  Application can override this method to produce
-     * bean-style serialization instead of field serialization.
-     *
-     * @param cl the class of the object that needs to be serialized.
-     * @return a serializer object for the serialization.
-     */
     @Override
-    protected Deserializer getDefaultDeserializer(Class cl) {
-        return new BeanDeserializer(cl);
+    public Class getType() {
+        return _cl;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in)
+            throws IOException {
+        return in.readObject();
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readList(AbstractHessianInput in, int length)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public Object readLengthList(AbstractHessianInput in, int length)
+            throws IOException {
+        throw new UnsupportedOperationException(String.valueOf(this));
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + _cl + "]";
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/RemoteSerializer.java
similarity index 86%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/RemoteSerializer.java
index 72a9822..8f49dce 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/RemoteSerializer.java
@@ -46,20 +46,18 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a remote object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class RemoteSerializer extends AbstractSerializer {
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        // XXX: needs to be handled as a separate class
+        throw new UnsupportedOperationException(getClass().getName());
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/Serializer.java
similarity index 87%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/Serializer.java
index be505fb..d675fc0 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/EnvelopeFactory.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/Serializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,11 +46,14 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
-import java.util.logging.Logger;
+import java.io.IOException;
 
-public class EnvelopeFactory {
-    private static final Logger log
-            = Logger.getLogger(EnvelopeFactory.class.getName());
+/**
+ * Serializing an object.
+ */
+public interface Serializer {
+    public void writeObject(Object obj, AbstractHessianOutput out)
+            throws IOException;
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/SerializerFactory.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/SerializerFactory.java
new file mode 100644
index 0000000..b513ef0
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/SerializerFactory.java
@@ -0,0 +1,722 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.io;
+
+import com.alibaba.com.caucho.hessian3.io.java8.DurationHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.InstantHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.LocalDateHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.LocalDateTimeHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.LocalTimeHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.MonthDayHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.OffsetDateTimeHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.OffsetTimeHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.PeriodHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.YearHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.YearMonthHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.ZoneIdSerializer;
+import com.alibaba.com.caucho.hessian3.io.java8.ZoneOffsetHandle;
+import com.alibaba.com.caucho.hessian3.io.java8.ZonedDateTimeHandle;
+
+import javax.management.ObjectName;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.alibaba.com.caucho.hessian3.io.java8.Java8TimeSerializer.create;
+
+/**
+ * Factory for returning serialization methods.
+ */
+public class SerializerFactory extends AbstractSerializerFactory {
+    private static final Logger log
+            = Logger.getLogger(SerializerFactory.class.getName());
+
+    private static Deserializer OBJECT_DESERIALIZER
+            = new BasicDeserializer(BasicDeserializer.OBJECT);
+    private static ConcurrentHashMap _unrecognizedTypeCache = new ConcurrentHashMap();
+    private static HashMap _staticSerializerMap;
+    private static HashMap _staticDeserializerMap;
+    private static HashMap _staticTypeMap;
+
+    static {
+        _staticSerializerMap = new HashMap();
+        _staticDeserializerMap = new HashMap();
+        _staticTypeMap = new HashMap();
+
+        addBasic(void.class, "void", BasicSerializer.NULL);
+
+        addBasic(Boolean.class, "boolean", BasicSerializer.BOOLEAN);
+        addBasic(Byte.class, "byte", BasicSerializer.BYTE);
+        addBasic(Short.class, "short", BasicSerializer.SHORT);
+        addBasic(Integer.class, "int", BasicSerializer.INTEGER);
+        addBasic(Long.class, "long", BasicSerializer.LONG);
+        addBasic(Float.class, "float", BasicSerializer.FLOAT);
+        addBasic(Double.class, "double", BasicSerializer.DOUBLE);
+        addBasic(Character.class, "char", BasicSerializer.CHARACTER_OBJECT);
+        addBasic(String.class, "string", BasicSerializer.STRING);
+        addBasic(Object.class, "object", BasicSerializer.OBJECT);
+        addBasic(java.util.Date.class, "date", BasicSerializer.DATE);
+
+        addBasic(boolean.class, "boolean", BasicSerializer.BOOLEAN);
+        addBasic(byte.class, "byte", BasicSerializer.BYTE);
+        addBasic(short.class, "short", BasicSerializer.SHORT);
+        addBasic(int.class, "int", BasicSerializer.INTEGER);
+        addBasic(long.class, "long", BasicSerializer.LONG);
+        addBasic(float.class, "float", BasicSerializer.FLOAT);
+        addBasic(double.class, "double", BasicSerializer.DOUBLE);
+        addBasic(char.class, "char", BasicSerializer.CHARACTER);
+
+        addBasic(boolean[].class, "[boolean", BasicSerializer.BOOLEAN_ARRAY);
+        addBasic(byte[].class, "[byte", BasicSerializer.BYTE_ARRAY);
+        addBasic(short[].class, "[short", BasicSerializer.SHORT_ARRAY);
+        addBasic(int[].class, "[int", BasicSerializer.INTEGER_ARRAY);
+        addBasic(long[].class, "[long", BasicSerializer.LONG_ARRAY);
+        addBasic(float[].class, "[float", BasicSerializer.FLOAT_ARRAY);
+        addBasic(double[].class, "[double", BasicSerializer.DOUBLE_ARRAY);
+        addBasic(char[].class, "[char", BasicSerializer.CHARACTER_ARRAY);
+        addBasic(String[].class, "[string", BasicSerializer.STRING_ARRAY);
+        addBasic(Object[].class, "[object", BasicSerializer.OBJECT_ARRAY);
+
+        _staticSerializerMap.put(Class.class, new ClassSerializer());
+
+        _staticDeserializerMap.put(Number.class, new BasicDeserializer(BasicSerializer.NUMBER));
+
+        _staticSerializerMap.put(BigDecimal.class, new StringValueSerializer());
+        try {
+            _staticDeserializerMap.put(BigDecimal.class,
+                    new StringValueDeserializer(BigDecimal.class));
+            _staticDeserializerMap.put(BigInteger.class,
+                    new BigIntegerDeserializer());
+        } catch (Throwable e) {
+        }
+
+
+        _staticSerializerMap.put(UUID.class, new StringValueSerializer());
+        _staticDeserializerMap.put(UUID.class, new UUIDDeserializer());
+
+        _staticSerializerMap.put(File.class, new StringValueSerializer());
+        try {
+            _staticDeserializerMap.put(File.class,
+                    new StringValueDeserializer(File.class));
+        } catch (Throwable e) {
+        }
+
+        _staticSerializerMap.put(ObjectName.class, new StringValueSerializer());
+        try {
+            _staticDeserializerMap.put(ObjectName.class,
+                    new StringValueDeserializer(ObjectName.class));
+        } catch (Throwable e) {
+        }
+
+        _staticSerializerMap.put(java.sql.Date.class, new SqlDateSerializer());
+        _staticSerializerMap.put(java.sql.Time.class, new SqlDateSerializer());
+        _staticSerializerMap.put(java.sql.Timestamp.class, new SqlDateSerializer());
+
+        _staticSerializerMap.put(java.io.InputStream.class,
+                new InputStreamSerializer());
+        _staticDeserializerMap.put(java.io.InputStream.class,
+                new InputStreamDeserializer());
+
+        try {
+            _staticDeserializerMap.put(java.sql.Date.class,
+                    new SqlDateDeserializer(java.sql.Date.class));
+            _staticDeserializerMap.put(java.sql.Time.class,
+                    new SqlDateDeserializer(java.sql.Time.class));
+            _staticDeserializerMap.put(java.sql.Timestamp.class,
+                    new SqlDateDeserializer(java.sql.Timestamp.class));
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+
+        // hessian/3bb5
+        try {
+            Class stackTrace = StackTraceElement.class;
+
+            _staticDeserializerMap.put(stackTrace, new StackTraceElementDeserializer());
+        } catch (Throwable e) {
+        }
+
+        try {
+            if (isJava8()) {
+                _staticSerializerMap.put(Class.forName("java.time.LocalTime"), create(LocalTimeHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.LocalDate"), create(LocalDateHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.LocalDateTime"), create(LocalDateTimeHandle.class));
+
+                _staticSerializerMap.put(Class.forName("java.time.Instant"), create(InstantHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.Duration"), create(DurationHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.Period"), create(PeriodHandle.class));
+
+                _staticSerializerMap.put(Class.forName("java.time.Year"), create(YearHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.YearMonth"), create(YearMonthHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.MonthDay"), create(MonthDayHandle.class));
+
+                _staticSerializerMap.put(Class.forName("java.time.OffsetDateTime"), create(OffsetDateTimeHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.ZoneOffset"), create(ZoneOffsetHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.OffsetTime"), create(OffsetTimeHandle.class));
+                _staticSerializerMap.put(Class.forName("java.time.ZonedDateTime"), create(ZonedDateTimeHandle.class));
+            }
+        } catch (Throwable t) {
+            log.warning(String.valueOf(t.getCause()));
+        }
+    }
+
+    protected Serializer _defaultSerializer;
+
+    // Additional factories
+    protected ArrayList _factories = new ArrayList();
+
+    protected CollectionSerializer _collectionSerializer;
+    protected MapSerializer _mapSerializer;
+    private ClassLoader _loader;
+    private Deserializer _hashMapDeserializer;
+    private Deserializer _arrayListDeserializer;
+    private ConcurrentHashMap _cachedSerializerMap;
+    private ConcurrentHashMap _cachedDeserializerMap;
+    private ConcurrentHashMap _cachedTypeDeserializerMap;
+    private boolean _isAllowNonSerializable;
+    /**
+     * For those classes are unknown in current classloader, record them in this set to avoid
+     * frequently class loading and to reduce performance overhead.
+     */
+    private Map<String, Object> _typeNotFoundDeserializerMap = new ConcurrentHashMap<>(8);
+    private static final Object PRESENT = new Object();
+
+    private ClassFactory _classFactory;
+
+    public SerializerFactory() {
+        this(Thread.currentThread().getContextClassLoader());
+    }
+
+    public SerializerFactory(ClassLoader loader) {
+        _loader = loader;
+    }
+
+    public Class<?> loadSerializedClass(String className)
+            throws ClassNotFoundException
+    {
+        return getClassFactory().load(className);
+    }
+
+    public ClassFactory getClassFactory()
+    {
+        synchronized (this) {
+            if (_classFactory == null) {
+                _classFactory = new ClassFactory(getClassLoader());
+            }
+
+            return _classFactory;
+        }
+    }
+
+    private static void addBasic(Class cl, String typeName, int type) {
+        _staticSerializerMap.put(cl, new BasicSerializer(type));
+
+        Deserializer deserializer = new BasicDeserializer(type);
+        _staticDeserializerMap.put(cl, deserializer);
+        _staticTypeMap.put(typeName, deserializer);
+    }
+
+    public ClassLoader getClassLoader() {
+        return _loader;
+    }
+
+    /**
+     * Set true if the collection serializer should send the java type.
+     */
+    public void setSendCollectionType(boolean isSendType) {
+        if (_collectionSerializer == null)
+            _collectionSerializer = new CollectionSerializer();
+
+        _collectionSerializer.setSendJavaType(isSendType);
+
+        if (_mapSerializer == null)
+            _mapSerializer = new MapSerializer();
+
+        _mapSerializer.setSendJavaType(isSendType);
+    }
+
+    /**
+     * Adds a factory.
+     */
+    public void addFactory(AbstractSerializerFactory factory) {
+        _factories.add(factory);
+    }
+
+    /**
+     * If true, non-serializable objects are allowed.
+     */
+    public boolean isAllowNonSerializable() {
+        return _isAllowNonSerializable;
+    }
+
+    /**
+     * If true, non-serializable objects are allowed.
+     */
+    public void setAllowNonSerializable(boolean allow) {
+        _isAllowNonSerializable = allow;
+    }
+
+    /**
+     * Returns the serializer for a class.
+     *
+     * @param cl the class of the object that needs to be serialized.
+     * @return a serializer object for the serialization.
+     */
+    @Override
+    public Serializer getSerializer(Class cl)
+            throws HessianProtocolException {
+        Serializer serializer;
+
+        serializer = (Serializer) _staticSerializerMap.get(cl);
+        if (serializer != null) {
+            return serializer;
+        }
+
+        if (_cachedSerializerMap != null) {
+            serializer = (Serializer) _cachedSerializerMap.get(cl);
+            if (serializer != null) {
+                return serializer;
+            }
+        }
+
+        for (int i = 0;
+             serializer == null && _factories != null && i < _factories.size();
+             i++) {
+            AbstractSerializerFactory factory;
+
+            factory = (AbstractSerializerFactory) _factories.get(i);
+
+            serializer = factory.getSerializer(cl);
+        }
+
+        if (serializer != null) {
+
+        } else if (isZoneId(cl)) //must before "else if (JavaSerializer.getWriteReplace(cl) != null)"
+            serializer = ZoneIdSerializer.getInstance();
+        else if (isEnumSet(cl))
+            serializer = EnumSetSerializer.getInstance();
+        else if (JavaSerializer.getWriteReplace(cl) != null)
+            serializer = new JavaSerializer(cl, _loader);
+
+        else if (HessianRemoteObject.class.isAssignableFrom(cl))
+            serializer = new RemoteSerializer();
+
+//    else if (BurlapRemoteObject.class.isAssignableFrom(cl))
+//      serializer = new RemoteSerializer();
+
+        else if (Map.class.isAssignableFrom(cl)) {
+            if (_mapSerializer == null)
+                _mapSerializer = new MapSerializer();
+
+            serializer = _mapSerializer;
+        } else if (Collection.class.isAssignableFrom(cl)) {
+            if (_collectionSerializer == null) {
+                _collectionSerializer = new CollectionSerializer();
+            }
+
+            serializer = _collectionSerializer;
+        } else if (cl.isArray()) {
+            serializer = new ArraySerializer();
+        } else if (Throwable.class.isAssignableFrom(cl)) {
+            serializer = new ThrowableSerializer(cl, getClassLoader());
+        } else if (InputStream.class.isAssignableFrom(cl)) {
+            serializer = new InputStreamSerializer();
+        } else if (Iterator.class.isAssignableFrom(cl)) {
+            serializer = IteratorSerializer.create();
+        } else if (Enumeration.class.isAssignableFrom(cl)) {
+            serializer = EnumerationSerializer.create();
+        } else if (Calendar.class.isAssignableFrom(cl)) {
+            serializer = CalendarSerializer.create();
+        } else if (Locale.class.isAssignableFrom(cl)) {
+            serializer = LocaleSerializer.create();
+        } else if (Enum.class.isAssignableFrom(cl)) {
+            serializer = new EnumSerializer(cl);
+        } else if (BitSet.class.isAssignableFrom(cl)) {
+            serializer = BitSetSerializer.create();
+        }
+
+        if (serializer == null) {
+            serializer = getDefaultSerializer(cl);
+        }
+
+        if (_cachedSerializerMap == null) {
+            _cachedSerializerMap = new ConcurrentHashMap(8);
+        }
+
+        _cachedSerializerMap.put(cl, serializer);
+
+        return serializer;
+    }
+
+    /**
+     * Returns the default serializer for a class that isn't matched
+     * directly.  Application can override this method to produce
+     * bean-style serialization instead of field serialization.
+     *
+     * @param cl the class of the object that needs to be serialized.
+     * @return a serializer object for the serialization.
+     */
+    protected Serializer getDefaultSerializer(Class cl) {
+        if (_defaultSerializer != null)
+            return _defaultSerializer;
+
+        if (!Serializable.class.isAssignableFrom(cl)
+                && !_isAllowNonSerializable) {
+            throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable");
+        }
+
+        return new JavaSerializer(cl, _loader);
+    }
+
+    /**
+     * Returns the deserializer for a class.
+     *
+     * @param cl the class of the object that needs to be deserialized.
+     * @return a deserializer object for the serialization.
+     */
+    @Override
+    public Deserializer getDeserializer(Class cl)
+            throws HessianProtocolException {
+        Deserializer deserializer;
+
+        deserializer = (Deserializer) _staticDeserializerMap.get(cl);
+        if (deserializer != null)
+            return deserializer;
+
+        if (_cachedDeserializerMap != null) {
+            deserializer = (Deserializer) _cachedDeserializerMap.get(cl);
+            if (deserializer != null)
+                return deserializer;
+        }
+
+
+        for (int i = 0;
+             deserializer == null && _factories != null && i < _factories.size();
+             i++) {
+            AbstractSerializerFactory factory;
+            factory = (AbstractSerializerFactory) _factories.get(i);
+
+            deserializer = factory.getDeserializer(cl);
+        }
+
+        if (deserializer != null) {
+        } else if (Collection.class.isAssignableFrom(cl))
+            deserializer = new CollectionDeserializer(cl);
+
+        else if (Map.class.isAssignableFrom(cl))
+            deserializer = new MapDeserializer(cl);
+
+        else if (cl.isInterface())
+            deserializer = new ObjectDeserializer(cl);
+
+        else if (cl.isArray())
+            deserializer = new ArrayDeserializer(cl.getComponentType());
+
+        else if (Enumeration.class.isAssignableFrom(cl))
+            deserializer = EnumerationDeserializer.create();
+
+        else if (Enum.class.isAssignableFrom(cl))
+            deserializer = new EnumDeserializer(cl);
+
+        else if (Class.class.equals(cl))
+            deserializer = new ClassDeserializer(_loader);
+
+        else
+            deserializer = getDefaultDeserializer(cl);
+
+        if (_cachedDeserializerMap == null)
+            _cachedDeserializerMap = new ConcurrentHashMap(8);
+
+        _cachedDeserializerMap.put(cl, deserializer);
+
+        return deserializer;
+    }
+
+    /**
+     * Returns the default serializer for a class that isn't matched
+     * directly.  Application can override this method to produce
+     * bean-style serialization instead of field serialization.
+     *
+     * @param cl the class of the object that needs to be serialized.
+     * @return a serializer object for the serialization.
+     */
+    protected Deserializer getDefaultDeserializer(Class cl) {
+        if (!Serializable.class.isAssignableFrom(cl)
+                && !_isAllowNonSerializable) {
+            throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable");
+        }
+
+        return new JavaDeserializer(cl);
+    }
+
+    /**
+     * Reads the object as a list.
+     */
+    public Object readList(AbstractHessianInput in, int length, String type)
+            throws HessianProtocolException, IOException {
+        Deserializer deserializer = getDeserializer(type);
+
+        if (deserializer != null)
+            return deserializer.readList(in, length);
+        else
+            return new CollectionDeserializer(ArrayList.class).readList(in, length);
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Object readMap(AbstractHessianInput in, String type)
+            throws HessianProtocolException, IOException {
+        return readMap(in, type, null, null);
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Object readMap(AbstractHessianInput in, String type, Class<?> expectKeyType, Class<?> expectValueType)
+            throws HessianProtocolException, IOException {
+        Deserializer deserializer = getDeserializer(type);
+
+        if (deserializer != null)
+            return deserializer.readMap(in);
+        else if (_hashMapDeserializer != null)
+            return _hashMapDeserializer.readMap(in, expectKeyType, expectValueType);
+        else {
+            _hashMapDeserializer = new MapDeserializer(HashMap.class);
+
+            return _hashMapDeserializer.readMap(in, expectKeyType, expectValueType);
+        }
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Object readObject(AbstractHessianInput in,
+                             String type,
+                             String[] fieldNames)
+            throws HessianProtocolException, IOException {
+        Deserializer deserializer = getDeserializer(type);
+
+        if (deserializer != null)
+            return deserializer.readObject(in, fieldNames);
+        else if (_hashMapDeserializer != null)
+            return _hashMapDeserializer.readObject(in, fieldNames);
+        else {
+            _hashMapDeserializer = new MapDeserializer(HashMap.class);
+
+            return _hashMapDeserializer.readObject(in, fieldNames);
+        }
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Deserializer getObjectDeserializer(String type, Class cl)
+            throws HessianProtocolException {
+        Deserializer reader = getObjectDeserializer(type);
+
+        if (cl == null
+                || cl.equals(reader.getType())
+                || cl.isAssignableFrom(reader.getType())
+                || HessianHandle.class.isAssignableFrom(reader.getType())) {
+            return reader;
+        }
+
+        if (log.isLoggable(Level.FINE)) {
+            log.fine("hessian: expected '" + cl.getName() + "' at '" + type + "' ("
+                    + reader.getType().getName() + ")");
+        }
+
+        return getDeserializer(cl);
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Deserializer getObjectDeserializer(String type)
+            throws HessianProtocolException {
+        Deserializer deserializer = getDeserializer(type);
+
+        if (deserializer != null)
+            return deserializer;
+        else if (_hashMapDeserializer != null)
+            return _hashMapDeserializer;
+        else {
+            _hashMapDeserializer = new MapDeserializer(HashMap.class);
+
+            return _hashMapDeserializer;
+        }
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Deserializer getListDeserializer(String type, Class cl)
+            throws HessianProtocolException {
+        Deserializer reader = getListDeserializer(type);
+
+        if (cl == null
+                || cl.equals(reader.getType())
+                || cl.isAssignableFrom(reader.getType())) {
+            return reader;
+        }
+
+        if (log.isLoggable(Level.FINE)) {
+            log.fine("hessian: expected '" + cl.getName() + "' at '" + type + "' ("
+                    + reader.getType().getName() + ")");
+        }
+
+        return getDeserializer(cl);
+    }
+
+    /**
+     * Reads the object as a map.
+     */
+    public Deserializer getListDeserializer(String type)
+            throws HessianProtocolException {
+        Deserializer deserializer = getDeserializer(type);
+
+        if (deserializer != null)
+            return deserializer;
+        else if (_arrayListDeserializer != null)
+            return _arrayListDeserializer;
+        else {
+            _arrayListDeserializer = new CollectionDeserializer(ArrayList.class);
+
+            return _arrayListDeserializer;
+        }
+    }
+
+    /**
+     * Returns a deserializer based on a string type.
+     */
+    public Deserializer getDeserializer(String type)
+            throws HessianProtocolException {
+        if (type == null || type.equals("") || _typeNotFoundDeserializerMap.containsKey(type))
+            return null;
+
+        Deserializer deserializer;
+
+        if (_cachedTypeDeserializerMap != null) {
+            deserializer = (Deserializer) _cachedTypeDeserializerMap.get(type);
+
+            if (deserializer != null)
+                return deserializer;
+        }
+
+
+        deserializer = (Deserializer) _staticTypeMap.get(type);
+        if (deserializer != null)
+            return deserializer;
+
+        if (type.startsWith("[")) {
+            Deserializer subDeserializer = getDeserializer(type.substring(1));
+
+            if (subDeserializer != null)
+                deserializer = new ArrayDeserializer(subDeserializer.getType());
+            else
+                deserializer = new ArrayDeserializer(Object.class);
+        } else if (_unrecognizedTypeCache.get(type) == null) {
+            try {
+//                Class cl = Class.forName(type, false, _loader);
+                Class cl = loadSerializedClass(type);
+                deserializer = getDeserializer(cl);
+            } catch (Exception e) {
+                log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e);
+                _typeNotFoundDeserializerMap.put(type, PRESENT);
+                log.log(Level.FINER, e.toString(), e);
+                _unrecognizedTypeCache.put(type, new AtomicLong(1L));
+            }
+        } else {
+            ((AtomicLong) _unrecognizedTypeCache.get(type)).incrementAndGet();
+            if (((AtomicLong) _unrecognizedTypeCache.get(type)).get() % 2000L == 0L)
+                ((AtomicLong) _unrecognizedTypeCache.get(type)).getAndSet(1L);
+        }
+
+        if (deserializer != null) {
+            if (_cachedTypeDeserializerMap == null)
+                _cachedTypeDeserializerMap = new ConcurrentHashMap(8);
+
+            _cachedTypeDeserializerMap.put(type, deserializer);
+        }
+
+        return deserializer;
+    }
+
+    private static boolean isZoneId(Class cl) {
+        try {
+            return isJava8() && Class.forName("java.time.ZoneId").isAssignableFrom(cl);
+        } catch (ClassNotFoundException e) {
+            // ignore
+        }
+        return false;
+    }
+
+    private static boolean isEnumSet(Class cl) {
+        return EnumSet.class.isAssignableFrom(cl);
+    }
+
+    /**
+     * check if the environment is java 8 or beyond
+     *
+     * @return if on java 8
+     */
+    private static boolean isJava8() {
+        String javaVersion = System.getProperty("java.specification.version");
+        return Double.valueOf(javaVersion) >= 1.8;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/SqlDateDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/SqlDateDeserializer.java
new file mode 100644
index 0000000..f9c5660
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/SqlDateDeserializer.java
@@ -0,0 +1,131 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+/**
+ * Deserializing a string valued object
+ */
+public class SqlDateDeserializer extends AbstractDeserializer {
+    private Class _cl;
+    private Constructor _constructor;
+
+    public SqlDateDeserializer(Class cl)
+            throws NoSuchMethodException {
+        _cl = cl;
+        _constructor = cl.getConstructor(new Class[]{long.class});
+    }
+
+    @Override
+    public Class getType() {
+        return _cl;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        int ref = in.addRef(null);
+
+        long initValue = Long.MIN_VALUE;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("value"))
+                initValue = in.readUTCDate();
+            else
+                in.readString();
+        }
+
+        in.readMapEnd();
+
+        Object value = create(initValue);
+
+        in.setRef(ref, value);
+
+        return value;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        int ref = in.addRef(null);
+
+        long initValue = Long.MIN_VALUE;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            String key = fieldNames[i];
+
+            if (key.equals("value"))
+                initValue = in.readUTCDate();
+            else
+                in.readObject();
+        }
+
+        Object value = create(initValue);
+
+        in.setRef(ref, value);
+
+        return value;
+    }
+
+    private Object create(long initValue)
+            throws IOException {
+        if (initValue == Long.MIN_VALUE)
+            throw new IOException(_cl.getName() + " expects name.");
+
+        try {
+            return _constructor.newInstance(new Object[]{new Long(initValue)});
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/SqlDateSerializer.java
similarity index 71%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/SqlDateSerializer.java
index 72a9822..067960d 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/SqlDateSerializer.java
@@ -46,20 +46,41 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
+import java.util.Date;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a sql date object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class SqlDateSerializer extends AbstractSerializer {
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            Class cl = obj.getClass();
+
+            if (out.addRef(obj))
+                return;
+
+            int ref = out.writeObjectBegin(cl.getName());
+
+            if (ref < -1) {
+                out.writeString("value");
+                out.writeUTCDate(((Date) obj).getTime());
+                out.writeMapEnd();
+            } else {
+                if (ref == -1) {
+                    out.writeInt(1);
+                    out.writeString("value");
+                    out.writeObjectBegin(cl.getName());
+                }
+
+                out.writeUTCDate(((Date) obj).getTime());
+            }
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/StackTraceElementDeserializer.java
similarity index 84%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/StackTraceElementDeserializer.java
index 72a9822..234d790 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/StackTraceElementDeserializer.java
@@ -46,20 +46,19 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
-
-import java.io.IOException;
+package com.alibaba.com.caucho.hessian3.io;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing a JDK 1.4 StackTraceElement
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class StackTraceElementDeserializer extends JavaDeserializer {
+    public StackTraceElementDeserializer() {
+        super(StackTraceElement.class);
+    }
+
     @Override
-    public Object lookup(String type, String url)
-            throws IOException {
-        return new HessianRemote(type, url);
+    protected Object instantiate()
+            throws Exception {
+        return new StackTraceElement("", "", "", 0);
     }
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/StringValueDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/StringValueDeserializer.java
new file mode 100644
index 0000000..fbab268
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/StringValueDeserializer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.hessian3.io;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+/**
+ * Deserializing a string valued object
+ */
+public class StringValueDeserializer extends AbstractDeserializer {
+    private Class _cl;
+    private Constructor _constructor;
+
+    public StringValueDeserializer(Class cl) {
+        try {
+            _cl = cl;
+            _constructor = cl.getConstructor(new Class[]{String.class});
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Class getType() {
+        return _cl;
+    }
+
+    @Override
+    public Object readMap(AbstractHessianInput in)
+            throws IOException {
+        String value = null;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("value"))
+                value = in.readString();
+            else
+                in.readObject();
+        }
+
+        in.readMapEnd();
+
+        Object object = create(value);
+
+        in.addRef(object);
+
+        return object;
+    }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        String value = null;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            if ("value".equals(fieldNames[i]))
+                value = in.readString();
+            else
+                in.readObject();
+        }
+
+        Object object = create(value);
+
+        in.addRef(object);
+
+        return object;
+    }
+
+    private Object create(String value)
+            throws IOException {
+        if (value == null)
+            throw new IOException(_cl.getName() + " expects name.");
+
+        try {
+            return _constructor.newInstance(new Object[]{value});
+        } catch (Exception e) {
+            throw new IOExceptionWrapper(e);
+        }
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/StringValueSerializer.java
similarity index 72%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/StringValueSerializer.java
index 72a9822..90c82f4 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/StringValueSerializer.java
@@ -46,20 +46,40 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing a remote object.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class StringValueSerializer extends AbstractSerializer {
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        if (obj == null)
+            out.writeNull();
+        else {
+            if (out.addRef(obj))
+                return;
+
+            Class cl = obj.getClass();
+
+            int ref = out.writeObjectBegin(cl.getName());
+
+            if (ref < -1) {
+                out.writeString("value");
+                out.writeString(obj.toString());
+                out.writeMapEnd();
+            } else {
+                if (ref == -1) {
+                    out.writeInt(1);
+                    out.writeString("value");
+                    out.writeObjectBegin(cl.getName());
+                }
+
+                out.writeString(obj.toString());
+            }
+        }
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ThrowableSerializer.java
similarity index 81%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/ThrowableSerializer.java
index 72a9822..287497a 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ThrowableSerializer.java
@@ -22,7 +22,7 @@
  *    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
+ * 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.
@@ -46,20 +46,25 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Serializing an object for known object types.
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+public class ThrowableSerializer extends JavaSerializer {
+    public ThrowableSerializer(Class cl, ClassLoader loader) {
+        super(cl, loader);
+    }
+
     @Override
-    public Object lookup(String type, String url)
+    public void writeObject(Object obj, AbstractHessianOutput out)
             throws IOException {
-        return new HessianRemote(type, url);
+        Throwable e = (Throwable) obj;
+
+        e.getStackTrace();
+
+        super.writeObject(obj, out);
     }
 }
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/UUIDDeserializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/UUIDDeserializer.java
similarity index 92%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/UUIDDeserializer.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/UUIDDeserializer.java
index ffe1fca..229968c 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/UUIDDeserializer.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/UUIDDeserializer.java
@@ -1,4 +1,4 @@
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 import java.util.UUID;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/ValueDeserializer.java
similarity index 69%
copy from src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
copy to src/test/java/com/alibaba/com/caucho/hessian3/io/ValueDeserializer.java
index 72a9822..76ab5a2 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/AbstractHessianResolver.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/ValueDeserializer.java
@@ -46,20 +46,48 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.io;
+package com.alibaba.com.caucho.hessian3.io;
 
 import java.io.IOException;
 
 /**
- * Looks up remote objects.  The default just returns a HessianRemote object.
+ * Deserializing a string valued object
  */
-public class AbstractHessianResolver implements HessianRemoteResolver {
-    /**
-     * Looks up a proxy object.
-     */
+abstract public class ValueDeserializer extends AbstractDeserializer {
     @Override
-    public Object lookup(String type, String url)
+    public Object readMap(AbstractHessianInput in)
             throws IOException {
-        return new HessianRemote(type, url);
+        String initValue = null;
+
+        while (!in.isEnd()) {
+            String key = in.readString();
+
+            if (key.equals("value"))
+                initValue = in.readString();
+            else
+                in.readObject();
+        }
+
+        in.readMapEnd();
+
+        return create(initValue);
     }
+
+    @Override
+    public Object readObject(AbstractHessianInput in, String[] fieldNames)
+            throws IOException {
+        String initValue = null;
+
+        for (int i = 0; i < fieldNames.length; i++) {
+            if ("value".equals(fieldNames[i]))
+                initValue = in.readString();
+            else
+                in.readObject();
+        }
+
+        return create(initValue);
+    }
+
+    abstract Object create(String value)
+            throws IOException;
 }
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/DurationHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/DurationHandle.java
new file mode 100755
index 0000000..e06f57d
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/DurationHandle.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+
+@SuppressWarnings("unchecked")
+public class DurationHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -4367309317780077156L;
+
+    private long seconds;
+    private int nanos;
+
+    public DurationHandle() {
+    }
+
+    public DurationHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.Duration");
+            Method m = c.getDeclaredMethod("getSeconds");
+            this.seconds = (Long) m.invoke(o);
+            m = c.getDeclaredMethod("getNano");
+            this.nanos = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.Duration");
+            Method m = c.getDeclaredMethod("ofSeconds", long.class, long.class);
+            return m.invoke(null, seconds, nanos);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/InstantHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/InstantHandle.java
new file mode 100755
index 0000000..060c855
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/InstantHandle.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class InstantHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -4367309317780077156L;
+
+    private long seconds;
+    private int nanos;
+
+    public InstantHandle() {
+    }
+
+    public InstantHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.Instant");
+            Method m = c.getDeclaredMethod("getEpochSecond");
+            this.seconds = (Long) m.invoke(o);
+            m = c.getDeclaredMethod("getNano");
+            this.nanos = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.Instant");
+            Method m = c.getDeclaredMethod("ofEpochSecond", long.class, long.class);
+            return m.invoke(null, seconds, nanos);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/io/java8/Java8TimeSerializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/Java8TimeSerializer.java
similarity index 90%
rename from src/main/java/com/alibaba/com/caucho/hessian/io/java8/Java8TimeSerializer.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/io/java8/Java8TimeSerializer.java
index dcc09ec..32af662 100755
--- a/src/main/java/com/alibaba/com/caucho/hessian/io/java8/Java8TimeSerializer.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/Java8TimeSerializer.java
@@ -15,13 +15,13 @@
  * limitations under the License.
  */
 
-package com.alibaba.com.caucho.hessian.io.java8;
+package com.alibaba.com.caucho.hessian3.io.java8;
 
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 
-import com.alibaba.com.caucho.hessian.io.AbstractHessianOutput;
-import com.alibaba.com.caucho.hessian.io.AbstractSerializer;
+import com.alibaba.com.caucho.hessian3.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian3.io.AbstractSerializer;
 
 public class Java8TimeSerializer<T> extends AbstractSerializer {
 
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalDateHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalDateHandle.java
new file mode 100755
index 0000000..541d920
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalDateHandle.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class LocalDateHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = 166018689500019951L;
+
+    private int year;
+    private int month;
+    private int day;
+
+    public LocalDateHandle() {
+    }
+
+    public LocalDateHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.LocalDate");
+            Method m = c.getDeclaredMethod("getYear");
+            this.year = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getMonthValue");
+            this.month = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getDayOfMonth");
+            this.day = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    public Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.LocalDate");
+            Method m = c.getDeclaredMethod("of", int.class, int.class, int.class);
+            return m.invoke(null, year, month, day);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalDateTimeHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalDateTimeHandle.java
new file mode 100755
index 0000000..042fd24
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalDateTimeHandle.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class LocalDateTimeHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = 7563825215275989361L;
+
+    private Object date;
+    private Object time;
+
+    public LocalDateTimeHandle() {
+    }
+
+    public LocalDateTimeHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.LocalDateTime");
+            Method m = c.getDeclaredMethod("toLocalDate");
+            date = m.invoke(o);
+            m = c.getDeclaredMethod("toLocalTime");
+            time = m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.LocalDateTime");
+            Method m = c.getDeclaredMethod("of", Class.forName("java.time.LocalDate"),
+                    Class.forName("java.time.LocalTime"));
+            return m.invoke(null, date, time);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalTimeHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalTimeHandle.java
new file mode 100755
index 0000000..695c278
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/LocalTimeHandle.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class LocalTimeHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -5892919085390462315L;
+
+    private int hour;
+    private int minute;
+    private int second;
+    private int nano;
+
+    public LocalTimeHandle() {
+    }
+
+    public LocalTimeHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.LocalTime");
+            Method m = c.getDeclaredMethod("getHour");
+            this.hour = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getMinute");
+            this.minute = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getSecond");
+            this.second = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getNano");
+            this.nano = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.LocalTime");
+            Method m = c.getDeclaredMethod("of", int.class, int.class, int.class, int.class);
+            return m.invoke(null, hour, minute, second, nano);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/MonthDayHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/MonthDayHandle.java
new file mode 100755
index 0000000..c27b390
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/MonthDayHandle.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class MonthDayHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = 5288238558666577745L;
+
+    private int month;
+    private int day;
+
+    public MonthDayHandle() {
+    }
+
+    public MonthDayHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.MonthDay");
+            Method m = c.getDeclaredMethod("getMonthValue");
+            this.month = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getDayOfMonth");
+            this.day = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.MonthDay");
+            Method m = c.getDeclaredMethod("of", int.class, int.class);
+            return m.invoke(null, month, day);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/OffsetDateTimeHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/OffsetDateTimeHandle.java
new file mode 100755
index 0000000..4330a2e
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/OffsetDateTimeHandle.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class OffsetDateTimeHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -7823900532640515312L;
+
+    private Object dateTime;
+    private Object offset;
+
+    public OffsetDateTimeHandle() {
+    }
+
+    public OffsetDateTimeHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.OffsetDateTime");
+            Method m = c.getDeclaredMethod("toLocalDateTime");
+            this.dateTime = m.invoke(o);
+            m = c.getDeclaredMethod("getOffset");
+            this.offset = m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+
+        try {
+            Class c = Class.forName("java.time.OffsetDateTime");
+            Method m = c.getDeclaredMethod("of", Class.forName("java.time.LocalDateTime"),
+                    Class.forName("java.time.ZoneOffset"));
+            return m.invoke(null, dateTime, offset);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/OffsetTimeHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/OffsetTimeHandle.java
new file mode 100755
index 0000000..8d17080
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/OffsetTimeHandle.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class OffsetTimeHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -3269846941421652860L;
+
+    private Object localTime;
+    private Object zoneOffset;
+
+    public OffsetTimeHandle() {
+    }
+
+    public OffsetTimeHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.OffsetTime");
+            Method m = c.getDeclaredMethod("getOffset");
+            this.zoneOffset = m.invoke(o);
+            m = c.getDeclaredMethod("toLocalTime");
+            this.localTime = m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.OffsetTime");
+            Method m = c.getDeclaredMethod("of", Class.forName("java.time.LocalTime"),
+                    Class.forName("java.time.ZoneOffset"));
+            return m.invoke(null, localTime, zoneOffset);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/PeriodHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/PeriodHandle.java
new file mode 100755
index 0000000..b02893d
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/PeriodHandle.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+
+@SuppressWarnings("unchecked")
+public class PeriodHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = 4399720381283781186L;
+
+    private int years;
+    private int months;
+    private int days;
+
+    public PeriodHandle() {
+    }
+
+    public PeriodHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.Period");
+            Method m = c.getDeclaredMethod("getYears");
+            this.years = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getMonths");
+            this.months = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getDays");
+            this.days = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.Period");
+            Method m = c.getDeclaredMethod("of", int.class, int.class, int.class);
+            return m.invoke(null, years, months, days);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/YearHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/YearHandle.java
new file mode 100755
index 0000000..99544fd
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/YearHandle.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class YearHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -6299552890287487926L;
+
+    private int year;
+
+    public YearHandle() {
+    }
+
+    public YearHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.Year");
+            Method m = c.getDeclaredMethod("getValue");
+            this.year = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.Year");
+            Method m = c.getDeclaredMethod("of", int.class);
+            return m.invoke(null, year);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/YearMonthHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/YearMonthHandle.java
new file mode 100755
index 0000000..7ae3cf3
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/YearMonthHandle.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class YearMonthHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -4150786187896925314L;
+
+    private int year;
+    private int month;
+
+    public YearMonthHandle() {
+    }
+
+    public YearMonthHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.YearMonth");
+            Method m = c.getDeclaredMethod("getYear");
+            this.year = (Integer) m.invoke(o);
+            m = c.getDeclaredMethod("getMonthValue");
+            this.month = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.YearMonth");
+            Method m = c.getDeclaredMethod("of", int.class, int.class);
+            return m.invoke(null, year, month);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneIdHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneIdHandle.java
new file mode 100755
index 0000000..97d722d
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneIdHandle.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class ZoneIdHandle implements HessianHandle, Serializable {
+
+    private static final long serialVersionUID = 8789182864066905552L;
+
+    private String zoneId;
+
+    public ZoneIdHandle() {
+    }
+
+    public ZoneIdHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.ZoneId");
+            Method m = c.getDeclaredMethod("getId");
+            this.zoneId = (String) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.ZoneId");
+            Method m = c.getDeclaredMethod("of", String.class);
+            return m.invoke(null, this.zoneId);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneIdSerializer.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneIdSerializer.java
new file mode 100755
index 0000000..dbf3363
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneIdSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.AbstractHessianOutput;
+import com.alibaba.com.caucho.hessian3.io.AbstractSerializer;
+
+import java.io.IOException;
+
+public class ZoneIdSerializer extends AbstractSerializer {
+
+    private static final ZoneIdSerializer SERIALIZER = new ZoneIdSerializer();
+
+    public static ZoneIdSerializer getInstance() {
+        return SERIALIZER;
+    }
+
+    @Override
+    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
+        if (obj == null) {
+            out.writeNull();
+        } else {
+            out.writeObject(new ZoneIdHandle(obj));
+        }
+    }
+
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneOffsetHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneOffsetHandle.java
new file mode 100755
index 0000000..d25dcb8
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZoneOffsetHandle.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class ZoneOffsetHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = 8841589723587858789L;
+
+    private int seconds;
+
+    public ZoneOffsetHandle() {
+    }
+
+    public ZoneOffsetHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.ZoneOffset");
+            Method m = c.getDeclaredMethod("getTotalSeconds");
+            this.seconds = (Integer) m.invoke(o);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class c = Class.forName("java.time.ZoneOffset");
+            Method m = c.getDeclaredMethod("ofTotalSeconds", int.class);
+            return m.invoke(null, seconds);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZonedDateTimeHandle.java b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZonedDateTimeHandle.java
new file mode 100755
index 0000000..4ab5100
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/io/java8/ZonedDateTimeHandle.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.com.caucho.hessian3.io.java8;
+
+import com.alibaba.com.caucho.hessian3.io.HessianHandle;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("unchecked")
+public class ZonedDateTimeHandle implements HessianHandle, Serializable {
+    private static final long serialVersionUID = -6933460123278647569L;
+
+    private Object dateTime;
+    private Object offset;
+    private String zoneId;
+
+
+    public ZonedDateTimeHandle() {
+    }
+
+    public ZonedDateTimeHandle(Object o) {
+        try {
+            Class c = Class.forName("java.time.ZonedDateTime");
+            Method m = c.getDeclaredMethod("toLocalDateTime");
+            this.dateTime = m.invoke(o);
+            m = c.getDeclaredMethod("getOffset");
+            this.offset = m.invoke(o);
+            m = c.getDeclaredMethod("getZone");
+            Object zone = m.invoke(o);
+            if (zone != null) {
+                Class zoneId = Class.forName("java.time.ZoneId");
+                m = zoneId.getDeclaredMethod("getId");
+                this.zoneId = (String) m.invoke(zone);
+            }
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    private Object readResolve() {
+        try {
+            Class zoneDateTime = Class.forName("java.time.ZonedDateTime");
+            Method ofLocal = zoneDateTime.getDeclaredMethod("ofLocal", Class.forName("java.time.LocalDateTime"),
+                    Class.forName("java.time.ZoneId"), Class.forName("java.time.ZoneOffset"));
+            Class c = Class.forName("java.time.ZoneId");
+            Method of = c.getDeclaredMethod("of", String.class);
+            return ofLocal.invoke(null, dateTime, of.invoke(null, this.zoneId), offset);
+        } catch (Throwable t) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/security/X509Encryption.java b/src/test/java/com/alibaba/com/caucho/hessian3/security/X509Encryption.java
similarity index 97%
rename from src/main/java/com/alibaba/com/caucho/hessian/security/X509Encryption.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/security/X509Encryption.java
index 2156cec..12c61ee 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/security/X509Encryption.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/security/X509Encryption.java
@@ -46,11 +46,11 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.security;
+package com.alibaba.com.caucho.hessian3.security;
 
-import com.alibaba.com.caucho.hessian.io.Hessian2Input;
-import com.alibaba.com.caucho.hessian.io.Hessian2Output;
-import com.alibaba.com.caucho.hessian.io.HessianEnvelope;
+import com.alibaba.com.caucho.hessian3.io.Hessian2Input;
+import com.alibaba.com.caucho.hessian3.io.Hessian2Output;
+import com.alibaba.com.caucho.hessian3.io.HessianEnvelope;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
diff --git a/src/main/java/com/alibaba/com/caucho/hessian/security/X509Signature.java b/src/test/java/com/alibaba/com/caucho/hessian3/security/X509Signature.java
similarity index 97%
rename from src/main/java/com/alibaba/com/caucho/hessian/security/X509Signature.java
rename to src/test/java/com/alibaba/com/caucho/hessian3/security/X509Signature.java
index a8023e7..208e5a9 100644
--- a/src/main/java/com/alibaba/com/caucho/hessian/security/X509Signature.java
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/security/X509Signature.java
@@ -46,11 +46,11 @@
  * @author Scott Ferguson
  */
 
-package com.alibaba.com.caucho.hessian.security;
+package com.alibaba.com.caucho.hessian3.security;
 
-import com.alibaba.com.caucho.hessian.io.Hessian2Input;
-import com.alibaba.com.caucho.hessian.io.Hessian2Output;
-import com.alibaba.com.caucho.hessian.io.HessianEnvelope;
+import com.alibaba.com.caucho.hessian3.io.Hessian2Input;
+import com.alibaba.com.caucho.hessian3.io.Hessian2Output;
+import com.alibaba.com.caucho.hessian3.io.HessianEnvelope;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/util/IdentityIntMap.java b/src/test/java/com/alibaba/com/caucho/hessian3/util/IdentityIntMap.java
new file mode 100644
index 0000000..24a038a
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/util/IdentityIntMap.java
@@ -0,0 +1,244 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.util;
+
+/**
+ * The IntMap provides a simple hashmap from keys to integers.  The API is
+ * an abbreviation of the HashMap collection API.
+ * <p>
+ * <p>The convenience of IntMap is avoiding all the silly wrapping of
+ * integers.
+ */
+public class IdentityIntMap {
+    /**
+     * Encoding of a null entry.  Since NULL is equal to Integer.MIN_VALUE,
+     * it's impossible to distinguish between the two.
+     */
+    public final static int NULL = 0xdeadbeef; // Integer.MIN_VALUE + 1;
+
+    private static final Object DELETED = new Object();
+
+    private Object[] _keys;
+    private int[] _values;
+
+    private int _size;
+    private int _mask;
+
+    /**
+     * Create a new IntMap.  Default size is 16.
+     */
+    public IdentityIntMap() {
+        _keys = new Object[256];
+        _values = new int[256];
+
+        _mask = _keys.length - 1;
+        _size = 0;
+    }
+
+    /**
+     * Clear the hashmap.
+     */
+    public void clear() {
+        Object[] keys = _keys;
+        int[] values = _values;
+
+        for (int i = keys.length - 1; i >= 0; i--) {
+            keys[i] = null;
+            values[i] = 0;
+        }
+
+        _size = 0;
+    }
+
+    /**
+     * Returns the current number of entries in the map.
+     */
+    public int size() {
+        return _size;
+    }
+
+    /**
+     * Puts a new value in the property table with the appropriate flags
+     */
+    public int get(Object key) {
+        int mask = _mask;
+        int hash = System.identityHashCode(key) % mask & mask;
+
+        Object[] keys = _keys;
+
+        while (true) {
+            Object mapKey = keys[hash];
+
+            if (mapKey == null)
+                return NULL;
+            else if (mapKey == key)
+                return _values[hash];
+
+            hash = (hash + 1) % mask;
+        }
+    }
+
+    /**
+     * Expands the property table
+     */
+    private void resize(int newSize) {
+        Object[] newKeys = new Object[newSize];
+        int[] newValues = new int[newSize];
+
+        int mask = _mask = newKeys.length - 1;
+
+        Object[] keys = _keys;
+        int values[] = _values;
+
+        for (int i = keys.length - 1; i >= 0; i--) {
+            Object key = keys[i];
+
+            if (key == null || key == DELETED)
+                continue;
+
+            int hash = System.identityHashCode(key) % mask & mask;
+
+            while (true) {
+                if (newKeys[hash] == null) {
+                    newKeys[hash] = key;
+                    newValues[hash] = values[i];
+                    break;
+                }
+
+                hash = (hash + 1) % mask;
+            }
+        }
+
+        _keys = newKeys;
+        _values = newValues;
+    }
+
+    /**
+     * Puts a new value in the property table with the appropriate flags
+     */
+    public int put(Object key, int value) {
+        int mask = _mask;
+        int hash = System.identityHashCode(key) % mask & mask;
+
+        Object[] keys = _keys;
+
+        while (true) {
+            Object testKey = keys[hash];
+
+            if (testKey == null || testKey == DELETED) {
+                keys[hash] = key;
+                _values[hash] = value;
+
+                _size++;
+
+                if (keys.length <= 4 * _size)
+                    resize(4 * keys.length);
+
+                return NULL;
+            } else if (key != testKey) {
+                hash = (hash + 1) % mask;
+
+                continue;
+            } else {
+                int old = _values[hash];
+
+                _values[hash] = value;
+
+                return old;
+            }
+        }
+    }
+
+    /**
+     * Deletes the entry.  Returns true if successful.
+     */
+    public int remove(Object key) {
+        int mask = _mask;
+        int hash = System.identityHashCode(key) % mask & mask;
+
+        while (true) {
+            Object mapKey = _keys[hash];
+
+            if (mapKey == null)
+                return NULL;
+            else if (mapKey == key) {
+                _keys[hash] = DELETED;
+
+                _size--;
+
+                return _values[hash];
+            }
+
+            hash = (hash + 1) % mask;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sbuf = new StringBuffer();
+
+        sbuf.append("IntMap[");
+        boolean isFirst = true;
+
+        for (int i = 0; i <= _mask; i++) {
+            if (_keys[i] != null && _keys[i] != DELETED) {
+                if (!isFirst)
+                    sbuf.append(", ");
+
+                isFirst = false;
+                sbuf.append(_keys[i]);
+                sbuf.append(":");
+                sbuf.append(_values[i]);
+            }
+        }
+        sbuf.append("]");
+
+        return sbuf.toString();
+    }
+}
diff --git a/src/test/java/com/alibaba/com/caucho/hessian3/util/IntMap.java b/src/test/java/com/alibaba/com/caucho/hessian3/util/IntMap.java
new file mode 100644
index 0000000..850b8f6
--- /dev/null
+++ b/src/test/java/com/alibaba/com/caucho/hessian3/util/IntMap.java
@@ -0,0 +1,244 @@
+/*
+ * 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.alibaba.com.caucho.hessian3.util;
+
+/**
+ * The IntMap provides a simple hashmap from keys to integers.  The API is
+ * an abbreviation of the HashMap collection API.
+ * <p>
+ * <p>The convenience of IntMap is avoiding all the silly wrapping of
+ * integers.
+ */
+public class IntMap {
+    /**
+     * Encoding of a null entry.  Since NULL is equal to Integer.MIN_VALUE,
+     * it's impossible to distinguish between the two.
+     */
+    public final static int NULL = 0xdeadbeef; // Integer.MIN_VALUE + 1;
+
+    private static final Object DELETED = new Object();
+
+    private Object[] _keys;
+    private int[] _values;
+
+    private int _size;
+    private int _mask;
+
+    /**
+     * Create a new IntMap.  Default size is 16.
+     */
+    public IntMap() {
+        _keys = new Object[256];
+        _values = new int[256];
+
+        _mask = _keys.length - 1;
+        _size = 0;
+    }
+
+    /**
+     * Clear the hashmap.
+     */
+    public void clear() {
+        Object[] keys = _keys;
+        int[] values = _values;
+
+        for (int i = keys.length - 1; i >= 0; i--) {
+            keys[i] = null;
+            values[i] = 0;
+        }
+
+        _size = 0;
+    }
+
+    /**
+     * Returns the current number of entries in the map.
+     */
+    public int size() {
+        return _size;
+    }
+
+    /**
+     * Puts a new value in the property table with the appropriate flags
+     */
+    public int get(Object key) {
+        int mask = _mask;
+        int hash = key.hashCode() % mask & mask;
+
+        Object[] keys = _keys;
+
+        while (true) {
+            Object mapKey = keys[hash];
+
+            if (mapKey == null)
+                return NULL;
+            else if (mapKey == key || mapKey.equals(key))
+                return _values[hash];
+
+            hash = (hash + 1) % mask;
+        }
+    }
+
+    /**
+     * Expands the property table
+     */
+    private void resize(int newSize) {
+        Object[] newKeys = new Object[newSize];
+        int[] newValues = new int[newSize];
+
+        int mask = _mask = newKeys.length - 1;
+
+        Object[] keys = _keys;
+        int values[] = _values;
+
+        for (int i = keys.length - 1; i >= 0; i--) {
+            Object key = keys[i];
+
+            if (key == null || key == DELETED)
+                continue;
+
+            int hash = key.hashCode() % mask & mask;
+
+            while (true) {
+                if (newKeys[hash] == null) {
+                    newKeys[hash] = key;
+                    newValues[hash] = values[i];
+                    break;
+                }
+
+                hash = (hash + 1) % mask;
+            }
+        }
+
+        _keys = newKeys;
+        _values = newValues;
+    }
+
+    /**
+     * Puts a new value in the property table with the appropriate flags
+     */
+    public int put(Object key, int value) {
+        int mask = _mask;
+        int hash = key.hashCode() % mask & mask;
+
+        Object[] keys = _keys;
+
+        while (true) {
+            Object testKey = keys[hash];
+
+            if (testKey == null || testKey == DELETED) {
+                keys[hash] = key;
+                _values[hash] = value;
+
+                _size++;
+
+                if (keys.length <= 4 * _size)
+                    resize(4 * keys.length);
+
+                return NULL;
+            } else if (key != testKey && !key.equals(testKey)) {
+                hash = (hash + 1) % mask;
+
+                continue;
+            } else {
+                int old = _values[hash];
+
+                _values[hash] = value;
+
+                return old;
+            }
+        }
+    }
+
+    /**
+     * Deletes the entry.  Returns true if successful.
+     */
+    public int remove(Object key) {
+        int mask = _mask;
+        int hash = key.hashCode() % mask & mask;
+
+        while (true) {
+            Object mapKey = _keys[hash];
+
+            if (mapKey == null)
+                return NULL;
+            else if (mapKey == key) {
+                _keys[hash] = DELETED;
+
+                _size--;
+
+                return _values[hash];
+            }
+
+            hash = (hash + 1) % mask;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sbuf = new StringBuffer();
+
+        sbuf.append("IntMap[");
+        boolean isFirst = true;
+
+        for (int i = 0; i <= _mask; i++) {
+            if (_keys[i] != null && _keys[i] != DELETED) {
+                if (!isFirst)
+                    sbuf.append(", ");
+
+                isFirst = false;
+                sbuf.append(_keys[i]);
+                sbuf.append(":");
+                sbuf.append(_values[i]);
+            }
+        }
+        sbuf.append("]");
+
+        return sbuf.toString();
+    }
+}