haogh 11 月之前
當前提交
7bb07b8567
共有 100 個文件被更改,包括 20257 次插入0 次删除
  1. 2 0
      README.txt
  2. 二進制
      lib/commons-beanutils-1.8.3.jar
  3. 二進制
      lib/commons-codec-1.10.jar
  4. 二進制
      lib/commons-collections4-4.1.jar
  5. 二進制
      lib/commons-io-2.5.jar
  6. 二進制
      lib/commons-lang-2.6.jar
  7. 二進制
      lib/commons-logging-1.2.jar
  8. 二進制
      lib/fastjson-1.2.2.jar
  9. 二進制
      lib/log4j-1.2.17.jar
  10. 二進制
      lib/log4j-api-2.11.0.jar
  11. 二進制
      lib/log4j-core-2.11.0.jar
  12. 二進制
      lib/netty-all-4.1.28.Final.jar
  13. 二進制
      lib/spring-core-5.1.3.RELEASE.jar
  14. 二進制
      lib/spring-retry-1.2.2.RELEASE.jar
  15. 二進制
      lib/xmlbeans-2.6.0.jar
  16. 153 0
      src/com/hmsoft/app/Transfer.java
  17. 62 0
      src/com/hmsoft/common/AppConst.java
  18. 30 0
      src/com/hmsoft/common/BatchConst.java
  19. 18 0
      src/com/hmsoft/common/FileConst.java
  20. 38 0
      src/com/hmsoft/common/MsgTypeConst.java
  21. 7 0
      src/com/hmsoft/common/TransConst.java
  22. 13 0
      src/com/hmsoft/common/bizinf/IExecuteLogic.java
  23. 37 0
      src/com/hmsoft/common/bizlogic/RequestLogic.java
  24. 263 0
      src/com/hmsoft/common/socket/ExchangeSever.java
  25. 70 0
      src/com/hmsoft/common/util/ExecuteRetryUtil.java
  26. 130 0
      src/com/hmsoft/model/Batch.java
  27. 224 0
      src/com/hmsoft/model/CoursePlan.java
  28. 93 0
      src/com/hmsoft/model/School.java
  29. 93 0
      src/com/hmsoft/model/request/BizRequest.java
  30. 66 0
      src/com/hmsoft/model/request/LoginRequest.java
  31. 30 0
      src/com/hmsoft/model/request/LogoutRequest.java
  32. 15 0
      src/com/hmsoft/model/response/LoginResponse.java
  33. 85 0
      src/com/hmsoft/remote/caucho/hessian/HessianException.java
  34. 118 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractDeserializer.java
  35. 442 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianInput.java
  36. 531 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianOutput.java
  37. 65 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianResolver.java
  38. 67 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractListDeserializer.java
  39. 74 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractMapDeserializer.java
  40. 63 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializer.java
  41. 74 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializerFactory.java
  42. 164 0
      src/com/hmsoft/remote/caucho/hessian/io/ArrayDeserializer.java
  43. 94 0
      src/com/hmsoft/remote/caucho/hessian/io/ArraySerializer.java
  44. 608 0
      src/com/hmsoft/remote/caucho/hessian/io/BasicDeserializer.java
  45. 285 0
      src/com/hmsoft/remote/caucho/hessian/io/BasicSerializer.java
  46. 295 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanDeserializer.java
  47. 315 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanSerializer.java
  48. 82 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanSerializerFactory.java
  49. 93 0
      src/com/hmsoft/remote/caucho/hessian/io/CalendarHandle.java
  50. 77 0
      src/com/hmsoft/remote/caucho/hessian/io/CalendarSerializer.java
  51. 160 0
      src/com/hmsoft/remote/caucho/hessian/io/ClassDeserializer.java
  52. 87 0
      src/com/hmsoft/remote/caucho/hessian/io/ClassSerializer.java
  53. 134 0
      src/com/hmsoft/remote/caucho/hessian/io/CollectionDeserializer.java
  54. 108 0
      src/com/hmsoft/remote/caucho/hessian/io/CollectionSerializer.java
  55. 204 0
      src/com/hmsoft/remote/caucho/hessian/io/Deflation.java
  56. 73 0
      src/com/hmsoft/remote/caucho/hessian/io/Deserializer.java
  57. 138 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumDeserializer.java
  58. 108 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumSerializer.java
  59. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumerationDeserializer.java
  60. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumerationSerializer.java
  61. 57 0
      src/com/hmsoft/remote/caucho/hessian/io/EnvelopeFactory.java
  62. 107 0
      src/com/hmsoft/remote/caucho/hessian/io/ExtSerializerFactory.java
  63. 146 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Constants.java
  64. 2798 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Input.java
  65. 1604 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Output.java
  66. 165 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingInput.java
  67. 109 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingOutput.java
  68. 174 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugInputStream.java
  69. 168 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugOutputStream.java
  70. 2090 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugState.java
  71. 78 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianEnvelope.java
  72. 87 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianFieldException.java
  73. 55 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianHandle.java
  74. 1700 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianInput.java
  75. 99 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianInputFactory.java
  76. 949 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianOutput.java
  77. 110 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianProtocolException.java
  78. 130 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianRemote.java
  79. 57 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianRemoteObject.java
  80. 62 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianRemoteResolver.java
  81. 183 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianSerializerInput.java
  82. 146 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianSerializerOutput.java
  83. 91 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianServiceException.java
  84. 77 0
      src/com/hmsoft/remote/caucho/hessian/io/IOExceptionWrapper.java
  85. 67 0
      src/com/hmsoft/remote/caucho/hessian/io/InputStreamDeserializer.java
  86. 80 0
      src/com/hmsoft/remote/caucho/hessian/io/InputStreamSerializer.java
  87. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/IteratorSerializer.java
  88. 679 0
      src/com/hmsoft/remote/caucho/hessian/io/JavaDeserializer.java
  89. 434 0
      src/com/hmsoft/remote/caucho/hessian/io/JavaSerializer.java
  90. 119 0
      src/com/hmsoft/remote/caucho/hessian/io/LocaleHandle.java
  91. 76 0
      src/com/hmsoft/remote/caucho/hessian/io/LocaleSerializer.java
  92. 158 0
      src/com/hmsoft/remote/caucho/hessian/io/MapDeserializer.java
  93. 104 0
      src/com/hmsoft/remote/caucho/hessian/io/MapSerializer.java
  94. 100 0
      src/com/hmsoft/remote/caucho/hessian/io/ObjectDeserializer.java
  95. 63 0
      src/com/hmsoft/remote/caucho/hessian/io/RemoteSerializer.java
  96. 59 0
      src/com/hmsoft/remote/caucho/hessian/io/Serializer.java
  97. 646 0
      src/com/hmsoft/remote/caucho/hessian/io/SerializerFactory.java
  98. 133 0
      src/com/hmsoft/remote/caucho/hessian/io/SqlDateDeserializer.java
  99. 88 0
      src/com/hmsoft/remote/caucho/hessian/io/SqlDateSerializer.java
  100. 69 0
      src/com/hmsoft/remote/caucho/hessian/io/StackTraceElementDeserializer.java

+ 2 - 0
README.txt

@@ -0,0 +1,2 @@
+命题办和学校之间的通信客户端,目前部署在云端
+使用UDP通信协议

二進制
lib/commons-beanutils-1.8.3.jar


二進制
lib/commons-codec-1.10.jar


二進制
lib/commons-collections4-4.1.jar


二進制
lib/commons-io-2.5.jar


二進制
lib/commons-lang-2.6.jar


二進制
lib/commons-logging-1.2.jar


二進制
lib/fastjson-1.2.2.jar


二進制
lib/log4j-1.2.17.jar


二進制
lib/log4j-api-2.11.0.jar


二進制
lib/log4j-core-2.11.0.jar


二進制
lib/netty-all-4.1.28.Final.jar


二進制
lib/spring-core-5.1.3.RELEASE.jar


二進制
lib/spring-retry-1.2.2.RELEASE.jar


二進制
lib/xmlbeans-2.6.0.jar


+ 153 - 0
src/com/hmsoft/app/Transfer.java

@@ -0,0 +1,153 @@
+package com.hmsoft.app;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.common.AppConst;
+import com.hmsoft.common.MsgTypeConst;
+import com.hmsoft.common.TransConst;
+import com.hmsoft.common.socket.ExchangeSever;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.model.request.LoginRequest;
+import com.hmsoft.model.request.LogoutRequest;
+import com.hmsoft.remote.rmi.common.utils.NetUtils;
+import com.hmsoft.remote.rmi.exception.RemotingException;
+import com.hmsoft.remote.rmi.handler.ExchangeHandler;
+import com.hmsoft.remote.rmi.model.Message;
+import com.hmsoft.remote.rmi.transport.NettyChannel;
+
+/**
+ * 中转服务器.
+ * @author zq
+ *
+ */
+public class Transfer {
+	
+	public static ExchangeSever SERVER = null;
+	public static Map<NettyChannel, String> CLI_LOGIN_MAP = new HashMap<NettyChannel, String>();//客户端登录学校
+	public static ExecutorService EXECUTOR = Executors.newFixedThreadPool(3);
+	
+	//java -jar mtzj-transfer.jar 27.174.44.184 20880
+	public static void main(String[] args) {
+		if (args == null || args.length != 2) {
+			args = new String[2];
+			args[0] = "127.0.0.1";
+			args[1] = "20880";
+			//throw new IllegalArgumentException("绑定ip、端口参数输入不正确!");
+		}
+		SERVER = new ExchangeSever(args[0] + ":" + args[1], new ExchangeHandler() {
+
+			@Override
+			public void connected(NettyChannel channel) throws RemotingException {
+				System.err.println("Transfer: connected");
+			}
+
+			@Override
+			public void disconnected(NettyChannel channel) throws RemotingException {
+				channel.close();
+				//可能是学校端断开,也可能是命题办管理端断开
+				String userName = CLI_LOGIN_MAP.remove(channel);
+				if (userName != null) {
+					final LogoutRequest logout = new LogoutRequest(userName);
+					final String json = JSON.toJSONString(logout);
+					final Message message = new Message(MsgTypeConst.TYPE_NOTIFY_CLIENT_LOGOUT, json);
+					try {
+						SERVER.sendToMgr(message);
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+
+			@Override
+			public void sent(NettyChannel channel, Object message) throws RemotingException {
+				
+			}
+
+			@Override
+			public void received(NettyChannel channel, Object message) throws RemotingException {
+				
+				if (message instanceof Message) {
+					
+					System.err.println("Transfer: received," + message);
+					try {
+						Message msg =  JSON.parseObject(message.toString(), Message.class);
+						byte type = msg.getType();
+						//管理端注册,标识通道
+						if (type == MsgTypeConst.TYPE_LOGIN_RESP_TO_CLIENT) {
+							
+							//登录返回
+							Object flag = SERVER.send(msg);
+							
+						} else if (type == MsgTypeConst.TYPE_HEARTBEAT) {
+							//心跳验证  TODO...
+						}
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+					
+				}
+			}
+
+			@Override
+			public void caught(NettyChannel channel, Throwable exception) throws RemotingException {
+				exception.printStackTrace();
+			}
+			
+			@Override
+			public Object reply(NettyChannel channel, Object request) throws RemotingException {
+				Object ret = null;
+				if (request instanceof Message) {
+					final Message msg =  (Message)request;
+					byte type = msg.getType();
+					//登录到管理端
+					if (type == MsgTypeConst.TYPE_MGE_REGISTER) {
+						
+						ExchangeSever.MGR_REMOTE_ADDR = NetUtils.toAddressString(channel.getRemoteAddress());
+						ret = TransConst.SUCCESS;
+						
+					} else if (type == MsgTypeConst.TYPE_LOGIN_REQ_TO_MGE || type == MsgTypeConst.TYPE_BIZ_REQ_TO_MGE) {
+						
+						//ret = SERVER.sendToMgrSpecial(msg);
+						ret = SERVER.sendToMgr(msg);
+
+						if (type == MsgTypeConst.TYPE_LOGIN_REQ_TO_MGE) {
+							final LoginRequest login = JSON.parseObject(msg.getMsgBody(), LoginRequest.class);
+							CLI_LOGIN_MAP.put(channel, login.getUserName());
+						}
+						
+					} else if (type == MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT) {
+						
+						//响应到客户端,直接转发
+						final BizRequest req = JSON.parseObject(msg.getMsgBody(), BizRequest.class);
+						if (req.getReqType().intValue() == AppConst.SRV_NOTIFY_SELECT
+								|| req.getReqType().intValue() == AppConst.SRV_SPECIFIC_COURSE
+								|| req.getReqType().intValue() == AppConst.SRV_FRESH_INIT_LIST
+								|| req.getReqType().intValue() == AppConst.SRV_FINAL_PUBLISH
+								|| req.getReqType().intValue() == AppConst.SRV_NOTIFY_CANCEL
+								|| req.getReqType().intValue() == AppConst.SRV_PUSH_NEW_LIST
+								|| req.getReqType().intValue() == AppConst.SRV_DELETE_COURSE
+								|| req.getReqType().intValue() == AppConst.SRV_TRIGGER_FRESH) {
+							//广播到所有学校
+							ret = SERVER.sendToAllSch(msg);
+						} else {
+							ret = SERVER.send(msg);
+						}
+						
+					}
+				}
+				return ret;
+			}
+		});
+        synchronized (Transfer.class) {
+            while (true) {
+                try {
+                	Transfer.class.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+	}
+}

+ 62 - 0
src/com/hmsoft/common/AppConst.java

@@ -0,0 +1,62 @@
+package com.hmsoft.common;
+
+/**
+ * 通讯业务常量类.
+ * @author Lenovo
+ *
+ */
+public class AppConst {
+
+	/**
+	 * 学校选择课程
+	 */
+	public static final int SCH_SELECT_COURSE = 1;
+	/**
+	 * 命题办通知其他学校课程被选择了
+	 */
+	public static final int SRV_NOTIFY_SELECT = 2;
+	/**
+	 * 命题办指定该课程征集学校
+	 */
+	public static final int SRV_SPECIFIC_COURSE = 3;
+	/**
+	 * 学校取消选择的课程
+	 */
+	public static final int SCH_CANCEL_COURSE = 4;
+	/**
+	 * 命题办刷新课程列表数据到各个学校客户端
+	 */
+	public static final int SRV_FRESH_INIT_LIST = 5;
+	/**
+	 * 命题办最终发布
+	 */
+	public static final int SRV_FINAL_PUBLISH = 6;
+	/**
+	 * 命题办通知有学校取消了选择
+	 */
+	public static final int SRV_NOTIFY_CANCEL = 7;
+	/**
+	 * 学校连接上服务器后拉取最新课程计划.
+	 */
+	public static final int SCH_FETCH_NEW_LIST = 8;
+	/**
+	 * 管理端发送最新征集中的课程计划.
+	 */
+	public static final int SRV_PUSH_NEW_LIST = 9;
+	/**
+	 * 管理端删除课程.
+	 */
+	public static final int SRV_DELETE_COURSE = 10;
+	/**
+	 * 定时或手动刷新.
+	 */
+	public static final int SRV_TRIGGER_FRESH = 11;
+	/**
+	 * 确认反馈.
+	 */
+	public static final int SCH_CONFIRM_OK = 12;
+	/**
+	 * 修改密码请求.
+	 */
+	public static final int SCH_CHANGE_PASSWD = 13;
+}

+ 30 - 0
src/com/hmsoft/common/BatchConst.java

@@ -0,0 +1,30 @@
+package com.hmsoft.common;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class BatchConst {
+
+	/**
+	 * 新建
+	 */
+	public static final byte STATUS_NEW = 0;
+	/**
+	 * 已导入
+	 */
+	public static final byte STATUS_IMPORTED = 1;
+	/**
+	 * 征集中
+	 */
+	public static final byte STATUS_COLLECTING = 2;
+	/**
+	 * 已发布
+	 */
+	public static final byte STATUS_PUBLISHED = 3;
+	/**
+	 * 已确认
+	 */
+	public static final Integer STATUS_CONFIRMED = 4;
+}

+ 18 - 0
src/com/hmsoft/common/FileConst.java

@@ -0,0 +1,18 @@
+package com.hmsoft.common;
+
+import java.io.File;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class FileConst {
+	public static final String FILE_NEWLINE = "\r\n";
+	public static String SYS_HOME = System.getenv("MTZJ_HOME") == null ? System.getProperty("user.dir") : System.getenv("MTZJ_HOME");
+	public static String SCHOOL_DATA_PATH = SYS_HOME + File.separator + "Data" + File.separator + "SchoolStore";
+	public static String Batch_DATA_PATH = SYS_HOME + File.separator + "Data" + File.separator + "BatchStore";
+	public static String COURSEPLAN_DATA_PATH = SYS_HOME + File.separator + "Data" + File.separator + "CoursePlanStore";
+	
+	
+}

+ 38 - 0
src/com/hmsoft/common/MsgTypeConst.java

@@ -0,0 +1,38 @@
+package com.hmsoft.common;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class MsgTypeConst {
+
+	/**
+	 * 客户端请求登录.
+	 */
+	public static byte TYPE_LOGIN_REQ_TO_MGE = 0;
+	/**
+	 * 管理端登录响应.
+	 */
+	public static byte TYPE_LOGIN_RESP_TO_CLIENT = 1;
+	/**
+	 * 客户端请求业务.
+	 */
+	public static byte TYPE_BIZ_REQ_TO_MGE = 2;
+	/**
+	 * 管理端响应业务.
+	 */
+	public static byte TYPE_BIZ_RESP_TO_CLIENT = 3;
+	/**
+	 * 心跳.
+	 */
+	public static byte TYPE_HEARTBEAT = 4;
+	/**
+	 * 管理端注册.
+	 */
+	public static byte TYPE_MGE_REGISTER = 5;
+	/**
+	 * 客户端断开、登出.
+	 */
+	public static byte TYPE_NOTIFY_CLIENT_LOGOUT = 6;
+}

+ 7 - 0
src/com/hmsoft/common/TransConst.java

@@ -0,0 +1,7 @@
+package com.hmsoft.common;
+
+public class TransConst {
+
+	public static final int SUCCESS = 0;
+	public static final int FAIL = - 1;
+}

+ 13 - 0
src/com/hmsoft/common/bizinf/IExecuteLogic.java

@@ -0,0 +1,13 @@
+package com.hmsoft.common.bizinf;
+
+import com.hmsoft.remote.rmi.exception.RemotingException;
+
+/**
+ * 业务逻辑
+ * @author zq
+ *
+ */
+public interface IExecuteLogic {
+
+	Object execute() throws RemotingException;
+}

+ 37 - 0
src/com/hmsoft/common/bizlogic/RequestLogic.java

@@ -0,0 +1,37 @@
+package com.hmsoft.common.bizlogic;
+
+import com.hmsoft.common.bizinf.IExecuteLogic;
+import com.hmsoft.remote.rmi.exception.RemotingException;
+import com.hmsoft.remote.rmi.future.IFuture;
+import com.hmsoft.remote.rmi.future.TransFuture;
+import com.hmsoft.remote.rmi.model.Request;
+import com.hmsoft.remote.rmi.transport.NettyChannel;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class RequestLogic implements IExecuteLogic {
+
+	private final int TIME_OUT = 3000;
+	private Request req;
+	private NettyChannel channel;
+	
+	public RequestLogic(final Request req, final NettyChannel channel) {
+		this.req = req;
+		this.channel = channel;
+	}
+	
+	@Override
+	public Object execute() throws RemotingException {
+        IFuture future = new TransFuture(this.channel, req, TIME_OUT);
+        try {
+			this.channel.send(req, false);
+			return future.get();
+		} catch (RemotingException e) {
+			throw e;
+		}
+	}
+
+}

+ 263 - 0
src/com/hmsoft/common/socket/ExchangeSever.java

@@ -0,0 +1,263 @@
+package com.hmsoft.common.socket;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.springframework.retry.RecoveryCallback;
+import org.springframework.retry.RetryCallback;
+import org.springframework.retry.RetryContext;
+import org.springframework.retry.backoff.FixedBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+
+import com.hmsoft.remote.rmi.common.utils.NetUtils;
+import com.hmsoft.remote.rmi.exception.RemotingException;
+import com.hmsoft.remote.rmi.future.IFuture;
+import com.hmsoft.remote.rmi.future.TransFuture;
+import com.hmsoft.remote.rmi.handler.ExchangeHandler;
+import com.hmsoft.remote.rmi.model.Message;
+import com.hmsoft.remote.rmi.model.Request;
+import com.hmsoft.remote.rmi.rpc.NettyServer;
+import com.hmsoft.remote.rmi.transport.NettyChannel;
+import com.hmsoft.remote.rmi.transport.NettyTransporter;
+
+/**
+ * 
+ * ExchangeClient
+ *
+ */
+public class ExchangeSever {
+
+	private NettyServer server;
+	//TODO...
+	private static final int TIME_OUT = 5000;
+	public static String MGR_REMOTE_ADDR = null;
+	
+	public ExchangeSever(String url, ExchangeHandler handler) {
+		try {
+			this.server = NettyTransporter.getInstance().bind(url, handler);
+		} catch (RemotingException e) {
+			throw new IllegalArgumentException("handler == null");
+		}
+	}
+	
+	/**
+	 * 发送到指定客户端
+	 * @param message
+	 * @param sent
+	 * @throws RemotingException
+	 */
+    /*public Object send(Message message) throws RemotingException {
+    	NettyChannel channel = getSpecifyChannel(message.getShoolRemoteAddr());
+        final Request request = new Request();
+        request.setVersion("1.0");
+        request.setTwoWay(true);
+        request.setData(message);
+        //TransFuture future = new TransFuture(channel, request, TIME_OUT);
+    	Set<String> excuteSet = new HashSet<String>();
+		final IExecuteLogic logic = new RequestLogic(request, channel);
+		Object obj = ExecuteRetryUtil.execute(4, 5000, logic, excuteSet);
+		return obj;
+    }*/
+	
+	/**
+	 * 发送到指定客户端
+	 * @param message
+	 * @param sent
+	 * @throws RemotingException
+	 */
+    public TransFuture send(Message message) throws RemotingException {
+    	NettyChannel channel = this.getSpecifyChannel(message.getShoolRemoteAddr());
+        final Request request = new Request();
+        request.setVersion("1.0");
+        request.setTwoWay(true);
+        request.setData(message);
+        TransFuture future = new TransFuture(channel, request, TIME_OUT);
+        try {
+        	channel.send(request, false);
+        } catch (RemotingException e) {
+            future.cancel();
+            throw e;
+        }
+        return future;
+    }
+	
+	/**
+	 * 发送到管理端
+	 * @param message
+	 * @param sent
+	 * @throws RemotingException
+	 */
+    /*public Object sendToMgr(Message message) throws RemotingException {
+    	NettyChannel channel = getSpecifyChannel(MGR_REMOTE_ADDR);
+        final Request request = new Request();
+        request.setVersion("1.0");
+        request.setTwoWay(true);
+        request.setData(message);
+        Set<String> excuteSet = new HashSet<String>();
+		final IExecuteLogic logic = new RequestLogic(request, channel);
+		Object obj = ExecuteRetryUtil.execute(4, 5000, logic, excuteSet);
+		return obj;
+    }*/
+    
+    public Object sendToMgr(Message message) throws RemotingException {
+    	NettyChannel channel = this.getSpecifyChannel(MGR_REMOTE_ADDR);
+        final Request request = new Request();
+        request.setVersion("1.0");
+        request.setTwoWay(true);
+        request.setData(message);
+        TransFuture future = new TransFuture(channel, request, TIME_OUT);
+        try {
+        	channel.send(request, false);
+        } catch (RemotingException e) {
+            future.cancel();
+            throw e;
+        }
+        return future.get();
+    }
+    
+    /**
+     * 发送到所有学校
+     * @param request
+     * @param timeout
+     * @return
+     * @throws RemotingException
+     */
+    /*public int sendToAllSch(Message message) throws RemotingException {
+        // create request.
+    	int errCode = 0;
+    	Set<String> set = this.server.getChannelsMap().keySet();
+    	Set<String> excuteSet = new HashSet<String>();
+    	Set<String> failSet = new HashSet<String>();
+    	for (String schAddr : set) {
+    		if (!schAddr.equals(MGR_REMOTE_ADDR)) {
+    			final Request req = new Request();
+    	        req.setVersion("1.0");
+    	        req.setTwoWay(true);
+    	        req.setData(message);
+    	        final NettyChannel chnnl = this.getSpecifyChannel(schAddr);
+    	        excuteSet.add(schAddr);
+    			final IExecuteLogic logic = new RequestLogic(req, chnnl);
+    			excuteSet.add(schAddr);
+    			Object obj = ExecuteRetryUtil.execute(4, 5000, logic, excuteSet);
+    			failSet.add(excuteSet.iterator().next());
+    		}
+    	}
+    	if (failSet.size() > 0) {
+    		errCode = -1;
+    	}
+        return errCode;
+    }*/
+    
+    /**
+     * 发送到所有学校
+     * @param request
+     * @param timeout
+     * @return
+     * @throws RemotingException
+     */
+    public int sendToAllSch(Message message) throws RemotingException {
+        // create request.
+    	Map<Long, NettyChannel> failMap = new HashMap<Long, NettyChannel>();
+    	List<IFuture> futureList = new ArrayList<IFuture>();
+    	Set<String> set = this.server.getChannelsMap().keySet();
+    	for (String schAddr : set) {
+    		if (!schAddr.equals(MGR_REMOTE_ADDR)) {
+    			final Request req = new Request();
+    	        req.setVersion("1.0");
+    	        req.setTwoWay(true);
+    	        req.setData(message);
+    	        final NettyChannel chnnl = this.server.getChannelsMap().get(schAddr);
+    	        IFuture future = new TransFuture(chnnl, req, TIME_OUT);
+    			if (chnnl.isConnected()) {
+    				chnnl.send(req, false);
+    				futureList.add(future);
+    				failMap.put(req.getId(), chnnl);
+    			} else {
+    				this.server.getChannelsMap().remove(NetUtils.toAddressString(chnnl.getRemoteAddress()));
+    				failMap.remove(NetUtils.toAddressString(chnnl.getRemoteAddress()));
+    			}
+    		}
+    	}
+    	int errCode = 0;
+        for (IFuture fut : futureList) {
+        	try {
+        		fut.get();
+        	} catch(RemotingException e) {
+        		//失败重发
+        		errCode = -1;
+        		NettyChannel ch = failMap.get(fut.getId());
+        		if (ch.isConnected()) {
+        			ch.send(fut.getRequest(), false);
+        		}
+        	}
+        }
+        return errCode;
+    }
+    
+    private NettyChannel getSpecifyChannel(String specifyAddr) {
+		NettyChannel specifyChannel = this.server.getChannelsMap().get(specifyAddr);
+		if (specifyChannel.isConnected()) {
+			return specifyChannel;
+		} else {
+			this.server.getChannelsMap().remove(NetUtils.toAddressString(specifyChannel.getRemoteAddress()));
+		}
+    	return null;
+    }
+    
+    public static void main(String[] args) {
+    	final RetryTemplate retryTemplate = new RetryTemplate();
+    	SimpleRetryPolicy policy = new SimpleRetryPolicy(5, Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
+    	FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
+    	fixedBackOffPolicy.setBackOffPeriod(1000);
+        retryTemplate.setRetryPolicy(policy);
+        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
+        //重试业务操作
+        final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {
+
+			@Override
+			public Object doWithRetry(RetryContext context) throws Exception {
+				//业务逻辑
+				boolean result = pushCouponByVpmsaa();
+				if (!result) {
+					throw new RuntimeException();
+				}
+				System.err.println("重试后执行成功." + result);
+				return true;
+			}
+        	
+        };
+        //执行指定次数后任然失败的回调
+        final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() {
+
+			@Override
+			public Object recover(RetryContext context) throws Exception {
+				//System.err.println("执行3次重试后还是报错,回调.");
+				return Integer.MAX_VALUE;
+			}
+        	
+        };
+        try {
+        	retryTemplate.execute(retryCallback, recoveryCallback);
+        } catch(Exception e) {
+        	e.printStackTrace();
+        }
+    }
+    
+    public static Boolean pushCouponByVpmsaa() {
+        Random random = new Random();
+        int a = random.nextInt(11);
+        System.out.println("a是--->" + a);
+        if (a == 8) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+}

+ 70 - 0
src/com/hmsoft/common/util/ExecuteRetryUtil.java

@@ -0,0 +1,70 @@
+package com.hmsoft.common.util;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.springframework.retry.RecoveryCallback;
+import org.springframework.retry.RetryCallback;
+import org.springframework.retry.RetryContext;
+import org.springframework.retry.backoff.FixedBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+
+import com.hmsoft.common.bizinf.IExecuteLogic;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class ExecuteRetryUtil {
+
+	/**
+	 * 重试执行
+	 * @param retrys
+	 * @param period
+	 * @param logic
+	 */
+	public static Object execute(int retrys, int period, IExecuteLogic logic, Set<String> failSet) {
+    	final RetryTemplate retryTemplate = new RetryTemplate();
+    	SimpleRetryPolicy policy = new SimpleRetryPolicy(retrys, Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
+    	FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
+    	fixedBackOffPolicy.setBackOffPeriod(period);
+        retryTemplate.setRetryPolicy(policy);
+        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
+        //重试业务操作
+        final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {
+
+			@Override
+			public Object doWithRetry(RetryContext context) throws Exception {
+				if (context.getRetryCount() > 0) {
+					System.err.println("第"+ context.getRetryCount() +"次尝试调用......!");
+				}
+				//业务逻辑
+				Object result = logic.execute();
+				if (context.getRetryCount() > 0) {
+					System.err.println("重试"+ context.getRetryCount() +"次后执行成功!");
+				}
+				failSet.clear();
+				return result;
+			}
+        	
+        };
+        //执行指定次数后任然失败的回调
+        final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() {
+
+			@Override
+			public Object recover(RetryContext context) throws Exception {
+				System.err.println("执行指定次重试后还是报错,回调.");
+				return null;
+			}
+        	
+        };
+        try {
+        	retryTemplate.execute(retryCallback, recoveryCallback);
+        } catch(Exception e) {
+        	e.printStackTrace();
+        }
+        return null;
+	}
+}

+ 130 - 0
src/com/hmsoft/model/Batch.java

@@ -0,0 +1,130 @@
+package com.hmsoft.model;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class Batch implements Serializable {
+
+	private static final long serialVersionUID = 9138363630377951422L;
+
+	private LongProperty batchId;
+	private StringProperty batchNo;
+	private IntegerProperty status;//状态(新建-0、已导入-1、已发布-2)
+	private StringProperty statusDes;
+	private StringProperty createTime;//创建时间
+	private StringProperty publishTime;//发布时间
+	private StringProperty batchDesc;//描述
+	
+	public Batch() {
+		this.batchId = new SimpleLongProperty();
+		this.batchNo = new SimpleStringProperty();
+		this.status = new SimpleIntegerProperty();
+		this.statusDes = new SimpleStringProperty();
+		this.createTime = new SimpleStringProperty();
+		this.publishTime = new SimpleStringProperty();
+		this.batchDesc = new SimpleStringProperty();
+	}
+	
+	public Long getBatchId() {
+		return batchId.get();
+	}
+
+	public String getBatchNo() {
+		return batchNo.get();
+	}
+
+	public Integer getStatus() {
+		return status.get();
+	}
+
+	public String getStatusDes() {
+		return statusDes.get();
+	}
+
+	public String getCreateTime() {
+		return createTime.get();
+	}
+
+	public String getPublishTime() {
+		return publishTime.get();
+	}
+
+	public void setBatchId(Long batchId) {
+		this.batchId.set(batchId);
+	}
+
+	public void setBatchNo(String batchNo) {
+		this.batchNo.set(batchNo);
+	}
+
+	public void setStatus(Integer status) {
+		this.status.set(status);
+	}
+
+	public void setStatusDes(String statusDes) {
+		this.statusDes.set(statusDes);
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime.set(createTime);
+	}
+
+	public void setPublishTime(String publishTime) {
+		this.publishTime.set(publishTime);
+	}
+
+	public String getBatchDesc() {
+		return batchDesc.get();
+	}
+
+	public void setBatchDesc(String batchDesc) {
+		this.batchDesc.set(batchDesc);;
+	}
+
+	public String toString() {
+		return new StringBuilder()
+				.append(batchId.get()).append(" | ")
+				.append(batchNo.get()).append(" | ")
+				.append(status.get() == 0 ? "新建" : (status.get() == 1 ? "已导入" : "已发布"))
+				.toString();
+	}
+	
+	public int hashCode() {
+	     return new HashCodeBuilder(17, 37).
+	       append(this.batchId.get()).
+	       append(this.batchNo.get()).
+	       append(this.status.get()).
+	       append(this.createTime.get()).
+	       toHashCode();
+	}
+	
+	public boolean equals(Object obj) {
+		   if (obj == null) { return false; }
+		   if (obj == this) { return true; }
+		   if (obj.getClass() != getClass()) {
+		     return false;
+		   }
+		   Batch oth = (Batch) obj;
+		   return new EqualsBuilder()
+		                 .append(this.batchId.get(), oth.batchId.get())
+		                 .append(this.batchNo.get(), oth.batchNo.get())
+		                 .append(this.status.get(), oth.status.get())
+		                 .append(this.createTime.get(), oth.createTime.get())
+		                 .isEquals();
+	}
+	
+}

+ 224 - 0
src/com/hmsoft/model/CoursePlan.java

@@ -0,0 +1,224 @@
+package com.hmsoft.model;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.scene.control.ComboBox;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class CoursePlan implements Serializable {
+
+	private static final long serialVersionUID = 5308384112992831269L;
+	
+	private IntegerProperty coursePlanId;
+	private StringProperty courseCode;
+	private StringProperty courseName;
+	private StringProperty outlineName;//教材大纲
+	private StringProperty bookName;//教材名称
+	private StringProperty bianZhu;//编著
+	private StringProperty press;//出版社
+	private StringProperty publications;//版次
+	private StringProperty cengci;//层次
+	private LongProperty schoolId;//命题办指定的学校ID(最终征集学校ID)
+	private StringProperty schoolName;//ָ命题办指定的学校名称(最终征集学校名称)
+	
+	private StringProperty schSelectIds;//选择该课程的学校IDs
+	private StringProperty schSelectNames;//选择该课程的学校名称
+	
+	//待指定学校列表
+	//private transient ComboBox<School> comboBox = new ComboBox<School>();
+	
+	public CoursePlan() {
+		this.coursePlanId = new SimpleIntegerProperty();
+		this.courseCode = new SimpleStringProperty();
+		this.courseName = new SimpleStringProperty();
+		this.outlineName = new SimpleStringProperty();
+		this.bookName = new SimpleStringProperty();
+		this.bianZhu = new SimpleStringProperty();
+		this.press = new SimpleStringProperty();
+		this.publications = new SimpleStringProperty();
+		this.cengci = new SimpleStringProperty();
+		this.schoolId = new SimpleLongProperty();
+		this.schoolName = new SimpleStringProperty();
+		this.schSelectIds = new SimpleStringProperty();
+		this.schSelectNames = new SimpleStringProperty();
+	}
+	
+	public Integer getCoursePlanId() {
+		return coursePlanId.get();
+	}
+	public void setCoursePlanId(Integer coursePlanId) {
+		this.coursePlanId.set(coursePlanId);
+	}
+	public String getCourseCode() {
+		return courseCode.get();
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode.set(courseCode);
+	}
+	public String getCourseName() {
+		return courseName.get();
+	}
+	public void setCourseName(String courseName) {
+		this.courseName.set(courseName);
+	}
+	public String getOutlineName() {
+		return outlineName.get();
+	}
+	public void setOutlineName(String outlineName) {
+		this.outlineName.set(outlineName);
+	}
+	public String getBookName() {
+		return bookName.get();
+	}
+	public void setBookName(String bookName) {
+		this.bookName.set(bookName);
+	}
+	public String getBianZhu() {
+		return bianZhu.get();
+	}
+	public void setBianZhu(String bianZhu) {
+		this.bianZhu.set(bianZhu);
+	}
+	public String getPress() {
+		return press.get();
+	}
+	public void setPress(String press) {
+		this.press.set(press);
+	}
+	public String getPublications() {
+		return publications.get();
+	}
+	public void setPublications(String publications) {
+		this.publications.set(publications);
+	}
+	
+	public String getCengci() {
+		return cengci.get();
+	}
+
+	public void setCengci(String cengci) {
+		this.cengci.set(cengci);
+	}
+
+	public Long getSchoolId() {
+		return schoolId.get();
+	}
+	
+	public void setSchoolId(Long schoolId) {
+		this.schoolId.set(schoolId);
+	}
+	
+	public String getSchoolName() {
+		return schoolName.get();
+	}
+	
+	public void setSchoolName(String schoolName) {
+		this.schoolName.set(schoolName);
+	}
+
+	public String getSchSelectIds() {
+		return schSelectIds.get();
+	}
+	public String getSchSelectNames() {
+		return schSelectNames.get();
+	}
+	public void setSchSelectIds(String schSelectIds) {
+		this.schSelectIds.set(schSelectIds);
+	}
+	public void setSchSelectNames(String schSelectNames) {
+		this.schSelectNames.set(schSelectNames);
+	}
+	
+//	public ComboBox<School> getComboBox() {
+//		return comboBox;
+//	}
+//
+//	public void setComboBox(ComboBox<School> comboBox) {
+//		this.comboBox = comboBox;
+//	}
+
+	public String toString() {
+		return new StringBuilder()
+				.append(coursePlanId).append(" | ")
+				.append(courseCode).append(" | ")
+				.append(courseName).append(" | ")
+				.toString();
+	}
+	
+	public int hashCode() {
+	     return new HashCodeBuilder(17, 37).
+	       append(this.coursePlanId.get()).
+	       append(this.courseCode.get()).
+	       append(this.courseName.get()).
+	       append(this.outlineName.get()).
+	       append(this.bookName.get()).
+	       append(this.bianZhu.get()).
+	       append(this.press.get()).
+	       append(this.publications.get()).
+	       append(this.schoolId.get()).
+	       append(this.schoolName.get()).
+	       toHashCode();
+	}
+	
+	public boolean equals(Object obj) {
+		   if (obj == null) { return false; }
+		   if (obj == this) { return true; }
+		   if (obj.getClass() != getClass()) {
+		     return false;
+		   }
+		   CoursePlan oth = (CoursePlan) obj;
+		   return new EqualsBuilder()
+		                 .append(this.coursePlanId.get(), oth.coursePlanId.get())
+		                 .append(this.courseCode.get(), oth.courseCode.get())
+		                 .append(this.courseName.get(), oth.courseName.get())
+		                 .append(this.outlineName.get(), oth.outlineName.get())
+		                 .append(this.bookName.get(), oth.bookName.get())
+		                 .append(this.bianZhu.get(), oth.bianZhu.get())
+		                 .append(this.press.get(), oth.press.get())
+		                 .append(this.publications.get(), oth.publications.get())
+		                 .append(this.schoolId.get(), oth.schoolId.get())
+		                 .append(this.schoolName.get(), oth.schoolName.get())
+		                 .isEquals();
+	}
+	
+	public static void main(String[] args) {
+		CoursePlan plan = new CoursePlan();
+		plan.setCoursePlanId(2);
+		plan.setBianZhu("111");
+		plan.setBookName("222");
+		plan.setCourseCode("002778");
+		plan.setCourseName("333");
+		plan.setOutlineName("444");
+		plan.setPress("555");
+		plan.setPublications("66");
+		plan.setSchoolId(13L);
+		plan.setSchoolName("77");
+		
+		CoursePlan plan2 = new CoursePlan();
+		plan2.setCoursePlanId(2);
+		plan2.setBianZhu("222333");
+		plan2.setBookName("4455");
+		plan2.setCourseCode("002778");
+		plan2.setCourseName("666777");
+		plan2.setOutlineName("333444");
+		plan2.setPress("ee33");
+		plan2.setPublications("332");
+		plan2.setSchoolId(12L);
+		plan2.setSchoolName("23");
+		
+		System.err.println(plan.equals(plan2));
+	}
+}

+ 93 - 0
src/com/hmsoft/model/School.java

@@ -0,0 +1,93 @@
+package com.hmsoft.model;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class School implements Serializable {
+
+	private static final long serialVersionUID = -9178032507946351789L;
+	
+	private LongProperty schoolId;
+	private StringProperty schoolName;
+	
+	//目前学校和账号一对一!
+	private StringProperty userName;
+	private StringProperty passWord;
+	
+	public School() {
+		this.schoolId =  new SimpleLongProperty();
+		this.schoolName = new SimpleStringProperty();
+		this.userName = new SimpleStringProperty();
+		this.passWord = new SimpleStringProperty();
+	}
+	
+	public School(int schooId, String schoolName, String userName, String passWord) {
+		this.schoolId =  new SimpleLongProperty(schooId);
+		this.schoolName = new SimpleStringProperty(schoolName);
+		this.userName = new SimpleStringProperty(userName);
+		this.passWord = new SimpleStringProperty(passWord);
+	}
+	
+	public Long getSchoolId() {
+		return schoolId.get();
+	}
+	public void setSchoolId(Long schoolId) {
+		this.schoolId.set(schoolId);
+	}
+	public String getSchoolName() {
+		return schoolName.get();
+	}
+	public void setSchoolName(String schoolName) {
+		this.schoolName.set(schoolName);
+	}
+	
+	public String getUserName() {
+		return userName.get();
+	}
+	public String getPassWord() {
+		return passWord.get();
+	}
+	public void setUserName(String userName) {
+		this.userName.set(userName);
+	}
+	public void setPassWord(String passWord) {
+		this.passWord.set(passWord);
+	}
+	public String toString() {
+		return new StringBuilder().append(schoolId.get()).append(" | ").append(schoolName.get()).toString();
+	}
+	
+	public int hashCode() {
+	     return new HashCodeBuilder(17, 37).
+	       append(this.schoolId.get()).
+	       append(this.schoolName.get()).
+	       toHashCode();
+	}
+	
+	public boolean equals(Object obj) {
+		   if (obj == null) { return false; }
+		   if (obj == this) { return true; }
+		   if (obj.getClass() != getClass()) {
+		     return false;
+		   }
+		   School oth = (School) obj;
+		   return new EqualsBuilder()
+		                 .append(this.schoolId.get(), oth.schoolId.get())
+		                 .append(this.schoolName.get(), oth.schoolName.get())
+		                 .isEquals();
+	}
+}

+ 93 - 0
src/com/hmsoft/model/request/BizRequest.java

@@ -0,0 +1,93 @@
+package com.hmsoft.model.request;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import com.hmsoft.model.CoursePlan;
+
+/**
+ * 请求业务类.
+ * @author zq
+ *
+ */
+public class BizRequest implements Serializable {
+	
+	private static final long serialVersionUID = -8814212998506317013L;
+	protected Integer reqType; //1-学校选择课程,2-命题办通知其他学校课程被选择了,3-命题办指定该课程征集学校,4-学校取消选择的课程,5-命题办刷新课程列表数据到各个学校客户端
+	protected String batchNo;//批次编号
+	protected Long schoolId;
+	protected String schoolName;
+	protected String selectIds;//选择的课程代码,可以选多个
+	protected String selectRowIndx;//选择的行标
+	protected List<CoursePlan> sourceList;//服务端发送过来的列表
+	
+	public String getBatchNo() {
+		return batchNo;
+	}
+	public void setBatchNo(String batchNo) {
+		this.batchNo = batchNo;
+	}
+	public Integer getReqType() {
+		return reqType;
+	}
+	public List<CoursePlan> getSourceList() {
+		return sourceList;
+	}
+	public void setSourceList(List<CoursePlan> sourceList) {
+		this.sourceList = sourceList;
+	}
+	public void setReqType(Integer reqType) {
+		this.reqType = reqType;
+	}
+	public Long getSchoolId() {
+		return schoolId;
+	}
+	public void setSchoolId(Long schoolId) {
+		this.schoolId = schoolId;
+	}
+	public String getSchoolName() {
+		return schoolName;
+	}
+	public void setSchoolName(String schoolName) {
+		this.schoolName = schoolName;
+	}
+	public String getSelectIds() {
+		return selectIds;
+	}
+	public void setSelectIds(String selectIds) {
+		this.selectIds = selectIds;
+	}
+	public String getSelectRowIndx() {
+		return selectRowIndx;
+	}
+	public void setSelectRowIndx(String selectRowIndx) {
+		this.selectRowIndx = selectRowIndx;
+	}
+	
+	public int hashCode() {
+	     return new HashCodeBuilder(17, 37).
+	       append(this.reqType).
+	       append(this.schoolId).
+	       append(this.schoolName).
+	       append(this.selectIds).
+	       toHashCode();
+	}
+	
+	public boolean equals(Object obj) {
+		   if (obj == null) { return false; }
+		   if (obj == this) { return true; }
+		   if (obj.getClass() != getClass()) {
+		     return false;
+		   }
+		   BizRequest oth = (BizRequest) obj;
+		   return new EqualsBuilder()
+				   		 .append(this.reqType, oth.reqType)
+		                 .append(this.schoolId, oth.schoolId)
+		                 .append(this.schoolName, oth.schoolName)
+		                 .append(this.selectIds, oth.selectIds)
+		                 .isEquals();
+	}
+}

+ 66 - 0
src/com/hmsoft/model/request/LoginRequest.java

@@ -0,0 +1,66 @@
+package com.hmsoft.model.request;
+
+import java.io.Serializable;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class LoginRequest implements Serializable {
+
+	private static final long serialVersionUID = 3802628747524629893L;
+	protected String userName;
+	protected String passWord;
+	protected int status;//登录成功失败状态(0-成功,-1失败)
+	protected Long schoolId;
+	protected String schoolName;
+	
+	public LoginRequest() {
+		
+	}
+	
+	public LoginRequest(String userName, String passWord) {
+		this.userName = userName;
+		this.passWord = passWord;
+	}
+	
+	public String getUserName() {
+		return userName;
+	}
+	public String getPassWord() {
+		return passWord;
+	}
+	public void setUserName(String userName) {
+		this.userName = userName;
+	}
+	public void setPassWord(String passWord) {
+		this.passWord = passWord;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public Long getSchoolId() {
+		return schoolId;
+	}
+
+	public String getSchoolName() {
+		return schoolName;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public void setSchoolId(Long schoolId) {
+		this.schoolId = schoolId;
+	}
+
+	public void setSchoolName(String schoolName) {
+		this.schoolName = schoolName;
+	}
+	
+	
+}

+ 30 - 0
src/com/hmsoft/model/request/LogoutRequest.java

@@ -0,0 +1,30 @@
+package com.hmsoft.model.request;
+
+import java.io.Serializable;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class LogoutRequest implements Serializable {
+
+	private static final long serialVersionUID = -473104082250660603L;
+	protected String userName;
+	
+	public LogoutRequest() {
+		
+	}
+	
+	public LogoutRequest(String userName) {
+		this.userName = userName;
+	}
+
+	public String getUserName() {
+		return userName;
+	}
+
+	public void setUserName(String userName) {
+		this.userName = userName;
+	}
+}

+ 15 - 0
src/com/hmsoft/model/response/LoginResponse.java

@@ -0,0 +1,15 @@
+package com.hmsoft.model.response;
+
+import java.io.Serializable;
+import com.hmsoft.model.request.LoginRequest;
+
+/**
+ * 登录响应类.
+ * @author zq
+ *
+ */
+public class LoginResponse extends LoginRequest implements Serializable {
+
+	private static final long serialVersionUID = -3887085836060598718L;
+	
+}

+ 85 - 0
src/com/hmsoft/remote/caucho/hessian/HessianException.java

@@ -0,0 +1,85 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian;
+
+/**
+ * Base runtime exception for Hessian exceptions. 
+ */
+public class HessianException extends RuntimeException {
+  /**
+   * Zero-arg constructor.
+   */
+  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);
+  }
+}

+ 118 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractDeserializer.java

@@ -0,0 +1,118 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Deserializing an object. 
+ */
+abstract public class AbstractDeserializer implements Deserializer {
+  public Class getType()
+  {
+    return Object.class;
+  }
+
+  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() + " (" + obj + ")");
+    else
+      throw error(className + ": unexpected null value");
+  }
+  
+  public Object readList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    throw new UnsupportedOperationException(String.valueOf(this));
+  }
+  
+  public Object readLengthList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    throw new UnsupportedOperationException(String.valueOf(this));
+  }
+  
+  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() + " (" + obj + ")");
+    else
+      throw error(className + ": unexpected null value");
+  }
+  
+  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);
+  }
+}

+ 442 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianInput.java

@@ -0,0 +1,442 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Abstract base class for Hessian requests.  Hessian users should only
+ * need to use the methods in this class.
+ *
+ * <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 void setRemoteResolver(HessianRemoteResolver resolver)
+  {
+    this.resolver = resolver;
+  }
+
+  /**
+   * Sets the resolver used to lookup remote objects.
+   */
+  public HessianRemoteResolver getRemoteResolver()
+  {
+    return resolver;
+  }
+
+  /**
+   * Sets the serializer factory.
+   */
+  public void setSerializerFactory(SerializerFactory ser)
+  {
+  }
+
+  /**
+   * Reads the call
+   *
+   * <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.
+   *
+   * <pre>
+   * H b16 b8 value
+   * </pre>
+   */
+  abstract public String readHeader()
+    throws IOException;
+
+  /**
+   * Starts reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <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>The call expects the following protocol data
+   *
+   * <pre>
+   * c major minor
+   * m b16 b8 method
+   * </pre>
+   */
+  abstract public void startCall()
+    throws IOException;
+
+  /**
+   * Completes reading the call
+   *
+   * <p>The call expects the following protocol data
+   *
+   * <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>A successful completion will have a single value:
+   *
+   * <pre>
+   * r
+   * v
+   * </pre>
+   */
+  abstract public void startReply()
+    throws Throwable;
+
+  /**
+   * Completes reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  abstract public void completeReply()
+    throws IOException;
+
+  /**
+   * Reads a boolean
+   *
+   * <pre>
+   * T
+   * F
+   * </pre>
+   */
+  abstract public boolean readBoolean()
+    throws IOException;
+
+  /**
+   * Reads a null
+   *
+   * <pre>
+   * N
+   * </pre>
+   */
+  abstract public void readNull()
+    throws IOException;
+
+  /**
+   * Reads an integer
+   *
+   * <pre>
+   * I b32 b24 b16 b8
+   * </pre>
+   */
+  abstract public int readInt()
+    throws IOException;
+
+  /**
+   * Reads a long
+   *
+   * <pre>
+   * L b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  abstract public long readLong()
+    throws IOException;
+
+  /**
+   * Reads a double.
+   *
+   * <pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  abstract public double readDouble()
+    throws IOException;
+
+  /**
+   * Reads a date.
+   *
+   * <pre>
+   * T b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  abstract public long readUTCDate()
+    throws IOException;
+
+  /**
+   * Reads a string encoded in UTF-8
+   *
+   * <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
+   *
+   * <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).
+   *
+   * <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.
+   *
+   * <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.
+   *
+   * <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.
+   */
+  abstract public Object readObject()
+    throws IOException;
+
+  /**
+   * Reads a remote object reference to the stream.  The type is the
+   * type of the remote interface.
+   *
+   * <code><pre>
+   * 'r' 't' b16 b8 type url
+   * </pre></code>
+   */
+  abstract public Object readRemote()
+    throws IOException;
+
+  /**
+   * Reads a reference
+   *
+   * <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
+  {
+  }
+}

+ 531 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianOutput.java

@@ -0,0 +1,531 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Abstract output stream for Hessian requests.
+ *
+ * <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;
+
+  /**
+   * Sets the serializer factory.
+   */
+  public void setSerializerFactory(SerializerFactory factory)
+  {
+    _serializerFactory = factory;
+  }
+
+  /**
+   * Gets the serializer factory.
+   */
+  public SerializerFactory getSerializerFactory()
+  {
+    return _serializerFactory;
+  }
+
+  /**
+   * 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:
+   *
+   * <code><pre>
+   * C
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  abstract public void startCall()
+    throws IOException;
+
+  /**
+   * Starts the method call:
+   *
+   * <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.
+   *
+   * <code><pre>
+   * string
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  abstract public void writeMethod(String method)
+    throws IOException;
+
+  /**
+   * Completes the method call:
+   *
+   * <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:
+   *
+   * <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:
+   *
+   * <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:
+   *
+   * <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:
+   *
+   * <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.
+   *
+   * <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
+   *
+   * <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:
+   *
+   * <code><pre>
+   * S b16 b8 string-value
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  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:
+   *
+   * <code><pre>
+   * S b16 b8 string-value
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  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:
+   *
+   * <code><pre>
+   * B b16 b18 bytes
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  abstract public void writeBytes(byte []buffer)
+    throws IOException;
+  /**
+   * Writes a byte array to the stream.
+   * The array will be written with the following syntax:
+   *
+   * <code><pre>
+   * B b16 b18 bytes
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  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.
+   *
+   * <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.
+   *
+   * <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.
+   *
+   * <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.
+   *
+   * <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>.
+   *
+   * <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>.
+   *
+   * <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>.
+   *
+   * <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
+  {
+  }
+}

+ 65 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianResolver.java

@@ -0,0 +1,65 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Looks up remote objects.  The default just returns a HessianRemote object.
+ */
+public class AbstractHessianResolver implements HessianRemoteResolver {
+  /**
+   * Looks up a proxy object.
+   */
+  public Object lookup(String type, String url)
+    throws IOException
+  {
+    return new HessianRemote(type, url);
+  }
+}

+ 67 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractListDeserializer.java

@@ -0,0 +1,67 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Deserializing a JDK 1.2 Collection.
+ */
+public class AbstractListDeserializer extends AbstractDeserializer {
+  public Object readObject(AbstractHessianInput in)
+    throws IOException
+  {
+    Object obj = in.readObject();
+
+    if (obj != null)
+      throw error("expected list at " + obj.getClass().getName() + " (" + obj + ")");
+    else
+      throw error("expected list at null");
+  }
+}

+ 74 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractMapDeserializer.java

@@ -0,0 +1,74 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class AbstractMapDeserializer extends AbstractDeserializer {
+  
+  public Class getType()
+  {
+    return HashMap.class;
+  }
+  
+  public Object readObject(AbstractHessianInput in)
+    throws IOException
+  {
+    Object obj = in.readObject();
+
+    if (obj != null)
+      throw error("expected map/object at " + obj.getClass().getName() + " (" + obj + ")");
+    else
+      throw error("expected map/object at null");
+  }
+}

+ 63 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializer.java

@@ -0,0 +1,63 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.logging.*;
+
+/**
+ * Serializing an object. 
+ */
+abstract public class AbstractSerializer implements Serializer {
+  protected static final Logger log
+    = Logger.getLogger(AbstractSerializer.class.getName());
+  
+  abstract public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException;
+}

+ 74 - 0
src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializerFactory.java

@@ -0,0 +1,74 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+/**
+ * Factory for returning serialization methods.
+ */
+abstract public class AbstractSerializerFactory {
+  /**
+   * 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.
+   */
+  abstract public Serializer getSerializer(Class cl)
+    throws HessianProtocolException;
+
+  /**
+   * 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.
+   */
+  abstract public Deserializer getDeserializer(Class cl)
+    throws HessianProtocolException;
+}

+ 164 - 0
src/com/hmsoft/remote/caucho/hessian/io/ArrayDeserializer.java

@@ -0,0 +1,164 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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;
+  }
+
+  public Class getType()
+  {
+    return _type;
+  }
+
+  /**
+   * Reads the array.
+   */
+  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.
+   */
+  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];
+  }
+
+  public String toString()
+  {
+    return "ArrayDeserializer[" + _componentType + "]";
+  }
+}

+ 94 - 0
src/com/hmsoft/remote/caucho/hessian/io/ArraySerializer.java

@@ -0,0 +1,94 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Serializing a Java array.
+ */
+public class ArraySerializer extends AbstractSerializer {
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    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;
+  }
+}

+ 608 - 0
src/com/hmsoft/remote/caucho/hessian/io/BasicDeserializer.java

@@ -0,0 +1,608 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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;
+  }
+
+  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();
+    }
+  }
+  
+  public Object readObject(AbstractHessianInput in)
+    throws IOException
+  {
+    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();
+    }
+  }
+  
+  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));
+    }
+  }
+  
+  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));
+    }
+  }
+}

+ 285 - 0
src/com/hmsoft/remote/caucho/hessian/io/BasicSerializer.java

@@ -0,0 +1,285 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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;
+  }
+  
+  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:
+    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()));
+    }
+  }
+}

+ 295 - 0
src/com/hmsoft/remote/caucho/hessian/io/BeanDeserializer.java

@@ -0,0 +1,295 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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]);
+      }
+    }
+  }
+
+  public Class getType()
+  {
+    return _type;
+  }
+    
+  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;
+  }
+
+  /**
+   * 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();
+  }
+}

+ 315 - 0
src/com/hmsoft/remote/caucho/hessian/io/BeanSerializer.java

@@ -0,0 +1,315 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.logging.*;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class BeanSerializer extends AbstractSerializer {
+  private static final Logger log
+    = Logger.getLogger(BeanSerializer.class.getName());
+  
+  private static final Object []NULL_ARGS = new Object[0];
+  private Method []_methods;
+  private String []_names;
+
+  private Object _writeReplaceFactory;
+  private Method _writeReplace;
+  
+  public BeanSerializer(Class cl, ClassLoader loader)
+  {
+    introspectWriteReplace(cl, loader);
+
+    ArrayList primitiveMethods = new ArrayList();
+    ArrayList compoundMethods = new ArrayList();
+    
+    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;
+
+	if (method.getParameterTypes().length != 0)
+	  continue;
+
+	String name = method.getName();
+
+	if (! name.startsWith("get"))
+	  continue;
+
+	Class type = method.getReturnType();
+
+	if (type.equals(void.class))
+	  continue;
+
+	if (findSetter(methods, name, type) == null)
+	  continue;
+
+	// XXX: could parameterize the handler to only deal with public
+	method.setAccessible(true);
+
+	if (type.isPrimitive()
+	    || type.getName().startsWith("java.lang.")
+	    && ! type.equals(Object.class))
+	  primitiveMethods.add(method);
+	else
+	  compoundMethods.add(method);
+      }
+    }
+
+    ArrayList methodList = new ArrayList();
+    methodList.addAll(primitiveMethods);
+    methodList.addAll(compoundMethods);
+
+    Collections.sort(methodList, new MethodNameCmp());
+
+    _methods = new Method[methodList.size()];
+    methodList.toArray(_methods);
+
+    _names = new String[_methods.length];
+    
+    for (int i = 0; i < _methods.length; i++) {
+      String name = _methods[i].getName();
+
+      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);
+
+      _names[i] = name;
+    }
+  }
+
+  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)
+  {
+    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;
+  }
+
+  /**
+   * 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;
+  }
+  
+  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);
+
+	out.removeRef(obj);
+
+	out.writeObject(repl);
+
+	out.replaceRef(repl, obj);
+
+	return;
+      }
+    } catch (Exception e) {
+      log.log(Level.FINER, e.toString(), e);
+    }
+
+    int ref = out.writeObjectBegin(cl.getName());
+
+    if (ref < -1) {
+      // Hessian 1.1 uses a map
+      
+      for (int i = 0; i < _methods.length; i++) {
+	Method method = _methods[i];
+	Object value = null;
+
+	try {
+	  value = _methods[i].invoke(obj, (Object []) null);
+	} catch (Exception e) {
+	  log.log(Level.FINE, e.toString(), e);
+	}
+
+	out.writeString(_names[i]);
+	
+	out.writeObject(value);
+      }
+      
+      out.writeMapEnd();
+    }
+    else {
+      if (ref == -1) {
+	out.writeInt(_names.length);
+	
+	for (int i = 0; i < _names.length; i++)
+	  out.writeString(_names[i]);
+	
+	out.writeObjectBegin(cl.getName());
+      }
+
+      for (int i = 0; i < _methods.length; i++) {
+	Method method = _methods[i];
+	Object value = null;
+
+	try {
+	  value = _methods[i].invoke(obj, (Object []) null);
+	} catch (Exception e) {
+	  log.log(Level.FINER, e.toString(), e);
+	}
+	
+	out.writeObject(value);
+      }
+    }
+  }
+
+  /**
+   * Finds any matching setter.
+   */
+  private Method findSetter(Method []methods, String getterName, Class arg)
+  {
+    String setterName = "set" + getterName.substring(3);
+    
+    for (int i = 0; i < methods.length; i++) {
+      Method method = methods[i];
+
+      if (! method.getName().equals(setterName))
+	continue;
+      
+      if (! method.getReturnType().equals(void.class))
+	continue;
+
+      Class []params = method.getParameterTypes();
+
+      if (params.length == 1 && params[0].equals(arg))
+	return method;
+    }
+
+    return null;
+  }
+
+  static class MethodNameCmp implements Comparator<Method> {
+    public int compare(Method a, Method b)
+    {
+      return a.getName().compareTo(b.getName());
+    }
+  }
+}

+ 82 - 0
src/com/hmsoft/remote/caucho/hessian/io/BeanSerializerFactory.java

@@ -0,0 +1,82 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+/**
+ * Factory for returning serialization methods.
+ */
+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.
+   */
+  protected Serializer getDefaultSerializer(Class cl)
+  {
+    return new BeanSerializer(cl, getClassLoader());
+  }
+  
+  /**
+   * 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.
+   */
+  protected Deserializer getDefaultDeserializer(Class cl)
+  {
+    return new BeanDeserializer(cl);
+  }
+}

+ 93 - 0
src/com/hmsoft/remote/caucho/hessian/io/CalendarHandle.java

@@ -0,0 +1,93 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * Handle for a calendar object.
+ */
+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);
+    }
+  }
+}

+ 77 - 0
src/com/hmsoft/remote/caucho/hessian/io/CalendarSerializer.java

@@ -0,0 +1,77 @@
+/*
+ * 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.hmsoft.remote.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 CalendarSerializer create()
+  {
+    return SERIALIZER;
+  }
+  
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    if (obj == null)
+      out.writeNull();
+    else {
+      Calendar cal = (Calendar) obj;
+
+      out.writeObject(new CalendarHandle(cal.getClass(),
+					 cal.getTimeInMillis()));
+    }
+  }
+}

+ 160 - 0
src/com/hmsoft/remote/caucho/hessian/io/ClassDeserializer.java

@@ -0,0 +1,160 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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>();
+
+  private ClassLoader _loader;
+  
+  public ClassDeserializer(ClassLoader loader)
+  {
+    _loader = loader;
+  }
+  
+  public Class getType()
+  {
+    return Class.class;
+  }
+  
+  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;
+  }
+  
+  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);
+    }
+  }
+
+  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);
+  }
+}

+ 87 - 0
src/com/hmsoft/remote/caucho/hessian/io/ClassSerializer.java

@@ -0,0 +1,87 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Serializing a remote object.
+ */
+public class ClassSerializer extends AbstractSerializer {
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    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());
+      }
+    }
+  }
+}

+ 134 - 0
src/com/hmsoft/remote/caucho/hessian/io/CollectionDeserializer.java

@@ -0,0 +1,134 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Deserializing a JDK 1.2 Collection.
+ */
+public class CollectionDeserializer extends AbstractListDeserializer {
+  private Class _type;
+  
+  public CollectionDeserializer(Class type)
+  {
+    _type = type;
+  }
+  
+  public Class getType()
+  {
+    return _type;
+  }
+  
+  public Object readList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    Collection list = createList();
+
+    in.addRef(list);
+
+    while (! in.isEnd())
+      list.add(in.readObject());
+
+    in.readEnd();
+
+    return list;
+  }
+  
+  public Object readLengthList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    Collection list = createList();
+
+    in.addRef(list);
+
+    for (; length > 0; length--)
+      list.add(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;
+  }
+}
+
+

+ 108 - 0
src/com/hmsoft/remote/caucho/hessian/io/CollectionSerializer.java

@@ -0,0 +1,108 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Serializing a JDK 1.2 Collection.
+ */
+public class CollectionSerializer extends AbstractSerializer
+{
+  private boolean _sendJavaType = true;
+
+  /**
+   * Set true if the java type of the collection should be sent.
+   */
+  public void setSendJavaType(boolean sendJavaType)
+  {
+    _sendJavaType = sendJavaType;
+  }
+
+  /**
+   * Return true if the java type of the collection should be sent.
+   */
+  public boolean getSendJavaType()
+  {
+    return _sendJavaType;
+  }
+    
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    if (out.addRef(obj))
+      return;
+
+    Collection list = (Collection) obj;
+
+    Class cl = obj.getClass();
+    boolean hasEnd;
+    
+    if (cl.equals(ArrayList.class)
+	|| ! _sendJavaType
+	|| ! Serializable.class.isAssignableFrom(cl))
+      hasEnd = out.writeListBegin(list.size(), null);
+    else
+      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();
+  }
+}

+ 204 - 0
src/com/hmsoft/remote/caucho/hessian/io/Deflation.java

@@ -0,0 +1,204 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+public class Deflation extends HessianEnvelope {
+  public Deflation()
+  {
+  }
+
+  public Hessian2Output wrap(Hessian2Output out)
+    throws IOException
+  {
+    OutputStream os = new DeflateOutputStream(out);
+    
+    Hessian2Output filterOut = new Hessian2Output(os);
+
+    filterOut.setCloseStreamOnClose(true);
+    
+    return filterOut;
+  }
+
+  public Hessian2Input unwrap(Hessian2Input in)
+    throws IOException
+  {
+    int version = in.readEnvelope();
+
+    String method = in.readMethod();
+
+    if (! method.equals(getClass().getName()))
+      throw new IOException("expected hessian Envelope method '" +
+			    getClass().getName() + "' at '" + method + "'");
+
+    return unwrapHeaders(in);
+  }
+
+  public Hessian2Input unwrapHeaders(Hessian2Input in)
+    throws IOException
+  {
+    InputStream is = new DeflateInputStream(in);
+
+    Hessian2Input filter = new Hessian2Input(is);
+    
+    filter.setCloseStreamOnClose(true);
+    
+    return filter;
+  }
+  
+  static class DeflateOutputStream extends OutputStream {
+    private Hessian2Output _out;
+    private OutputStream _bodyOut;
+    private DeflaterOutputStream _deflateOut;
+    
+    DeflateOutputStream(Hessian2Output out)
+      throws IOException
+    {
+      _out = out;
+
+      _out.startEnvelope(Deflation.class.getName());
+    
+      _out.writeInt(0);
+
+      _bodyOut = _out.getBytesOutputStream();
+    
+      _deflateOut = new DeflaterOutputStream(_bodyOut);
+    }
+    
+    public void write(int ch)
+      throws IOException
+    {
+      _deflateOut.write(ch);
+    }
+    
+    public void write(byte []buffer, int offset, int length)
+      throws IOException
+    {
+      _deflateOut.write(buffer, offset, length);
+    }
+
+    public void close()
+      throws IOException
+    {
+      Hessian2Output out = _out;
+      _out = null;
+
+      if (out != null) {
+	_deflateOut.close();
+	_bodyOut.close();
+
+	out.writeInt(0);
+
+        out.completeEnvelope();
+          
+	out.close();
+      }
+    }
+  }
+  
+  static class DeflateInputStream extends InputStream {
+    private Hessian2Input _in;
+    
+    private InputStream _bodyIn;
+    private InflaterInputStream _inflateIn;
+    
+    DeflateInputStream(Hessian2Input in)
+      throws IOException
+    {
+      _in = in;
+
+      int len = in.readInt();
+
+      if (len != 0)
+        throw new IOException("expected no headers");
+      
+      _bodyIn = _in.readInputStream();
+
+      _inflateIn = new InflaterInputStream(_bodyIn);
+    }
+    
+    public int read()
+      throws IOException
+    {
+      return _inflateIn.read();
+    }
+    
+    public int read(byte []buffer, int offset, int length)
+      throws IOException
+    {
+      return _inflateIn.read(buffer, offset, length);
+    }
+
+    public void close()
+      throws IOException
+    {
+      Hessian2Input in = _in;
+      _in = null;
+
+      if (in != null) {
+	_inflateIn.close();
+	_bodyIn.close();
+
+	int len = in.readInt();
+
+	if (len != 0)
+	  throw new IOException("Unexpected footer");
+
+        in.completeEnvelope();
+
+	in.close();
+      }
+    }
+  }
+}

+ 73 - 0
src/com/hmsoft/remote/caucho/hessian/io/Deserializer.java

@@ -0,0 +1,73 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Deserializing an object. 
+ */
+public interface Deserializer {
+  public Class getType();
+
+  public Object readObject(AbstractHessianInput in)
+    throws IOException;
+  
+  public Object readList(AbstractHessianInput in, int length)
+    throws IOException;
+  
+  public Object readLengthList(AbstractHessianInput in, int length)
+    throws IOException;
+  
+  public Object readMap(AbstractHessianInput in)
+    throws IOException;
+  
+  public Object readObject(AbstractHessianInput in, String []fieldNames)
+    throws IOException;
+}

+ 138 - 0
src/com/hmsoft/remote/caucho/hessian/io/EnumDeserializer.java

@@ -0,0 +1,138 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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);
+    }
+  }
+  
+  public Class getType()
+  {
+    return _enumType;
+  }
+  
+  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;
+  }
+  
+  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);
+    }
+  }
+}

+ 108 - 0
src/com/hmsoft/remote/caucho/hessian/io/EnumSerializer.java

@@ -0,0 +1,108 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Serializing an object for known object types.
+ */
+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);
+    }
+  }
+  
+  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);
+    }
+  }
+}

+ 84 - 0
src/com/hmsoft/remote/caucho/hessian/io/EnumerationDeserializer.java

@@ -0,0 +1,84 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.Vector;
+
+/**
+ * Deserializing a JDK 1.2 Collection.
+ */
+public class EnumerationDeserializer extends AbstractListDeserializer {
+  private static EnumerationDeserializer _deserializer;
+
+  public static EnumerationDeserializer create()
+  {
+    if (_deserializer == null)
+      _deserializer = new EnumerationDeserializer();
+
+    return _deserializer;
+  }
+  
+  public Object readList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    Vector list = new Vector();
+
+    in.addRef(list);
+
+    while (! in.isEnd())
+      list.add(in.readObject());
+
+    in.readEnd();
+
+    return list.elements();
+  }
+}
+
+

+ 84 - 0
src/com/hmsoft/remote/caucho/hessian/io/EnumerationSerializer.java

@@ -0,0 +1,84 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * Serializing a JDK 1.2 Enumeration.
+ */
+public class EnumerationSerializer extends AbstractSerializer {
+  private static EnumerationSerializer _serializer;
+
+  public static EnumerationSerializer create()
+  {
+    if (_serializer == null)
+      _serializer = new EnumerationSerializer();
+
+    return _serializer;
+  }
+  
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    Enumeration iter = (Enumeration) obj;
+
+    boolean hasEnd = out.writeListBegin(-1, null);
+
+    while (iter.hasMoreElements()) {
+      Object value = iter.nextElement();
+
+      out.writeObject(value);
+    }
+
+    if (hasEnd)
+      out.writeListEnd();
+  }
+}

+ 57 - 0
src/com/hmsoft/remote/caucho/hessian/io/EnvelopeFactory.java

@@ -0,0 +1,57 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.util.logging.*;
+
+public class EnvelopeFactory
+{
+  private static final Logger log
+    = Logger.getLogger(EnvelopeFactory.class.getName());
+}

+ 107 - 0
src/com/hmsoft/remote/caucho/hessian/io/ExtSerializerFactory.java

@@ -0,0 +1,107 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.util.HashMap;
+
+/**
+ * Factory for returning serialization methods.
+ */
+public class ExtSerializerFactory extends AbstractSerializerFactory {
+  private HashMap _serializerMap = new HashMap();
+  private HashMap _deserializerMap = new HashMap();
+
+  /**
+   * Adds a serializer.
+   *
+   * @param cl the class of the serializer
+   * @param serializer the serializer
+   */
+  public void addSerializer(Class cl, Serializer serializer)
+  {
+    _serializerMap.put(cl, serializer);
+  }
+
+  /**
+   * Adds a deserializer.
+   *
+   * @param cl the class of the deserializer
+   * @param deserializer the deserializer
+   */
+  public void addDeserializer(Class cl, Deserializer deserializer)
+  {
+    _deserializerMap.put(cl, deserializer);
+  }
+  
+  /**
+   * 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
+  {
+    return (Serializer) _serializerMap.get(cl);
+  }
+  
+  /**
+   * 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.
+   */
+  public Deserializer getDeserializer(Class cl)
+    throws HessianProtocolException
+  {
+    return (Deserializer) _deserializerMap.get(cl);
+  }
+}

+ 146 - 0
src/com/hmsoft/remote/caucho/hessian/io/Hessian2Constants.java

@@ -0,0 +1,146 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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;
+}

+ 2798 - 0
src/com/hmsoft/remote/caucho/hessian/io/Hessian2Input.java

@@ -0,0 +1,2798 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.logging.*;
+
+/**
+ * Input stream for Hessian requests.
+ *
+ * <p>HessianInput is unbuffered, so any client needs to provide
+ * its own buffering.
+ *
+ * <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 Field _detailMessageField;
+
+  private static final int SIZE = 256;
+  private static final int GAP = 16;
+  
+  // factory for deserializing objects in the input stream
+  protected SerializerFactory _serializerFactory;
+
+  private static boolean _isCloseStreamOnClose;
+  
+  protected ArrayList _refs;
+  protected ArrayList _classDefs;
+  protected ArrayList _types;
+  
+  // the underlying input stream
+  private InputStream _is;
+  private final byte []_buffer = new byte[SIZE];
+  
+  // a peek character
+  private int _offset;
+  private int _length;
+
+  // true for streaming data
+  private boolean _isStreaming;
+  
+  // the method for a call
+  private String _method;
+  private int _argLength;
+
+  private Reader _chunkReader;
+  private InputStream _chunkInputStream;
+
+  private Throwable _replyFault;
+
+  private StringBuffer _sbuf = new StringBuffer();
+  
+  // 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;
+  }
+
+  /**
+   * Sets the serializer factory.
+   */
+  public void setSerializerFactory(SerializerFactory factory)
+  {
+    _serializerFactory = factory;
+  }
+
+  /**
+   * Gets the serializer factory.
+   */
+  public SerializerFactory getSerializerFactory()
+  {
+    return _serializerFactory;
+  }
+
+  /**
+   * 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 void setCloseStreamOnClose(boolean isClose)
+  {
+    _isCloseStreamOnClose = isClose;
+  }
+
+  public boolean isCloseStreamOnClose()
+  {
+    return _isCloseStreamOnClose;
+  }
+
+  /**
+   * Returns the calls method
+   */
+  public String getMethod()
+  {
+    return _method;
+  }
+
+  /**
+   * Returns any reply fault.
+   */
+  public Throwable getReplyFault()
+  {
+    return _replyFault;
+  }
+
+  /**
+   * Starts reading the call
+   *
+   * <pre>
+   * c major minor
+   * </pre>
+   */
+  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
+   *
+   * <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>A successful completion will have a single value:
+   *
+   * <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>A successful completion will have a single value:
+   *
+   * <pre>
+   * string
+   * </pre>
+   */
+  public String readMethod()
+    throws IOException
+  {
+    _method = readString();
+
+    return _method;
+  }
+
+  /**
+   * Returns the number of method arguments
+   *
+   * <pre>
+   * int
+   * </pre>
+   */
+  @Override
+  public int readMethodArgLength()
+    throws IOException
+  {
+    return readInt();
+  }
+
+  /**
+   * Starts reading the call, including the headers.
+   *
+   * <p>The call expects the following protocol data
+   *
+   * <pre>
+   * c major minor
+   * m b16 b8 method
+   * </pre>
+   */
+  public void startCall()
+    throws IOException
+  {
+    readCall();
+
+    readMethod();
+  }
+
+  /**
+   * Completes reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * </pre>
+   */
+  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>A successful completion will have a single value:
+   *
+   * <pre>
+   * r
+   * </pre>
+   */
+  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>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  public void completeReply()
+    throws IOException
+  {
+  }
+
+  /**
+   * Completes reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <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.
+   *
+   * <pre>
+   * H b16 b8 value
+   * </pre>
+   */
+  public String readHeader()
+    throws IOException
+  {
+    return null;
+  }
+
+  /**
+   * Starts reading the message
+   *
+   * <pre>
+   * p major minor
+   * </pre>
+   */
+  public int startMessage()
+    throws IOException
+  {
+    int tag = read();
+
+    if (tag == 'p')
+      _isStreaming = false;
+    else if (tag == 'P')
+      _isStreaming = true;
+    else
+      throw error("expected Hessian message ('p') at " + codeName(tag));
+
+    int major = read();
+    int minor = read();
+
+    return (major << 16) + minor;
+  }
+
+  /**
+   * Completes reading the message
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <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
+   *
+   * <pre>
+   * N
+   * </pre>
+   */
+  public void readNull()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N': return;
+      
+    default:
+      throw expect("null", tag);
+    }
+  }
+
+  /**
+   * Reads a boolean
+   *
+   * <pre>
+   * T
+   * F
+   * </pre>
+   */
+  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
+   *
+   * <pre>
+   * I b32 b24 b16 b8
+   * </pre>
+   */
+  public short readShort()
+    throws IOException
+  {
+    return (short) readInt();
+  }
+
+  /**
+   * Reads an integer
+   *
+   * <pre>
+   * I b32 b24 b16 b8
+   * </pre>
+   */
+  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
+   *
+   * <pre>
+   * L b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  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
+   *
+   * <pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  public float readFloat()
+    throws IOException
+  {
+    return (float) readDouble();
+  }
+
+  /**
+   * Reads a double
+   *
+   * <pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  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.
+   *
+   * <pre>
+   * T b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  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
+   *
+   * <pre>
+   * S b16 b8 string value
+   * </pre>
+   */
+  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
+   *
+   * <pre>
+   * B b16 b8 data value
+   * </pre>
+   */
+  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.
+   */
+  public Object readObject(Class cl)
+    throws IOException
+  {
+    if (cl == null || cl == Object.class)
+      return readObject();
+    
+    int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'H':
+      {
+	Deserializer reader = findSerializerFactory().getDeserializer(cl);
+
+	return reader.readMap(this);
+      }
+
+    case 'M':
+      {
+	String type = readType();
+
+	// hessian/3bb3
+	if ("".equals(type)) {
+	  Deserializer reader;
+	  reader = findSerializerFactory().getDeserializer(cl);
+
+	  return reader.readMap(this);
+	}
+	else {
+	  Deserializer reader;
+	  reader = findSerializerFactory().getObjectDeserializer(type, cl);
+
+	  return reader.readMap(this);
+	}
+      }
+
+    case 'C':
+      {
+	readObjectDefinition(cl);
+
+	return readObject(cl);
+      }
+
+    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(cl, 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(cl, def);
+      }
+
+    case BC_LIST_VARIABLE:
+      {
+	String type = readType();
+      
+	Deserializer reader;
+	reader = findSerializerFactory().getListDeserializer(type, cl);
+
+	Object v = reader.readList(this, -1);
+
+	return v;
+      }
+
+    case BC_LIST_FIXED:
+      {
+	String type = readType();
+	int length = readInt();
+      
+	Deserializer reader;
+	reader = findSerializerFactory().getListDeserializer(type, cl);
+
+	Object v = reader.readLengthList(this, length);
+
+	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, cl);
+
+	Object v = reader.readLengthList(this, length);
+
+	return v;
+      }
+
+    case BC_LIST_VARIABLE_UNTYPED:
+      {
+	Deserializer reader;
+	reader = findSerializerFactory().getListDeserializer(null, cl);
+
+	Object v = reader.readList(this, -1);
+
+	return v;
+      }
+
+    case BC_LIST_FIXED_UNTYPED:
+      {
+	int length = readInt();
+      
+	Deserializer reader;
+	reader = findSerializerFactory().getListDeserializer(null, cl);
+
+	Object v = reader.readLengthList(this, length);
+
+	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, cl);
+
+	Object v = reader.readLengthList(this, length);
+
+	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(cl).readObject(this);
+    return value;
+  }
+  
+  /**
+   * Reads an arbitrary object from the input stream when the type
+   * is unknown.
+   */
+  public Object readObject()
+    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();
+
+	int data;
+	_sbuf.setLength(0);
+      
+	while ((data = parseChar()) >= 0)
+	  _sbuf.append((char) data);
+
+	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;
+
+	int data;
+	_sbuf.setLength(0);
+      
+	while ((data = parseChar()) >= 0)
+	  _sbuf.append((char) data);
+
+	return _sbuf.toString();
+      }
+
+    case 0x30: case 0x31: case 0x32: case 0x33:
+      {
+	_isLastChunk = true;
+	_chunkLength = (tag - 0x30) * 256 + read();
+
+	_sbuf.setLength(0);
+
+	int ch;
+	while ((ch = parseChar()) >= 0)
+	  _sbuf.append((char) ch);
+
+	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);
+      
+	return reader.readLengthList(this, length);
+      }
+
+    case BC_LIST_FIXED_UNTYPED:
+      {
+	// fixed length lists
+	int length = readInt();
+
+	Deserializer reader;
+	reader = findSerializerFactory().getListDeserializer(null, null);
+      
+	return reader.readLengthList(this, length);
+      }
+
+      // 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);
+      
+	return reader.readLengthList(this, length);
+      }
+
+      // 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);
+      
+	return reader.readLengthList(this, length);
+      }
+
+    case 'H':
+      {
+	return findSerializerFactory().readMap(this, 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));
+    }
+  }
+
+  /**
+   * Reads an object definition:
+   *
+   * <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.
+   */
+  public Object readRemote()
+    throws IOException
+  {
+    String type = readType();
+    String url = readString();
+
+    return resolveRemote(type, url);
+  }
+
+  /**
+   * Reads a reference.
+   */
+  public Object readRef()
+    throws IOException
+  {
+    return _refs.get(parseInt());
+  }
+
+  /**
+   * Reads the start of a list.
+   */
+  public int readListStart()
+    throws IOException
+  {
+    return read();
+  }
+
+  /**
+   * Reads the start of a list.
+   */
+  public int readMapStart()
+    throws IOException
+  {
+    return read();
+  }
+
+  /**
+   * Returns true if this is the end of a list or a map.
+   */
+  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.
+   */
+  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.
+   */
+  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.
+   */
+  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.
+   */
+  public int addRef(Object ref)
+  {
+    if (_refs == null)
+      _refs = new ArrayList();
+    
+    _refs.add(ref);
+
+    return _refs.size() - 1;
+  }
+
+  /**
+   * Adds a list/map reference.
+   */
+  public void setRef(int i, Object ref)
+  {
+    _refs.set(i, ref);
+  }
+  
+  /**
+   * Resets the references for streaming.
+   */
+  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.
+   *
+   * <pre>
+   * type ::= string
+   * type ::= int
+   * </pre>
+   */
+  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
+   *
+   * <pre>
+   * l b32 b24 b16 b8
+   * </pre>
+   */
+  public int readLength()
+    throws IOException
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Parses a 32-bit integer value from the stream.
+   *
+   * <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.
+   *
+   * <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.
+   *
+   * <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();
+  }
+  
+  /**
+   * Reads a character from the underlying stream.
+   */
+  private int parseChar()
+    throws IOException
+  {
+    while (_chunkLength <= 0) {
+      if (_isLastChunk)
+        return -1;
+
+      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;
+
+	// qian.lei 2010-7-21
+      case 0x30: case 0x31: case 0x32: case 0x33:
+    _isLastChunk = true;
+    _chunkLength = ((code - 0x30) << 8) + read();
+    break;
+
+      default:
+        throw expect("string", code);
+      }
+
+    }
+
+    _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.
+   */
+  public InputStream readInputStream()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'B':
+    case 'b':
+      _isLastChunk = tag == 'B';
+      _chunkLength = (read() << 8) + read();
+      break;
+
+    case 0x20: case 0x21: case 0x22: case 0x23:
+    case 0x24: case 0x25: case 0x26: case 0x27:
+    case 0x28: case 0x29: case 0x2a: case 0x2b:
+    case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+      _isLastChunk = true;
+      _chunkLength = tag - 0x20;
+      break;
+      
+    default:
+      throw expect("binary", tag);
+    }
+    
+    return new ReadInputStream();
+  }
+  
+  /**
+   * Reads bytes from the underlying stream.
+   */
+  int read(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    int readLength = 0;
+
+    while (length > 0) {
+      while (_chunkLength <= 0) {
+        if (_isLastChunk)
+          return readLength == 0 ? -1 : readLength;
+
+        int code = read();
+
+        switch (code) {
+        case 'b':
+          _isLastChunk = false;
+
+          _chunkLength = (read() << 8) + read();
+          break;
+        
+        case 'B':
+          _isLastChunk = true;
+
+          _chunkLength = (read() << 8) + read();
+          break;
+
+	case 0x20: case 0x21: case 0x22: case 0x23:
+	case 0x24: case 0x25: case 0x26: case 0x27:
+	case 0x28: case 0x29: case 0x2a: case 0x2b:
+	case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+	  _isLastChunk = true;
+	  _chunkLength = code - 0x20;
+	  break;
+
+        default:
+          throw expect("byte[]", code);
+        }
+      }
+
+      int sublen = _chunkLength;
+      if (length < sublen)
+        sublen = length;
+
+      if (_length <= _offset && ! readBuffer())
+	return -1;
+      
+      if (_length - _offset < sublen)
+	sublen = _length - _offset;
+
+      System.arraycopy(_buffer, _offset, buffer, offset, sublen);
+
+      _offset += sublen;
+      
+      offset += sublen;
+      readLength += sublen;
+      length -= sublen;
+      _chunkLength -= sublen;
+    }
+
+    return readLength;
+  }
+
+  /**
+   * Normally, shouldn't be called externally, but needed for QA, e.g.
+   * ejb/3b01.
+   */
+  public final int read()
+    throws IOException
+  {
+    if (_length <= _offset && ! readBuffer())
+      return -1;
+
+    return _buffer[_offset++] & 0xff;
+  }
+
+  private final boolean readBuffer()
+    throws IOException
+  {
+    byte []buffer = _buffer;
+    int offset = _offset;
+    int length = _length;
+    
+    if (offset < length) {
+      System.arraycopy(buffer, offset, buffer, 0, length - offset);
+      offset = length - offset;
+    }
+    else
+      offset = 0;
+    
+    int len = _is.read(buffer, offset, SIZE - offset);
+
+    if (len <= 0) {
+      _length = offset;
+      _offset = 0;
+      
+      return offset > 0;
+    }
+
+    _length = offset + len;
+    _offset = 0;
+
+    return true;
+  }
+
+  public Reader getReader()
+  {
+    return null;
+  }
+
+  protected IOException expect(String expect, int ch)
+    throws IOException
+  {
+    if (ch < 0)
+      return error("expected " + expect + " at end of file");
+    else {
+      _offset--;
+
+      try {
+	Object obj = readObject();
+
+	if (obj != null) {
+	  return error("expected " + expect
+		       + " at 0x" + Integer.toHexString(ch & 0xff)
+		       + " " + obj.getClass().getName() + " (" + obj + ")");
+	}
+	else
+	  return error("expected " + expect
+		       + " at 0x" + Integer.toHexString(ch & 0xff) + " null");
+      } catch (IOException e) {
+	log.log(Level.FINE, e.toString(), e);
+	
+	return error("expected " + expect
+		     + " at 0x" + Integer.toHexString(ch & 0xff));
+      }
+    }
+  }
+
+  protected String codeName(int ch)
+  {
+    if (ch < 0)
+      return "end of file";
+    else
+      return "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) + ch + ")";
+  }
+  
+  protected IOException error(String message)
+  {
+    if (_method != null)
+      return new HessianProtocolException(_method + ": " + message);
+    else
+      return new HessianProtocolException(message);
+  }
+
+  public void close()
+    throws IOException
+  {
+    InputStream is = _is;
+    _is = null;
+
+    if (_isCloseStreamOnClose && is != null)
+      is.close();
+  }
+  
+  class ReadInputStream extends InputStream {
+    boolean _isClosed = false;
+	
+    public int read()
+      throws IOException
+    {
+      if (_isClosed)
+	return -1;
+
+      int ch = parseByte();
+      if (ch < 0)
+	_isClosed = true;
+
+      return ch;
+    }
+	
+    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;
+    }
+
+    public void close()
+      throws IOException
+    {
+      while (read() >= 0) {
+      }
+    }
+  };
+
+  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;
+    }
+  }
+
+  static {
+    try {
+      _detailMessageField = Throwable.class.getDeclaredField("detailMessage");
+      _detailMessageField.setAccessible(true);
+    } catch (Throwable e) {
+    }
+  }
+}

+ 1604 - 0
src/com/hmsoft/remote/caucho/hessian/io/Hessian2Output.java

@@ -0,0 +1,1604 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import com.hmsoft.remote.caucho.hessian.util.IdentityIntMap;
+
+/**
+ * Output stream for Hessian 2 requests.
+ *
+ * <p>Since HessianOutput does not depend on any classes other than
+ * in the JDK, it can be extracted independently into a smaller package.
+ *
+ * <p>HessianOutput is unbuffered, so any client needs to provide
+ * its own buffering.
+ *
+ * <pre>
+ * OutputStream os = ...; // from http connection
+ * Hessian2Output out = new Hessian2Output(os);
+ * String value;
+ *
+ * out.startCall("hello", 1); // start hello call
+ * out.writeString("arg1");   // write a string argument
+ * out.completeCall();        // complete the call
+ * </pre>
+ */
+public class Hessian2Output
+  extends AbstractHessianOutput
+  implements Hessian2Constants
+{
+  // 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;
+
+  public final static int SIZE = 4096;
+  
+  private final byte []_buffer = new byte[SIZE];
+  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;
+  }
+  
+  public void setCloseStreamOnClose(boolean isClose)
+  {
+    _isCloseStreamOnClose = isClose;
+  }
+  
+  public boolean isCloseStreamOnClose()
+  {
+    return _isCloseStreamOnClose;
+  }
+  
+
+  /**
+   * 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.
+   *
+   * <code><pre>
+   * C
+   * string # method name
+   * int    # arg count
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  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
+   *
+   * <code><pre>
+   * C
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  public void startCall()
+    throws IOException
+  {
+    flushIfFull();
+    
+    _buffer[_offset++] = (byte) 'C';
+  }
+  
+  /**
+   * Starts an envelope.
+   *
+   * <code><pre>
+   * E major minor
+   * m b16 b8 method-name
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  public void startEnvelope(String method)
+    throws IOException
+  {
+    int offset = _offset;
+
+    if (SIZE < offset + 32) {
+      flush();
+      offset = _offset;
+    }
+
+    _buffer[_offset++] = (byte) 'E';
+
+    writeString(method);
+  }
+
+  /**
+   * Completes an envelope.
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * Z
+   * </pre>
+   */
+  public void completeEnvelope()
+    throws IOException
+  {
+    flushIfFull();
+    
+    _buffer[_offset++] = (byte) 'Z';
+  }
+
+  /**
+   * Writes the method tag.
+   *
+   * <code><pre>
+   * string
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  public void writeMethod(String method)
+    throws IOException
+  {
+    writeString(method);
+  }
+
+  /**
+   * Completes.
+   *
+   * <code><pre>
+   * z
+   * </pre></code>
+   */
+  public void completeCall()
+    throws IOException
+  {
+    /*
+    flushIfFull();
+    
+    _buffer[_offset++] = (byte) 'Z';
+    */
+  }
+
+  /**
+   * Starts the reply
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * R
+   * </pre>
+   */
+  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>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  public void completeReply()
+    throws IOException
+  {
+  }
+
+  /**
+   * Starts a packet
+   *
+   * <p>A message contains several objects encapsulated by a length</p>
+   *
+   * <pre>
+   * p x02 x00
+   * </pre>
+   */
+  public void startMessage()
+    throws IOException
+  {
+    flushIfFull();
+    
+    _buffer[_offset++] = (byte) 'p';
+    _buffer[_offset++] = (byte) 2;
+    _buffer[_offset++] = (byte) 0;
+  }
+
+  /**
+   * Completes reading the message
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  public void completeMessage()
+    throws IOException
+  {
+    flushIfFull();
+    
+    _buffer[_offset++] = (byte) 'z';
+  }
+
+  /**
+   * Writes a fault.  The fault will be written
+   * as a descriptive string followed by an object:
+   *
+   * <code><pre>
+   * F map
+   * </pre></code>
+   *
+   * <code><pre>
+   * F H
+   * \x04code
+   * \x10the fault code
+   *
+   * \x07message
+   * \x11the fault message
+   *
+   * \x06detail
+   * M\xnnjavax.ejb.FinderException
+   *     ...
+   * Z
+   * Z
+   * </pre></code>
+   *
+   * @param code the fault code, a three digit
+   */
+  public void writeFault(String code, String message, Object detail)
+    throws IOException
+  {
+    flushIfFull();
+
+    writeVersion();
+    
+    _buffer[_offset++] = (byte) 'F';
+    _buffer[_offset++] = (byte) 'H';
+
+    _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.
+   */
+  public void writeObject(Object object)
+    throws IOException
+  {
+    if (object == null) {
+      writeNull();
+      return;
+    }
+
+    Serializer 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>.
+   *
+   * <code><pre>
+   * list ::= V type value* Z
+   *      ::= v type int value*
+   * </pre></code>
+   *
+   * @return true for variable lists, false for fixed lists
+   */
+  public boolean writeListBegin(int length, String type)
+    throws IOException
+  {
+    flushIfFull();
+
+    if (length < 0) {
+      if (type != null) {
+	_buffer[_offset++] = (byte) BC_LIST_VARIABLE;
+	writeType(type);
+      }
+      else
+	_buffer[_offset++] = (byte) BC_LIST_VARIABLE_UNTYPED;
+
+      return true;
+    }
+    else if (length <= LIST_DIRECT_MAX) {
+      if (type != null) {
+	_buffer[_offset++] = (byte) (BC_LIST_DIRECT + length);
+	writeType(type);
+      }
+      else {
+	_buffer[_offset++] = (byte) (BC_LIST_DIRECT_UNTYPED + length);
+      }
+
+      return false;
+    }
+    else {
+      if (type != null) {
+	_buffer[_offset++] = (byte) BC_LIST_FIXED;
+	writeType(type);
+      }
+      else {
+	_buffer[_offset++] = (byte) BC_LIST_FIXED_UNTYPED;
+      }
+      
+      writeInt(length);
+
+      return false;
+    }
+  }
+
+  /**
+   * Writes the tail of the list to the stream for a variable-length list.
+   */
+  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>.
+   *
+   * <code><pre>
+   * map ::= M type (<value> <value>)* Z
+   *     ::= H (<value> <value>)* Z
+   * </pre></code>
+   */
+  public void writeMapBegin(String type)
+    throws IOException
+  {
+    if (SIZE < _offset + 32)
+      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.
+   */
+  public void writeMapEnd()
+    throws IOException
+  {
+    if (SIZE < _offset + 32)
+      flush();
+    
+    _buffer[_offset++] = (byte) BC_END;
+  }
+
+  /**
+   * Writes the object definition
+   *
+   * <code><pre>
+   * C &lt;string> &lt;int> &lt;string>*
+   * </pre></code>
+   */
+  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.
+   */
+  public void writeClassFieldLength(int len)
+    throws IOException
+  {
+    writeInt(len);
+  }
+
+  /**
+   * Writes the tail of the object definition to the stream.
+   */
+  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:
+   *
+   * <code><pre>
+   * T
+   * F
+   * </pre></code>
+   *
+   * @param value the boolean value to write.
+   */
+  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:
+   *
+   * <code><pre>
+   * I b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the integer value to write.
+   */
+  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:
+   *
+   * <code><pre>
+   * L b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the long value to write.
+   */
+  public void writeLong(long value)
+    throws IOException
+  {
+    int offset = _offset;
+    byte []buffer = _buffer;
+
+    if (SIZE <= offset + 16) {
+      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:
+   *
+   * <code><pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the double value to write.
+   */
+  public void writeDouble(double value)
+    throws IOException
+  {
+    int offset = _offset;
+    byte []buffer = _buffer;
+
+    if (SIZE <= offset + 16) {
+      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.
+   *
+   * <code><pre>
+   * date ::= d   b7 b6 b5 b4 b3 b2 b1 b0
+   *      ::= x65 b3 b2 b1 b0
+   * </pre></code>
+   *
+   * @param time the date in milliseconds from the epoch in UTC
+   */
+  public void writeUTCDate(long time)
+    throws IOException
+  {
+    if (SIZE < _offset + 32)
+      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
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeNull()
+    throws IOException
+  {
+    int offset = _offset;
+    byte []buffer = _buffer;
+
+    if (SIZE <= offset + 16) {
+      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:
+   *
+   * <code><pre>
+   * S b16 b8 string-value
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeString(String value)
+    throws IOException
+  {
+    int offset = _offset;
+    byte []buffer = _buffer;
+
+    if (SIZE <= offset + 16) {
+      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:
+   *
+   * <code><pre>
+   * S b16 b8 string-value
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeString(char []buffer, int offset, int length)
+    throws IOException
+  {
+    if (buffer == null) {
+      if (SIZE < _offset + 16)
+	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:
+   *
+   * <code><pre>
+   * B b16 b18 bytes
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeBytes(byte []buffer)
+    throws IOException
+  {
+    if (buffer == null) {
+      if (SIZE < _offset + 16)
+	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:
+   *
+   * <code><pre>
+   * B b16 b18 bytes
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeBytes(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    if (buffer == null) {
+      if (SIZE < _offset + 16)
+	flushBuffer();
+      
+      _buffer[_offset++] = (byte) 'N';
+    }
+    else {
+      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.
+   *
+   * <code><pre>
+   * </pre></code>
+   */
+  public void writeByteBufferStart()
+    throws IOException
+  {
+  }
+  
+  /**
+   * Writes a byte buffer to the stream.
+   *
+   * <code><pre>
+   * b b16 b18 bytes
+   * </pre></code>
+   */
+  public void writeByteBufferPart(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    while (length > 0) {
+      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.
+   *
+   * <code><pre>
+   * b b16 b18 bytes
+   * </pre></code>
+   */
+  public void writeByteBufferEnd(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    writeBytes(buffer, offset, length);
+  }
+
+  /**
+   * Returns an output stream to write binary data.
+   */
+  public OutputStream getBytesOutputStream()
+    throws IOException
+  {
+    return new BytesOutputStream();
+  }
+
+  /**
+   * Writes a reference.
+   *
+   * <code><pre>
+   * x51 &lt;int>
+   * </pre></code>
+   *
+   * @param value the integer value to write.
+   */
+  @Override
+  protected void writeRef(int value)
+    throws IOException
+  {
+    if (SIZE < _offset + 16)
+      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.
+   */
+  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.
+   */
+  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.
+   */
+  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.
+   */
+  public void resetReferences()
+  {
+    if (_refs != null)
+      _refs.clear();
+  }
+
+  /**
+   * Starts the streaming message
+   *
+   * <p>A streaming message starts with 'P'</p>
+   *
+   * <pre>
+   * P x02 x00
+   * </pre>
+   */
+  public void writeStreamingObject(Object obj)
+    throws IOException
+  {
+    startStreamingPacket();
+
+    writeObject(obj);
+
+    endStreamingPacket();
+  }
+
+  /**
+   * Starts a streaming packet
+   *
+   * <p>A streaming message starts with '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);
+    }
+  }
+
+  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);
+    }
+  }
+
+  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();
+    }
+  }
+}

+ 165 - 0
src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingInput.java

@@ -0,0 +1,165 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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;
+    }
+
+    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();
+    }
+
+    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;
+    }
+  }
+}

+ 109 - 0
src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingOutput.java

@@ -0,0 +1,109 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+
+/**
+ * Output stream for Hessian 2 streaming requests.
+ */
+public class Hessian2StreamingOutput
+{
+  private Hessian2Output _out;
+  
+  /**
+   * Creates a new Hessian output stream, initialized with an
+   * underlying output stream.
+   *
+   * @param os the underlying output stream.
+   */
+  public Hessian2StreamingOutput(OutputStream os)
+  {
+    _out = new Hessian2Output(os);
+  }
+  
+  public void setCloseStreamOnClose(boolean isClose)
+  {
+    _out.setCloseStreamOnClose(isClose);
+  }
+  
+  public boolean isCloseStreamOnClose()
+  {
+    return _out.isCloseStreamOnClose();
+  }
+
+  /**
+   * Writes any object to the output stream.
+   */
+  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();
+  }
+}

+ 174 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianDebugInputStream.java

@@ -0,0 +1,174 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * Debugging input stream for Hessian requests.
+ */
+public class HessianDebugInputStream extends InputStream
+{
+  private InputStream _is;
+  
+  private HessianDebugState _state;
+  
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianDebugInputStream(InputStream is, PrintWriter dbg)
+  {
+    _is = is;
+
+    if (dbg == null)
+      dbg = new PrintWriter(System.out);
+
+    _state = new HessianDebugState(dbg);
+  }
+  
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianDebugInputStream(InputStream is, Logger log, Level level)
+  {
+    this(is, new PrintWriter(new LogWriter(log, level)));
+  }
+
+  public void startTop2()
+  {
+    _state.startTop2();
+  }
+
+  /**
+   * Reads a character.
+   */
+  public int read()
+    throws IOException
+  {
+    int ch;
+
+    InputStream is = _is;
+
+    if (is == null)
+      return -1;
+    else {
+      ch = is.read();
+    }
+
+    _state.next(ch);
+
+    return ch;
+  }
+
+  /**
+   * closes the stream.
+   */
+  public void close()
+    throws IOException
+  {
+    InputStream is = _is;
+    _is = null;
+
+    if (is != null)
+      is.close();
+    
+    _state.println();
+  }
+
+  static class LogWriter extends Writer {
+    private Logger _log;
+    private Level _level;
+    private StringBuilder _sb = new StringBuilder();
+
+    LogWriter(Logger log, Level level)
+    {
+      _log = log;
+      _level = level;
+    }
+
+    public void write(char ch)
+    {
+      if (ch == '\n' && _sb.length() > 0) {
+	_log.log(_level, _sb.toString());
+	_sb.setLength(0);
+      }
+      else
+	_sb.append((char) ch);
+    }
+
+    public void write(char []buffer, int offset, int length)
+    {
+      for (int i = 0; i < length; i++) {
+	char ch = buffer[offset + i];
+	
+	if (ch == '\n' && _sb.length() > 0) {
+	  _log.log(_level, _sb.toString());
+	  _sb.setLength(0);
+	}
+	else
+	  _sb.append((char) ch);
+      }
+    }
+
+    public void flush()
+    {
+    }
+
+    public void close()
+    {
+    }
+  }
+}

+ 168 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianDebugOutputStream.java

@@ -0,0 +1,168 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.io.PrintWriter;
+
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * Debugging output stream for Hessian requests.
+ */
+public class HessianDebugOutputStream extends OutputStream
+{
+  private OutputStream _os;
+  
+  private HessianDebugState _state;
+  
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianDebugOutputStream(OutputStream os, PrintWriter dbg)
+  {
+    _os = os;
+
+    _state = new HessianDebugState(dbg);
+  }
+  
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianDebugOutputStream(OutputStream os, Logger log, Level level)
+  {
+    this(os, new PrintWriter(new LogWriter(log, level)));
+  }
+
+  public void startTop2()
+  {
+    _state.startTop2();
+  }
+
+  /**
+   * Writes a character.
+   */
+  public void write(int ch)
+    throws IOException
+  {
+    ch = ch & 0xff;
+    
+    _os.write(ch);
+
+    _state.next(ch);
+  }
+
+  public void flush()
+    throws IOException
+  {
+    _os.flush();
+  }
+
+  /**
+   * closes the stream.
+   */
+  public void close()
+    throws IOException
+  {
+    OutputStream os = _os;
+    _os = null;
+
+    if (os != null)
+      os.close();
+
+    _state.println();
+  }
+
+  static class LogWriter extends Writer {
+    private Logger _log;
+    private Level _level;
+    private StringBuilder _sb = new StringBuilder();
+
+    LogWriter(Logger log, Level level)
+    {
+      _log = log;
+      _level = level;
+    }
+
+    public void write(char ch)
+    {
+      if (ch == '\n' && _sb.length() > 0) {
+	_log.log(_level, _sb.toString());
+	_sb.setLength(0);
+      }
+      else
+	_sb.append((char) ch);
+    }
+
+    public void write(char []buffer, int offset, int length)
+    {
+      for (int i = 0; i < length; i++) {
+	char ch = buffer[offset + i];
+	
+	if (ch == '\n' && _sb.length() > 0) {
+	  _log.log(_level, _sb.toString());
+	  _sb.setLength(0);
+	}
+	else
+	  _sb.append((char) ch);
+      }
+    }
+
+    public void flush()
+    {
+    }
+
+    public void close()
+    {
+    }
+  }
+}

+ 2090 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianDebugState.java

@@ -0,0 +1,2090 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Debugging input stream for Hessian requests.
+ */
+public class HessianDebugState implements Hessian2Constants
+{
+  private PrintWriter _dbg;
+
+  private State _state;
+  private ArrayList<State> _stateStack = new ArrayList<State>();
+
+  private ArrayList<ObjectDef> _objectDefList
+    = new ArrayList<ObjectDef>();
+
+  private ArrayList<String> _typeDefList
+    = new ArrayList<String>();
+
+  private int _refId;
+  private boolean _isNewline = true;
+  private boolean _isObject = false;
+  private int _column;
+  
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianDebugState(PrintWriter dbg)
+  {
+    _dbg = dbg;
+
+    _state = new InitialState();
+  }
+
+  public void startTop2()
+  {
+    _state = new Top2State();
+  }
+
+  /**
+   * Reads a character.
+   */
+  public void next(int ch)
+    throws IOException
+  {
+    _state = _state.next(ch);
+  }
+
+  void pushStack(State state)
+  {
+    _stateStack.add(state);
+  }
+
+  State popStack()
+  {
+    return _stateStack.remove(_stateStack.size() - 1);
+  }
+
+  void println()
+  {
+    if (! _isNewline) {
+      _dbg.println();
+      _dbg.flush();
+    }
+
+    _isNewline = true;
+    _column = 0;
+  }
+
+  static boolean isString(int ch)
+  {
+    switch (ch) {
+    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 'R':
+    case 'S':
+      return true;
+	
+    default:
+      return false;
+    }
+  }
+
+  static boolean isInteger(int ch)
+  {
+    switch (ch) {
+    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:
+
+    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:
+
+    case 0xd0: case 0xd1: case 0xd2: case 0xd3: 
+    case 0xd4: case 0xd5: case 0xd6: case 0xd7: 
+
+    case 'I':
+      return true;
+
+    default:
+      return false;
+    }
+  }
+
+  abstract class State {
+    State _next;
+
+    State()
+    {
+    }
+
+    State(State next)
+    {
+      _next = next;
+    }
+    
+    abstract State next(int ch);
+
+    boolean isShift(Object value)
+    {
+      return false;
+    }
+
+    State shift(Object value)
+    {
+      return this;
+    }
+
+    int depth()
+    {
+      if (_next != null)
+	return _next.depth();
+      else
+	return 0;
+    }
+
+    void printIndent(int depth)
+    {
+      if (_isNewline) {
+	for (int i = _column; i < depth() + depth; i++) {
+	  _dbg.print(" ");
+	  _column++;
+	}
+      }
+    }
+
+    void print(String string)
+    {
+      print(0, string);
+    }
+
+    void print(int depth, String string)
+    {
+      printIndent(depth);
+      
+      _dbg.print(string);
+      _isNewline = false;
+      _isObject = false;
+
+      int p = string.lastIndexOf('\n');
+      if (p > 0)
+	_column = string.length() - p - 1;
+      else
+	_column += string.length();
+    }
+
+    void println(String string)
+    {
+      println(0, string);
+    }
+
+    void println(int depth, String string)
+    {
+      printIndent(depth);
+
+      _dbg.println(string);
+      _dbg.flush();
+      _isNewline = true;
+      _isObject = false;
+      _column = 0;
+    }
+
+    void println()
+    {
+      if (! _isNewline) {
+	_dbg.println();
+	_dbg.flush();
+      }
+
+      _isNewline = true;
+      _isObject = false;
+      _column = 0;
+    }
+
+    void printObject(String string)
+    {
+      if (_isObject)
+	println();
+      
+      printIndent(0);
+
+      _dbg.print(string);
+      _dbg.flush();
+
+      _column += string.length();
+
+      _isNewline = false;
+      _isObject = true;
+    }
+    
+    protected State nextObject(int ch)
+    {
+      switch (ch) {
+      case -1:
+	println();
+	return this;
+	
+      case 'N':
+	if (isShift(null))
+	  return shift(null);
+	else {
+	  printObject("null");
+	  return this;
+	}
+	
+      case 'T':
+	if (isShift(Boolean.TRUE))
+	  return shift(Boolean.TRUE);
+	else {
+	  printObject("true");
+	  return this;
+	}
+	
+      case 'F':
+	if (isShift(Boolean.FALSE))
+	  return shift(Boolean.FALSE);
+	else {
+	  printObject("false");
+	  return this;
+	}
+
+      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:
+	{
+	  Integer value = new Integer(ch - 0x90);
+	  
+	  if (isShift(value))
+	    return shift(value);
+	  else {
+	    printObject(value.toString());
+	    return this;
+	  }
+	}
+
+      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 new IntegerState(this, "int", ch - 0xc8, 3);
+
+      case 0xd0: case 0xd1: case 0xd2: case 0xd3: 
+      case 0xd4: case 0xd5: case 0xd6: case 0xd7: 
+	return new IntegerState(this, "int", ch - 0xd4, 2);
+
+      case 'I':
+	return new IntegerState(this, "int");
+	
+      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:
+	{
+	  Long value = new Long(ch - 0xe0);
+	  
+	  if (isShift(value))
+	    return shift(value);
+	  else {
+	    printObject(value.toString() + "L");
+	    return this;
+	  }
+	}
+	
+      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 new LongState(this, "long", ch - 0xf8, 7);
+	
+      case 0x38: case 0x39: case 0x3a: case 0x3b: 
+      case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+	return new LongState(this, "long", ch - 0x3c, 6);
+
+      case BC_LONG_INT:
+	return new LongState(this, "long", 0, 4);
+
+      case 'L':
+	return new LongState(this, "long");
+
+      case 0x5b: case 0x5c:
+	{
+	  Double value = new Double(ch - 0x5b);
+	  
+	  if (isShift(value))
+	    return shift(value);
+	  else {
+	    printObject(value.toString());
+	    return this;
+	  }
+	}
+
+      case 0x5d:
+	return new DoubleIntegerState(this, 3);
+
+      case 0x5e:
+	return new DoubleIntegerState(this, 2);
+
+      case 0x5f:
+	return new MillsState(this);
+	
+      case 'D':
+	return new DoubleState(this);
+
+      case 'Q':
+	return new RefState(this);
+
+      case BC_DATE:
+	return new DateState(this);
+
+      case BC_DATE_MINUTE:
+	return new DateState(this, true);
+
+      case 0x00:
+	{
+	  String value = "\"\"";
+	  
+	  if (isShift(value))
+	    return shift(value);
+	  else {
+	    printObject(value.toString());
+	    return this;
+	  }
+	}
+
+      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:
+	return new StringState(this, 'S', ch);
+
+      case 0x30: case 0x31: case 0x32: case 0x33:
+	return new StringState(this, 'S', ch - 0x30, true);
+	
+      case 'R':
+	return new StringState(this, 'S', false);
+
+      case 'S':
+	return new StringState(this, 'S', true);
+
+      case 0x20:
+	{
+	  String value = "binary(0)";
+	  
+	  if (isShift(value))
+	    return shift(value);
+	  else {
+	    printObject(value.toString());
+	    return this;
+	  }
+	}
+
+      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:
+	return new BinaryState(this, 'B', ch - 0x20);
+	
+      case 0x34: case 0x35: case 0x36: case 0x37:
+	return new BinaryState(this, 'B', ch - 0x34, true);
+
+      case 'A':
+	return new BinaryState(this, 'B', false);
+
+      case 'B':
+	return new BinaryState(this, 'B', true);
+
+      case 'M':
+	return new MapState(this, _refId++);
+
+      case 'H':
+	return new MapState(this, _refId++, false);
+
+      case BC_LIST_VARIABLE:
+	return new ListState(this, _refId++, true);
+
+      case BC_LIST_VARIABLE_UNTYPED:
+	return new ListState(this, _refId++, false);
+
+      case BC_LIST_FIXED:
+	return new CompactListState(this, _refId++, true);
+	
+      case BC_LIST_FIXED_UNTYPED:
+	return new CompactListState(this, _refId++, false);
+
+      case 0x70: case 0x71: case 0x72: case 0x73:
+      case 0x74: case 0x75: case 0x76: case 0x77:
+	return new CompactListState(this, _refId++, true, ch - 0x70);
+
+      case 0x78: case 0x79: case 0x7a: case 0x7b:
+      case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+	return new CompactListState(this, _refId++, false, ch - 0x78);
+
+      case 'C':
+	return new ObjectDefState(this);
+
+      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:
+	return new ObjectState(this, _refId++, ch - 0x60);
+	
+      case 'O':
+	return new ObjectState(this, _refId++);
+	
+      default:
+	return this;
+      }
+    }
+  }
+  
+  class InitialState extends State {
+    State next(int ch)
+    {
+      println();
+      
+      if (ch == 'r') {
+	return new ReplyState(this);
+      }
+      else if (ch == 'c') {
+	return new CallState(this);
+      }
+      else
+	return nextObject(ch);
+    }
+  }
+  
+  class Top2State extends State {
+    State next(int ch)
+    {
+      println();
+      
+      if (ch == 'R') {
+	return new Reply2State(this);
+      }
+      else if (ch == 'F') {
+	return new Fault2State(this);
+      }
+      else if (ch == 'C') {
+	return new Call2State(this);
+      }
+      else if (ch == 'H') {
+	return new Hessian2State(this);
+      }
+      else if (ch == 'r') {
+	return new ReplyState(this);
+      }
+      else if (ch == 'c') {
+	return new CallState(this);
+      }
+      else
+	return nextObject(ch);
+    }
+  }
+  
+  class IntegerState extends State {
+    String _typeCode;
+    
+    int _length;
+    int _value;
+
+    IntegerState(State next, String typeCode)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+    }
+
+    IntegerState(State next, String typeCode, int value, int length)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+
+      _value = value;
+      _length = length;
+    }
+
+    State next(int ch)
+    {
+      _value = 256 * _value + (ch & 0xff);
+
+      if (++_length == 4) {
+	Integer value = new Integer(_value);
+	
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value.toString());
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class LongState extends State {
+    String _typeCode;
+    
+    int _length;
+    long _value;
+
+    LongState(State next, String typeCode)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+    }
+
+    LongState(State next, String typeCode, long value, int length)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+
+      _value = value;
+      _length = length;
+    }
+
+    State next(int ch)
+    {
+      _value = 256 * _value + (ch & 0xff);
+
+      if (++_length == 8) {
+	Long value = new Long(_value);
+	
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value.toString() + "L");
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class DoubleIntegerState extends State {
+    int _length;
+    int _value;
+    boolean _isFirst = true;
+
+    DoubleIntegerState(State next, int length)
+    {
+      super(next);
+
+      _length = length;
+    }
+
+    State next(int ch)
+    {
+      if (_isFirst)
+	_value = (byte) ch;
+      else
+	_value = 256 * _value + (ch & 0xff);
+
+      _isFirst = false;
+
+      if (++_length == 4) {
+	Double value = new Double(_value);
+	
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value.toString());
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class RefState extends State {
+    String _typeCode;
+    
+    int _length;
+    int _value;
+
+    RefState(State next)
+    {
+      super(next);
+    }
+
+    RefState(State next, String typeCode)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+    }
+
+    RefState(State next, String typeCode, int value, int length)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+
+      _value = value;
+      _length = length;
+    }
+
+    @Override
+    boolean isShift(Object o)
+    {
+      return true;
+    }
+
+    @Override
+    State shift(Object o)
+    {
+      println("ref #" + o);
+
+      return _next;
+    }
+
+    @Override
+    State next(int ch)
+    {
+      return nextObject(ch);
+    }
+  }
+  
+  class DateState extends State {
+    int _length;
+    long _value;
+    boolean _isMinute;
+
+    DateState(State next)
+    {
+      super(next);
+    }
+
+    DateState(State next, boolean isMinute)
+    {
+      super(next);
+
+      _length = 4;
+      _isMinute = isMinute;
+    }
+      
+    
+    State next(int ch)
+    {
+      _value = 256 * _value + (ch & 0xff);
+
+      if (++_length == 8) {
+	java.util.Date value;
+
+	if (_isMinute)
+	  value = new java.util.Date(_value * 60000L);
+	else
+	  value = new java.util.Date(_value);
+
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value.toString());
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class DoubleState extends State {
+    int _length;
+    long _value;
+
+    DoubleState(State next)
+    {
+      super(next);
+    }
+    
+    State next(int ch)
+    {
+      _value = 256 * _value + (ch & 0xff);
+
+      if (++_length == 8) {
+	Double value = Double.longBitsToDouble(_value);
+
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value.toString());
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class MillsState extends State {
+    int _length;
+    int _value;
+
+    MillsState(State next)
+    {
+      super(next);
+    }
+    
+    State next(int ch)
+    {
+      _value = 256 * _value + (ch & 0xff);
+
+      if (++_length == 4) {
+	Double value = 0.001 * _value;
+
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value.toString());
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class StringState extends State {
+    private static final int TOP = 0;
+    private static final int UTF_2_1 = 1;
+    private static final int UTF_3_1 = 2;
+    private static final int UTF_3_2 = 3;
+
+    char _typeCode;
+    
+    StringBuilder _value = new StringBuilder();
+    int _lengthIndex;
+    int _length;
+    boolean _isLastChunk;
+    
+    int _utfState;
+    char _ch;
+
+    StringState(State next, char typeCode, boolean isLastChunk)
+    {
+      super(next);
+      
+      _typeCode = typeCode;
+      _isLastChunk = isLastChunk;
+    }
+
+    StringState(State next, char typeCode, int length)
+    {
+      super(next);
+      
+      _typeCode = typeCode;
+      _isLastChunk = true;
+      _length = length;
+      _lengthIndex = 2;
+    }
+
+    StringState(State next, char typeCode, int length, boolean isLastChunk)
+    {
+      super(next);
+      
+      _typeCode = typeCode;
+      _isLastChunk = isLastChunk;
+      _length = length;
+      _lengthIndex = 1;
+    }
+    
+    State next(int ch)
+    {
+      if (_lengthIndex < 2) {
+	_length = 256 * _length + (ch & 0xff);
+	
+	if (++_lengthIndex == 2 && _length == 0 && _isLastChunk) {
+	  if (_next.isShift(_value.toString()))
+	    return _next.shift(_value.toString());
+	  else {
+	    printObject("\"" + _value + "\"");
+	    return _next;
+	  }
+	}
+	else
+	  return this;
+      }
+      else if (_length == 0) {
+	if (ch == 's' || ch == 'x') {
+	  _isLastChunk = false;
+	  _lengthIndex = 0;
+	  return this;
+	}
+	else if (ch == 'S' || ch == 'X') {
+	  _isLastChunk = true;
+	  _lengthIndex = 0;
+	  return this;
+	}
+	else if (ch == 0x00) {
+	  if (_next.isShift(_value.toString()))
+	    return _next.shift(_value.toString());
+	  else {
+	    printObject("\"" + _value + "\"");
+	    return _next;
+	  }
+	}
+	else if (0x00 <= ch && ch < 0x20) {
+	  _isLastChunk = true;
+	  _lengthIndex = 2;
+	  _length = ch & 0xff;
+	  return this;
+	}
+	else if (0x30 <= ch && ch < 0x34) {
+	  _isLastChunk = true;
+	  _lengthIndex = 1;
+	  _length = (ch - 0x30);
+	  return this;
+	}
+	else {
+	  println(String.valueOf((char) ch) + ": unexpected character");
+	  return _next;
+	}
+      }
+
+      switch (_utfState) {
+      case TOP:
+	if (ch < 0x80) {
+	  _length--;
+
+	  _value.append((char) ch);
+	}
+	else if (ch < 0xe0) {
+	  _ch = (char) ((ch & 0x1f) << 6);
+	  _utfState = UTF_2_1;
+	}
+	else {
+	  _ch = (char) ((ch & 0xf) << 12);
+	  _utfState = UTF_3_1;
+	}
+	break;
+
+      case UTF_2_1:
+      case UTF_3_2:
+	_ch += ch & 0x3f;
+	_value.append(_ch);
+	_length--;
+	_utfState = TOP;
+	break;
+
+      case UTF_3_1:
+	_ch += (char) ((ch & 0x3f) << 6);
+	_utfState = UTF_3_2;
+	break;
+      }
+
+      if (_length == 0 && _isLastChunk) {
+	if (_next.isShift(_value.toString()))
+	  return _next.shift(_value.toString());
+	else {
+	  printObject("\"" + _value + "\"");
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class BinaryState extends State {
+    char _typeCode;
+    
+    int _totalLength;
+    
+    int _lengthIndex;
+    int _length;
+    boolean _isLastChunk;
+    
+    BinaryState(State next, char typeCode, boolean isLastChunk)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+      _isLastChunk = isLastChunk;
+    }
+
+    BinaryState(State next, char typeCode, int length)
+    {
+      super(next);
+
+      _typeCode = typeCode;
+      _isLastChunk = true;
+      _length = length;
+      _lengthIndex = 2;
+    }
+
+    BinaryState(State next, char typeCode, int length, boolean isLastChunk)
+    {
+      super(next);
+      
+      _typeCode = typeCode;
+      _isLastChunk = isLastChunk;
+      _length = length;
+      _lengthIndex = 1;
+    }
+    
+    State next(int ch)
+    {
+      if (_lengthIndex < 2) {
+	_length = 256 * _length + (ch & 0xff);
+	
+	if (++_lengthIndex == 2 && _length == 0 && _isLastChunk) {
+	  String value = "binary(" + _totalLength + ")";
+	  
+	  if (_next.isShift(value))
+	    return _next.shift(value);
+	  else {
+	    printObject(value);
+	    return _next;
+	  }
+	}
+	else
+	  return this;
+      }
+      else if (_length == 0) {
+	if (ch == 'b') {
+	  _isLastChunk = false;
+	  _lengthIndex = 0;
+	  return this;
+	}
+	else if (ch == 'B') {
+	  _isLastChunk = true;
+	  _lengthIndex = 0;
+	  return this;
+	}
+	else if (ch == 0x20) {
+	  String value = "binary(" + _totalLength + ")";
+	  
+	  if (_next.isShift(value))
+	    return _next.shift(value);
+	  else {
+	    printObject(value);
+	    return _next;
+	  }
+	}
+	else if (0x20 <=ch && ch < 0x30) {
+	  _isLastChunk = true;
+	  _lengthIndex = 2;
+	  _length = (ch & 0xff) - 0x20;
+	  return this;
+	}
+	else {
+	  println(String.valueOf((char) ch) + ": unexpected character");
+	  return _next;
+	}
+      }
+      
+      _length--;
+      _totalLength++;
+
+      if (_length == 0 && _isLastChunk) {
+	String value = "binary(" + _totalLength + ")";
+	
+	if (_next.isShift(value))
+	  return _next.shift(value);
+	else {
+	  printObject(value);
+	  
+	  return _next;
+	}
+      }
+      else
+	return this;
+    }
+  }
+  
+  class MapState extends State {
+    private static final int TYPE = 0;
+    private static final int KEY = 1;
+    private static final int VALUE = 2;
+
+    private int _refId;
+
+    private int _state;
+    private int _valueDepth;
+    private boolean _hasData;
+
+    MapState(State next, int refId)
+    {
+      super(next);
+      
+      _refId = refId;
+      _state = TYPE;
+    }
+
+    MapState(State next, int refId, boolean isType)
+    {
+      super(next);
+      
+      _refId = refId;
+
+      if (isType)
+	_state = TYPE;
+      else {
+	printObject("map (#" + _refId + ")");
+	_state = VALUE;
+      }
+    }
+
+    @Override
+    boolean isShift(Object value)
+    {
+      return _state == TYPE;
+    }
+
+    @Override
+    State shift(Object type)
+    {
+      if (_state == TYPE) {
+	if (type instanceof String) {
+	  _typeDefList.add((String) type);
+	}
+	else if (type instanceof Integer) {
+	  int iValue = (Integer) type;
+
+	  if (iValue >= 0 && iValue < _typeDefList.size())
+	    type = _typeDefList.get(iValue);
+	}
+	
+	printObject("map " + type + " (#" + _refId + ")");
+
+	_state = VALUE;
+      
+	return this;
+      }
+      else
+	throw new IllegalStateException();
+    }
+
+    @Override
+    int depth()
+    {
+      if (_state == TYPE)
+	return _next.depth();
+      else if (_state == KEY)
+	return _next.depth() + 2;
+      else
+	return _valueDepth;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case TYPE:
+	return nextObject(ch);
+	
+      case VALUE:
+	if (ch == 'Z') {
+	  if (_hasData)
+	    println();
+	  
+	  return _next;
+	}
+	else {
+	  if (_hasData)
+	    println();
+
+	  _hasData = true;
+	  _state = KEY;
+	  
+	  return nextObject(ch);
+	}
+	
+      case KEY:
+	print(" => ");
+	_isObject = false;
+	_valueDepth = _column;
+
+	_state = VALUE;
+	
+	return nextObject(ch);
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class ObjectDefState extends State {
+    private static final int TYPE = 1;
+    private static final int COUNT = 2;
+    private static final int FIELD = 3;
+    private static final int COMPLETE = 4;
+
+    private int _refId;
+
+    private int _state;
+    private boolean _hasData;
+    private int _count;
+
+    private String _type;
+    private ArrayList<String> _fields = new ArrayList<String>();
+
+    ObjectDefState(State next)
+    {
+      super(next);
+      
+      _state = TYPE;
+    }
+
+    @Override
+    boolean isShift(Object value)
+    {
+      return true;
+    }
+
+    @Override
+    State shift(Object object)
+    {
+      if (_state == TYPE) {
+	_type = (String) object;
+
+	print("/* defun " + _type + " [");
+
+	_objectDefList.add(new ObjectDef(_type, _fields));
+
+	_state = COUNT;
+      }
+      else if (_state == COUNT) {
+	_count = (Integer) object;
+
+	_state = FIELD;
+      }
+      else if (_state == FIELD) {
+	String field = (String) object;
+
+	_count--;
+
+	_fields.add(field);
+
+	if (_fields.size() == 1)
+	  print(field);
+	else
+	  print(", " + field);
+      }
+      else {
+	throw new UnsupportedOperationException();
+      }
+
+      return this;
+    }
+
+    @Override
+    int depth()
+    {
+      if (_state <= TYPE)
+	return _next.depth();
+      else
+	return _next.depth() + 2;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case TYPE:
+	return nextObject(ch);
+	
+      case COUNT:
+	return nextObject(ch);
+	
+      case FIELD:
+	if (_count == 0) {
+	  println("] */");
+	  _next.printIndent(0);
+
+	  return _next.nextObject(ch);
+	}
+	else
+	  return nextObject(ch);
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class ObjectState extends State {
+    private static final int TYPE = 0;
+    private static final int FIELD = 1;
+
+    private int _refId;
+
+    private int _state;
+    private ObjectDef _def;
+    private int _count;
+    private int _fieldDepth;
+
+    ObjectState(State next, int refId)
+    {
+      super(next);
+
+      _refId = refId;
+      _state = TYPE;
+    }
+
+    ObjectState(State next, int refId, int def)
+    {
+      super(next);
+
+      _refId = refId;
+      _state = FIELD;
+
+      if (def < 0 || _objectDefList.size() <= def) {
+	throw new IllegalStateException(def + " is an unknown object type");
+      }
+
+      _def = _objectDefList.get(def);
+
+      println("object " + _def.getType() + " (#" + _refId + ")");
+    }
+
+    @Override
+    boolean isShift(Object value)
+    {
+      if (_state == TYPE)
+	return true;
+      else
+	return false;
+    }
+
+    @Override
+    State shift(Object object)
+    {
+      if (_state == TYPE) {
+	int def = (Integer) object;
+
+	_def = _objectDefList.get(def);
+
+	println("object " + _def.getType() + " (#" + _refId + ")");
+
+	_state = FIELD;
+
+	if (_def.getFields().size() == 0)
+	  return _next;
+      }
+
+      return this;
+    }
+
+    @Override
+    int depth()
+    {
+      if (_state <= TYPE)
+	return _next.depth();
+      else
+	return _fieldDepth;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case TYPE:
+	return nextObject(ch);
+	
+      case FIELD:
+	if (_def.getFields().size() <= _count)
+	  return _next.next(ch);
+
+	_fieldDepth = _next.depth() + 2;
+	println();
+	print(_def.getFields().get(_count++) + ": ");
+
+	_fieldDepth = _column;
+
+	_isObject = false;
+	return nextObject(ch);
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class ListState extends State {
+    private static final int TYPE = 0;
+    private static final int LENGTH = 1;
+    private static final int VALUE = 2;
+
+    private int _refId;
+
+    private int _state;
+    private boolean _hasData;
+    private int _count;
+    private int _valueDepth;
+
+    ListState(State next, int refId, boolean isType)
+    {
+      super(next);
+      
+      _refId = refId;
+      
+      if (isType)
+	_state = TYPE;
+      else {
+	printObject("list (#" + _refId + ")");
+	_state = VALUE;
+      }
+    }
+
+    @Override
+    boolean isShift(Object value)
+    {
+      return _state == TYPE || _state == LENGTH;
+    }
+
+    @Override
+    State shift(Object object)
+    {
+      if (_state == TYPE) {
+	Object type = object;
+	
+	if (type instanceof String) {
+	  _typeDefList.add((String) type);
+	}
+	else if (object instanceof Integer) {
+	  int index = (Integer) object;
+
+	  if (index >= 0 && index < _typeDefList.size())
+	    type = _typeDefList.get(index);
+	  else
+	    type = "type-unknown(" + index + ")";
+	}
+	
+	printObject("list " + type + "(#" + _refId + ")");
+      
+	_state = VALUE;
+      
+	return this;
+      }
+      else if (_state == LENGTH) {
+	_state = VALUE;
+
+	return this;
+      }
+      else
+	return this;
+    }
+
+    @Override
+    int depth()
+    {
+      if (_state <= LENGTH)
+	return _next.depth();
+      else if (_state == VALUE)
+	return _valueDepth;
+      else
+	return _next.depth() + 2;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case TYPE:
+	return nextObject(ch);
+	
+      case VALUE:
+	if (ch == 'Z') {
+	  if (_count > 0)
+	    println();
+	  
+	  return _next;
+	}
+	else {
+	  _valueDepth = _next.depth() + 2;
+	  println();
+	  printObject(_count++ + ": ");
+	  _valueDepth = _column;
+	  _isObject = false;
+	  
+	  return nextObject(ch);
+	}
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class CompactListState extends State {
+    private static final int TYPE = 0;
+    private static final int LENGTH = 1;
+    private static final int VALUE = 2;
+
+    private int _refId;
+
+    private boolean _isTyped;
+    private boolean _isLength;
+
+    private int _state;
+    private boolean _hasData;
+    private int _length;
+    private int _count;
+    private int _valueDepth;
+
+    CompactListState(State next, int refId, boolean isTyped)
+    {
+      super(next);
+
+      _isTyped = isTyped;
+      _refId = refId;
+      
+      if (isTyped)
+	_state = TYPE;
+      else
+	_state = LENGTH;
+    }
+
+    CompactListState(State next, int refId, boolean isTyped, int length)
+    {
+      super(next);
+
+      _isTyped = isTyped;
+      _refId = refId;
+      _length = length;
+
+      _isLength = true;
+      
+      if (isTyped)
+	_state = TYPE;
+      else {
+	printObject("list (#" + _refId + ")");
+	
+	_state = VALUE;
+      }
+    }
+
+    @Override
+    boolean isShift(Object value)
+    {
+      return _state == TYPE || _state == LENGTH;
+    }
+
+    @Override
+    State shift(Object object)
+    {
+      if (_state == TYPE) {
+	Object type = object;
+	
+	if (object instanceof Integer) {
+	  int index = (Integer) object;
+
+	  if (index >= 0 && index < _typeDefList.size())
+	    type = _typeDefList.get(index);
+	  else
+	    type = "type-unknown(" + index + ")";
+	}
+	else if (object instanceof String)
+	  _typeDefList.add((String) object);
+	
+	printObject("list " + type + " (#" + _refId + ")");
+
+	if (_isLength) {
+	  _state = VALUE;
+
+	  if (_length == 0)
+	    return _next;
+	}
+	else
+	  _state = LENGTH;
+      
+	return this;
+      }
+      else if (_state == LENGTH) {
+	_length = (Integer) object;
+
+	if (! _isTyped)
+	  printObject("list (#" + _refId + ")");
+	
+	_state = VALUE;
+
+	if (_length == 0)
+	  return _next;
+	else
+	  return this;
+      }
+      else
+	return this;
+    }
+
+    @Override
+    int depth()
+    {
+      if (_state <= LENGTH)
+	return _next.depth();
+      else if (_state == VALUE)
+	return _valueDepth;
+      else
+	return _next.depth() + 2;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case TYPE:
+	return nextObject(ch);
+	
+      case LENGTH:
+	return nextObject(ch);
+	
+      case VALUE:
+	if (_length <= _count)
+	  return _next.next(ch);
+	else {
+	  _valueDepth = _next.depth() + 2;
+	  println();
+	  printObject(_count++ + ": ");
+	  _valueDepth = _column;
+	  _isObject = false;
+	  
+	  return nextObject(ch);
+	}
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class Hessian2State extends State {
+    private static final int MAJOR = 0;
+    private static final int MINOR = 1;
+
+    private int _state;
+    private int _major;
+    private int _minor;
+
+    Hessian2State(State next)
+    {
+      super(next);
+    }
+
+    int depth()
+    {
+      return _next.depth() + 2;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case MAJOR:
+	_major = ch;
+	_state = MINOR;
+	return this;
+	
+      case MINOR:
+	_minor = ch;
+	println(-2, "hessian " + _major + "." + _minor);
+	return _next;
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class CallState extends State {
+    private static final int MAJOR = 0;
+    private static final int MINOR = 1;
+    private static final int HEADER = 2;
+    private static final int METHOD = 3;
+    private static final int VALUE = 4;
+    private static final int ARG = 5;
+
+    private int _state;
+    private int _major;
+    private int _minor;
+
+    CallState(State next)
+    {
+      super(next);
+    }
+
+    int depth()
+    {
+      return _next.depth() + 2;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case MAJOR:
+	_major = ch;
+	_state = MINOR;
+	return this;
+	
+      case MINOR:
+	_minor = ch;
+	_state = HEADER;
+	println(-2, "call " + _major + "." + _minor);
+	return this;
+	
+      case HEADER:
+	if (ch == 'H') {
+	  println();
+	  print("header ");
+	  _isObject = false;
+	  _state = VALUE;
+	  return new StringState(this, 'H', true);
+	}
+ 	else if (ch == 'm') {
+	  println();
+	  print("method ");
+	  _isObject = false;
+	  _state = ARG;
+	  return new StringState(this, 'm', true);
+	}
+	else {
+	  println((char) ch + ": unexpected char");
+	  return popStack();
+	}
+	
+      case VALUE:
+	print(" => ");
+	_isObject = false;
+	_state = HEADER;
+	return nextObject(ch);
+	
+      case ARG:
+	if (ch == 'Z')
+	  return _next;
+	else
+	  return nextObject(ch);
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class Call2State extends State {
+    private static final int METHOD = 0;
+    private static final int COUNT = 1;
+    private static final int ARG = 2;
+
+    private int _state = METHOD;
+    private int _i;
+    private int _count;
+
+    Call2State(State next)
+    {
+      super(next);
+    }
+
+    int depth()
+    {
+      return _next.depth() + 5;
+    }
+
+    @Override
+    boolean isShift(Object value)
+    {
+      return _state != ARG;
+    }
+
+    @Override
+    State shift(Object object)
+    {
+      if (_state == METHOD) {
+	println(-5, "Call " + object);
+
+	_state = COUNT;
+	return this;
+      }
+      else if (_state == COUNT) {
+	Integer count = (Integer) object;
+
+	_count = count;
+      
+	_state = ARG;
+
+	if (_count == 0)
+	  return _next;
+	else
+	  return this;
+      }
+      else
+	return this;
+    }
+
+    @Override
+    State next(int ch)
+    {
+      switch (_state) {
+      case COUNT:
+	return nextObject(ch);
+	
+      case METHOD:
+	return nextObject(ch);
+	
+      case ARG:
+	if (_count <= _i)
+	  return _next.next(ch);
+	else {
+	  println();
+	  print(-3, _i++ + ": ");
+	  
+	  return nextObject(ch);
+	}
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class ReplyState extends State {
+    private static final int MAJOR = 0;
+    private static final int MINOR = 1;
+    private static final int HEADER = 2;
+    private static final int VALUE = 3;
+    private static final int END = 4;
+
+    private int _state;
+    private int _major;
+    private int _minor;
+
+    ReplyState(State next)
+    {
+      _next = next;
+    }
+
+    int depth()
+    {
+      return _next.depth() + 2;
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case MAJOR:
+	if (ch == 't' || ch == 'S')
+	  return new RemoteState(this).next(ch);
+	
+	_major = ch;
+	_state = MINOR;
+	return this;
+	
+      case MINOR:
+	_minor = ch;
+	_state = HEADER;
+	println(-2, "reply " + _major + "." + _minor);
+	return this;
+	
+      case HEADER:
+	if (ch == 'H') {
+	  _state = VALUE;
+	  return new StringState(this, 'H', true);
+	}
+	else if (ch == 'f') {
+	  print("fault ");
+	  _isObject = false;
+	  _state = END;
+	  return new MapState(this, 0);
+	}
+ 	else {
+	  _state = END;
+	  return nextObject(ch);
+	}
+	
+      case VALUE:
+	_state = HEADER;
+	return nextObject(ch);
+	
+      case END:
+	println();
+	if (ch == 'Z') {
+	  return _next;
+	}
+	else
+	  return _next.next(ch);
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class Reply2State extends State {
+    Reply2State(State next)
+    {
+      super(next);
+
+      println(-2, "Reply");
+    }
+
+    int depth()
+    {
+      return _next.depth() + 2;
+    }
+
+    @Override
+    State next(int ch)
+    {
+      return nextObject(ch);
+    }
+  }
+  
+  class Fault2State extends State {
+    Fault2State(State next)
+    {
+      super(next);
+
+      println(-2, "Fault");
+    }
+
+    int depth()
+    {
+      return _next.depth() + 2;
+    }
+
+    @Override
+    State next(int ch)
+    {
+      return nextObject(ch);
+    }
+  }
+  
+  class IndirectState extends State {
+    IndirectState(State next)
+    {
+      super(next);
+    }
+
+    boolean isShift(Object object)
+    {
+      return _next.isShift(object);
+    }
+
+    State shift(Object object)
+    {
+      return _next.shift(object);
+    }
+    
+    State next(int ch)
+    {
+      return nextObject(ch);
+    }
+  }
+  
+  class RemoteState extends State {
+    private static final int TYPE = 0;
+    private static final int VALUE = 1;
+    private static final int END = 2;
+
+    private int _state;
+    private int _major;
+    private int _minor;
+
+    RemoteState(State next)
+    {
+      super(next);
+    }
+    
+    State next(int ch)
+    {
+      switch (_state) {
+      case TYPE:
+	println(-1, "remote");
+	if (ch == 't') {
+	  _state = VALUE;
+	  return new StringState(this, 't', false);
+	}
+	else {
+	  _state = END;
+	  return nextObject(ch);
+	}
+
+      case VALUE:
+	_state = END;
+	return _next.nextObject(ch);
+
+      case END:
+	return _next.next(ch);
+
+      default:
+	throw new IllegalStateException();
+      }
+    }
+  }
+  
+  class StreamingState extends State {
+    private int _digit;
+    private int _length;
+    private boolean _isLast;
+    private boolean _isFirst = true;
+
+    private State _childState;
+
+    StreamingState(State next, boolean isLast)
+    {
+      super(next);
+
+      _isLast = isLast;
+      _childState = new InitialState();
+    }
+    
+    State next(int ch)
+    {
+      if (_digit < 2) {
+	_length = 256 * _length + ch;
+	_digit++;
+
+	if (_digit == 2 && _length == 0 && _isLast) {
+	  _refId = 0;
+	  return _next;
+	}
+	else {
+	  if (_digit == 2)
+	    println(-1, "packet-start(" + _length + ")");
+	  
+	  return this;
+	}
+      }
+      else if (_length == 0) {
+	_isLast = (ch == 'P');
+	_digit = 0;
+	
+	return this;
+      }
+
+      _childState = _childState.next(ch);
+
+      _length--;
+
+      if (_length == 0 && _isLast) {
+	println(-1, "");
+	println(-1, "packet-end");
+	_refId = 0;
+	return _next;
+      }
+      else
+	return this;
+    }
+  }
+
+  static class ObjectDef {
+    private String _type;
+    private ArrayList<String> _fields;
+
+    ObjectDef(String type, ArrayList<String> fields)
+    {
+      _type = type;
+      _fields = fields;
+    }
+
+    String getType()
+    {
+      return _type;
+    }
+
+    ArrayList<String> getFields()
+    {
+      return _fields;
+    }
+  }
+}

+ 78 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianEnvelope.java

@@ -0,0 +1,78 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.*;
+
+/**
+ * Factory class for wrapping and unwrapping hessian streams.
+ */
+abstract public class HessianEnvelope {
+  /**
+   * Wrap the Hessian output stream in an envelope.
+   */
+  abstract public Hessian2Output wrap(Hessian2Output out)
+    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.
+   */
+  abstract public Hessian2Input unwrap(Hessian2Input in)
+    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.
+   */
+  abstract public Hessian2Input unwrapHeaders(Hessian2Input in)
+    throws IOException;
+}

+ 87 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianFieldException.java

@@ -0,0 +1,87 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Exception during field reading.
+ */
+public class HessianFieldException extends HessianProtocolException {
+  /**
+   * Zero-arg constructor.
+   */
+  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);
+  }
+}

+ 55 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianHandle.java

@@ -0,0 +1,55 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+/**
+ * Marks a type as a handle
+ */
+public interface HessianHandle {
+}

+ 1700 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianInput.java

@@ -0,0 +1,1700 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+
+/**
+ * Input stream for Hessian requests.
+ *
+ * <p>HessianInput is unbuffered, so any client needs to provide
+ * its own buffering.
+ *
+ * <pre>
+ * InputStream is = ...; // from http connection
+ * HessianInput in = new HessianInput(is);
+ * String value;
+ *
+ * in.startReply();         // read reply header
+ * value = in.readString(); // read string value
+ * in.completeReply();      // read reply footer
+ * </pre>
+ */
+public class HessianInput extends AbstractHessianInput {
+  private static int END_OF_DATA = -2;
+
+  private static Field _detailMessageField;
+  
+  // factory for deserializing objects in the input stream
+  protected SerializerFactory _serializerFactory;
+  
+  protected ArrayList _refs;
+  
+  // the underlying input stream
+  private InputStream _is;
+  // a peek character
+  protected int _peek = -1;
+  
+  // the method for a call
+  private String _method;
+
+  private Reader _chunkReader;
+  private InputStream _chunkInputStream;
+
+  private Throwable _replyFault;
+
+  private StringBuffer _sbuf = new StringBuffer();
+  
+  // true if this is the last chunk
+  private boolean _isLastChunk;
+  // the chunk length
+  private int _chunkLength;
+
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianInput()
+  {
+  }
+  
+  /**
+   * Creates a new Hessian input stream, initialized with an
+   * underlying input stream.
+   *
+   * @param is the underlying input stream.
+   */
+  public HessianInput(InputStream is)
+  {
+    init(is);
+  }
+
+  /**
+   * Sets the serializer factory.
+   */
+  public void setSerializerFactory(SerializerFactory factory)
+  {
+    _serializerFactory = factory;
+  }
+
+  /**
+   * Gets the serializer factory.
+   */
+  public SerializerFactory getSerializerFactory()
+  {
+    return _serializerFactory;
+  }
+
+  /**
+   * Initialize the hessian stream with the underlying input stream.
+   */
+  public void init(InputStream is)
+  {
+    _is = is;
+    _method = null;
+    _isLastChunk = true;
+    _chunkLength = 0;
+    _peek = -1;
+    _refs = null;
+    _replyFault = null;
+
+    if (_serializerFactory == null)
+      _serializerFactory = new SerializerFactory();
+  }
+
+  /**
+   * Returns the calls method
+   */
+  public String getMethod()
+  {
+    return _method;
+  }
+
+  /**
+   * Returns any reply fault.
+   */
+  public Throwable getReplyFault()
+  {
+    return _replyFault;
+  }
+
+  /**
+   * Starts reading the call
+   *
+   * <pre>
+   * c major minor
+   * </pre>
+   */
+  public int readCall()
+    throws IOException
+  {
+    int tag = read();
+    
+    if (tag != 'c')
+      throw error("expected hessian call ('c') at " + codeName(tag));
+
+    int major = read();
+    int minor = read();
+
+    return (major << 16) + minor;
+  }
+
+  /**
+   * For backward compatibility with HessianSkeleton
+   */
+  public void skipOptionalCall()
+    throws IOException
+  {
+    int tag = read();
+
+    if (tag == 'c') {
+      read();
+      read();
+    }
+    else
+      _peek = tag;
+  }
+
+  /**
+   * Starts reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * m b16 b8 method
+   * </pre>
+   */
+  public String readMethod()
+    throws IOException
+  {
+    int tag = read();
+    
+    if (tag != 'm')
+      throw error("expected hessian method ('m') at " + codeName(tag));
+    int d1 = read();
+    int d2 = read();
+
+    _isLastChunk = true;
+    _chunkLength = d1 * 256 + d2;
+    _sbuf.setLength(0);
+    int ch;
+    while ((ch = parseChar()) >= 0)
+      _sbuf.append((char) ch);
+    
+    _method = _sbuf.toString();
+
+    return _method;
+  }
+
+  /**
+   * Starts reading the call, including the headers.
+   *
+   * <p>The call expects the following protocol data
+   *
+   * <pre>
+   * c major minor
+   * m b16 b8 method
+   * </pre>
+   */
+  public void startCall()
+    throws IOException
+  {
+    readCall();
+
+    while (readHeader() != null) {
+      readObject();
+    }
+
+    readMethod();
+  }
+
+  /**
+   * Completes reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  public void completeCall()
+    throws IOException
+  {
+    int tag = read();
+
+    if (tag == 'z') {
+    }
+    else
+      throw error("expected end of call ('z') at " + codeName(tag) + ".  Check method arguments and ensure method overloading is enabled if necessary");
+  }
+
+  /**
+   * Reads a reply as an object.
+   * If the reply has a fault, throws the exception.
+   */
+  public Object readReply(Class expectedClass)
+    throws Throwable
+  {
+    int tag = read();
+    
+    if (tag != 'r')
+      error("expected hessian reply at " + codeName(tag));
+
+    int major = read();
+    int minor = read();
+
+    tag = read();
+    if (tag == 'f')
+      throw prepareFault();
+    else {
+      _peek = tag;
+    
+      Object value = readObject(expectedClass);
+
+      completeValueReply();
+
+      return value;
+    }
+  }
+
+  /**
+   * Starts reading the reply
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * r
+   * </pre>
+   */
+  public void startReply()
+    throws Throwable
+  {
+    int tag = read();
+    
+    if (tag != 'r')
+      error("expected hessian reply at " + codeName(tag));
+
+    int major = read();
+    int minor = read();
+    
+    tag = read();
+    if (tag == 'f')
+      throw prepareFault();
+    else
+      _peek = tag;
+  }
+
+  /**
+   * Prepares the fault.
+   */
+  private Throwable prepareFault()
+    throws IOException
+  {
+    HashMap fault = readFault();
+
+    Object detail = fault.get("detail");
+    String message = (String) fault.get("message");
+
+    if (detail instanceof Throwable) {
+      _replyFault = (Throwable) detail;
+      
+      if (message != null && _detailMessageField != null) {
+	try {
+	  _detailMessageField.set(_replyFault, message);
+	} catch (Throwable e) {
+	}
+      }
+	
+      return _replyFault;
+    }
+
+    else {
+      String code = (String) fault.get("code");
+        
+      _replyFault = new HessianServiceException(message, code, detail);
+
+      return _replyFault;
+    }
+  }
+
+  /**
+   * Completes reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  public void completeReply()
+    throws IOException
+  {
+    int tag = read();
+    
+    if (tag != 'z')
+      error("expected end of reply at " + codeName(tag));
+  }
+
+  /**
+   * Completes reading the call
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <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.
+   *
+   * <pre>
+   * H b16 b8 value
+   * </pre>
+   */
+  public String readHeader()
+    throws IOException
+  {
+    int tag = read();
+
+    if (tag == 'H') {
+      _isLastChunk = true;
+      _chunkLength = (read() << 8) + read();
+
+      _sbuf.setLength(0);
+      int ch;
+      while ((ch = parseChar()) >= 0)
+        _sbuf.append((char) ch);
+
+      return _sbuf.toString();
+    }
+
+    _peek = tag;
+
+    return null;
+  }
+
+  /**
+   * Reads a null
+   *
+   * <pre>
+   * N
+   * </pre>
+   */
+  public void readNull()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N': return;
+      
+    default:
+      throw expect("null", tag);
+    }
+  }
+
+  /**
+   * Reads a boolean
+   *
+   * <pre>
+   * T
+   * F
+   * </pre>
+   */
+  public boolean readBoolean()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'T': return true;
+    case 'F': return false;
+    case 'I': return parseInt() == 0;
+    case 'L': return parseLong() == 0;
+    case 'D': return parseDouble() == 0.0;
+    case 'N': return false;
+      
+    default:
+      throw expect("boolean", tag);
+    }
+  }
+
+  /**
+   * Reads a byte
+   *
+   * <pre>
+   * I b32 b24 b16 b8
+   * </pre>
+   */
+  /*
+  public byte readByte()
+    throws IOException
+  {
+    return (byte) readInt();
+  }
+  */
+
+  /**
+   * Reads a short
+   *
+   * <pre>
+   * I b32 b24 b16 b8
+   * </pre>
+   */
+  public short readShort()
+    throws IOException
+  {
+    return (short) readInt();
+  }
+
+  /**
+   * Reads an integer
+   *
+   * <pre>
+   * I b32 b24 b16 b8
+   * </pre>
+   */
+  public int readInt()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'T': return 1;
+    case 'F': return 0;
+    case 'I': return parseInt();
+    case 'L': return (int) parseLong();
+    case 'D': return (int) parseDouble();
+      
+    default:
+      throw expect("int", tag);
+    }
+  }
+
+  /**
+   * Reads a long
+   *
+   * <pre>
+   * L b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  public long readLong()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'T': return 1;
+    case 'F': return 0;
+    case 'I': return parseInt();
+    case 'L': return parseLong();
+    case 'D': return (long) parseDouble();
+      
+    default:
+      throw expect("long", tag);
+    }
+  }
+
+  /**
+   * Reads a float
+   *
+   * <pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  public float readFloat()
+    throws IOException
+  {
+    return (float) readDouble();
+  }
+
+  /**
+   * Reads a double
+   *
+   * <pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  public double readDouble()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'T': return 1;
+    case 'F': return 0;
+    case 'I': return parseInt();
+    case 'L': return (double) parseLong();
+    case 'D': return parseDouble();
+      
+    default:
+      throw expect("long", tag);
+    }
+  }
+
+  /**
+   * Reads a date.
+   *
+   * <pre>
+   * T b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  public long readUTCDate()
+    throws IOException
+  {
+    int tag = read();
+
+    if (tag != 'd')
+      throw error("expected date at " + codeName(tag));
+
+    long b64 = read();
+    long b56 = read();
+    long b48 = read();
+    long b40 = read();
+    long b32 = read();
+    long b24 = read();
+    long b16 = read();
+    long b8 = read();
+
+    return ((b64 << 56) +
+            (b56 << 48) +
+            (b48 << 40) +
+            (b40 << 32) +
+            (b32 << 24) +
+            (b24 << 16) +
+            (b16 << 8) +
+            b8);
+  }
+
+  /**
+   * Reads a byte from the stream.
+   */
+  public int readChar()
+    throws IOException
+  {
+    if (_chunkLength > 0) {
+      _chunkLength--;
+      if (_chunkLength == 0 && _isLastChunk)
+        _chunkLength = END_OF_DATA;
+
+      int ch = parseUTF8Char();
+      return ch;
+    }
+    else if (_chunkLength == END_OF_DATA) {
+      _chunkLength = 0;
+      return -1;
+    }
+    
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return -1;
+
+    case 'S':
+    case 's':
+    case 'X':
+    case 'x':
+      _isLastChunk = tag == 'S' || tag == 'X';
+      _chunkLength = (read() << 8) + read();
+
+      _chunkLength--;
+      int value = parseUTF8Char();
+
+      // special code so successive read byte won't
+      // be read as a single object.
+      if (_chunkLength == 0 && _isLastChunk)
+        _chunkLength = END_OF_DATA;
+
+      return value;
+      
+    default:
+      throw new IOException("expected 'S' at " + (char) tag);
+    }
+  }
+
+  /**
+   * Reads a byte array from the stream.
+   */
+  public int readString(char []buffer, int offset, int length)
+    throws IOException
+  {
+    int readLength = 0;
+
+    if (_chunkLength == END_OF_DATA) {
+      _chunkLength = 0;
+      return -1;
+    }
+    else if (_chunkLength == 0) {
+      int tag = read();
+
+      switch (tag) {
+      case 'N':
+        return -1;
+      
+      case 'S':
+      case 's':
+      case 'X':
+      case 'x':
+        _isLastChunk = tag == 'S' || tag == 'X';
+        _chunkLength = (read() << 8) + read();
+        break;
+
+      default:
+        throw new IOException("expected 'S' at " + (char) tag);
+      }
+    }
+
+    while (length > 0) {
+      if (_chunkLength > 0) {
+        buffer[offset++] = (char) parseUTF8Char();
+        _chunkLength--;
+        length--;
+        readLength++;
+      }
+      else if (_isLastChunk) {
+        if (readLength == 0)
+          return -1;
+        else {
+          _chunkLength = END_OF_DATA;
+          return readLength;
+        }
+      }
+      else {
+        int tag = read();
+
+        switch (tag) {
+        case 'S':
+        case 's':
+        case 'X':
+        case 'x':
+          _isLastChunk = tag == 'S' || tag == 'X';
+          _chunkLength = (read() << 8) + read();
+          break;
+      
+        default:
+          throw new IOException("expected 'S' at " + (char) tag);
+        }
+      }
+    }
+    
+    if (readLength == 0)
+      return -1;
+    else if (_chunkLength > 0 || ! _isLastChunk)
+      return readLength;
+    else {
+      _chunkLength = END_OF_DATA;
+      return readLength;
+    }
+  }
+
+  /**
+   * Reads a string
+   *
+   * <pre>
+   * S b16 b8 string value
+   * </pre>
+   */
+  public String readString()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'I':
+      return String.valueOf(parseInt());
+    case 'L':
+      return String.valueOf(parseLong());
+    case 'D':
+      return String.valueOf(parseDouble());
+
+    case 'S':
+    case 's':
+    case 'X':
+    case 'x':
+      _isLastChunk = tag == 'S' || tag == 'X';
+      _chunkLength = (read() << 8) + read();
+
+      _sbuf.setLength(0);
+      int ch;
+
+      while ((ch = parseChar()) >= 0)
+        _sbuf.append((char) ch);
+
+      return _sbuf.toString();
+
+    default:
+      throw expect("string", tag);
+    }
+  }
+
+  /**
+   * Reads an XML node.
+   *
+   * <pre>
+   * S b16 b8 string value
+   * </pre>
+   */
+  public org.w3c.dom.Node readNode()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'S':
+    case 's':
+    case 'X':
+    case 'x':
+      _isLastChunk = tag == 'S' || tag == 'X';
+      _chunkLength = (read() << 8) + read();
+
+      throw error("Can't handle string in this context");
+
+    default:
+      throw expect("string", tag);
+    }
+  }
+
+  /**
+   * Reads a byte array
+   *
+   * <pre>
+   * B b16 b8 data value
+   * </pre>
+   */
+  public byte []readBytes()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'B':
+    case 'b':
+      _isLastChunk = tag == 'B';
+      _chunkLength = (read() << 8) + read();
+
+      ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+      int data;
+      while ((data = parseByte()) >= 0)
+        bos.write(data);
+
+      return bos.toByteArray();
+      
+    default:
+      throw expect("bytes", tag);
+    }
+  }
+
+  /**
+   * Reads a byte from the stream.
+   */
+  public int readByte()
+    throws IOException
+  {
+    if (_chunkLength > 0) {
+      _chunkLength--;
+      if (_chunkLength == 0 && _isLastChunk)
+        _chunkLength = END_OF_DATA;
+
+      return read();
+    }
+    else if (_chunkLength == END_OF_DATA) {
+      _chunkLength = 0;
+      return -1;
+    }
+    
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return -1;
+
+    case 'B':
+    case 'b':
+      _isLastChunk = tag == 'B';
+      _chunkLength = (read() << 8) + read();
+
+      int value = parseByte();
+
+      // special code so successive read byte won't
+      // be read as a single object.
+      if (_chunkLength == 0 && _isLastChunk)
+        _chunkLength = END_OF_DATA;
+
+      return value;
+      
+    default:
+      throw new IOException("expected 'B' at " + (char) tag);
+    }
+  }
+
+  /**
+   * Reads a byte array from the stream.
+   */
+  public int readBytes(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    int readLength = 0;
+
+    if (_chunkLength == END_OF_DATA) {
+      _chunkLength = 0;
+      return -1;
+    }
+    else if (_chunkLength == 0) {
+      int tag = read();
+
+      switch (tag) {
+      case 'N':
+        return -1;
+      
+      case 'B':
+      case 'b':
+        _isLastChunk = tag == 'B';
+        _chunkLength = (read() << 8) + read();
+        break;
+      
+      default:
+        throw new IOException("expected 'B' at " + (char) tag);
+      }
+    }
+
+    while (length > 0) {
+      if (_chunkLength > 0) {
+        buffer[offset++] = (byte) read();
+        _chunkLength--;
+        length--;
+        readLength++;
+      }
+      else if (_isLastChunk) {
+        if (readLength == 0)
+          return -1;
+        else {
+          _chunkLength = END_OF_DATA;
+          return readLength;
+        }
+      }
+      else {
+        int tag = read();
+
+        switch (tag) {
+        case 'B':
+        case 'b':
+          _isLastChunk = tag == 'B';
+          _chunkLength = (read() << 8) + read();
+          break;
+      
+        default:
+          throw new IOException("expected 'B' at " + (char) tag);
+        }
+      }
+    }
+    
+    if (readLength == 0)
+      return -1;
+    else if (_chunkLength > 0 || ! _isLastChunk)
+      return readLength;
+    else {
+      _chunkLength = END_OF_DATA;
+      return readLength;
+    }
+  }
+
+  /**
+   * Reads a fault.
+   */
+  private HashMap readFault()
+    throws IOException
+  {
+    HashMap map = new HashMap();
+
+    int code = read();
+    for (; code > 0 && code != 'z'; code = read()) {
+      _peek = code;
+      
+      Object key = readObject();
+      Object value = readObject();
+
+      if (key != null && value != null)
+        map.put(key, value);
+    }
+
+    if (code != 'z')
+      throw expect("fault", code);
+
+    return map;
+  }
+
+  /**
+   * Reads an object from the input stream with an expected type.
+   */
+  public Object readObject(Class cl)
+    throws IOException
+  {
+    if (cl == null || cl == Object.class)
+      return readObject();
+    
+    int tag = read();
+    
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'M':
+    {
+      String type = readType();
+
+      // hessian/3386
+      if ("".equals(type)) {
+	Deserializer reader;
+	reader = _serializerFactory.getDeserializer(cl);
+
+	return reader.readMap(this);
+      }
+      else {
+	Deserializer reader;
+	reader = _serializerFactory.getObjectDeserializer(type,cl);
+
+        return reader.readMap(this);
+      }
+    }
+
+    case 'V':
+    {
+      String type = readType();
+      int length = readLength();
+      
+      Deserializer reader;
+      reader = _serializerFactory.getObjectDeserializer(type);
+      
+      if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
+        return reader.readList(this, length);
+
+      reader = _serializerFactory.getDeserializer(cl);
+
+      Object v = reader.readList(this, length);
+
+      return v;
+    }
+
+    case 'R':
+    {
+      int ref = parseInt();
+
+      return _refs.get(ref);
+    }
+
+    case 'r':
+    {
+      String type = readType();
+      String url = readString();
+
+      return resolveRemote(type, url);
+    }
+    }
+
+    _peek = tag;
+
+    // hessian/332i vs hessian/3406
+    //return readObject();
+    
+    Object value = _serializerFactory.getDeserializer(cl).readObject(this);
+
+    return value;
+  }
+  
+  /**
+   * Reads an arbitrary object from the input stream when the type
+   * is unknown.
+   */
+  public Object readObject()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+      
+    case 'T':
+      return Boolean.valueOf(true);
+      
+    case 'F':
+      return Boolean.valueOf(false);
+      
+    case 'I':
+      return Integer.valueOf(parseInt());
+    
+    case 'L':
+      return Long.valueOf(parseLong());
+    
+    case 'D':
+      return Double.valueOf(parseDouble());
+    
+    case 'd':
+      return new Date(parseLong());
+    
+    case 'x':
+    case 'X': {
+      _isLastChunk = tag == 'X';
+      _chunkLength = (read() << 8) + read();
+
+      return parseXML();
+    }
+
+    case 's':
+    case 'S': {
+      _isLastChunk = tag == 'S';
+      _chunkLength = (read() << 8) + read();
+
+      int data;
+      _sbuf.setLength(0);
+      
+      while ((data = parseChar()) >= 0)
+        _sbuf.append((char) data);
+
+      return _sbuf.toString();
+    }
+
+    case 'b':
+    case 'B': {
+      _isLastChunk = tag == 'B';
+      _chunkLength = (read() << 8) + read();
+
+      int data;
+      ByteArrayOutputStream bos = new ByteArrayOutputStream();
+      
+      while ((data = parseByte()) >= 0)
+        bos.write(data);
+
+      return bos.toByteArray();
+    }
+
+    case 'V': {
+      String type = readType();
+      int length = readLength();
+
+      return _serializerFactory.readList(this, length, type);
+    }
+
+    case 'M': {
+      String type = readType();
+
+      return _serializerFactory.readMap(this, type);
+    }
+
+    case 'R': {
+      int ref = parseInt();
+
+      return _refs.get(ref);
+    }
+
+    case 'r': {
+      String type = readType();
+      String url = readString();
+
+      return resolveRemote(type, url);
+    }
+
+    default:
+      throw error("unknown code for readObject at " + codeName(tag));
+    }
+  }
+
+  /**
+   * Reads a remote object.
+   */
+  public Object readRemote()
+    throws IOException
+  {
+    String type = readType();
+    String url = readString();
+
+    return resolveRemote(type, url);
+  }
+
+  /**
+   * Reads a reference.
+   */
+  public Object readRef()
+    throws IOException
+  {
+    return _refs.get(parseInt());
+  }
+
+  /**
+   * Reads the start of a list.
+   */
+  public int readListStart()
+    throws IOException
+  {
+    return read();
+  }
+
+  /**
+   * Reads the start of a list.
+   */
+  public int readMapStart()
+    throws IOException
+  {
+    return read();
+  }
+
+  /**
+   * Returns true if this is the end of a list or a map.
+   */
+  public boolean isEnd()
+    throws IOException
+  {
+    int code = read();
+
+    _peek = code;
+
+    return (code < 0 || code == 'z');
+  }
+
+  /**
+   * Reads the end byte.
+   */
+  public void readEnd()
+    throws IOException
+  {
+    int code = read();
+
+    if (code != 'z')
+      throw error("unknown code at " + codeName(code));
+  }
+
+  /**
+   * Reads the end byte.
+   */
+  public void readMapEnd()
+    throws IOException
+  {
+    int code = read();
+
+    if (code != 'z')
+      throw error("expected end of map ('z') at " + codeName(code));
+  }
+
+  /**
+   * Reads the end byte.
+   */
+  public void readListEnd()
+    throws IOException
+  {
+    int code = read();
+
+    if (code != 'z')
+      throw error("expected end of list ('z') at " + codeName(code));
+  }
+
+  /**
+   * Adds a list/map reference.
+   */
+  public int addRef(Object ref)
+  {
+    if (_refs == null)
+      _refs = new ArrayList();
+    
+    _refs.add(ref);
+
+    return _refs.size() - 1;
+  }
+
+  /**
+   * Adds a list/map reference.
+   */
+  public void setRef(int i, Object ref)
+  {
+    _refs.set(i, ref);
+  }
+
+  /**
+   * Resets the references for streaming.
+   */
+  public void resetReferences()
+  {
+    if (_refs != null)
+      _refs.clear();
+  }
+
+  /**
+   * Resolves a remote object.
+   */
+  public Object resolveRemote(String type, String url)
+    throws IOException
+  {
+    HessianRemoteResolver resolver = getRemoteResolver();
+
+    if (resolver != null)
+      return resolver.lookup(type, url);
+    else
+      return new HessianRemote(type, url);
+  }
+
+  /**
+   * Parses a type from the stream.
+   *
+   * <pre>
+   * t b16 b8
+   * </pre>
+   */
+  public String readType()
+    throws IOException
+  {
+    int code = read();
+
+    if (code != 't') {
+      _peek = code;
+      return "";
+    }
+
+    _isLastChunk = true;
+    _chunkLength = (read() << 8) + read();
+
+    _sbuf.setLength(0);
+    int ch;
+    while ((ch = parseChar()) >= 0)
+      _sbuf.append((char) ch);
+
+    return _sbuf.toString();
+  }
+
+  /**
+   * Parses the length for an array
+   *
+   * <pre>
+   * l b32 b24 b16 b8
+   * </pre>
+   */
+  public int readLength()
+    throws IOException
+  {
+    int code = read();
+
+    if (code != 'l') {
+      _peek = code;
+      return -1;
+    }
+
+    return parseInt();
+  }
+
+  /**
+   * Parses a 32-bit integer value from the stream.
+   *
+   * <pre>
+   * b32 b24 b16 b8
+   * </pre>
+   */
+  private int parseInt()
+    throws IOException
+  {
+    int b32 = read();
+    int b24 = read();
+    int b16 = read();
+    int b8 = read();
+
+    return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8;
+  }
+
+  /**
+   * Parses a 64-bit long value from the stream.
+   *
+   * <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.
+   *
+   * <pre>
+   * b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre>
+   */
+  private double parseDouble()
+    throws IOException
+  {
+    long b64 = read();
+    long b56 = read();
+    long b48 = read();
+    long b40 = read();
+    long b32 = read();
+    long b24 = read();
+    long b16 = read();
+    long b8 = read();
+
+    long bits = ((b64 << 56) +
+                 (b56 << 48) +
+                 (b48 << 40) +
+                 (b40 << 32) +
+                 (b32 << 24) +
+                 (b24 << 16) +
+                 (b16 << 8) +
+                 b8);
+  
+    return Double.longBitsToDouble(bits);
+  }
+
+  org.w3c.dom.Node parseXML()
+    throws IOException
+  {
+    throw new UnsupportedOperationException();
+  }
+  
+  /**
+   * Reads a character from the underlying stream.
+   */
+  private int parseChar()
+    throws IOException
+  {
+    while (_chunkLength <= 0) {
+      if (_isLastChunk)
+        return -1;
+
+      int code = read();
+
+      switch (code) {
+      case 's':
+      case 'x':
+        _isLastChunk = false;
+
+        _chunkLength = (read() << 8) + read();
+        break;
+        
+      case 'S':
+      case 'X':
+        _isLastChunk = true;
+
+        _chunkLength = (read() << 8) + read();
+        break;
+
+      default:
+        throw expect("string", code);
+      }
+
+    }
+
+    _chunkLength--;
+
+    return parseUTF8Char();
+  }
+
+  /**
+   * Parses a single UTF8 character.
+   */
+  private int parseUTF8Char()
+    throws IOException
+  {
+    int ch = read();
+
+    if (ch < 0x80)
+      return ch;
+    else if ((ch & 0xe0) == 0xc0) {
+      int ch1 = read();
+      int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f);
+
+      return v;
+    }
+    else if ((ch & 0xf0) == 0xe0) {
+      int ch1 = read();
+      int ch2 = read();
+      int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f);
+
+      return v;
+    }
+    else
+      throw error("bad utf-8 encoding at " + codeName(ch));
+  }
+  
+  /**
+   * Reads a byte from the underlying stream.
+   */
+  private int parseByte()
+    throws IOException
+  {
+    while (_chunkLength <= 0) {
+      if (_isLastChunk) {
+        return -1;
+      }
+
+      int code = read();
+
+      switch (code) {
+      case 'b':
+        _isLastChunk = false;
+
+        _chunkLength = (read() << 8) + read();
+        break;
+        
+      case 'B':
+        _isLastChunk = true;
+
+        _chunkLength = (read() << 8) + read();
+        break;
+
+      default:
+        throw expect("byte[]", code);
+      }
+    }
+
+    _chunkLength--;
+
+    return read();
+  }
+
+  /**
+   * Reads bytes based on an input stream.
+   */
+  public InputStream readInputStream()
+    throws IOException
+  {
+    int tag = read();
+
+    switch (tag) {
+    case 'N':
+      return null;
+
+    case 'B':
+    case 'b':
+      _isLastChunk = tag == 'B';
+      _chunkLength = (read() << 8) + read();
+      break;
+      
+    default:
+      throw expect("inputStream", tag);
+    }
+    
+    return new InputStream() {
+	boolean _isClosed = false;
+	
+	public int read()
+	  throws IOException
+	{
+	  if (_isClosed || _is == null)
+	    return -1;
+
+	  int ch = parseByte();
+	  if (ch < 0)
+	    _isClosed = true;
+
+	  return ch;
+	}
+	
+	public int read(byte []buffer, int offset, int length)
+	  throws IOException
+	{
+	  if (_isClosed || _is == null)
+	    return -1;
+
+	  int len = HessianInput.this.read(buffer, offset, length);
+	  if (len < 0)
+	    _isClosed = true;
+
+	  return len;
+	}
+
+	public void close()
+	  throws IOException
+	{
+	  while (read() >= 0) {
+	  }
+
+	  _isClosed = true;
+	}
+      };
+  }
+  
+  /**
+   * Reads bytes from the underlying stream.
+   */
+  int read(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    int readLength = 0;
+    
+    while (length > 0) {
+      while (_chunkLength <= 0) {
+        if (_isLastChunk)
+          return readLength == 0 ? -1 : readLength;
+
+        int code = read();
+
+        switch (code) {
+        case 'b':
+          _isLastChunk = false;
+
+          _chunkLength = (read() << 8) + read();
+          break;
+        
+        case 'B':
+          _isLastChunk = true;
+
+          _chunkLength = (read() << 8) + read();
+          break;
+
+        default:
+          throw expect("byte[]", code);
+        }
+      }
+
+      int sublen = _chunkLength;
+      if (length < sublen)
+        sublen = length;
+
+      sublen = _is.read(buffer, offset, sublen);
+      offset += sublen;
+      readLength += sublen;
+      length -= sublen;
+      _chunkLength -= sublen;
+    }
+
+    return readLength;
+  }
+
+  final int read()
+    throws IOException
+  {
+    if (_peek >= 0) {
+      int value = _peek;
+      _peek = -1;
+      return value;
+    }
+
+    int ch = _is.read();
+      
+    return ch;
+  }
+
+  public void close()
+  {
+    _is = null;
+  }
+
+  public Reader getReader()
+  {
+    return null;
+  }
+
+  protected IOException expect(String expect, int ch)
+  {
+    return error("expected " + expect + " at " + codeName(ch));
+  }
+
+  protected String codeName(int ch)
+  {
+    if (ch < 0)
+      return "end of file";
+    else
+      return "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) + ch + ")";
+  }
+  
+  protected IOException error(String message)
+  {
+    if (_method != null)
+      return new HessianProtocolException(_method + ": " + message);
+    else
+      return new HessianProtocolException(message);
+  }
+
+  static {
+    try {
+      _detailMessageField = Throwable.class.getDeclaredField("detailMessage");
+      _detailMessageField.setAccessible(true);
+    } catch (Throwable e) {
+    }
+  }
+}

+ 99 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianInputFactory.java

@@ -0,0 +1,99 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.util.logging.*;
+import java.io.*;
+
+public class HessianInputFactory
+{
+  public static final Logger log
+    = Logger.getLogger(HessianInputFactory.class.getName());
+
+  private SerializerFactory _serializerFactory;
+
+  public void setSerializerFactory(SerializerFactory factory)
+  {
+    _serializerFactory = factory;
+  }
+
+  public SerializerFactory getSerializerFactory()
+  {
+    return _serializerFactory;
+  }
+
+  public AbstractHessianInput open(InputStream is)
+    throws IOException
+  {
+    int code = is.read();
+
+    int major = is.read();
+    int minor = is.read();
+
+    switch (code) {
+    case 'c':
+    case 'C':
+    case 'r':
+    case 'R':
+      if (major >= 2) {
+	AbstractHessianInput in = new Hessian2Input(is);
+	in.setSerializerFactory(_serializerFactory);
+	return in;
+      }
+      else {
+	AbstractHessianInput in = new HessianInput(is);
+	in.setSerializerFactory(_serializerFactory);
+	return in;
+      }
+
+    default:
+      throw new IOException((char) code + " is an unknown Hessian message code.");
+    }
+  }
+}

+ 949 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianOutput.java

@@ -0,0 +1,949 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.IdentityHashMap;
+
+/**
+ * Output stream for Hessian requests, compatible with microedition
+ * Java.  It only uses classes and types available in JDK.
+ *
+ * <p>Since HessianOutput does not depend on any classes other than
+ * in the JDK, it can be extracted independently into a smaller package.
+ *
+ * <p>HessianOutput is unbuffered, so any client needs to provide
+ * its own buffering.
+ *
+ * <pre>
+ * OutputStream os = ...; // from http connection
+ * HessianOutput out = new HessianOutput(os);
+ * String value;
+ *
+ * out.startCall("hello");  // start hello call
+ * out.writeString("arg1"); // write a string argument
+ * out.completeCall();      // complete the call
+ * </pre>
+ */
+public class HessianOutput extends AbstractHessianOutput {
+  // the output stream/
+  protected OutputStream os;
+  // map of references
+  private IdentityHashMap _refs;
+  private int _version = 1;
+  
+  /**
+   * Creates a new Hessian output stream, initialized with an
+   * underlying output stream.
+   *
+   * @param os the underlying output stream.
+   */
+  public HessianOutput(OutputStream os)
+  {
+    init(os);
+  }
+
+  /**
+   * Creates an uninitialized Hessian output stream.
+   */
+  public HessianOutput()
+  {
+  }
+
+  /**
+   * Initializes the output
+   */
+  public void init(OutputStream os)
+  {
+    this.os = os;
+
+    _refs = null;
+
+    if (_serializerFactory == null)
+      _serializerFactory = new SerializerFactory();
+  }
+
+  /**
+   * Sets the client's version.
+   */
+  public void setVersion(int version)
+  {
+    _version = version;
+  }
+
+  /**
+   * 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.  Clients would use <code>startCall</code>
+   * instead of <code>call</code> if they wanted finer control over
+   * writing the arguments, or needed to write headers.
+   *
+   * <code><pre>
+   * c major minor
+   * m b16 b8 method-name
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  public void startCall(String method, int length)
+    throws IOException
+  {
+    os.write('c');
+    os.write(_version);
+    os.write(0);
+
+    os.write('m');
+    int len = method.length();
+    os.write(len >> 8);
+    os.write(len);
+    printString(method, 0, len);
+  }
+
+  /**
+   * Writes the call tag.  This would be followed by the
+   * headers and the method tag.
+   *
+   * <code><pre>
+   * c major minor
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  public void startCall()
+    throws IOException
+  {
+    os.write('c');
+    os.write(0);
+    os.write(1);
+  }
+
+  /**
+   * Writes the method tag.
+   *
+   * <code><pre>
+   * m b16 b8 method-name
+   * </pre></code>
+   *
+   * @param method the method name to call.
+   */
+  public void writeMethod(String method)
+    throws IOException
+  {
+    os.write('m');
+    int len = method.length();
+    os.write(len >> 8);
+    os.write(len);
+    printString(method, 0, len);
+  }
+
+  /**
+   * Completes.
+   *
+   * <code><pre>
+   * z
+   * </pre></code>
+   */
+  public void completeCall()
+    throws IOException
+  {
+    os.write('z');
+  }
+
+  /**
+   * Starts the reply
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * r
+   * </pre>
+   */
+  public void startReply()
+    throws IOException
+  {
+    os.write('r');
+    os.write(1);
+    os.write(0);
+  }
+
+  /**
+   * Completes reading the reply
+   *
+   * <p>A successful completion will have a single value:
+   *
+   * <pre>
+   * z
+   * </pre>
+   */
+  public void completeReply()
+    throws IOException
+  {
+    os.write('z');
+  }
+
+  /**
+   * Writes a header name.  The header value must immediately follow.
+   *
+   * <code><pre>
+   * H b16 b8 foo <em>value</em>
+   * </pre></code>
+   */
+  public void writeHeader(String name)
+    throws IOException
+  {
+    int len = name.length();
+    
+    os.write('H');
+    os.write(len >> 8);
+    os.write(len);
+
+    printString(name);
+  }
+
+  /**
+   * Writes a fault.  The fault will be written
+   * as a descriptive string followed by an object:
+   *
+   * <code><pre>
+   * f
+   * &lt;string>code
+   * &lt;string>the fault code
+   *
+   * &lt;string>message
+   * &lt;string>the fault mesage
+   *
+   * &lt;string>detail
+   * mt\x00\xnnjavax.ejb.FinderException
+   *     ...
+   * z
+   * z
+   * </pre></code>
+   *
+   * @param code the fault code, a three digit
+   */
+  public void writeFault(String code, String message, Object detail)
+    throws IOException
+  {
+    os.write('f');
+    writeString("code");
+    writeString(code);
+
+    writeString("message");
+    writeString(message);
+
+    if (detail != null) {
+      writeString("detail");
+      writeObject(detail);
+    }
+    os.write('z');
+  }
+
+  /**
+   * Writes any object to the output stream.
+   */
+  public void writeObject(Object object)
+    throws IOException
+  {
+    if (object == null) {
+      writeNull();
+      return;
+    }
+
+    Serializer serializer;
+
+    serializer = _serializerFactory.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>.
+   *
+   * <code><pre>
+   * V
+   * t b16 b8 type
+   * l b32 b24 b16 b8
+   * </pre></code>
+   */
+  public boolean writeListBegin(int length, String type)
+    throws IOException
+  {
+    os.write('V');
+
+    if (type != null) {
+      os.write('t');
+      printLenString(type);
+    }
+
+    if (length >= 0) {
+      os.write('l');
+      os.write(length >> 24);
+      os.write(length >> 16);
+      os.write(length >> 8);
+      os.write(length);
+    }
+
+    return true;
+  }
+
+  /**
+   * Writes the tail of the list to the stream.
+   */
+  public void writeListEnd()
+    throws IOException
+  {
+    os.write('z');
+  }
+
+  /**
+   * Writes the map header to the stream.  Map writers will call
+   * <code>writeMapBegin</code> followed by the map contents and then
+   * call <code>writeMapEnd</code>.
+   *
+   * <code><pre>
+   * Mt b16 b8 (<key> <value>)z
+   * </pre></code>
+   */
+  public void writeMapBegin(String type)
+    throws IOException
+  {
+    os.write('M');
+    os.write('t');
+    printLenString(type);
+  }
+
+  /**
+   * Writes the tail of the map to the stream.
+   */
+  public void writeMapEnd()
+    throws IOException
+  {
+    os.write('z');
+  }
+
+  /**
+   * Writes a remote object reference to the stream.  The type is the
+   * type of the remote interface.
+   *
+   * <code><pre>
+   * 'r' 't' b16 b8 type url
+   * </pre></code>
+   */
+  public void writeRemote(String type, String url)
+    throws IOException
+  {
+    os.write('r');
+    os.write('t');
+    printLenString(type);
+    os.write('S');
+    printLenString(url);
+  }
+
+  /**
+   * Writes a boolean value to the stream.  The boolean will be written
+   * with the following syntax:
+   *
+   * <code><pre>
+   * T
+   * F
+   * </pre></code>
+   *
+   * @param value the boolean value to write.
+   */
+  public void writeBoolean(boolean value)
+    throws IOException
+  {
+    if (value)
+      os.write('T');
+    else
+      os.write('F');
+  }
+
+  /**
+   * Writes an integer value to the stream.  The integer will be written
+   * with the following syntax:
+   *
+   * <code><pre>
+   * I b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the integer value to write.
+   */
+  public void writeInt(int value)
+    throws IOException
+  {
+    os.write('I');
+    os.write(value >> 24);
+    os.write(value >> 16);
+    os.write(value >> 8);
+    os.write(value);
+  }
+
+  /**
+   * Writes a long value to the stream.  The long will be written
+   * with the following syntax:
+   *
+   * <code><pre>
+   * L b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the long value to write.
+   */
+  public void writeLong(long value)
+    throws IOException
+  {
+    os.write('L');
+    os.write((byte) (value >> 56));
+    os.write((byte) (value >> 48));
+    os.write((byte) (value >> 40));
+    os.write((byte) (value >> 32));
+    os.write((byte) (value >> 24));
+    os.write((byte) (value >> 16));
+    os.write((byte) (value >> 8));
+    os.write((byte) (value));
+  }
+
+  /**
+   * Writes a double value to the stream.  The double will be written
+   * with the following syntax:
+   *
+   * <code><pre>
+   * D b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the double value to write.
+   */
+  public void writeDouble(double value)
+    throws IOException
+  {
+    long bits = Double.doubleToLongBits(value);
+    
+    os.write('D');
+    os.write((byte) (bits >> 56));
+    os.write((byte) (bits >> 48));
+    os.write((byte) (bits >> 40));
+    os.write((byte) (bits >> 32));
+    os.write((byte) (bits >> 24));
+    os.write((byte) (bits >> 16));
+    os.write((byte) (bits >> 8));
+    os.write((byte) (bits));
+  }
+
+  /**
+   * Writes a date to the stream.
+   *
+   * <code><pre>
+   * T  b64 b56 b48 b40 b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param time the date in milliseconds from the epoch in UTC
+   */
+  public void writeUTCDate(long time)
+    throws IOException
+  {
+    os.write('d');
+    os.write((byte) (time >> 56));
+    os.write((byte) (time >> 48));
+    os.write((byte) (time >> 40));
+    os.write((byte) (time >> 32));
+    os.write((byte) (time >> 24));
+    os.write((byte) (time >> 16));
+    os.write((byte) (time >> 8));
+    os.write((byte) (time));
+  }
+
+  /**
+   * Writes a null value to the stream.
+   * The null will be written with the following syntax
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeNull()
+    throws IOException
+  {
+    os.write('N');
+  }
+
+  /**
+   * Writes a string value to the stream using UTF-8 encoding.
+   * The string will be written with the following syntax:
+   *
+   * <code><pre>
+   * S b16 b8 string-value
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeString(String value)
+    throws IOException
+  {
+    if (value == null) {
+      os.write('N');
+    }
+    else {
+      int length = value.length();
+      int offset = 0;
+      
+      while (length > 0x8000) {
+        int sublen = 0x8000;
+
+	// chunk can't end in high surrogate
+	char tail = value.charAt(offset + sublen - 1);
+
+	if (0xd800 <= tail && tail <= 0xdbff)
+	  sublen--;
+        
+        os.write('s');
+        os.write(sublen >> 8);
+        os.write(sublen);
+
+        printString(value, offset, sublen);
+
+        length -= sublen;
+        offset += sublen;
+      }
+
+      os.write('S');
+      os.write(length >> 8);
+      os.write(length);
+
+      printString(value, offset, length);
+    }
+  }
+
+  /**
+   * Writes a string value to the stream using UTF-8 encoding.
+   * The string will be written with the following syntax:
+   *
+   * <code><pre>
+   * S b16 b8 string-value
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeString(char []buffer, int offset, int length)
+    throws IOException
+  {
+    if (buffer == null) {
+      os.write('N');
+    }
+    else {
+      while (length > 0x8000) {
+        int sublen = 0x8000;
+
+	// chunk can't end in high surrogate
+	char tail = buffer[offset + sublen - 1];
+
+	if (0xd800 <= tail && tail <= 0xdbff)
+	  sublen--;
+        
+        os.write('s');
+        os.write(sublen >> 8);
+        os.write(sublen);
+
+        printString(buffer, offset, sublen);
+
+        length -= sublen;
+        offset += sublen;
+      }
+
+      os.write('S');
+      os.write(length >> 8);
+      os.write(length);
+
+      printString(buffer, offset, length);
+    }
+  }
+
+  /**
+   * Writes a byte array to the stream.
+   * The array will be written with the following syntax:
+   *
+   * <code><pre>
+   * B b16 b18 bytes
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeBytes(byte []buffer)
+    throws IOException
+  {
+    if (buffer == null)
+      os.write('N');
+    else
+      writeBytes(buffer, 0, buffer.length);
+  }
+  
+  /**
+   * Writes a byte array to the stream.
+   * The array will be written with the following syntax:
+   *
+   * <code><pre>
+   * B b16 b18 bytes
+   * </pre></code>
+   *
+   * If the value is null, it will be written as
+   *
+   * <code><pre>
+   * N
+   * </pre></code>
+   *
+   * @param value the string value to write.
+   */
+  public void writeBytes(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    if (buffer == null) {
+      os.write('N');
+    }
+    else {
+      while (length > 0x8000) {
+        int sublen = 0x8000;
+        
+        os.write('b');
+        os.write(sublen >> 8);
+        os.write(sublen);
+
+        os.write(buffer, offset, sublen);
+
+        length -= sublen;
+        offset += sublen;
+      }
+
+      os.write('B');
+      os.write(length >> 8);
+      os.write(length);
+      os.write(buffer, offset, length);
+    }
+  }
+  
+  /**
+   * Writes a byte buffer to the stream.
+   *
+   * <code><pre>
+   * </pre></code>
+   */
+  public void writeByteBufferStart()
+    throws IOException
+  {
+  }
+  
+  /**
+   * Writes a byte buffer to the stream.
+   *
+   * <code><pre>
+   * b b16 b18 bytes
+   * </pre></code>
+   */
+  public void writeByteBufferPart(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    while (length > 0) {
+      int sublen = length;
+
+      if (0x8000 < sublen)
+	sublen = 0x8000;
+
+      os.write('b');
+      os.write(sublen >> 8);
+      os.write(sublen);
+
+      os.write(buffer, offset, sublen);
+
+      length -= sublen;
+      offset += sublen;
+    }
+  }
+  
+  /**
+   * Writes a byte buffer to the stream.
+   *
+   * <code><pre>
+   * b b16 b18 bytes
+   * </pre></code>
+   */
+  public void writeByteBufferEnd(byte []buffer, int offset, int length)
+    throws IOException
+  {
+    writeBytes(buffer, offset, length);
+  }
+
+  /**
+   * Writes a reference.
+   *
+   * <code><pre>
+   * R b32 b24 b16 b8
+   * </pre></code>
+   *
+   * @param value the integer value to write.
+   */
+  public void writeRef(int value)
+    throws IOException
+  {
+    os.write('R');
+    os.write(value >> 24);
+    os.write(value >> 16);
+    os.write(value >> 8);
+    os.write(value);
+  }
+
+  /**
+   * Writes a placeholder.
+   *
+   * <code><pre>
+   * P
+   * </pre></code>
+   */
+  public void writePlaceholder()
+    throws IOException
+  {
+    os.write('P');
+  }
+
+  /**
+   * If the object has already been written, just write its ref.
+   *
+   * @return true if we're writing a ref.
+   */
+  public boolean addRef(Object object)
+    throws IOException
+  {
+    if (_refs == null)
+      _refs = new IdentityHashMap();
+
+    Integer ref = (Integer) _refs.get(object);
+
+    if (ref != null) {
+      int value = ref.intValue();
+      
+      writeRef(value);
+      return true;
+    }
+    else {
+      _refs.put(object, new Integer(_refs.size()));
+      
+      return false;
+    }
+  }
+
+  /**
+   * Resets the references for streaming.
+   */
+  public void resetReferences()
+  {
+    if (_refs != null)
+      _refs.clear();
+  }
+
+  /**
+   * Removes a reference.
+   */
+  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.
+   */
+  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;
+  }
+
+  /**
+   * 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 (v == null) {
+      os.write(0);
+      os.write(0);
+    }
+    else {
+      int len = v.length();
+      os.write(len >> 8);
+      os.write(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 offset, int length)
+    throws IOException
+  {
+    for (int i = 0; i < length; i++) {
+      char ch = v.charAt(i + offset);
+
+      if (ch < 0x80)
+        os.write(ch);
+      else if (ch < 0x800) {
+        os.write(0xc0 + ((ch >> 6) & 0x1f));
+        os.write(0x80 + (ch & 0x3f));
+      }
+      else {
+        os.write(0xe0 + ((ch >> 12) & 0xf));
+        os.write(0x80 + ((ch >> 6) & 0x3f));
+        os.write(0x80 + (ch & 0x3f));
+      }
+    }
+  }
+  
+  /**
+   * Prints a string to the stream, encoded as UTF-8
+   *
+   * @param v the string to print.
+   */
+  public void printString(char []v, int offset, int length)
+    throws IOException
+  {
+    for (int i = 0; i < length; i++) {
+      char ch = v[i + offset];
+
+      if (ch < 0x80)
+        os.write(ch);
+      else if (ch < 0x800) {
+        os.write(0xc0 + ((ch >> 6) & 0x1f));
+        os.write(0x80 + (ch & 0x3f));
+      }
+      else {
+        os.write(0xe0 + ((ch >> 12) & 0xf));
+        os.write(0x80 + ((ch >> 6) & 0x3f));
+        os.write(0x80 + (ch & 0x3f));
+      }
+    }
+  }
+
+  public void flush()
+    throws IOException
+  {
+    if (this.os != null)
+      this.os.flush();
+  }
+
+  public void close()
+    throws IOException
+  {
+    if (this.os != null)
+      this.os.flush();
+  }
+}

+ 110 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianProtocolException.java

@@ -0,0 +1,110 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Exception for faults when the fault doesn't return a java exception.
+ * This exception is required for MicroHessianInput.
+ */
+public class HessianProtocolException extends IOException {
+  private Throwable rootCause;
+  
+  /**
+   * Zero-arg constructor.
+   */
+  public HessianProtocolException()
+  {
+  }
+  
+  /**
+   * 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.
+   */
+  public Throwable getCause()
+  {
+    return getRootCause();
+  }
+}

+ 130 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianRemote.java

@@ -0,0 +1,130 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+/**
+ * Encapsulates a remote address when no stub is available, e.g. for
+ * Java MicroEdition.
+ */
+public class HessianRemote {
+  private String type;
+  private String url;
+
+  /**
+   * 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.
+   */
+  public int hashCode()
+  {
+    return url.hashCode();
+  }
+
+  /**
+   * Defines equality
+   */
+  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.
+   */
+  public String toString()
+  {
+    return "[HessianRemote " + url + "]";
+  }
+}

+ 57 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianRemoteObject.java

@@ -0,0 +1,57 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+/**
+ * Interface for any hessian remote object.
+ */
+public interface HessianRemoteObject {
+  public String getHessianType();
+  public String getHessianURL();
+}

+ 62 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianRemoteResolver.java

@@ -0,0 +1,62 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Looks up remote objects.  The default just returns a HessianRemote object.
+ */
+public interface HessianRemoteResolver {
+  /**
+   * Looks up a proxy object.
+   */
+  public Object lookup(String type, String url)
+    throws IOException;
+}

+ 183 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianSerializerInput.java

@@ -0,0 +1,183 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Input stream for Hessian requests, deserializing objects using the
+ * java.io.Serialization protocol.
+ *
+ * <p>HessianSerializerInput is unbuffered, so any client needs to provide
+ * its own buffering.
+ *
+ * <h3>Serialization</h3>
+ *
+ * <pre>
+ * InputStream is = new FileInputStream("test.xml");
+ * HessianOutput in = new HessianSerializerOutput(is);
+ *
+ * Object obj = in.readObject();
+ * is.close();
+ * </pre>
+ *
+ * <h3>Parsing a Hessian reply</h3>
+ *
+ * <pre>
+ * InputStream is = ...; // from http connection
+ * HessianInput in = new HessianSerializerInput(is);
+ * String value;
+ *
+ * in.startReply();         // read reply header
+ * value = in.readString(); // read string value
+ * in.completeReply();      // read reply footer
+ * </pre>
+ */
+public class HessianSerializerInput extends HessianInput {
+  /**
+   * Creates a new Hessian input stream, initialized with an
+   * underlying input stream.
+   *
+   * @param is the underlying input stream.
+   */
+  public HessianSerializerInput(InputStream is)
+  {
+    super(is);
+  }
+
+  /**
+   * Creates an uninitialized Hessian input stream.
+   */
+  public HessianSerializerInput()
+  {
+  }
+
+  /**
+   * Reads an object from the input stream.  cl is known not to be
+   * a Map.
+   */
+  protected Object readObjectImpl(Class cl)
+    throws IOException
+  {
+    try {
+      Object obj = cl.newInstance();
+
+      if (_refs == null)
+        _refs = new ArrayList();
+      _refs.add(obj);
+
+      HashMap fieldMap = getFieldMap(cl);
+
+      int code = read();
+      for (; code >= 0 && code != 'z'; code = read()) {
+        _peek = code;
+        
+        Object key = readObject();
+        
+        Field field = (Field) fieldMap.get(key);
+
+        if (field != null) {
+          Object value = readObject(field.getType());
+          field.set(obj, value);
+        }
+        else {
+          Object value = readObject();
+        }
+      }
+      
+      if (code != 'z')
+        throw expect("map", code);
+
+      // if there's a readResolve method, call it
+      try {
+        Method method = cl.getMethod("readResolve", new Class[0]);
+        return method.invoke(obj, new Object[0]);
+      } catch (Exception e) {
+      }
+
+      return obj;
+    } catch (IOException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new IOExceptionWrapper(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;
+
+        // XXX: could parameterize the handler to only deal with public
+        field.setAccessible(true);
+
+        fieldMap.put(field.getName(), field);
+      }
+    }
+
+    return fieldMap;
+  }
+}

+ 146 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianSerializerOutput.java

@@ -0,0 +1,146 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Output stream for Hessian requests.
+ *
+ * <p>HessianOutput is unbuffered, so any client needs to provide
+ * its own buffering.
+ *
+ * <h3>Serialization</h3>
+ *
+ * <pre>
+ * OutputStream os = new FileOutputStream("test.xml");
+ * HessianOutput out = new HessianSerializerOutput(os);
+ *
+ * out.writeObject(obj);
+ * os.close();
+ * </pre>
+ *
+ * <h3>Writing an RPC Call</h3>
+ *
+ * <pre>
+ * OutputStream os = ...; // from http connection
+ * HessianOutput 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>
+ */
+public class HessianSerializerOutput extends HessianOutput {
+  /**
+   * Creates a new Hessian output stream, initialized with an
+   * underlying output stream.
+   *
+   * @param os the underlying output stream.
+   */
+  public HessianSerializerOutput(OutputStream os)
+  {
+    super(os);
+  }
+
+  /**
+   * Creates an uninitialized Hessian output stream.
+   */
+  public HessianSerializerOutput()
+  {
+  }
+
+  /**
+   * Applications which override this can do custom serialization.
+   *
+   * @param object the object to write.
+   */
+  public void writeObjectImpl(Object obj)
+    throws IOException
+  {
+    Class cl = obj.getClass();
+    
+    try {
+      Method method = cl.getMethod("writeReplace", new Class[0]);
+      Object repl = method.invoke(obj, new Object[0]);
+
+      writeObject(repl);
+      return;
+    } catch (Exception e) {
+    }
+
+    try {
+      writeMapBegin(cl.getName());
+      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);
+      
+          writeString(field.getName());
+          writeObject(field.get(obj));
+        }
+      }
+      writeMapEnd();
+    } catch (IllegalAccessException e) {
+      throw new IOExceptionWrapper(e);
+    }
+  }
+}

+ 91 - 0
src/com/hmsoft/remote/caucho/hessian/io/HessianServiceException.java

@@ -0,0 +1,91 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+/**
+ * Exception for faults when the fault doesn't return a java exception.
+ * This exception is required for MicroHessianInput.
+ */
+public class HessianServiceException extends Exception {
+  private String code;
+  private Object detail;
+
+  /**
+   * Zero-arg constructor.
+   */
+  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;
+  }
+}

+ 77 - 0
src/com/hmsoft/remote/caucho/hessian/io/IOExceptionWrapper.java

@@ -0,0 +1,77 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Exception wrapper for IO.
+ */
+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;
+  }
+
+  public Throwable getCause()
+  {
+    return _cause;
+  }
+}

+ 67 - 0
src/com/hmsoft/remote/caucho/hessian/io/InputStreamDeserializer.java

@@ -0,0 +1,67 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Serializing a stream object.
+ */
+public class InputStreamDeserializer extends AbstractDeserializer {
+  public InputStreamDeserializer()
+  {
+  }
+  
+  public Object readObject(AbstractHessianInput in)
+    throws IOException
+  {
+    return in.readInputStream();
+  }
+}

+ 80 - 0
src/com/hmsoft/remote/caucho/hessian/io/InputStreamSerializer.java

@@ -0,0 +1,80 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Serializing a stream object.
+ */
+public class InputStreamSerializer extends AbstractSerializer {
+  public InputStreamSerializer()
+  {
+  }
+  
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    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);
+    }
+  }
+}

+ 84 - 0
src/com/hmsoft/remote/caucho/hessian/io/IteratorSerializer.java

@@ -0,0 +1,84 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ * Serializing a JDK 1.2 Iterator.
+ */
+public class IteratorSerializer extends AbstractSerializer {
+  private static IteratorSerializer _serializer;
+
+  public static IteratorSerializer create()
+  {
+    if (_serializer == null)
+      _serializer = new IteratorSerializer();
+
+    return _serializer;
+  }
+  
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    Iterator iter = (Iterator) obj;
+
+    boolean hasEnd = out.writeListBegin(-1, null);
+
+    while (iter.hasNext()) {
+      Object value = iter.next();
+
+      out.writeObject(value);
+    }
+
+    if (hasEnd)
+      out.writeListEnd();
+  }
+}

+ 679 - 0
src/com/hmsoft/remote/caucho/hessian/io/JavaDeserializer.java

@@ -0,0 +1,679 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.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.util.HashMap;
+
+import java.util.logging.*;
+
+/**
+ * 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]);
+      }
+    }
+  }
+
+  public Class getType()
+  {
+    return _type;
+  }
+    
+  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);
+    }
+  }
+    
+  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);
+  }
+	else {
+	  deser = new ObjectFieldDeserializer(field);
+	}
+
+        fieldMap.put(field.getName(), deser);
+      }
+    }
+
+    return fieldMap;
+  }
+
+  /**
+   * 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();
+  }
+
+  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;
+    }
+    
+    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;
+    }
+    
+    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;
+    }
+    
+    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;
+    }
+    
+    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 extends FieldDeserializer {
+    private final Field _field;
+
+    IntFieldDeserializer(Field field)
+    {
+      _field = field;
+    }
+    
+    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;
+    }
+    
+    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;
+    }
+    
+    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;
+    }
+    
+    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;
+    }
+    
+    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;
+    }
+    
+    void deserialize(AbstractHessianInput in, Object obj)
+      throws IOException
+    {
+      java.sql.Date value = null;
+
+      try {
+        java.util.Date date = (java.util.Date) in.readObject();
+        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;
+    }
+    
+    void deserialize(AbstractHessianInput in, Object obj)
+      throws IOException
+    {
+      java.sql.Timestamp value = null;
+
+      try {
+        java.util.Date date = (java.util.Date) in.readObject();
+        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;
+    }
+    
+    void deserialize(AbstractHessianInput in, Object obj)
+      throws IOException
+    {
+      java.sql.Time value = null;
+
+      try {
+        java.util.Date date = (java.util.Date) in.readObject();
+        value = new java.sql.Time(date.getTime());
+
+        _field.set(obj, value);
+      } catch (Exception e) {
+        logDeserializeError(_field, obj, value, e);
+      }
+    }
+  }
+
+  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() + " (" + value + ")"
+				      + " cannot be assigned to '" + field.getType().getName() + "'", e);
+    else
+       throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);
+  }
+}

+ 434 - 0
src/com/hmsoft/remote/caucho/hessian/io/JavaSerializer.java

@@ -0,0 +1,434 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+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);
+
+    ArrayList primitiveFields = new ArrayList();
+    ArrayList 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);
+      }
+    }
+
+    ArrayList fields = new ArrayList();
+    fields.addAll(primitiveFields);
+    fields.addAll(compoundFields);
+
+    _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());
+    }
+  }
+
+  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 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;
+  }
+
+  /**
+   * 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;
+  }
+  
+  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);
+
+	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);
+    }
+  }
+
+  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;
+  }
+
+  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();
+    
+    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();
+    
+    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();
+    
+    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();
+    
+    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();
+    
+    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();
+
+    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());
+    }
+  }
+}

+ 119 - 0
src/com/hmsoft/remote/caucho/hessian/io/LocaleHandle.java

@@ -0,0 +1,119 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.util.Locale;
+
+/**
+ * Handle for a locale object.
+ */
+public class LocaleHandle implements java.io.Serializable, HessianHandle {
+  private String value;
+
+  public LocaleHandle(String locale)
+  {
+    this.value = locale;
+  }
+
+  private Object readResolve()
+  {
+    String s = this.value;
+    
+    if (s == null)
+      return null;
+    
+    int len = s.length();
+    char ch = ' ';
+
+    int i = 0;
+    for (;
+	 i < len && ('a' <= (ch = s.charAt(i)) && ch <= 'z'
+		     || 'A' <= ch && ch <= 'Z'
+		     || '0' <= ch && ch <= '9');
+	 i++) {
+    }
+
+    String language = s.substring(0, i);
+    String country = null;
+    String var = null;
+
+    if (ch == '-' || ch == '_') {
+      int head = ++i;
+      
+      for (;
+	   i < len && ('a' <= (ch = s.charAt(i)) && ch <= 'z'
+		       || 'A' <= ch && ch <= 'Z'
+		       || '0' <= ch && ch <= '9');
+	   i++) {
+      }
+      
+      country = s.substring(head, i);
+    }
+
+    if (ch == '-' || ch == '_') {
+      int head = ++i;
+      
+      for (;
+	   i < len && ('a' <= (ch = s.charAt(i)) && ch <= 'z'
+		       || 'A' <= ch && ch <= 'Z'
+		       || '0' <= ch && ch <= '9');
+	   i++) {
+      }
+      
+      var = s.substring(head, i);
+    }
+
+    if (var != null)
+      return new Locale(language, country, var);
+    else if (country != null)
+      return new Locale(language, country);
+    else
+      return new Locale(language);
+  }
+}

+ 76 - 0
src/com/hmsoft/remote/caucho/hessian/io/LocaleSerializer.java

@@ -0,0 +1,76 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * Serializing a locale.
+ */
+public class LocaleSerializer extends AbstractSerializer {
+  private static LocaleSerializer SERIALIZER = new LocaleSerializer();
+
+  public static LocaleSerializer create()
+  {
+    return SERIALIZER;
+  }
+  
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    if (obj == null)
+      out.writeNull();
+    else {
+      Locale locale = (Locale) obj;
+
+      out.writeObject(new LocaleHandle(locale.toString()));
+    }
+  }
+}

+ 158 - 0
src/com/hmsoft/remote/caucho/hessian/io/MapDeserializer.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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.*;
+import java.lang.reflect.*;
+
+/**
+ * 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);
+      }
+    }
+  }
+  
+  public Class getType()
+  {
+    if (_type != null)
+      return _type;
+    else
+      return HashMap.class;
+  }
+
+  public Object readMap(AbstractHessianInput in)
+    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);
+
+    while (! in.isEnd()) {
+      map.put(in.readObject(), in.readObject());
+    }
+
+    in.readEnd();
+
+    return map;
+  }
+
+  @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);
+      }
+    }
+  }
+}

+ 104 - 0
src/com/hmsoft/remote/caucho/hessian/io/MapSerializer.java

@@ -0,0 +1,104 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Serializing a JDK 1.2 java.util.Map.
+ */
+public class MapSerializer extends AbstractSerializer {
+  private boolean _isSendJavaType = true;
+
+  /**
+   * Set true if the java type of the collection should be sent.
+   */
+  public void setSendJavaType(boolean sendJavaType)
+  {
+    _isSendJavaType = sendJavaType;
+  }
+
+  /**
+   * Return true if the java type of the collection should be sent.
+   */
+  public boolean getSendJavaType()
+  {
+    return _isSendJavaType;
+  }
+    
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    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();
+  }
+}

+ 100 - 0
src/com/hmsoft/remote/caucho/hessian/io/ObjectDeserializer.java

@@ -0,0 +1,100 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * Serializing an object for known object types.
+ */
+public class ObjectDeserializer extends AbstractDeserializer {
+  private Class _cl;
+
+  public ObjectDeserializer(Class cl)
+  {
+    _cl = cl;
+  }
+
+  public Class getType()
+  {
+    return _cl;
+  }
+  
+  public Object readObject(AbstractHessianInput in)
+    throws IOException
+  {
+    return in.readObject();
+  }
+  
+  public Object readObject(AbstractHessianInput in, String []fieldNames)
+    throws IOException
+  {
+    throw new UnsupportedOperationException(String.valueOf(this));
+  }
+  
+  public Object readList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    throw new UnsupportedOperationException(String.valueOf(this));
+  }
+  
+  public Object readLengthList(AbstractHessianInput in, int length)
+    throws IOException
+  {
+    throw new UnsupportedOperationException(String.valueOf(this));
+  }
+
+  @Override
+  public String toString()
+  {
+    return getClass().getSimpleName() + "[" + _cl + "]";
+  }
+}

+ 63 - 0
src/com/hmsoft/remote/caucho/hessian/io/RemoteSerializer.java

@@ -0,0 +1,63 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Serializing a remote object.
+ */
+public class RemoteSerializer extends AbstractSerializer {
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    // XXX: needs to be handled as a separate class
+    throw new UnsupportedOperationException(getClass().getName());
+  }
+}

+ 59 - 0
src/com/hmsoft/remote/caucho/hessian/io/Serializer.java

@@ -0,0 +1,59 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+
+/**
+ * Serializing an object. 
+ */
+public interface Serializer {
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException;
+}

+ 646 - 0
src/com/hmsoft/remote/caucho/hessian/io/SerializerFactory.java

@@ -0,0 +1,646 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.*;
+
+/**
+ * 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 HashMap _staticSerializerMap;
+  private static HashMap _staticDeserializerMap;
+  private static HashMap _staticTypeMap;
+
+  private ClassLoader _loader;
+
+  protected Serializer _defaultSerializer;
+
+  // Additional factories
+  protected ArrayList _factories = new ArrayList();
+  
+  protected CollectionSerializer _collectionSerializer;
+  protected MapSerializer _mapSerializer;
+  
+  private Deserializer _hashMapDeserializer;
+  private Deserializer _arrayListDeserializer;
+  private HashMap _cachedSerializerMap;
+  private HashMap _cachedDeserializerMap;
+  private HashMap _cachedTypeDeserializerMap;
+
+  private boolean _isAllowNonSerializable;
+
+  public SerializerFactory()
+  {
+    this(Thread.currentThread().getContextClassLoader());
+  }
+
+  public SerializerFactory(ClassLoader loader)
+  {
+    _loader = loader;
+  }
+
+  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 void setAllowNonSerializable(boolean allow)
+  {
+    _isAllowNonSerializable = allow;
+  }
+
+  /**
+   * If true, non-serializable objects are allowed.
+   */
+  public boolean isAllowNonSerializable()
+  {
+    return _isAllowNonSerializable;
+  }
+
+  /**
+   * 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) {
+      synchronized (_cachedSerializerMap) {
+	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 (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);
+
+    if (serializer == null)
+      serializer = getDefaultSerializer(cl);
+
+    if (_cachedSerializerMap == null)
+      _cachedSerializerMap = new HashMap(8);
+
+    synchronized (_cachedSerializerMap) {
+      _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.
+   */
+  public Deserializer getDeserializer(Class cl)
+    throws HessianProtocolException
+  {
+    Deserializer deserializer;
+
+    deserializer = (Deserializer) _staticDeserializerMap.get(cl);
+    if (deserializer != null)
+      return deserializer;
+
+    if (_cachedDeserializerMap != null) {
+      synchronized (_cachedDeserializerMap) {
+	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 HashMap(8);
+
+    synchronized (_cachedDeserializerMap) {
+      _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)
+  {
+    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
+  {
+    Deserializer deserializer = getDeserializer(type);
+
+    if (deserializer != null)
+      return deserializer.readMap(in);
+    else if (_hashMapDeserializer != null)
+      return _hashMapDeserializer.readMap(in);
+    else {
+      _hashMapDeserializer = new MapDeserializer(HashMap.class);
+      
+      return _hashMapDeserializer.readMap(in);
+    }
+  }
+  
+  /**
+   * 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(""))
+      return null;
+
+    Deserializer deserializer;
+    
+    if (_cachedTypeDeserializerMap != null) {
+      synchronized (_cachedTypeDeserializerMap) {
+	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 {
+      try {
+	Class cl = Class.forName(type, false, _loader);
+	deserializer = getDeserializer(cl);
+      } catch (Exception e) {
+	log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e);
+	
+	log.log(Level.FINER, e.toString(), e);
+      }
+    }
+
+    if (deserializer != null) {
+      if (_cachedTypeDeserializerMap == null)
+	_cachedTypeDeserializerMap = new HashMap(8);
+
+      synchronized (_cachedTypeDeserializerMap) {
+	_cachedTypeDeserializerMap.put(type, deserializer);
+      }
+    }
+
+    return deserializer;
+  }
+
+  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);
+  }
+
+  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));
+    } catch (Throwable e) {
+    }
+    
+    _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) {
+    }
+  }
+}

+ 133 - 0
src/com/hmsoft/remote/caucho/hessian/io/SqlDateDeserializer.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 "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.hmsoft.remote.caucho.hessian.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 });
+  }
+  
+  public Class getType()
+  {
+    return _cl;
+  }
+  
+  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;
+  }
+  
+  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);
+    }
+  }
+}

+ 88 - 0
src/com/hmsoft/remote/caucho/hessian/io/SqlDateSerializer.java

@@ -0,0 +1,88 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * Serializing a sql date object.
+ */
+public class SqlDateSerializer extends AbstractSerializer
+{
+  public void writeObject(Object obj, AbstractHessianOutput out)
+    throws IOException
+  {
+    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());
+      }
+    }
+  }
+}

+ 69 - 0
src/com/hmsoft/remote/caucho/hessian/io/StackTraceElementDeserializer.java

@@ -0,0 +1,69 @@
+/*
+ * 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.hmsoft.remote.caucho.hessian.io;
+
+import java.io.*;
+import java.util.HashMap;
+
+/**
+ * Deserializing a JDK 1.4 StackTraceElement
+ */
+public class StackTraceElementDeserializer extends JavaDeserializer {
+  public StackTraceElementDeserializer()
+  {
+    super(StackTraceElement.class);
+  }
+
+  @Override
+  protected Object instantiate()
+    throws Exception
+  {
+    return new StackTraceElement("", "", "", 0);
+  }
+}

Some files were not shown because too many files changed in this diff