haogh 11 months ago
commit
781a8187eb
100 changed files with 18561 additions and 0 deletions
  1. 2 0
      Cfg/server.properties
  2. BIN
      lib/commons-beanutils-1.8.3.jar
  3. BIN
      lib/commons-codec-1.10.jar
  4. BIN
      lib/commons-collections-3.2.1.jar
  5. BIN
      lib/commons-io-2.5.jar
  6. BIN
      lib/commons-lang-2.6.jar
  7. BIN
      lib/commons-logging-1.1.1.jar
  8. BIN
      lib/commons-logging-1.2.jar
  9. BIN
      lib/fastjson-1.2.2.jar
  10. BIN
      lib/log4j-1.2.17.jar
  11. BIN
      lib/log4j-api-2.11.0.jar
  12. BIN
      lib/log4j-core-2.11.0.jar
  13. BIN
      lib/netty-all-4.1.28.Final.jar
  14. BIN
      lib/poi-3.17.jar
  15. BIN
      lib/poi-ooxml-3.17.jar
  16. BIN
      lib/poi-ooxml-schemas-3.17.jar
  17. BIN
      lib/poi-scratchpad-3.17.jar
  18. BIN
      lib/spring-core-5.1.3.RELEASE.jar
  19. BIN
      lib/spring-retry-1.2.2.RELEASE.jar
  20. BIN
      lib/xmlbeans-2.6.0.jar
  21. 468 0
      src/com/hmsoft/app/Login.java
  22. 103 0
      src/com/hmsoft/app/Main.java
  23. 10 0
      src/com/hmsoft/app/Test.java
  24. 1 0
      src/com/hmsoft/app/application.css
  25. 128 0
      src/com/hmsoft/bizpanel/HisPubTab.java
  26. 473 0
      src/com/hmsoft/bizpanel/MtzjTab.java
  27. 73 0
      src/com/hmsoft/bizpanel/SysTab.java
  28. 66 0
      src/com/hmsoft/common/AppConst.java
  29. 27 0
      src/com/hmsoft/common/BatchConst.java
  30. 21 0
      src/com/hmsoft/common/FileConst.java
  31. 34 0
      src/com/hmsoft/common/MsgTypeConst.java
  32. 13 0
      src/com/hmsoft/common/bizinf/IExecuteLogic.java
  33. 37 0
      src/com/hmsoft/common/bizlogic/RequestLogic.java
  34. 40 0
      src/com/hmsoft/common/cell/ActionButtonTableCell.java
  35. 43 0
      src/com/hmsoft/common/cell/checkbox.java
  36. 24 0
      src/com/hmsoft/common/util/BatchDataUtil.java
  37. 94 0
      src/com/hmsoft/common/util/CoursePlanDataUtil.java
  38. 40 0
      src/com/hmsoft/common/util/DialogUtil.java
  39. 68 0
      src/com/hmsoft/common/util/ExecuteRetryUtil.java
  40. 127 0
      src/com/hmsoft/model/Batch.java
  41. 246 0
      src/com/hmsoft/model/CoursePlan.java
  42. 89 0
      src/com/hmsoft/model/School.java
  43. 132 0
      src/com/hmsoft/model/SelectedCourse.java
  44. 89 0
      src/com/hmsoft/model/request/BizRequest.java
  45. 66 0
      src/com/hmsoft/model/request/LoginRequest.java
  46. 11 0
      src/com/hmsoft/model/response/BizResponse.java
  47. 15 0
      src/com/hmsoft/model/response/LoginResponse.java
  48. 85 0
      src/com/hmsoft/remote/caucho/hessian/HessianException.java
  49. 118 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractDeserializer.java
  50. 442 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianInput.java
  51. 531 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianOutput.java
  52. 65 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianResolver.java
  53. 67 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractListDeserializer.java
  54. 74 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractMapDeserializer.java
  55. 63 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializer.java
  56. 74 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializerFactory.java
  57. 164 0
      src/com/hmsoft/remote/caucho/hessian/io/ArrayDeserializer.java
  58. 94 0
      src/com/hmsoft/remote/caucho/hessian/io/ArraySerializer.java
  59. 608 0
      src/com/hmsoft/remote/caucho/hessian/io/BasicDeserializer.java
  60. 285 0
      src/com/hmsoft/remote/caucho/hessian/io/BasicSerializer.java
  61. 295 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanDeserializer.java
  62. 315 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanSerializer.java
  63. 82 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanSerializerFactory.java
  64. 93 0
      src/com/hmsoft/remote/caucho/hessian/io/CalendarHandle.java
  65. 77 0
      src/com/hmsoft/remote/caucho/hessian/io/CalendarSerializer.java
  66. 160 0
      src/com/hmsoft/remote/caucho/hessian/io/ClassDeserializer.java
  67. 87 0
      src/com/hmsoft/remote/caucho/hessian/io/ClassSerializer.java
  68. 134 0
      src/com/hmsoft/remote/caucho/hessian/io/CollectionDeserializer.java
  69. 108 0
      src/com/hmsoft/remote/caucho/hessian/io/CollectionSerializer.java
  70. 204 0
      src/com/hmsoft/remote/caucho/hessian/io/Deflation.java
  71. 73 0
      src/com/hmsoft/remote/caucho/hessian/io/Deserializer.java
  72. 138 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumDeserializer.java
  73. 108 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumSerializer.java
  74. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumerationDeserializer.java
  75. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumerationSerializer.java
  76. 57 0
      src/com/hmsoft/remote/caucho/hessian/io/EnvelopeFactory.java
  77. 107 0
      src/com/hmsoft/remote/caucho/hessian/io/ExtSerializerFactory.java
  78. 146 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Constants.java
  79. 2798 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Input.java
  80. 1604 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Output.java
  81. 165 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingInput.java
  82. 109 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingOutput.java
  83. 174 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugInputStream.java
  84. 168 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugOutputStream.java
  85. 2090 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugState.java
  86. 78 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianEnvelope.java
  87. 87 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianFieldException.java
  88. 55 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianHandle.java
  89. 1700 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianInput.java
  90. 99 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianInputFactory.java
  91. 949 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianOutput.java
  92. 110 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianProtocolException.java
  93. 130 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianRemote.java
  94. 57 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianRemoteObject.java
  95. 62 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianRemoteResolver.java
  96. 183 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianSerializerInput.java
  97. 146 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianSerializerOutput.java
  98. 91 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianServiceException.java
  99. 77 0
      src/com/hmsoft/remote/caucho/hessian/io/IOExceptionWrapper.java
  100. 67 0
      src/com/hmsoft/remote/caucho/hessian/io/InputStreamDeserializer.java

+ 2 - 0
Cfg/server.properties

@@ -0,0 +1,2 @@
+server.ip=127.0.0.1
+server.port=20880

BIN
lib/commons-beanutils-1.8.3.jar


BIN
lib/commons-codec-1.10.jar


BIN
lib/commons-collections-3.2.1.jar


BIN
lib/commons-io-2.5.jar


BIN
lib/commons-lang-2.6.jar


BIN
lib/commons-logging-1.1.1.jar


BIN
lib/commons-logging-1.2.jar


BIN
lib/fastjson-1.2.2.jar


BIN
lib/log4j-1.2.17.jar


BIN
lib/log4j-api-2.11.0.jar


BIN
lib/log4j-core-2.11.0.jar


BIN
lib/netty-all-4.1.28.Final.jar


BIN
lib/poi-3.17.jar


BIN
lib/poi-ooxml-3.17.jar


BIN
lib/poi-ooxml-schemas-3.17.jar


BIN
lib/poi-scratchpad-3.17.jar


BIN
lib/spring-core-5.1.3.RELEASE.jar


BIN
lib/spring-retry-1.2.2.RELEASE.jar


BIN
lib/xmlbeans-2.6.0.jar


+ 468 - 0
src/com/hmsoft/app/Login.java

@@ -0,0 +1,468 @@
+package com.hmsoft.app;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.bizpanel.HisPubTab;
+import com.hmsoft.bizpanel.MtzjTab;
+import com.hmsoft.common.AppConst;
+import com.hmsoft.common.BatchConst;
+import com.hmsoft.common.FileConst;
+import com.hmsoft.common.MsgTypeConst;
+import com.hmsoft.common.util.BatchDataUtil;
+import com.hmsoft.common.util.CoursePlanDataUtil;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.SelectedCourse;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.model.request.LoginRequest;
+import com.hmsoft.model.response.LoginResponse;
+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.rpc.ExchangeClient;
+import javafx.application.Application;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.PasswordField;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.paint.Color;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class Login extends Application {
+	
+	public static ExchangeClient CLIENT = null;
+	private static Properties PROPS = new Properties();
+	//private static volatile Map<String, LoginRequest> LOGIN_MAP = new HashMap<String, LoginRequest>();
+	//记录删除的记录,后续可能会用到.
+	//private static Map<Integer, CoursePlan> DELETED_MAP = new HashMap<Integer, CoursePlan>();
+	private TextField userTextField = null;
+	private PasswordField passwdField = null;
+	private Text scenetitle = null;
+	
+	
+	@Override
+	public void start(final Stage primaryStage) throws Exception {
+		primaryStage.setTitle("登录");
+		final GridPane grid = new GridPane();
+		grid.setAlignment(Pos.CENTER);
+		grid.setHgap(10);
+		grid.setVgap(10);
+		grid.setPadding(new Insets(25));
+		//文本框
+		scenetitle = new Text("Welcome");
+		scenetitle.setId("welcome-text");
+		grid.add(scenetitle, 0, 0, 2, 1);
+		//标签
+		Label userName = new Label("用户名:");
+		grid.add(userName, 0, 1);
+		//文本输入框
+		userTextField = new TextField();
+		grid.add(userTextField, 1, 1);
+		
+		Label passwd = new Label("密码:");
+		grid.add(passwd, 0, 2);
+		//密码输入框
+		passwdField = new PasswordField();
+		grid.add(passwdField, 1, 2);
+		passwdField.setOnKeyPressed(new EventHandler<KeyEvent>() {
+			@Override
+			public void handle(KeyEvent event) {
+				if (event.getCode() == KeyCode.ENTER) {
+					doLoginFunc(primaryStage);
+				}
+			}
+		});
+		//按钮及按钮布局
+		final HBox hbBtn = new HBox(10);
+		grid.add(hbBtn, 1, 4);
+		Text actiontarget = new Text();
+		actiontarget.setId("actiontarget");
+		grid.add(actiontarget, 1, 6);
+		//添加按钮
+		this.createBtn(hbBtn, primaryStage);
+		//scene
+		Scene scene = new Scene(grid, 350, 200);
+		primaryStage.setScene(scene);
+		primaryStage.setResizable(false);
+		primaryStage.setAlwaysOnTop(true);
+		primaryStage.show();
+		//socket thread
+		this.startConnectServer();
+	}
+
+	/**
+	 * 创建按钮.
+	 * @param hbBtn
+	 * @param primaryStage
+	 */
+	private void createBtn(final HBox hbBtn, final Stage primaryStage) {
+		Button btnLogin = new Button("登陆");
+		Button btncancel = new Button("取消");
+		hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
+		hbBtn.getChildren().add(btnLogin);
+		hbBtn.getChildren().add(btncancel);
+		btnLogin.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				doLoginFunc(primaryStage);
+			}
+		});
+		btncancel.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				primaryStage.close();
+			}
+		});
+	}
+	
+	public static void main(String[] args) {
+		launch(args);
+	}
+	
+	private void startConnectServer() {
+		try {
+			PROPS.load(new FileInputStream(FileConst.CONF_SERVER_PATH));
+		} catch (IOException e1) {
+			e1.printStackTrace();
+		}
+		final String IP_PORT = PROPS.getProperty(FileConst.KEY_IP, "127.0.0.1") + ":" + PROPS.getProperty(FileConst.KEY_PORT, "20880");
+		new Thread(new Runnable() {
+
+			@Override
+			public void run() {
+				CLIENT = new ExchangeClient(IP_PORT, new ExchangeHandler(){
+
+					@Override
+					public void connected(com.hmsoft.remote.rmi.transport.NettyChannel channel)
+							throws com.hmsoft.remote.rmi.exception.RemotingException {
+						// TODO Auto-generated method stub
+						
+					}
+
+					@Override
+					public void disconnected(com.hmsoft.remote.rmi.transport.NettyChannel channel)
+							throws com.hmsoft.remote.rmi.exception.RemotingException {
+						// TODO Auto-generated method stub
+						
+					}
+
+					@Override
+					public void sent(com.hmsoft.remote.rmi.transport.NettyChannel channel, Object message)
+							throws com.hmsoft.remote.rmi.exception.RemotingException {
+						// TODO Auto-generated method stub
+						
+					}
+
+					@Override
+					public void received(com.hmsoft.remote.rmi.transport.NettyChannel channel, Object message)
+							throws com.hmsoft.remote.rmi.exception.RemotingException {
+						System.err.println("client: received," + message);
+						if (message instanceof String) {
+							
+							final Message msg =  JSON.parseObject(message.toString(), Message.class);
+							byte type = msg.getType();
+							/*if (type == MsgTypeConst.TYPE_LOGIN_RESP_TO_CLIENT) {
+								
+								final LoginRequest login = JSON.parseObject(msg.getMsgBody(), LoginRequest.class);
+								LOGIN_MAP.put(login.getUserName() + "_" + login.getPassWord(), login);
+								
+							} else */if (type == MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT) {
+								
+								responseToClientFunc(msg);
+								
+							} else if (type == MsgTypeConst.TYPE_HEARTBEAT) {
+							
+							}
+							
+						}
+						
+					}
+
+					@Override
+					public void caught(com.hmsoft.remote.rmi.transport.NettyChannel channel, Throwable exception)
+							throws com.hmsoft.remote.rmi.exception.RemotingException {
+						
+					}
+
+					@Override
+					public Object reply(com.hmsoft.remote.rmi.transport.NettyChannel channel, Object request)
+							throws com.hmsoft.remote.rmi.exception.RemotingException {
+						Object ret = null;
+						if (request instanceof Message) {
+							final Message msg =  (Message)request;
+							byte type = msg.getType();
+							if (type == MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT) {
+								
+								ret = responseToClientFunc(msg);
+								
+							} else if (type == MsgTypeConst.TYPE_HEARTBEAT) {
+							
+							}
+						}
+		
+						return ret;
+					}
+					
+				});
+			}
+			
+		}).start();
+	}
+	
+	/**
+	 * 接受服务端响应.
+	 * @param msg
+	 */
+	private static Object responseToClientFunc(final Message msg) {
+		Object ret = null;
+		final BizRequest req = JSON.parseObject(msg.getMsgBody(), BizRequest.class);
+		if (req.getReqType() == AppConst.SRV_FRESH_INIT_LIST) {//服务端开启命题征集
+			MtzjTab.CURR_SELECTED_MAP.clear();
+			MtzjTab.DATALIST.clear();
+			List<CoursePlan> showList = req.getSourceList();
+			for (CoursePlan course : showList) {
+				course.setZjStatusDesc(BatchDataUtil.getStatusCaption(req.getZjStatus()));
+				course.getChkBox().setSelected(false);
+			}
+			MtzjTab.DATALIST.addAll(showList);
+			MtzjTab.TABLE.refresh();
+			MtzjTab.SELECTED_DATALIST.clear();
+			MtzjTab.ZJ_STATUS = req.getZjStatus();
+			
+			ret = 0;
+		} else if (req.getReqType() == AppConst.SRV_SPECIFIC_COURSE) {//服务端指定了课程的学校
+			
+			List<CoursePlan> removeList = new ArrayList<CoursePlan>();
+			List<CoursePlan> specificList = req.getSourceList();
+			for (CoursePlan spec : specificList) {
+				//不管左边是否有,都把指定的插入到右边已选择列表.如果左边有则删除左边的
+				SelectedCourse selected = MtzjTab.buildSelectedCourse(spec);
+				if (spec.getSchoolId().longValue() == Main.LOGIN_SCHOOL_ID.longValue()) {
+					//指定给当前学校的
+					if (!MtzjTab.SELECTED_DATALIST.contains(selected)) {
+						selected.setIsSpecifyDesc("命题办指定");
+						MtzjTab.addToSelectedTable(selected);
+					} else {
+						//之前已经自己选择了该课程
+						for (SelectedCourse o : MtzjTab.SELECTED_DATALIST) {
+							if (selected.getCoursePlanId().intValue() == o.getCoursePlanId().intValue()) {
+								o.setIsSpecifyDesc("命题办指定");
+								break;
+							}
+						}
+					}
+				} else {
+					//当前课程是指定给其他学校,如果自己也选择了则选择列表删除
+					if (MtzjTab.SELECTED_DATALIST.contains(selected)) {
+						MtzjTab.SELECTED_DATALIST.remove(selected);
+					}
+				}
+				//从左边列表查询有则删除 指定的记录
+				for (CoursePlan obj : MtzjTab.DATALIST) {
+					//没指定的schoolId默认为0,指定了的>0
+					if (spec.getCoursePlanId().intValue() == obj.getCoursePlanId().intValue()) {
+						removeList.add(obj);
+						
+						/*if (spec.getSchoolId().longValue() > 0) {
+							DELETED_MAP.put(obj.getCoursePlanId(), obj);
+							if (spec.getSchoolId().longValue() == Main.LOGIN_SCHOOL_ID.longValue()) {
+								removeList.add(obj);
+								SelectedCourse selected = MtzjTab.buildSelectedCourse(obj);
+								selected.setIsSpecifyDesc("命题办指定");
+								MtzjTab.addToSelectedTable(selected);
+							} else {
+								removeList.add(obj);
+							}
+						}*/
+					}
+				}
+			}
+			MtzjTab.SELECTED_TABLE.refresh();
+			MtzjTab.DATALIST.removeAll(removeList);
+			ret = 0;
+		} else if (req.getReqType() == AppConst.SRV_NOTIFY_SELECT) {//服务端通知其他客户,课程被选择了,客户端锁定
+			
+			if (req.getSchoolId().longValue() == Main.LOGIN_SCHOOL_ID.longValue()) {
+				//本学校选择的,列表不变
+			} else {
+				//其他学校选择了的课程,disable.本学校不能选择.
+				String ids = req.getSelectIds();
+				if (ids != null && ids.length() > 0) {
+					String[] idArr = ids.split(",");
+					final List<CoursePlan> toRemoveList = new ArrayList<CoursePlan>();
+					for (String id : idArr) {
+						Integer idx = Integer.parseInt(id);
+						for (CoursePlan course : MtzjTab.DATALIST) {
+							if (course.getCoursePlanId() == idx) {
+								toRemoveList.add(course);
+							}
+						}
+					}
+					MtzjTab.DATALIST.removeAll(toRemoveList);//直接从列表删除
+					MtzjTab.TABLE.refresh();
+				}
+			}
+			ret = 0;
+			
+		} else if (req.getReqType() == AppConst.SRV_NOTIFY_SELECT) {//服务端通知其他客户,有课程被取消了
+			
+			if (req.getSchoolId() == Main.LOGIN_SCHOOL_ID) {
+				//本学校取消的,列表不变
+			} else {
+				//其他学校
+				MtzjTab.DATALIST.addAll(req.getSourceList());
+			}
+			ret = 0;
+			
+		} else if (req.getReqType() == AppConst.SRV_FINAL_PUBLISH) { //最终发布通知
+			
+			MtzjTab.ZJ_STATUS = BatchConst.STATUS_PUBLISHED;
+			for (CoursePlan course : MtzjTab.DATALIST) {
+				course.setZjStatusDesc("最终发布");
+				course.getChkBox().setDisable(true);
+			}
+			MtzjTab.TABLE.refresh();
+			//保存到最终发布文件
+			CoursePlanDataUtil.writeCoursePlan(MtzjTab.SELECTED_DATALIST, req.getBatchNo());
+			//更新最终发布界面下拉框
+			HisPubTab.BATCH_COMBO_DATA_LIST.add(req.getBatchNo());
+			ret = 0;
+			
+		} else if (req.getReqType() == AppConst.SRV_PUSH_NEW_LIST) {//接收最新的,客户端断开重启后连接接收新征集,登录成功后接收新征集
+			
+			/*if (req.getSchoolId().longValue() == Main.LOGIN_SCHOOL_ID.longValue()) {//是当前学校才更新对应的拉取.
+				MtzjTab.DATALIST.clear();
+				MtzjTab.CURR_SELECTED_MAP.clear();
+				for (CoursePlan course : req.getSourceList()) {
+					//其他学校选择了的,不显示到列表
+					if ((course.getSchoolId() > 0 && course.getSchoolId().longValue() != Main.LOGIN_SCHOOL_ID.longValue())
+							|| ((course.getSchSelectIds() != null && course.getSchSelectIds().length() > 0) 
+									&& !course.getSchSelectIds().contains(Main.LOGIN_SCHOOL_ID.toString()))) {
+						continue;
+					} else {
+						course.setZjStatusDesc(BatchDataUtil.getStatusCaption(req.getZjStatus()));
+						//之前是命题办指定的并且指定给当前学校的,显示到已选择,且标识
+						if (course.getSchoolId() > 0 && course.getSchoolId().longValue() == Main.LOGIN_SCHOOL_ID.longValue()) {
+							SelectedCourse selected = MtzjTab.buildSelectedCourse(course);
+							selected.setIsSpecifyDesc("命题办指定");
+							selected.setConfirmDesc(course.getConfirmStatus() == 1 ? "已确认" : "否");
+							MtzjTab.SELECTED_DATALIST.add(selected);
+							MtzjTab.CURR_SELECTED_MAP.put(course.getCoursePlanId(), course);//保存到当前已选择MAP
+						} else if (course.getSchSelectIds() != null && course.getSchSelectIds().contains(Main.LOGIN_SCHOOL_ID.toString())) {
+							//之前是自己选择的,加入到已选择列表
+							SelectedCourse selected = MtzjTab.buildSelectedCourse(course);
+							selected.setIsSpecifyDesc("否");
+							selected.setConfirmDesc(course.getConfirmStatus() == 1 ? "已确认" : "否");
+							MtzjTab.SELECTED_DATALIST.add(selected);
+							MtzjTab.CURR_SELECTED_MAP.put(course.getCoursePlanId(), course);//保存到当前已选择MAP
+						} else {
+							//System.err.println(course.toString());
+							MtzjTab.DATALIST.add(course);//没有被任何学校选择的,包括本校也没选择
+						}
+					}
+				}
+				MtzjTab.TABLE.refresh();
+			}*/
+			
+		} else if (req.getReqType() == AppConst.SRV_TRIGGER_FRESH) {//定时或手动刷新推送
+			
+			final List<CoursePlan> removeList = new ArrayList<CoursePlan>();
+			for (CoursePlan pushCourse : req.getSourceList()) {
+				for (CoursePlan localCourse : MtzjTab.DATALIST) {
+					if (pushCourse.getCoursePlanId() == localCourse.getCoursePlanId()) {
+						if (!pushCourse.getSchSelectIds().contains(Main.LOGIN_SCHOOL_ID.toString())) {
+							removeList.add(localCourse);
+						}
+						break;
+					}
+				}
+			}
+			MtzjTab.DATALIST.removeAll(removeList);
+			ret = 0;
+			
+		} else if (req.getReqType() == AppConst.SRV_DELETE_COURSE) {//管理端删除了课程
+			
+			MtzjTab.DATALIST.removeAll(req.getSourceList());
+			ret = 0;
+			
+		} else if (req.getReqType() == AppConst.SRV_NOTIFY_CANCEL) {//其他客户端取消了
+			
+			for (CoursePlan cancelCP : req.getSourceList()) {
+				SelectedCourse sc = MtzjTab.buildSelectedCourse(cancelCP);
+				if (!MtzjTab.DATALIST.contains(cancelCP) && !MtzjTab.SELECTED_DATALIST.contains(sc)) {
+					MtzjTab.DATALIST.add(cancelCP);
+				}
+			}
+			ret = 0;
+		}
+		
+		return ret;
+	}
+	
+	private void doLoginFunc(Stage primaryStage) {
+		String userName = userTextField.getText();
+		String passWord = passwdField.getText();
+		if (userName.trim().length() == 0 || passWord.trim().length() == 0) {
+			DialogUtil.showAlertDialog(primaryStage, AlertType.WARNING, "提示", "用户名或者密码不能为空!");
+	        return;
+		}
+		//请求服务端验证用户名密码.
+		final LoginRequest login = new LoginRequest(userName, passWord);
+		final String json = JSON.toJSONString(login);
+		final Message message = new Message(MsgTypeConst.TYPE_LOGIN_REQ_TO_MGE, json);
+		try {
+			if (CLIENT == null) {
+				System.err.println("111");
+			}
+			Object obj = CLIENT.requestTwoWay(message);
+			if (obj instanceof Message) {
+				Message resMsg = (Message)obj;
+				final LoginResponse res = JSON.parseObject(resMsg.getMsgBody(), LoginResponse.class);
+				//收到登录返回
+				if (res.getStatus() == 0) {//登录成功
+					//登录成功切换场景
+					scenetitle.setText("成功!");
+					scenetitle.setFill(Color.RED);
+					final Main main = new Main();
+					main.start(new Stage());
+					Main.LOGIN_SCHOOL_ID = res.getSchoolId();
+					Main.LOGIN_SCHOOL_NAME = res.getSchoolName();
+					Main.MAIN_STAGE.setTitle("命题征集客户端 1.0" + " - " + Main.LOGIN_SCHOOL_NAME);
+					primaryStage.hide();
+					try {
+						//登录成功获取最新征集列表
+						MtzjTab.fetchNewList();
+					} catch (RemotingException e) {
+						e.printStackTrace();
+					}
+				} else {
+					//登录失败.
+					scenetitle.setText("失败,用户名或密码错误!");
+					scenetitle.setFill(Color.RED);
+				}
+			}
+		} catch (RemotingException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 103 - 0
src/com/hmsoft/app/Main.java

@@ -0,0 +1,103 @@
+package com.hmsoft.app;
+
+import java.util.Optional;
+
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.bizpanel.HisPubTab;
+import com.hmsoft.bizpanel.MtzjTab;
+import com.hmsoft.bizpanel.SysTab;
+import com.hmsoft.common.AppConst;
+import com.hmsoft.common.MsgTypeConst;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.remote.rmi.exception.RemotingException;
+import com.hmsoft.remote.rmi.model.Message;
+
+import javafx.application.Application;
+import javafx.event.EventHandler;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Scene;
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Screen;
+import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
+
+/**
+ * 征集客户端.
+ * @author zq
+ *
+ */
+public class Main extends Application {
+	
+	//public static AbstractClient CLIENT = null;
+	public static Long LOGIN_SCHOOL_ID = null;//当前登录的学校ID,从登录获取
+	public static String LOGIN_SCHOOL_NAME = null;
+	public static Stage MAIN_STAGE = null;
+	
+	@Override
+	public void start(final Stage primaryStage) {
+		MAIN_STAGE = primaryStage;
+		try {
+			primaryStage.setTitle("命题征集客户端 1.0");
+			BorderPane root = new BorderPane();
+			Scene scene = new Scene(root);
+			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
+			
+			Rectangle2D screen = Screen.getPrimary().getVisualBounds();
+			primaryStage.setX(screen.getMinX());
+			primaryStage.setY(screen.getMinY());
+			primaryStage.setWidth(screen.getWidth());
+			primaryStage.setHeight(screen.getHeight());
+			
+			TabPane tabPane = new TabPane();
+			
+			Tab sysTab = new SysTab(primaryStage);
+			tabPane.getTabs().add(sysTab);
+			tabPane.setMinWidth(150);
+			
+			Tab mtzjTab = new MtzjTab(primaryStage);
+			tabPane.getTabs().add(mtzjTab);
+			tabPane.setMinWidth(150);
+			
+			Tab hisTab = new HisPubTab(primaryStage);
+			tabPane.getTabs().add(hisTab);
+			tabPane.setMinWidth(150);
+			root.setCenter(tabPane);
+			
+			primaryStage.setResizable(false);
+			//primaryStage.setAlwaysOnTop(true);//始终最上面
+			primaryStage.setScene(scene);
+			primaryStage.show();
+			primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
+				@Override
+				public void handle(WindowEvent event) {
+					closeHandleFunc(primaryStage, event);
+				}
+			});
+		} catch(Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	protected void closeHandleFunc(Stage primaryStage, WindowEvent event) {
+		Alert confirm = new Alert(Alert.AlertType.CONFIRMATION,
+				"", 
+				new ButtonType("否", ButtonData.NO),
+                new ButtonType("是", ButtonData.YES));
+		confirm.setTitle("请确认");
+		confirm.setHeaderText("是否关闭?");
+		confirm.initOwner(primaryStage);
+		Optional<ButtonType> btnType = confirm.showAndWait();
+		if (btnType.get().getButtonData().equals(ButtonData.YES)) {
+			//close
+		} else {
+			event.consume();
+		}
+		
+	}
+
+}

+ 10 - 0
src/com/hmsoft/app/Test.java

@@ -0,0 +1,10 @@
+package com.hmsoft.app;
+
+public class Test {
+
+	public static void main(String[] args) {
+		StringBuilder sql = new StringBuilder();
+		sql.append(1).append(",").append(2).append(",").append(3).append(",");
+		System.err.println(sql.substring(0, sql.length() - 1));
+	}
+}

+ 1 - 0
src/com/hmsoft/app/application.css

@@ -0,0 +1 @@
+/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */

+ 128 - 0
src/com/hmsoft/bizpanel/HisPubTab.java

@@ -0,0 +1,128 @@
+package com.hmsoft.bizpanel;
+
+import com.hmsoft.common.util.CoursePlanDataUtil;
+import com.hmsoft.model.CoursePlan;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class HisPubTab extends Tab {
+	
+	public static final TableView<CoursePlan> HIS_TABLE = new TableView<CoursePlan>();
+	public static final ObservableList<CoursePlan> HIS_DATALIST = FXCollections.observableArrayList();
+	//历史发布批次下拉框
+	public static final ComboBox<String> BATCH_COMBO_BOX = new ComboBox<String>();
+	public static final ObservableList<String> BATCH_COMBO_DATA_LIST = FXCollections.observableArrayList();
+	
+	public HisPubTab(final Stage stage) {
+		super();
+		this.setText("最终发布");
+		this.setId("finalCollectId");
+		this.setClosable(false);
+		//Grid
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		//
+		this.createComboBox(gp);
+		//创建按钮
+		this.createBtn(gp, stage);
+		//创建表格
+		this.createHisTable(gp, stage);
+		//grid内容
+		this.setContent(gp);
+		
+		
+	}
+
+	//批次下拉框
+	private void createComboBox(final GridPane gp) {
+		//批次号
+		BATCH_COMBO_BOX.setPromptText("批次号");
+		BATCH_COMBO_BOX.setEditable(false);
+		BATCH_COMBO_DATA_LIST.addAll(CoursePlanDataUtil.getPublishedBatchNoList());
+		BATCH_COMBO_BOX.setItems(BATCH_COMBO_DATA_LIST);
+		BATCH_COMBO_BOX.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				String selectedNo = BATCH_COMBO_BOX.getSelectionModel().getSelectedItem();
+				//加载该批次之前已经导入的课程计划
+				HIS_DATALIST.clear();
+				HIS_DATALIST.addAll(CoursePlanDataUtil.loadCoursePlanData(selectedNo));
+			}
+		});
+		//批次下拉框
+		gp.add(BATCH_COMBO_BOX, 0, 0);
+	}
+	
+	private void createBtn(GridPane gp, Stage stage) {
+		
+	}
+	
+	private void createHisTable(GridPane gp, Stage stage) {
+		//表格宽度
+		HIS_TABLE.setMinWidth(stage.getWidth() - 70);
+		HIS_TABLE.setMinHeight(stage.getHeight() - 170);
+		HIS_TABLE.setEditable(true);
+		//表格列
+		final TableColumn<CoursePlan, Integer> coursePlanId = new TableColumn<CoursePlan, Integer>("ID");
+		coursePlanId.setCellValueFactory(new PropertyValueFactory<CoursePlan, Integer>("coursePlanId"));
+		coursePlanId.setMinWidth(10);
+		final TableColumn<CoursePlan, String> courseCode = new TableColumn<CoursePlan, String>("课程代码");
+		courseCode.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("courseCode"));
+		courseCode.setMinWidth(30);
+		final TableColumn<CoursePlan, String> courseName = new TableColumn<CoursePlan, String>("课程名称");
+		courseName.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("courseName"));
+		courseName.setMinWidth(50);
+		final TableColumn<CoursePlan, String> outlineName = new TableColumn<CoursePlan, String>("课程大纲");
+		outlineName.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("outlineName"));
+		outlineName.setMinWidth(60);
+		final TableColumn<CoursePlan, String> bookName = new TableColumn<CoursePlan, String>("教材名称");
+		bookName.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("bookName"));
+		bookName.setMinWidth(60);
+		final TableColumn<CoursePlan, String> bianZhu = new TableColumn<CoursePlan, String>("编著");
+		bianZhu.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("bianZhu"));
+		bianZhu.setMinWidth(30);
+		final TableColumn<CoursePlan, String> press = new TableColumn<CoursePlan, String>("出版社");
+		press.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("press"));
+		press.setMinWidth(50);
+		final TableColumn<CoursePlan, String> publications = new TableColumn<CoursePlan, String>("版次");
+		publications.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("publications"));
+		publications.setMinWidth(30);
+		final TableColumn<CoursePlan, String> cengci = new TableColumn<CoursePlan, String>("层次");
+		cengci.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("cengci"));
+		cengci.setMinWidth(30);
+//		final TableColumn<CoursePlan, String> zjStatus = new TableColumn<CoursePlan, String>("征集状态");
+//		zjStatus.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("zjStatusDesc"));
+//		zjStatus.setMinWidth(30);
+		//学校选择的课程IDs
+
+		HIS_TABLE.setItems(HIS_DATALIST);
+		HIS_TABLE.getColumns().addAll(coursePlanId, courseCode, courseName, outlineName, bookName, 
+				bianZhu, press, publications, cengci);
+		
+		final VBox vbox = new VBox();
+        vbox.setSpacing(5);
+        vbox.setPadding(new Insets(10, 0, 0, 10));
+        vbox.getChildren().addAll(HIS_TABLE);
+        
+        gp.add(vbox, 0, 1, 3, 1);
+	}
+
+}

+ 473 - 0
src/com/hmsoft/bizpanel/MtzjTab.java

@@ -0,0 +1,473 @@
+package com.hmsoft.bizpanel;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.app.Login;
+import com.hmsoft.app.Main;
+import com.hmsoft.common.AppConst;
+import com.hmsoft.common.BatchConst;
+import com.hmsoft.common.MsgTypeConst;
+import com.hmsoft.common.util.BatchDataUtil;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.SelectedCourse;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.remote.rmi.exception.RemotingException;
+import com.hmsoft.remote.rmi.model.Message;
+import org.apache.commons.beanutils.BeanUtils;
+import javafx.beans.InvalidationListener;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableColumn.CellDataFeatures;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import javafx.util.Callback;
+
+/**
+ * 命题征集
+ * @author zq
+ *
+ */
+public class MtzjTab extends Tab {
+
+	public static final TableView<CoursePlan> TABLE = new TableView<CoursePlan>();//征集中表格
+	public static final ObservableList<CoursePlan> DATALIST = FXCollections.observableArrayList();
+	public static final TableView<SelectedCourse> SELECTED_TABLE = new TableView<SelectedCourse>();//已选择的表格
+	public static final ObservableList<SelectedCourse> SELECTED_DATALIST = FXCollections.observableArrayList();
+	public static final Map<Integer, CoursePlan> CURR_SELECTED_MAP = new HashMap<Integer, CoursePlan>();//当前学校选择的课程,取消时回填
+	public static byte ZJ_STATUS = BatchConst.STATUS_COLLECTING;//默认征集中
+	//public static final Map<Integer, CoursePlan> OTHER_SELECTED_MAP = new HashMap<Integer, CoursePlan>();//其他学校已经选择的
+	
+	public MtzjTab(final Stage stage) {
+		super();
+		this.setText("命题征集");
+		this.setId("collectTabId");
+		this.setClosable(false);
+		//Grid
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		//创建按钮
+		this.createBtn(gp, stage);
+		//创建表格
+		double totalW = stage.getWidth() - 70;
+		double totalH = stage.getHeight() - 170;
+		double leftW = Math.round(totalW * 0.55);
+		double rigthW = Math.round(totalW * 0.45);
+		this.createMtzjTable(gp, stage, leftW, totalH);
+		this.createSelectedTable(gp, stage, rigthW, totalH);
+		//grid内容
+		this.setContent(gp);
+	}
+	
+	private void createBtn(final GridPane gp, final Stage stage) {
+		
+		//提交选择
+		Button btnSubmit = new Button();
+		btnSubmit.setText("提交选择");
+		//取消选择
+		Button btnFeedBack = new Button();
+		btnFeedBack.setText("确认反馈");
+		
+		//支持选择和取消,如果checkbox 为空则是取消
+		btnSubmit.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent arg0) {
+				if (ZJ_STATUS != BatchConst.STATUS_COLLECTING) {
+					return;
+				}
+				try {
+					StringBuilder ids = new StringBuilder();
+					final List<CoursePlan> selectList = new ArrayList<CoursePlan>();
+					ObservableList<CoursePlan> items = DATALIST;
+					for (int m=0; m<items.size(); m++) {
+						final CoursePlan course = items.get(m);
+						if (course.getChkBox().isSelected() && course.getChkBox().isDisabled() == false) {
+							if (!checkIsAlreadySelected(course)) {
+								ids.append(course.getCoursePlanId());
+								ids.append(",");
+								selectList.add(course);
+								CURR_SELECTED_MAP.put(course.getCoursePlanId(), course);
+							}
+						}
+					}
+					if (ids.length() > 0) {
+						final BizRequest req = new BizRequest();
+						req.setReqType(AppConst.SCH_SELECT_COURSE);//选择
+						req.setSelectIds(ids.substring(0, ids.length() - 1));
+						req.setSchoolId(Main.LOGIN_SCHOOL_ID);
+						req.setSchoolName(Main.LOGIN_SCHOOL_NAME);
+						final String json = JSON.toJSONString(req);
+						final Message msg = new Message(MsgTypeConst.TYPE_BIZ_REQ_TO_MGE, json);
+						//IFuture future = 
+						try {
+							Object obj = Login.CLIENT.requestTwoWay(msg);
+							if (obj != null) {
+								//左边删除选择的
+								deleteFromMainTable(selectList);
+								//加入到右边已选择列表
+								addToSelectedTable(selectList);
+								//输出
+								DialogUtil.showAlertDialog(stage, AlertType.INFORMATION, "成功", "提交成功!");
+							}
+						} catch(Exception e) {
+							//TODO ...重试
+						}
+
+					} else {
+						DialogUtil.showAlertDialog(stage, AlertType.WARNING, "提示", "请选择课程!");
+					}
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		});
+		
+		//确认反馈
+		btnFeedBack.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent arg0) {
+				if (ZJ_STATUS != BatchConst.STATUS_PUBLISHED) {
+					DialogUtil.showAlertDialog(stage, AlertType.WARNING, "提示", "命题办发布后再确认!");
+					return;
+				}
+				if (DialogUtil.confirm("是否提交反馈?", stage)) {
+					try {
+						StringBuilder idsSb = new StringBuilder();
+						for (SelectedCourse stc : SELECTED_DATALIST) {
+							idsSb.append(stc.getCoursePlanId()).append(",");
+						}
+						//send
+						final BizRequest req = new BizRequest();
+						req.setReqType(AppConst.SCH_CONFIRM_OK);//确认
+						req.setSchoolId(Main.LOGIN_SCHOOL_ID);
+						req.setSchoolName(Main.LOGIN_SCHOOL_NAME);
+						req.setSelectIds(idsSb.length() > 0 ? idsSb.substring(0, idsSb.length() - 1) : "");
+						final String json = JSON.toJSONString(req);
+						final Message msg = new Message(MsgTypeConst.TYPE_BIZ_REQ_TO_MGE, json);
+						//IFuture future = 
+						Object obj = Login.CLIENT.requestTwoWay(msg);
+						if (obj != null) {
+							for (SelectedCourse stc : SELECTED_DATALIST) {
+								stc.setConfirmDesc("已确认");
+							}
+							SELECTED_TABLE.refresh();
+							DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "反馈成功!");
+						}
+					} catch (RemotingException e) {
+						e.printStackTrace();
+						//TODO ....
+					}
+				}
+			}
+		});
+		
+		//加入容器
+		gp.add(btnSubmit, 0, 0);
+		gp.add(btnFeedBack, 1, 0);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void createMtzjTable(final GridPane gp, final Stage stage, final double width, final double height) {
+		//表格宽度
+		TABLE.setMinWidth(width);
+		TABLE.setMinHeight(height);
+		TABLE.setEditable(true);
+		//表格列
+		final TableColumn<CoursePlan, CheckBox> checkBoxCol = new TableColumn<CoursePlan, CheckBox>("选择");
+		checkBoxCol.setCellValueFactory(new PropertyValueFactory<CoursePlan, CheckBox>("chkBox"));
+		checkBoxCol.setMinWidth(10);
+		
+		final TableColumn<CoursePlan, Integer> coursePlanId = new TableColumn<CoursePlan, Integer>("ID");
+		coursePlanId.setCellValueFactory(new PropertyValueFactory<CoursePlan, Integer>("coursePlanId"));
+		coursePlanId.setMinWidth(10);
+		final TableColumn<CoursePlan, String> courseCode = new TableColumn<CoursePlan, String>("课程代码");
+		courseCode.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("courseCode"));
+		courseCode.setMinWidth(30);
+		final TableColumn<CoursePlan, String> courseName = new TableColumn<CoursePlan, String>("课程名称");
+		courseName.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("courseName"));
+		courseName.setMinWidth(50);
+		final TableColumn<CoursePlan, String> outlineName = new TableColumn<CoursePlan, String>("课程大纲");
+		outlineName.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("outlineName"));
+		outlineName.setMinWidth(60);
+		final TableColumn<CoursePlan, String> bookName = new TableColumn<CoursePlan, String>("教材名称");
+		bookName.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("bookName"));
+		bookName.setMinWidth(60);
+		final TableColumn<CoursePlan, String> bianZhu = new TableColumn<CoursePlan, String>("编著");
+		bianZhu.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("bianZhu"));
+		bianZhu.setMinWidth(30);
+		final TableColumn<CoursePlan, String> press = new TableColumn<CoursePlan, String>("出版社");
+		press.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("press"));
+		press.setMinWidth(50);
+		final TableColumn<CoursePlan, String> publications = new TableColumn<CoursePlan, String>("版次");
+		publications.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("publications"));
+		publications.setMinWidth(30);
+		final TableColumn<CoursePlan, String> cengci = new TableColumn<CoursePlan, String>("层次");
+		cengci.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("cengci"));
+		cengci.setMinWidth(30);
+		final TableColumn<CoursePlan, String> zjStatus = new TableColumn<CoursePlan, String>("征集状态");
+		zjStatus.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("zjStatusDesc"));
+		zjStatus.setMinWidth(30);
+		//学校选择的课程IDs
+		
+		TABLE.setItems(DATALIST);
+		TABLE.getColumns().addAll(checkBoxCol, coursePlanId, courseCode, courseName, outlineName, bookName, 
+				bianZhu, press, publications, cengci, zjStatus);
+		
+		final VBox vbox = new VBox();
+        vbox.setSpacing(5);
+        vbox.setPadding(new Insets(10, 0, 0, 10));
+        vbox.getChildren().addAll(TABLE);
+        
+        gp.add(vbox, 0, 1, 1, 1);
+	}
+	
+	//已选择的课程列表
+	@SuppressWarnings("unchecked")
+	private void createSelectedTable(final GridPane gp, final Stage stage, final double width, final double height) {
+		//表格宽度
+		SELECTED_TABLE.setMinWidth(width);
+		SELECTED_TABLE.setMinHeight(height);
+		SELECTED_TABLE.setEditable(true);
+		final TableColumn<SelectedCourse, Integer> coursePlanId = new TableColumn<SelectedCourse, Integer>("ID");
+		coursePlanId.setCellValueFactory(new PropertyValueFactory<SelectedCourse, Integer>("coursePlanId"));
+		coursePlanId.setMinWidth(10);
+		final TableColumn<SelectedCourse, String> courseCode = new TableColumn<SelectedCourse, String>("课程代码");
+		courseCode.setCellValueFactory(new PropertyValueFactory<SelectedCourse, String>("courseCode"));
+		courseCode.setMinWidth(60);
+		final TableColumn<SelectedCourse, String> courseName = new TableColumn<SelectedCourse, String>("课程名称");
+		courseName.setCellValueFactory(new PropertyValueFactory<SelectedCourse, String>("courseName"));
+		courseName.setMinWidth(150);
+		final TableColumn<SelectedCourse, String> isSpecify = new TableColumn<SelectedCourse, String>("是否命题办指定");
+		isSpecify.setCellValueFactory(new PropertyValueFactory<SelectedCourse, String>("isSpecifyDesc"));
+		isSpecify.setMinWidth(120);
+		final TableColumn<SelectedCourse, String> confirmDesc = new TableColumn<SelectedCourse, String>("是否已确认");
+		confirmDesc.setCellValueFactory(new PropertyValueFactory<SelectedCourse, String>("confirmDesc"));
+		confirmDesc.setMinWidth(120);
+		final TableColumn<SelectedCourse, Button> cancelCol = new TableColumn<SelectedCourse, Button>("操作");
+		cancelCol.setCellValueFactory(new PropertyValueFactory<SelectedCourse, Button>("btnCancel"));
+		cancelCol.setMinWidth(100);
+
+		cancelCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<SelectedCourse, Button>, ObservableValue<Button>>() {
+			
+			@Override
+			public ObservableValue<Button> call(CellDataFeatures<SelectedCourse, Button> param) {
+				return new ObservableValue<Button>(){
+
+					@Override
+					public void addListener(InvalidationListener listener) {
+					}
+
+					@Override
+					public void removeListener(InvalidationListener listener) {
+					}
+
+					@Override
+					public void addListener(ChangeListener<? super Button> listener) {
+					}
+
+					@Override
+					public void removeListener(ChangeListener<? super Button> listener) {
+					}
+
+					@Override
+					public Button getValue() {
+						Button btn = new Button("取消选择");
+						btn.setOnAction(new EventHandler<ActionEvent>() {
+							
+							@Override
+							public void handle(ActionEvent event) {
+								cancelSelect(param.getValue(), stage);
+							}
+						});
+						if (param.getValue().getIsSpecifyDesc() != null 
+								&& param.getValue().getIsSpecifyDesc().equals("命题办指定")) {
+							btn.setDisable(true);//命题办指定的不能取消
+						}
+						return btn;
+					}
+					
+				};
+			}
+		});
+		
+		SELECTED_TABLE.setItems(SELECTED_DATALIST);
+		SELECTED_TABLE.getColumns().addAll(coursePlanId, courseCode, courseName, isSpecify, confirmDesc, cancelCol);
+		
+		final VBox vbox = new VBox();
+        vbox.setSpacing(5);
+        vbox.setPadding(new Insets(10, 0, 0, 10));
+        vbox.getChildren().addAll(SELECTED_TABLE);
+        
+        gp.add(vbox, 1, 1, 1, 1);
+	}
+	
+	//左边待选择列表删除
+	private void deleteFromMainTable(final List<CoursePlan> selectList) {
+		DATALIST.removeAll(selectList);
+	}
+	//右边已选择列表添加
+	private void addToSelectedTable(final List<CoursePlan> selectList) {
+		SELECTED_DATALIST.addAll(buildSelectedList(selectList));
+	}
+	
+	//其他类调用
+	public static void addToSelectedTable(final SelectedCourse selectedCourse) {
+		SELECTED_DATALIST.addAll(selectedCourse);
+	}
+	
+	private List<SelectedCourse> buildSelectedList(final List<CoursePlan> selectList) {
+		List<SelectedCourse> list = new ArrayList<SelectedCourse>();
+		for (CoursePlan course : selectList) {
+			SelectedCourse selected = new SelectedCourse();
+			try {
+				BeanUtils.copyProperties(selected, course);
+				selected.setIsSpecifyDesc("否");
+				selected.setConfirmDesc(course.getConfirmStatus() == 1 ? "已确认" : "否");
+				list.add(selected);
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			} catch (InvocationTargetException e) {
+				e.printStackTrace();
+			}
+		}
+		return list;
+	}
+	
+	//其他类调用
+	public static SelectedCourse buildSelectedCourse(final CoursePlan coursePlan) {
+		SelectedCourse selected = new SelectedCourse();
+		try {
+			BeanUtils.copyProperties(selected, coursePlan);
+			selected.setConfirmDesc(coursePlan.getConfirmStatus() == 1 ? "已确认" : "否");
+		} catch (IllegalAccessException e) {
+			e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			e.printStackTrace();
+		}
+		return selected;
+	}
+	
+	//检查左边表勾选对象是否右边选择区已经存在
+	private boolean checkIsAlreadySelected(final CoursePlan coursePlan) {
+		for (SelectedCourse stc : SELECTED_DATALIST) {
+			if (stc.getCoursePlanId().intValue() == coursePlan.getCoursePlanId().intValue()) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	private void deleteFromSelectedTable(final SelectedCourse selectedCourse) {
+		SELECTED_DATALIST.remove(selectedCourse);
+	}
+	
+	//取消后加到左边待选择列表
+	private void addCancelToLeftTable(final SelectedCourse selectedCourse) {
+		CoursePlan course = CURR_SELECTED_MAP.remove(selectedCourse.getCoursePlanId());
+		course.getChkBox().setSelected(false);
+		DATALIST.add(course);
+	}
+	
+	//取消选择
+	private void cancelSelect(final SelectedCourse selectedCourse, final Stage stage) {
+		if (ZJ_STATUS == BatchConst.STATUS_PUBLISHED) {
+			DialogUtil.showAlertDialog(stage, AlertType.WARNING, "提示", "已发布,不能取消!");
+			return;
+		}
+		//主表取消选择
+		if (DialogUtil.confirm("是否取消?", stage)) {
+			//发送消息
+			final BizRequest req = new BizRequest();
+			req.setReqType(AppConst.SCH_CANCEL_COURSE);//取消
+			req.setSelectIds(selectedCourse.getCoursePlanId().toString());
+			req.setSchoolId(Main.LOGIN_SCHOOL_ID);
+			req.setSchoolName(Main.LOGIN_SCHOOL_NAME);
+			final String json = JSON.toJSONString(req);
+			final Message msg = new Message(MsgTypeConst.TYPE_BIZ_REQ_TO_MGE, json);
+			try {
+				//IFuture future = 
+				//Login.CLIENT.requestToMgr(msg).get();
+				Object obj = Login.CLIENT.requestTwoWay(msg);
+				if (obj != null) {
+					for (CoursePlan cp : DATALIST) {
+						if (cp.getCoursePlanId().intValue() == selectedCourse.getCoursePlanId().intValue()) {
+							cp.getChkBox().setSelected(false);
+						}
+					}
+					deleteFromSelectedTable(selectedCourse);//已选择列表中删除
+					addCancelToLeftTable(selectedCourse);
+					//输出
+					DialogUtil.showAlertDialog(stage, AlertType.INFORMATION, "成功", "已成功取消!");
+				}
+			} catch (RemotingException e) {
+				//TODO 重试
+				e.printStackTrace();
+			}
+		}
+	}
+	
+	//连接成功就去拉取最新的列表
+	public static void fetchNewList() throws RemotingException {
+		final BizRequest req = new BizRequest();
+		req.setReqType(AppConst.SCH_FETCH_NEW_LIST);//拉取最新
+		req.setSchoolId(Main.LOGIN_SCHOOL_ID);//拉取学校
+		final String json = JSON.toJSONString(req);
+		final Message msg = new Message(MsgTypeConst.TYPE_BIZ_REQ_TO_MGE, json);
+		Object obj = Login.CLIENT.requestTwoWay(msg);
+		if (obj != null) {
+			Message retMsg = (Message)obj;
+			BizRequest retReq = JSON.parseObject(retMsg.getMsgBody(), BizRequest.class);
+			MtzjTab.DATALIST.clear();
+			MtzjTab.CURR_SELECTED_MAP.clear();
+			for (CoursePlan course : retReq.getSourceList()) {
+				//其他学校选择了的,不显示到列表
+				if ((course.getSchoolId() > 0 && course.getSchoolId().longValue() != Main.LOGIN_SCHOOL_ID.longValue())
+						|| ((course.getSchSelectIds() != null && course.getSchSelectIds().length() > 0) 
+								&& !course.getSchSelectIds().contains(Main.LOGIN_SCHOOL_ID.toString()))) {
+					continue;
+				} else {
+					course.setZjStatusDesc(BatchDataUtil.getStatusCaption(req.getZjStatus()));
+					//之前是命题办指定的并且指定给当前学校的,显示到已选择,且标识
+					if (course.getSchoolId() > 0 && course.getSchoolId().longValue() == Main.LOGIN_SCHOOL_ID.longValue()) {
+						SelectedCourse selected = MtzjTab.buildSelectedCourse(course);
+						selected.setIsSpecifyDesc("命题办指定");
+						selected.setConfirmDesc(course.getConfirmStatus() == 1 ? "已确认" : "否");
+						MtzjTab.SELECTED_DATALIST.add(selected);
+						MtzjTab.CURR_SELECTED_MAP.put(course.getCoursePlanId(), course);//保存到当前已选择MAP
+					} else if (course.getSchSelectIds() != null && course.getSchSelectIds().contains(Main.LOGIN_SCHOOL_ID.toString())) {
+						//之前是自己选择的,加入到已选择列表
+						SelectedCourse selected = MtzjTab.buildSelectedCourse(course);
+						selected.setIsSpecifyDesc("否");
+						selected.setConfirmDesc(course.getConfirmStatus() == 1 ? "已确认" : "否");
+						MtzjTab.SELECTED_DATALIST.add(selected);
+						MtzjTab.CURR_SELECTED_MAP.put(course.getCoursePlanId(), course);//保存到当前已选择MAP
+					} else {
+						//System.err.println(course.toString());
+						MtzjTab.DATALIST.add(course);//没有被任何学校选择的,包括本校也没选择
+					}
+				}
+			}
+			MtzjTab.TABLE.refresh();
+		}
+	}
+}

+ 73 - 0
src/com/hmsoft/bizpanel/SysTab.java

@@ -0,0 +1,73 @@
+package com.hmsoft.bizpanel;
+
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.app.Login;
+import com.hmsoft.app.Main;
+import com.hmsoft.common.AppConst;
+import com.hmsoft.common.MsgTypeConst;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.remote.rmi.exception.RemotingException;
+import com.hmsoft.remote.rmi.model.Message;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Button;
+import javafx.scene.control.PasswordField;
+import javafx.scene.control.Tab;
+import javafx.scene.layout.GridPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+public class SysTab extends Tab {
+
+	public SysTab(final Stage stage) {
+		super();
+		this.setText("系统配置");
+		this.setId("systTabId");
+		this.setClosable(false);
+		//Grid容器
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		
+		Text passwdText = new Text("新密码:");
+		final PasswordField passwd = new PasswordField();
+		passwd.setMinWidth(60);
+		final Button btnSave = new Button("修改");
+		btnSave.setOnAction(new EventHandler<ActionEvent>() {
+			
+			@Override
+			public void handle(ActionEvent event) {
+				passwd.setText(passwd.getText().trim());
+				if (passwd.getText().length() == 0) {
+					DialogUtil.showAlertDialog(stage, AlertType.WARNING, "提示", "请输入新密码.");
+					return;
+				} else {
+					final BizRequest req = new BizRequest();
+					req.setReqType(AppConst.SCH_CHANGE_PASSWD);//修改密码请求
+					req.setSchoolId(Main.LOGIN_SCHOOL_ID);
+					req.setSchoolName(passwd.getText());
+					final String json = JSON.toJSONString(req);
+					final Message msg = new Message(MsgTypeConst.TYPE_BIZ_REQ_TO_MGE, json);
+					try {
+						//IFuture future = 
+						Object obj = Login.CLIENT.requestTwoWay(msg);
+						DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "修改成功!");
+					} catch (RemotingException e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		});
+		
+		gp.add(passwdText, 0, 0);
+		gp.add(passwd, 1, 0);
+		gp.add(btnSave, 0, 1);
+		//grid布局加到容器
+		this.setContent(gp);
+	}
+}

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

@@ -0,0 +1,66 @@
+package com.hmsoft.common;
+
+/**
+ * 常量类.
+ * @author Lenovo
+ *
+ */
+public class AppConst {
+
+	/**
+	 * 登录
+	 */
+	public static final int SCH_LOGIN = 0;
+	/**
+	 * 学校选择课程
+	 */
+	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;
+}

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

@@ -0,0 +1,27 @@
+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;
+
+}

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

@@ -0,0 +1,21 @@
+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");
+	//server.properties
+	public static String CONF_SERVER_PATH =  SYS_HOME + File.separator + "Cfg" + File.separator + "server.properties";
+	
+	public static String KEY_IP = "server.ip";
+	public static String KEY_PORT = "server.port";
+	//最终发布后的
+	public static String DATA_BASE = SYS_HOME + File.separator + "Data";
+	public static String COURSEPLAN_FINAL_PATH = DATA_BASE + File.separator + "CoursePlanStore";
+}

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

@@ -0,0 +1,34 @@
+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;
+}

+ 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;
+		}
+	}
+
+}

+ 40 - 0
src/com/hmsoft/common/cell/ActionButtonTableCell.java

@@ -0,0 +1,40 @@
+package com.hmsoft.common.cell;
+
+import java.util.function.Function;
+import javafx.event.ActionEvent;
+import javafx.scene.control.Button;
+import javafx.scene.control.TableCell;
+import javafx.scene.control.TableColumn;
+import javafx.util.Callback;
+
+public class ActionButtonTableCell<S> extends TableCell<S, Button> {
+
+	private final Button actionButton;
+	
+	public ActionButtonTableCell(String label, Function< S, S> function) {
+        //this.getStyleClass().add("action-button-table-cell");
+        this.actionButton = new Button(label);
+        this.actionButton.setOnAction((ActionEvent e) -> {
+            function.apply(getCurrentItem());
+        });
+        this.actionButton.setMaxWidth(100);
+    }
+	
+	public S getCurrentItem() {
+        return (S) getTableView().getItems().get(getIndex());
+    }
+
+	public static <S> Callback<TableColumn<S, Button>, TableCell<S, Button>> forTableColumn(String label, Function<S, S> function) {
+        return param -> new ActionButtonTableCell<>(label, function);
+    }
+
+    @Override
+    public void updateItem(Button item, boolean empty) {
+        super.updateItem(item, empty);
+        if (empty) {
+            setGraphic(null);
+        } else {                
+            setGraphic(actionButton);
+        }
+    }
+}

+ 43 - 0
src/com/hmsoft/common/cell/checkbox.java

@@ -0,0 +1,43 @@
+package com.hmsoft.common.cell;
+
+import javafx.beans.InvalidationListener;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.CheckBox;
+
+public class checkbox {
+	CheckBox checkbox = new CheckBox();
+
+	public ObservableValue<CheckBox> getCheckBox() {
+		return new ObservableValue<CheckBox>() {
+			@Override
+			public void addListener(ChangeListener<? super CheckBox> listener) {
+
+			}
+
+			@Override
+			public void removeListener(ChangeListener<? super CheckBox> listener) {
+
+			}
+
+			@Override
+			public CheckBox getValue() {
+				return checkbox;
+			}
+
+			@Override
+			public void addListener(InvalidationListener listener) {
+
+			}
+
+			@Override
+			public void removeListener(InvalidationListener listener) {
+
+			}
+		};
+	}
+
+	public Boolean isSelected() {
+		return checkbox.isSelected();
+	}
+}

+ 24 - 0
src/com/hmsoft/common/util/BatchDataUtil.java

@@ -0,0 +1,24 @@
+package com.hmsoft.common.util;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class BatchDataUtil {
+
+	public static String getStatusCaption(final byte status) {
+		String desc = "";
+		if (status == 0) {
+			desc = "新建";
+		} else if (status == 1) {
+			desc = "已导入";
+		} else if (status == 2) {
+			desc = "征集中";
+		} else if (status == 3) {
+			desc = "已发布";
+		}
+		
+		return desc;
+	}
+}

+ 94 - 0
src/com/hmsoft/common/util/CoursePlanDataUtil.java

@@ -0,0 +1,94 @@
+package com.hmsoft.common.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.common.FileConst;
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.SelectedCourse;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class CoursePlanDataUtil {
+
+	private CoursePlanDataUtil() {
+		
+	}
+	
+	//
+	public static List<String> getPublishedBatchNoList() {
+		final List<String> batchNoList = new ArrayList<String>();
+		final File file = new File(FileConst.DATA_BASE);
+		if (file.exists()) {
+			File[] fs = file.listFiles();
+			for (File f : fs) {
+				batchNoList.add(f.getName().split("-")[1]);
+			}
+		}
+		return batchNoList;
+	}
+	
+	//从指定文件中加载征集计划.
+	public static ObservableList<CoursePlan> loadCoursePlanData(final String batchNo) {
+		final ObservableList<CoursePlan> dataList = FXCollections.observableArrayList();
+		final File file = new File(FileConst.COURSEPLAN_FINAL_PATH + "-" + batchNo);
+		try {
+			if (file.exists()) {
+				final List<String> list = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+				for (String str : list) {
+					CoursePlan courseplan = JSON.parseObject(str, CoursePlan.class);
+					dataList.add(courseplan);
+				}
+			}
+
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return dataList;
+	}
+	
+	//导入写到文件中.
+	public static void writeCoursePlan(final List<SelectedCourse> coursePlanList, final String batchNo) {
+		PrintWriter writer = null;
+		File destFile = new File(FileConst.COURSEPLAN_FINAL_PATH + "-" + batchNo);
+		if (!destFile.exists()) {
+			try {
+				destFile.createNewFile();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		try {
+			writer = new PrintWriter(destFile, "UTF-8");
+			for (SelectedCourse obj : coursePlanList) {
+				String json = JSON.toJSONString(obj);
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException | UnsupportedEncodingException e) {
+			System.err.println("------------error---" + e.getMessage());			
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+}

+ 40 - 0
src/com/hmsoft/common/util/DialogUtil.java

@@ -0,0 +1,40 @@
+package com.hmsoft.common.util;
+
+import java.util.Optional;
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.stage.Stage;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class DialogUtil {
+
+	public static void showAlertDialog(final Stage stage, final Alert.AlertType alterType, 
+			final String title, final String msgBody) {
+		final Alert alert = new Alert(alterType);
+		alert.setTitle(title);
+		alert.setHeaderText(msgBody);
+		alert.setContentText("");
+		alert.initOwner(stage);
+		alert.show();
+	}
+	
+	public static boolean confirm(final String confirmMsg, final Stage stage) {
+		Alert confirm = new Alert(Alert.AlertType.CONFIRMATION,
+				"", 
+				new ButtonType("否", ButtonData.NO),
+                new ButtonType("是", ButtonData.YES));
+		confirm.setTitle("请确认");
+		confirm.setHeaderText(confirmMsg);
+		confirm.initOwner(stage);
+		Optional<ButtonType> btnType = confirm.showAndWait();
+		if (btnType.get().getButtonData().equals(ButtonData.YES)) {
+			return true;
+		}
+		return false;
+	}
+}

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

@@ -0,0 +1,68 @@
+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;
+	}
+}

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

@@ -0,0 +1,127 @@
+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();
+	}
+}

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

@@ -0,0 +1,246 @@
+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.CheckBox;
+
+/**
+ * 
+ * @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
+	private StringProperty schoolName;//ָ命题办指定的学校名称
+	private StringProperty schSelectIds;//选择该课程的学校IDs
+	private StringProperty schSelectNames;//选择该课程的学校名称
+	private IntegerProperty confirmStatus;//确认状态是在课程上面,同一个批次状态下每个学校都不一样可能,前面最终发布、征集中、已导入都是公用批次的状态
+	private transient StringProperty zjStatusDesc;//征集状态(征集中,最终发布),学校端只显示,不传输
+	private transient CheckBox chkBox = new CheckBox();
+	
+	public CoursePlan() {
+		this.zjStatusDesc = new SimpleStringProperty();
+		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();
+		this.confirmStatus = new SimpleIntegerProperty();
+	}
+	
+	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 String getZjStatusDesc() {
+		return zjStatusDesc.get();
+	}
+
+	public void setZjStatusDesc(String zjStatusDesc) {
+		this.zjStatusDesc.set(zjStatusDesc);;
+	}
+	public CheckBox getChkBox() {
+		return chkBox;
+	}
+	public void setChkBox(CheckBox chkBox) {
+		this.chkBox = chkBox;
+	}
+
+	public Integer getConfirmStatus() {
+		return confirmStatus.get();
+	}
+
+	public void setConfirmStatus(Integer confirmStatus) {
+		this.confirmStatus.set(confirmStatus);
+	}
+
+	
+	public String toString() {
+		return new StringBuilder()
+				.append(coursePlanId.get()).append(" | ")
+				.append(courseCode.get()).append(" | ")
+				.append(courseName.get()).append(" | ")
+				.append(outlineName.get()).append(" | ")
+				.append(bookName.get()).append(" | ")
+				.append(bianZhu.get()).append(" | ")
+				.append(press.get()).append(" | ")
+				.append(publications.get()).append(" | ")
+				.append(cengci.get()).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.cengci.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.cengci.get(), oth.cengci.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));
+	}
+}

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

@@ -0,0 +1,89 @@
+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.LongProperty;
+import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+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(Long schooId, String schoolName) {
+		this.schoolId =  new SimpleLongProperty(schooId);
+		this.schoolName = new SimpleStringProperty(schoolName);
+	}
+	
+	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();
+	}
+}

+ 132 - 0
src/com/hmsoft/model/SelectedCourse.java

@@ -0,0 +1,132 @@
+package com.hmsoft.model;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class SelectedCourse {
+
+	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 transient StringProperty confirmDesc;//学校确认反馈
+	private transient StringProperty isSpecifyDesc;//是否命题办指定的
+	
+	public SelectedCourse() {
+		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.confirmDesc = new SimpleStringProperty();
+		this.isSpecifyDesc = 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 String getConfirmDesc() {
+		return confirmDesc.get();
+	}
+	public void setConfirmDesc(String confirmDesc) {
+		this.confirmDesc.set(confirmDesc);
+	}
+	public String getIsSpecifyDesc() {
+		return isSpecifyDesc.get();
+	}
+
+	public void setIsSpecifyDesc(String isSpecifyDesc) {
+		this.isSpecifyDesc.set(isSpecifyDesc);
+	}
+
+	public int hashCode() {
+	     return new HashCodeBuilder(17, 37).
+	       append(this.coursePlanId.get()).
+	       append(this.courseCode.get()).
+	       append(this.courseName.get()).
+	       toHashCode();
+	}
+	
+	public boolean equals(Object obj) {
+		   if (obj == null) { return false; }
+		   if (obj == this) { return true; }
+		   if (obj.getClass() != getClass()) {
+		     return false;
+		   }
+		   SelectedCourse oth = (SelectedCourse) 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())
+		                 .isEquals();
+	}
+}

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

@@ -0,0 +1,89 @@
+package com.hmsoft.model.request;
+
+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 {
+	private Integer reqType; //1-学校选择课程,2-命题办通知其他学校课程被选择了,3-命题办指定该课程征集学校,4-学校取消选择的课程,5-命题办刷新课程列表数据到各个学校客户端
+	private String batchNo;//批次编号
+	private Long schoolId;
+	private String schoolName;
+	private String selectIds;//选择的课程代码,可以选多个
+	private List<CoursePlan> sourceList;//服务端发送过来的列表(开启命题征集的列表,或者是命题办指定的征集列表)
+	protected byte zjStatus;//0-征集中,1-最终发布
+	
+	public String getBatchNo() {
+		return batchNo;
+	}
+	public void setBatchNo(String batchNo) {
+		this.batchNo = batchNo;
+	}
+	public List<CoursePlan> getSourceList() {
+		return sourceList;
+	}
+	public void setSourceList(List<CoursePlan> sourceList) {
+		this.sourceList = sourceList;
+	}
+	public Integer getReqType() {
+		return reqType;
+	}
+	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 byte getZjStatus() {
+		return zjStatus;
+	}
+	public void setZjStatus(byte zjStatus) {
+		this.zjStatus = zjStatus;
+	}
+	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 = 6611763136706618131L;
+	private String userName;
+	private String passWord;
+	private int status;//登录成功失败状态(0-成功,-1失败)
+	private Long schoolId;
+	private 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;
+	}
+	
+	
+}

+ 11 - 0
src/com/hmsoft/model/response/BizResponse.java

@@ -0,0 +1,11 @@
+package com.hmsoft.model.response;
+
+import java.io.Serializable;
+
+import com.hmsoft.model.request.BizRequest;
+
+public class BizResponse extends BizRequest implements Serializable {
+
+	private static final long serialVersionUID = -7393690520884287076L;
+
+}

+ 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();
+  }
+}

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