haogh há 11 meses atrás
commit
e2cf272307
100 ficheiros alterados com 16551 adições e 0 exclusões
  1. 0 0
      Cfg/jdbc.properties
  2. 13 0
      Cfg/log4j.properties
  3. 2 0
      Cfg/server.properties
  4. 1 0
      Data/BatchStore
  5. 6 0
      Data/CoursePlanStore-1538478705909
  6. 2 0
      Data/SchoolStore
  7. 3 0
      Data/SysCfg.properties
  8. BIN
      lib/commons-beanutils-1.8.3.jar
  9. BIN
      lib/commons-codec-1.10.jar
  10. BIN
      lib/commons-collections4-4.1.jar
  11. BIN
      lib/commons-io-2.5.jar
  12. BIN
      lib/commons-lang-2.6.jar
  13. BIN
      lib/commons-logging-1.2.jar
  14. BIN
      lib/fastjson-1.2.2.jar
  15. BIN
      lib/log4j-1.2.17.jar
  16. BIN
      lib/log4j-api-2.11.0.jar
  17. BIN
      lib/log4j-core-2.11.0.jar
  18. BIN
      lib/netty-all-4.1.28.Final.jar
  19. BIN
      lib/poi-3.17.jar
  20. BIN
      lib/poi-ooxml-3.17.jar
  21. BIN
      lib/poi-ooxml-schemas-3.17.jar
  22. BIN
      lib/poi-scratchpad-3.17.jar
  23. BIN
      lib/spring-core-5.1.3.RELEASE.jar
  24. BIN
      lib/spring-retry-1.2.2.RELEASE.jar
  25. BIN
      lib/xmlbeans-2.6.0.jar
  26. 476 0
      src/com/hmsoft/app/Manage.java
  27. 1 0
      src/com/hmsoft/app/application.css
  28. 274 0
      src/com/hmsoft/bizpanel/BatchTab.java
  29. 730 0
      src/com/hmsoft/bizpanel/MtzjTab.java
  30. 295 0
      src/com/hmsoft/bizpanel/SchoolTab.java
  31. 154 0
      src/com/hmsoft/bizpanel/StatisTab.java
  32. 147 0
      src/com/hmsoft/bizpanel/SystTab.java
  33. 62 0
      src/com/hmsoft/common/AppConst.java
  34. 30 0
      src/com/hmsoft/common/BatchConst.java
  35. 25 0
      src/com/hmsoft/common/FileConst.java
  36. 38 0
      src/com/hmsoft/common/MsgTypeConst.java
  37. 7 0
      src/com/hmsoft/common/TransConst.java
  38. 13 0
      src/com/hmsoft/common/bizinf/IExecuteLogic.java
  39. 37 0
      src/com/hmsoft/common/bizlogic/RequestLogic.java
  40. 30 0
      src/com/hmsoft/common/cell/CheckBoxButtonTableCell.java
  41. 34 0
      src/com/hmsoft/common/cell/CombSelectTableCell.java
  42. 176 0
      src/com/hmsoft/common/util/BatchDataUtil.java
  43. 109 0
      src/com/hmsoft/common/util/CoursePlanDataUtil.java
  44. 26 0
      src/com/hmsoft/common/util/DateUtil.java
  45. 42 0
      src/com/hmsoft/common/util/DialogUtil.java
  46. 68 0
      src/com/hmsoft/common/util/ExecuteRetryUtil.java
  47. 328 0
      src/com/hmsoft/common/util/POIUtil.java
  48. 53 0
      src/com/hmsoft/common/util/RegexpUtils.java
  49. 154 0
      src/com/hmsoft/common/util/ShoolDateUtil.java
  50. 157 0
      src/com/hmsoft/common/util/StringHelper.java
  51. 29 0
      src/com/hmsoft/common/util/SysCfgUtil.java
  52. 127 0
      src/com/hmsoft/model/Batch.java
  53. 34 0
      src/com/hmsoft/model/Course.java
  54. 278 0
      src/com/hmsoft/model/CoursePlan.java
  55. 132 0
      src/com/hmsoft/model/School.java
  56. 90 0
      src/com/hmsoft/model/StatisObj.java
  57. 92 0
      src/com/hmsoft/model/request/BizRequest.java
  58. 66 0
      src/com/hmsoft/model/request/LoginRequest.java
  59. 30 0
      src/com/hmsoft/model/request/LogoutRequest.java
  60. 24 0
      src/com/hmsoft/model/request/RegisterRequest.java
  61. 11 0
      src/com/hmsoft/model/response/BizResponse.java
  62. 15 0
      src/com/hmsoft/model/response/LoginResponse.java
  63. 85 0
      src/com/hmsoft/remote/caucho/hessian/HessianException.java
  64. 118 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractDeserializer.java
  65. 442 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianInput.java
  66. 531 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianOutput.java
  67. 65 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractHessianResolver.java
  68. 67 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractListDeserializer.java
  69. 74 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractMapDeserializer.java
  70. 63 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializer.java
  71. 74 0
      src/com/hmsoft/remote/caucho/hessian/io/AbstractSerializerFactory.java
  72. 164 0
      src/com/hmsoft/remote/caucho/hessian/io/ArrayDeserializer.java
  73. 94 0
      src/com/hmsoft/remote/caucho/hessian/io/ArraySerializer.java
  74. 608 0
      src/com/hmsoft/remote/caucho/hessian/io/BasicDeserializer.java
  75. 285 0
      src/com/hmsoft/remote/caucho/hessian/io/BasicSerializer.java
  76. 295 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanDeserializer.java
  77. 315 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanSerializer.java
  78. 82 0
      src/com/hmsoft/remote/caucho/hessian/io/BeanSerializerFactory.java
  79. 93 0
      src/com/hmsoft/remote/caucho/hessian/io/CalendarHandle.java
  80. 77 0
      src/com/hmsoft/remote/caucho/hessian/io/CalendarSerializer.java
  81. 160 0
      src/com/hmsoft/remote/caucho/hessian/io/ClassDeserializer.java
  82. 87 0
      src/com/hmsoft/remote/caucho/hessian/io/ClassSerializer.java
  83. 134 0
      src/com/hmsoft/remote/caucho/hessian/io/CollectionDeserializer.java
  84. 108 0
      src/com/hmsoft/remote/caucho/hessian/io/CollectionSerializer.java
  85. 204 0
      src/com/hmsoft/remote/caucho/hessian/io/Deflation.java
  86. 73 0
      src/com/hmsoft/remote/caucho/hessian/io/Deserializer.java
  87. 138 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumDeserializer.java
  88. 108 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumSerializer.java
  89. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumerationDeserializer.java
  90. 84 0
      src/com/hmsoft/remote/caucho/hessian/io/EnumerationSerializer.java
  91. 57 0
      src/com/hmsoft/remote/caucho/hessian/io/EnvelopeFactory.java
  92. 107 0
      src/com/hmsoft/remote/caucho/hessian/io/ExtSerializerFactory.java
  93. 146 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Constants.java
  94. 2798 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Input.java
  95. 1604 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2Output.java
  96. 165 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingInput.java
  97. 109 0
      src/com/hmsoft/remote/caucho/hessian/io/Hessian2StreamingOutput.java
  98. 174 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugInputStream.java
  99. 168 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugOutputStream.java
  100. 2090 0
      src/com/hmsoft/remote/caucho/hessian/io/HessianDebugState.java

+ 0 - 0
Cfg/jdbc.properties


+ 13 - 0
Cfg/log4j.properties

@@ -0,0 +1,13 @@
+log4j.rootLogger=INFO,stdout,File
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+#log4j.appender.stdout.Target=System.err
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+   
+log4j.appender.stdout.layout.ConversionPattern=[%p] [%d] [%c] - [%m]%n 
+###### File appender definition #######  
+log4j.appender.File=org.apache.log4j.DailyRollingFileAppender  
+log4j.appender.File.File=ont.log  
+log4j.appender.File.Append=false  
+log4j.appender.File.layout=org.apache.log4j.PatternLayout 
+log4j.appender.File.layout.ConversionPattern=[%p] [%d] [%c] - [%m]%n
+

+ 2 - 0
Cfg/server.properties

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

+ 1 - 0
Data/BatchStore

@@ -0,0 +1 @@
+{"batchDesc":"批次1","batchId":1538478705909,"batchNo":"批次1","createTime":"2018-10-02 19:11:45","status":3,"statusDes":"已发布"}

+ 6 - 0
Data/CoursePlanStore-1538478705909

@@ -0,0 +1,6 @@
+{"bianZhu":"编著111","bookName":"计算机原理","cengci":"本科","courseCode":"000595","courseName":"计算机","coursePlanId":1,"outlineName":"大纲名称","press":"机械工业出版社","publications":"第二版","schoolId":1538478676415,"schoolName":"三峡大学"}
+{"bianZhu":"编著222","bookName":"经济学原理","cengci":"专科","courseCode":"002778","courseName":"经济学","coursePlanId":2,"outlineName":"经济学常识大纲","press":"中信出版社","publications":"第三版","schoolId":1538478676415,"schoolName":"三峡大学"}
+{"bianZhu":"编著333","bookName":"电子商务","cengci":"专科","courseCode":"004991","courseName":"互联网营销","coursePlanId":3,"outlineName":"互联网营销大纲","press":"华中科技大学出版社","publications":"第一版","schoolId":1538478676415,"schoolName":"三峡大学"}
+{"bianZhu":"编著444","bookName":"数据结构","cengci":"专科","courseCode":"004123","courseName":"算法数据结构","coursePlanId":4,"outlineName":"算法大纲","press":"武汉大学出版社","publications":"第六版","schoolId":1538478676415,"schoolName":"三峡大学"}
+{"bianZhu":"编著555","bookName":"人工智能1","cengci":"本科1","courseCode":"006133","courseName":"AI改变世界1","coursePlanId":5,"outlineName":"深入人工智能1","press":"浙江大学出版社1","publications":"第七版1","schoolId":1538478698788,"schoolName":"武汉大学"}
+{"bianZhu":"编著556","bookName":"人工智能2","cengci":"本科2","courseCode":"006134","courseName":"AI改变世界2","coursePlanId":6,"outlineName":"深入人工智能2","press":"浙江大学出版社2","publications":"第七版2","schoolId":1538478698788,"schoolName":"武汉大学"}

+ 2 - 0
Data/SchoolStore

@@ -0,0 +1,2 @@
+{"passWord":"123456","schoolId":1538478676415,"schoolName":"三峡大学","userName":"sxdx"}
+{"passWord":"123456","schoolId":1538478698788,"schoolName":"武汉大学","userName":"whdx"}

+ 3 - 0
Data/SysCfg.properties

@@ -0,0 +1,3 @@
+#Comment
+#Wed Oct 10 22:51:45 CST 2018
+frsh.minutes=30

BIN
lib/commons-beanutils-1.8.3.jar


BIN
lib/commons-codec-1.10.jar


BIN
lib/commons-collections4-4.1.jar


BIN
lib/commons-io-2.5.jar


BIN
lib/commons-lang-2.6.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


+ 476 - 0
src/com/hmsoft/app/Manage.java

@@ -0,0 +1,476 @@
+package com.hmsoft.app;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+import org.apache.commons.beanutils.BeanUtils;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.bizpanel.BatchTab;
+import com.hmsoft.bizpanel.MtzjTab;
+import com.hmsoft.bizpanel.SchoolTab;
+import com.hmsoft.bizpanel.StatisTab;
+import com.hmsoft.bizpanel.SystTab;
+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.CoursePlanDataUtil;
+import com.hmsoft.common.util.DateUtil;
+import com.hmsoft.common.util.ShoolDateUtil;
+import com.hmsoft.model.Batch;
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.School;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.model.request.LoginRequest;
+import com.hmsoft.model.request.LogoutRequest;
+import com.hmsoft.model.request.RegisterRequest;
+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 com.hmsoft.remote.rmi.transport.NettyChannel;
+import javafx.application.Application;
+import javafx.collections.ObservableList;
+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 Manage extends Application {
+	
+	public static ExchangeClient CLIENT = null;
+	private static Properties PROPS = new Properties();
+	
+	@Override
+	public void start(final Stage primaryStage) {
+		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 systTab = new SystTab(primaryStage);
+		Tab batchTab = new BatchTab(primaryStage);
+		Tab mtzjTab = new MtzjTab(primaryStage);
+		Tab schoolTab = new SchoolTab(primaryStage);
+		Tab statisTab = new StatisTab(primaryStage);
+		//加入显示内容
+		tabPane.getTabs().add(systTab);
+		tabPane.getTabs().add(schoolTab);
+		tabPane.getTabs().add(batchTab);
+		tabPane.getTabs().add(mtzjTab);
+		tabPane.getTabs().add(statisTab);
+		
+		tabPane.setMinWidth(100);
+		tabPane.setMinWidth(100);
+		
+		root.setCenter(tabPane);
+		//primaryStage.setAlwaysOnTop(true);//始终最上面
+		primaryStage.setScene(scene);
+		primaryStage.show();
+		primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
+			
+			@Override
+			public void handle(WindowEvent event) {
+				closeHandleFunc(primaryStage, event);
+			}
+
+		});
+	}
+	
+	public static void main(String[] args) throws RemotingException {
+		startServerSocket();
+		launch(args);
+
+	}
+	
+	private static void startServerSocket() {
+		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 caught(NettyChannel arg0, Throwable arg1) throws RemotingException {
+					}
+
+					@Override
+					public void connected(NettyChannel arg0) throws RemotingException {
+						System.err.println("manage: connected");
+						//client建立完、连接建立后注册
+						new Thread(new Runnable() {
+
+							@Override
+							public void run() {
+								try {
+									registerMgr();
+								} catch (RemotingException e) {
+									e.printStackTrace();
+								}
+							}
+							
+						}).start();
+	
+					}
+
+					@Override
+					public void disconnected(NettyChannel arg0) throws RemotingException {
+					}
+
+					@Override
+					public void received(NettyChannel arg0, Object message) throws RemotingException {
+						if (message instanceof Message) {
+							System.err.println("manage: received," + message);
+						}
+					}
+
+					@Override
+					public void sent(NettyChannel arg0, Object arg1) throws RemotingException {
+						
+					}
+
+					@Override
+					public Object reply(NettyChannel channel, Object request) throws RemotingException {
+						Object ret = null;
+						if (request instanceof Message) {
+							Message msg = (Message)request;
+							byte type = msg.getType();
+							try {
+								if (type == MsgTypeConst.TYPE_LOGIN_REQ_TO_MGE) {
+									//登录消息
+									ret = loginReqFunc(msg);
+									
+								} else if (type == MsgTypeConst.TYPE_NOTIFY_CLIENT_LOGOUT) {
+									//收到登出
+									ret = logoutReqFunc(msg);
+									
+								} else if (type == MsgTypeConst.TYPE_BIZ_REQ_TO_MGE) {
+									
+									final BizRequest req = JSON.parseObject(msg.getMsgBody(), BizRequest.class);
+									if (req.getReqType().intValue() == AppConst.SCH_SELECT_COURSE) {//学校提交选择课程
+										
+										ret = notifySelectFunc(req, msg);
+										
+									} else if (req.getReqType() == AppConst.SCH_CANCEL_COURSE) {//学校取消课程
+										
+										ret = cancelCourseFunc(req, msg);
+										
+									} else if (req.getReqType() == AppConst.SCH_FETCH_NEW_LIST) {//
+										
+										ret = pushLastestZjDataFunc(req, msg);
+										
+									} else if (req.getReqType() == AppConst.SCH_CONFIRM_OK) {//学校确认了
+										
+										ret = schConfirmFunc(req);
+										
+									} else if (req.getReqType() == AppConst.SCH_CHANGE_PASSWD) {//修改密码
+										ret = changePasswdFunc(req);
+									}
+									
+								} else if (type == MsgTypeConst.TYPE_HEARTBEAT) {
+									//心跳验证  TODO...
+								}
+
+							} catch (Exception e) {
+								e.printStackTrace();
+							}
+							
+						}
+						return ret;
+					}
+					
+				});
+	
+			}
+			
+		}).start();
+
+	}
+	
+	private static void registerMgr() throws RemotingException {
+		long time = 0;
+		while(Manage.CLIENT == null) {
+			try {
+				Thread.sleep(200L);
+				time += 200L;
+				if (time >= 4000) {
+					System.err.println("manage: Manage.CLIENT create timeout.");
+					break;
+				}
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+		final RegisterRequest register = new RegisterRequest();
+		register.setReqDateTime(LocalDateTime.now());
+		String json = JSON.toJSONString(register);
+		final Message regMsg = new Message(MsgTypeConst.TYPE_MGE_REGISTER, json);
+		Object objReg = Manage.CLIENT.requestTwoWay(regMsg);
+		System.err.println("------------>" + objReg);
+		//TODO..
+	}
+	
+	/**
+	 * 处理登录.
+	 * @param msg
+	 * @throws RemotingException
+	 */
+	private static Message loginReqFunc(final Message msg) throws RemotingException {
+		final LoginRequest login = JSON.parseObject(msg.getMsgBody(), LoginRequest.class);
+		LoginResponse logRes = new LoginResponse();
+		try {
+			BeanUtils.copyProperties(logRes, login);
+		} catch (IllegalAccessException e) {
+			e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			e.printStackTrace();
+		}
+		final ObservableList<School> items = SchoolTab.DATALIST;
+		for (int i=0; i<items.size(); i++) {
+			final School school = items.get(i);
+			if (school.getUserName().equals(login.getUserName())
+					&& school.getPassWord().equals(login.getPassWord())) {
+				
+				logRes.setStatus(0);//成功
+				logRes.setSchoolId(school.getSchoolId());
+				logRes.setSchoolName(school.getSchoolName());
+				String json = JSON.toJSONString(logRes);
+				final Message resMsg = new Message(MsgTypeConst.TYPE_LOGIN_RESP_TO_CLIENT, json);
+				resMsg.setShoolRemoteAddr(msg.getShoolRemoteAddr());
+				//modify manage table
+				school.setStatusDesc("已连接");
+				school.setConnectedTime(DateUtil.format(LocalDateTime.now()));
+				SchoolTab.SCH_TABLE.refresh();
+				return resMsg;
+			} 
+			if (i == items.size() - 1) {
+				logRes.setStatus(-1);//失败
+				String json = JSON.toJSONString(login);
+				Message resMsg = new Message(MsgTypeConst.TYPE_LOGIN_RESP_TO_CLIENT, json);
+				resMsg.setShoolRemoteAddr(msg.getShoolRemoteAddr());
+				return resMsg;
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * 学校登出.
+	 * @param msg
+	 * @throws RemotingException
+	 */
+	private static Object logoutReqFunc(final Message msg) throws RemotingException {
+		final LogoutRequest out = JSON.parseObject(msg.getMsgBody(), LogoutRequest.class);
+		for (School school : SchoolTab.DATALIST) {
+			if (school.getUserName().equals(out.getUserName())) {
+				school.setStatusDesc("已断开");
+				school.setDisconnectTime((DateUtil.format(LocalDateTime.now())));
+				break;
+			}
+		}
+		SchoolTab.SCH_TABLE.refresh();
+		return 0;
+	}
+	
+	/**
+	 * 学校选择结果广播出去.
+	 * @param req
+	 * @throws RemotingException
+	 */
+	private static Object notifySelectFunc(final BizRequest req, final Message msg) throws RemotingException {
+		req.setReqType(AppConst.SRV_NOTIFY_SELECT);
+		//更新服务端列表
+		String ids = req.getSelectIds();
+		if (ids != null && ids.length() > 0) {
+			String[] idArr = ids.split(",");
+			for (String rowIndx : idArr) {
+				final int id = Integer.parseInt(rowIndx);
+				for (CoursePlan course : MtzjTab.DATALIST) {
+					if (course.getCoursePlanId().intValue() == id) {
+						final School school = new School(req.getSchoolId(), req.getSchoolName());
+						course.getMultiSelectList().add(school);
+						course.setSchSelectIds(getShoolIds(course.getMultiSelectList()));
+						course.setSchSelectNames(getShoolNames(course.getMultiSelectList()));
+						course.setSchSelectTime(DateUtil.format(LocalDateTime.now()));
+					}
+				}
+
+			}
+			MtzjTab.TABLE.refresh();
+		}
+		//实时方式才通知其他学校
+		if (SystTab.FRSH_MINUTES == 0) {
+			String json = JSON.toJSONString(req);
+			Message resMsg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+			resMsg.setShoolRemoteAddr(msg.getShoolRemoteAddr());
+			//IFuture future = 
+			Object obj = Manage.CLIENT.requestTwoWay(resMsg);
+			//TODO
+		}
+		return msg;
+	}
+	
+	/**
+	 * 学校取消课程广播出去.
+	 * @param req
+	 * @throws RemotingException
+	 */
+	private static Object cancelCourseFunc(final BizRequest req, final Message msg) throws RemotingException {
+		//更新服务端列表
+		String ids = req.getSelectIds();
+		final List<CoursePlan> showList = new ArrayList<CoursePlan>();
+		if (ids != null && ids.length() > 0) {
+			String[] idArr = ids.split(",");
+			for (String rowIndx : idArr) {
+				final int id = Integer.parseInt(rowIndx);
+				for (CoursePlan course : MtzjTab.DATALIST) {
+					if (course.getCoursePlanId().intValue() == id) {
+						final School school = new School(req.getSchoolId(), req.getSchoolName());
+						course.getMultiSelectList().remove(school);
+						showList.add(course);
+						course.setSchSelectIds(getShoolIds(course.getMultiSelectList()));
+						course.setSchSelectNames(getShoolNames(course.getMultiSelectList()));
+						if (course.getMultiSelectList().size() == 0) {//2学校其中一个取消了,不能清空选择时间
+							course.setSchSelectTime("");
+						}
+					}
+				}
+			}
+			MtzjTab.TABLE.refresh();
+		}
+		//通知到其他客户端
+		//notifyCancelToClient(req, msg, showList);
+		return 0;
+	}
+	
+	private static void notifyCancelToClient(final BizRequest req, final Message msg, final List<CoursePlan> showList) {
+		req.setReqType(AppConst.SRV_NOTIFY_CANCEL);
+		req.setSourceList(showList);//删除的列表重新加载到客户端.
+		String json = JSON.toJSONString(req);
+		Message resMsg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+		resMsg.setShoolRemoteAddr(msg.getShoolRemoteAddr());
+		Object retObj;
+		try {
+			retObj = Manage.CLIENT.requestTwoWay(msg);
+			if (retObj != null) {
+				
+			}
+		} catch (RemotingException e) {
+			e.printStackTrace();
+			//TODO...
+		}
+	}
+
+	/**
+	 * 推送最新征集/已发布数据到学校.
+	 * @param req
+	 * @throws RemotingException
+	 */
+	private static Object pushLastestZjDataFunc(final BizRequest req, final Message msg) throws RemotingException {
+		Batch batch = MtzjTab.BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+		if (batch != null && (batch.getStatus() == BatchConst.STATUS_COLLECTING || batch.getStatus() == BatchConst.STATUS_PUBLISHED)) {
+			req.setReqType(AppConst.SRV_PUSH_NEW_LIST);
+			req.setSourceList(MtzjTab.DATALIST);
+			req.setZjStatus(batch.getStatus().byteValue());
+			String json = JSON.toJSONString(req);
+			final Message resMsg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+			resMsg.setShoolRemoteAddr(msg.getShoolRemoteAddr());
+			return resMsg;
+		}
+		return null;
+	}
+	
+	/**
+	 * 学校确认处理.
+	 * @param req
+	 */
+	private static Object schConfirmFunc(final BizRequest req) {
+		if (req.getSelectIds() != null && req.getSelectIds().length() > 0) {
+			String[] arr = req.getSelectIds().split(",");
+			for (String id : arr) {
+				for (CoursePlan cp : MtzjTab.DATALIST) {
+					if (cp.getCoursePlanId().intValue() == Integer.parseInt(id)) {
+						cp.setConfirmDesc("已确认");
+						cp.setConfirmStatus(1);
+						break;
+					}
+				}
+			}
+			MtzjTab.TABLE.refresh();
+		}
+		return 0;
+	}
+	
+	private static Object changePasswdFunc(final BizRequest req) {
+		String newPasswd = req.getSchoolName();//schoolName中为passwd
+		ShoolDateUtil.resetPasswd(req.getSchoolId(), newPasswd);
+		SchoolTab.changeTablePasswdData(req.getSchoolId(), newPasswd);
+		return 0;
+	}
+	
+	public static String getShoolNames(List<School> schoolList) {
+		StringBuilder sb = new StringBuilder();
+		for (School sch : schoolList) {
+			sb.append(sch.getSchoolName()).append("|");
+		}
+		return sb.length() > 0 ? sb.substring(0, sb.length() - 1) : "";
+	}
+	
+	private static String getShoolIds(List<School> schoolList) {
+		StringBuilder sb = new StringBuilder();
+		for (School sch : schoolList) {
+			sb.append(sch.getSchoolId()).append("|");
+		}
+		return sb.length() > 0 ? sb.substring(0, sb.length() - 1) : "";
+	}
+	
+	private void closeHandleFunc(final 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)) {
+			Batch batch = MtzjTab.BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+			if (batch != null && MtzjTab.DATALIST.size() > 0) {
+				long batchId = MtzjTab.BATCH_SELECT_BOX.getSelectionModel().getSelectedItem().getBatchId();
+				CoursePlanDataUtil.updateCoursePlan(MtzjTab.DATALIST, batchId);
+			}
+		} else {
+			event.consume();
+		}
+	}
+}

+ 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 */

+ 274 - 0
src/com/hmsoft/bizpanel/BatchTab.java

@@ -0,0 +1,274 @@
+package com.hmsoft.bizpanel;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import com.hmsoft.common.BatchConst;
+import com.hmsoft.common.util.BatchDataUtil;
+import com.hmsoft.common.util.DateUtil;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.model.Batch;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+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;
+import javafx.scene.control.Button;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Label;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
+
+/**
+ * 批次维护
+ * @author zq
+ *
+ */
+public class BatchTab extends Tab {
+
+	private Button btnNew;
+	private Button btnDelete;
+	public static final TableView<Batch> TABLE = new TableView<Batch>();
+	public static final ObservableList<Batch> DATALIST = FXCollections.observableArrayList();
+	
+	public BatchTab(final Stage stage) {
+		super();
+		this.setText("批次维护");
+		this.setId("batchTabId");
+		this.setClosable(false);
+		
+		//Grid容器
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		//创建按钮
+		this.createBtn(stage, gp);
+		//创建表格
+		this.createBatchTable(gp, stage);
+		//grid布局加到容器
+		this.setContent(gp);
+	}
+	
+	private void createBtn(final Stage stage, GridPane gp) {
+		//按钮
+		btnNew = new Button();
+		btnNew.setText("新建批次");
+		
+/*		Button btnActive = new Button();
+		btnActive.setText("激活批次");
+		
+		Button btnPublish = new Button();
+		btnPublish.setText("发布批次");*/
+		
+		btnDelete = new Button();
+		btnDelete.setText("删除批次");
+		
+		btnNew.setOnAction(new EventHandler<ActionEvent>() {
+
+			@Override
+			public void handle(ActionEvent arg0) {
+				btnNew.setDisable(true);
+				showCreateWin();
+			}
+			
+		});
+		
+/*		btnActive.setOnAction(new EventHandler<ActionEvent>() {
+
+			@Override
+			public void handle(ActionEvent arg0) {
+				
+			}
+			
+		});*/
+		
+		btnDelete.setOnAction(new EventHandler<ActionEvent>() {
+			
+			@Override
+			public void handle(ActionEvent arg0) {
+				deleteBatchFunc(stage);
+			}
+			
+		});
+		
+		//加子对象
+		gp.add(btnNew, 0, 0);
+/*		gp.add(btnActive, 1, 0);
+		gp.add(btnPublish, 2, 0);*/
+		gp.add(btnDelete, 1, 0);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void createBatchTable(final GridPane gp, final Stage stage) {
+		//设置表格整体宽度
+		TABLE.setMinWidth(stage.getWidth() - 70);
+		TABLE.setMinHeight(stage.getHeight() - 170);
+		//设置所有列
+		TableColumn<Batch, Integer> batchId = new TableColumn<Batch, Integer>("序号");
+		batchId.setCellValueFactory(new PropertyValueFactory<Batch, Integer>("batchId"));
+		batchId.setMinWidth(50);
+		TableColumn<Batch, String> batchName = new TableColumn<Batch, String>("批次号");
+		batchName.setCellValueFactory(new PropertyValueFactory<Batch, String>("batchNo"));
+		batchName.setMinWidth(200);
+		TableColumn<Batch, String> batchDate = new TableColumn<Batch, String>("创建时间");
+		batchDate.setCellValueFactory(new PropertyValueFactory<Batch, String>("createTime"));
+		batchDate.setMinWidth(180);
+		TableColumn<Batch, String> publishTime = new TableColumn<Batch, String>("发布时间");
+		publishTime.setCellValueFactory(new PropertyValueFactory<Batch, String>("publishTime"));
+		publishTime.setMinWidth(180);
+		TableColumn<Batch, String> batchDesc = new TableColumn<Batch, String>("批次描述");
+		batchDesc.setCellValueFactory(new PropertyValueFactory<Batch, String>("batchDesc"));
+		batchDesc.setMinWidth(600);
+		TableColumn<Batch, String> statusDes = new TableColumn<Batch, String>("批次状态");
+		statusDes.setCellValueFactory(new PropertyValueFactory<Batch, String>("statusDes"));
+		statusDes.setMinWidth(100);
+		TableColumn<Batch, String> status = new TableColumn<Batch, String>("批次状态");
+		status.setCellValueFactory(new PropertyValueFactory<Batch, String>("status"));
+		status.setVisible(false);
+		//查询并显示到列表
+		DATALIST.addAll(BatchDataUtil.loadBatchData());
+		TABLE.setItems(DATALIST);
+		TABLE.getColumns().addAll(batchId, batchName, batchDate, publishTime, statusDes, batchDesc);
+		
+		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, 2, 1);//第1行
+	}
+	
+	/**
+	 * 弹出新建窗口.
+	 */
+	private void showCreateWin() {
+		final Stage addStage = new Stage();
+		addStage.setTitle("添加批次");
+		final GridPane grid = new GridPane();
+		grid.setAlignment(Pos.CENTER);
+		grid.setHgap(10);
+		grid.setVgap(10);
+		grid.setPadding(new Insets(50));
+		
+		//批次号标签
+		Label batchNo = new Label("批次号: ");
+		grid.add(batchNo, 0, 1);
+		//批次号输入框
+		final TextField batchNoField = new TextField();
+		grid.add(batchNoField, 1, 1);
+		
+		Label batchDese = new Label("批次描述:");
+		grid.add(batchDese, 0, 2);
+		//批次描述
+		final TextField batchDeseField = new TextField();
+		batchDeseField.setOnKeyPressed(new EventHandler<KeyEvent>() {
+			@Override
+			public void handle(KeyEvent event) {
+				if (event.getCode() == KeyCode.ENTER) {
+					createBatchFunc(addStage, batchNoField, batchDeseField);
+				}
+			}
+		});
+		grid.add(batchDeseField, 1, 2);
+		
+		final HBox hbBtn = new HBox(10);
+		grid.add(hbBtn, 1, 4);
+		
+		Button btnSave = new Button("保存");
+		Button btncancel = new Button("取消");
+		hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
+		hbBtn.getChildren().add(btnSave);
+		hbBtn.getChildren().add(btncancel);
+		
+		//新建窗口退出事件
+		addStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
+			@Override
+			public void handle(WindowEvent event) {
+				btnNew.setDisable(false);
+			}
+		});
+		
+		btnSave.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				createBatchFunc(addStage, batchNoField, batchDeseField);
+			}
+		});
+		
+		btncancel.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				btnNew.setDisable(false);
+				addStage.close();
+			}
+		});
+		
+		Scene scene = new Scene(grid, 400, 220);
+		addStage.setScene(scene);
+		addStage.setResizable(false);
+		addStage.alwaysOnTopProperty();
+		addStage.show();
+	}
+	
+	/**
+	 * 删除批次.
+	 */
+	private void deleteBatchFunc(Stage stage) {
+		Batch selected = TABLE.getSelectionModel().getSelectedItem();
+		if (selected == null) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次!");
+			return;
+		}
+/*		if (selected.getStatus() != BatchConst.STATUS_NEW) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "只能删除新建状态批次!");
+	        return;
+		}*/
+		
+		if (DialogUtil.confirm("确定删除该批次?", stage)) {
+			BatchDataUtil.deleteBatch(selected.getBatchId());
+			DATALIST.remove(selected);//列表删除
+			MtzjTab.BATCH_SELECT_BOX.getItems().remove(selected);//命题征集界面 下拉框删除
+		}
+	}
+	
+	private void createBatchFunc(final Stage addStage, final TextField batchNoField,
+			final TextField batchDeseField) {
+		try {
+			for (Batch b : DATALIST) {
+				if (b.getBatchNo().equals(batchNoField.getText())) {
+					DialogUtil.showAlertDialog(addStage, Alert.AlertType.WARNING, "提示", "批次号不能重复!");
+					return;
+				}
+			}
+			final Batch batch = new Batch();
+			batch.setBatchId(System.currentTimeMillis());
+			batch.setBatchNo(batchNoField.getText());
+			batch.setStatus(Integer.valueOf(BatchConst.STATUS_NEW));
+			batch.setBatchDesc(batchDeseField.getText());
+			batch.setCreateTime(DateUtil.format(LocalDateTime.now()));
+			BatchDataUtil.insertBatch(batch);
+			DATALIST.add(batch);//批次列表增加显示
+			MtzjTab.BATCH_COMBO_DATA_LIST.add(batch);//命题征集界面 下拉框增加
+		} catch(Exception e) {
+			e.printStackTrace();
+		}
+		addStage.close();
+		btnNew.setDisable(false);
+	}
+}

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

@@ -0,0 +1,730 @@
+package com.hmsoft.bizpanel;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.apache.poi.hssf.OldExcelFormatException;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.app.Manage;
+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.CoursePlanDataUtil;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.common.util.POIUtil;
+import com.hmsoft.common.util.ShoolDateUtil;
+import com.hmsoft.common.util.StringHelper;
+import com.hmsoft.model.Batch;
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.School;
+import com.hmsoft.model.request.BizRequest;
+import com.hmsoft.remote.rmi.common.utils.NamedThreadFactory;
+import com.hmsoft.remote.rmi.model.Message;
+
+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.Button;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Text;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+/**
+ * 命题征集
+ * @author zq
+ *
+ */
+public class MtzjTab extends Tab {
+	public static Button BTN_MANUAL = null;
+	public static Button BTN_DELETE = null;
+	public static Button BTN_IMP = null;
+	public static Button BTN_START = null;
+	public static Button BTN_EXPORT = null;
+	public static Button BTN_PUBLISH = null;
+	public static Button BTN_SPECIFIC = null;
+	public static final TableView<CoursePlan> TABLE = new TableView<CoursePlan>();
+	public static final ObservableList<CoursePlan> DATALIST = FXCollections.observableArrayList();//base数据集,客户选择后体现这里
+	public static final ObservableList<CoursePlan> FILTER_DATALIST = FXCollections.observableArrayList();//查询过滤用
+	public static final List<School> SPECIFIC_SCHOOL_MAP = new ArrayList<School>();//命题办指定的学校
+	//批次下拉框对象.
+	public static final ComboBox<Batch> BATCH_SELECT_BOX = new ComboBox<Batch>();
+	//批次下拉框数据源.
+	public static final ObservableList<Batch> BATCH_COMBO_DATA_LIST = FXCollections.observableArrayList();
+	//定时刷新服务
+	private static final ScheduledThreadPoolExecutor SCHEDULE_FRESH_SERVICE = 
+			new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("MtzjFreshTimer", true));
+	private volatile ScheduledFuture<?> SCHEDULE_FUTRUE = null;
+	
+	public MtzjTab(final Stage stage) {
+		super();
+		this.setText("命题征集");
+		this.setId("collectTabId");
+		this.setClosable(false);
+		stage.setResizable(false);
+		//Grid
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		//创建彼此下拉框
+		this.createComboBox(gp, stage);
+		//创建按钮
+		this.createBtn(gp, stage);
+		//创建表格
+		this.createMtzjTable(gp, stage);
+		//grid内容
+		this.setContent(gp);
+	}
+	
+	//批次下拉框
+	private void createComboBox(final GridPane gp, final Stage stage) {
+		//批次号
+		BATCH_SELECT_BOX.setPromptText("批次号");
+		BATCH_SELECT_BOX.setEditable(false);
+		BATCH_COMBO_DATA_LIST.addAll(BatchDataUtil.loadBatchData());
+		BATCH_SELECT_BOX.setItems(BATCH_COMBO_DATA_LIST);
+		BATCH_SELECT_BOX.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				//加载该批次之前已经导入的课程计划
+				DATALIST.clear();
+				DATALIST.addAll(CoursePlanDataUtil.loadCoursePlanData(SELECTED_BATCH.getBatchId()));
+				TABLE.setItems(DATALIST);
+				if (SELECTED_BATCH.getStatus() == BatchConst.STATUS_COLLECTING
+						&& SCHEDULE_FUTRUE == null) {
+					startSheduleThread(stage);
+				}
+				if (SELECTED_BATCH.getStatus() == BatchConst.STATUS_NEW) {
+					BTN_IMP.setDisable(false);
+					BTN_START.setDisable(false);
+					BTN_PUBLISH.setDisable(true);
+					BTN_SPECIFIC.setDisable(true);
+					BTN_MANUAL.setDisable(true);
+					BTN_DELETE.setDisable(false);
+					BTN_EXPORT.setDisable(true);
+				} else if (SELECTED_BATCH.getStatus() == BatchConst.STATUS_IMPORTED) {
+					BTN_IMP.setDisable(false);
+					BTN_START.setDisable(false);
+					BTN_PUBLISH.setDisable(true);
+					BTN_SPECIFIC.setDisable(true);
+					BTN_MANUAL.setDisable(true);
+					BTN_DELETE.setDisable(false);
+					BTN_EXPORT.setDisable(true);
+				} else if (SELECTED_BATCH.getStatus() == BatchConst.STATUS_COLLECTING) {
+					BTN_IMP.setDisable(false);
+					BTN_START.setDisable(false);
+					BTN_PUBLISH.setDisable(false);
+					BTN_SPECIFIC.setDisable(false);
+					BTN_MANUAL.setDisable(false);
+					if (SystTab.FRSH_MINUTES >=0) {
+						BTN_MANUAL.setDisable(true);
+					} else {
+						BTN_MANUAL.setDisable(false);
+					}
+					BTN_DELETE.setDisable(false);
+					BTN_EXPORT.setDisable(true);
+				} else if (SELECTED_BATCH.getStatus() == BatchConst.STATUS_PUBLISHED) {
+					BTN_IMP.setDisable(true);
+					BTN_START.setDisable(true);
+					BTN_PUBLISH.setDisable(true);
+					BTN_SPECIFIC.setDisable(true);
+					BTN_MANUAL.setDisable(true);
+					BTN_DELETE.setDisable(true);
+					BTN_EXPORT.setDisable(false);
+				}
+			}
+		});
+		//批次下拉框
+		gp.add(BATCH_SELECT_BOX, 0, 0);
+	}
+	
+	private void setAllBtnInitDisable() {
+		BTN_IMP.setDisable(true);
+		BTN_START.setDisable(true);
+		BTN_PUBLISH.setDisable(true);
+		BTN_SPECIFIC.setDisable(true);
+		BTN_MANUAL.setDisable(true);
+		BTN_DELETE.setDisable(true);
+		BTN_EXPORT.setDisable(true);
+	}
+	
+	private void createBtn(final GridPane gp, final Stage stage) {
+		//导入
+		BTN_IMP = new Button();
+		BTN_IMP.setText("导入征集计划");
+		//删除课程
+		BTN_DELETE = new Button();
+		BTN_DELETE.setText("删除课程");
+		//开始征集,刷新服务端列表数据到客户端
+		BTN_START = new Button();
+		BTN_START.setText("开启征集");
+		//手动刷新
+		BTN_MANUAL = new Button();
+		BTN_MANUAL.setText("手动刷新");
+		
+		//命题办指定
+		BTN_SPECIFIC = new Button();
+		BTN_SPECIFIC.setText("指定学校");
+		//最终发布
+		BTN_PUBLISH = new Button();
+		BTN_PUBLISH.setText("最终发布");
+		//导出最终结果
+		BTN_EXPORT = new Button();
+		BTN_EXPORT.setText("导出最终计划");
+		BTN_MANUAL.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				if (DialogUtil.confirm("确认刷新?", stage)) {
+					pushLastestZjDataFunc(stage);
+					//刷新征集下拉框学校显示
+					filterZjSelectList();
+				}
+			}
+		});
+		//初始化所有按钮disable
+		setAllBtnInitDisable();
+		Text likeText = new Text(" 模糊匹配:");
+		final TextField likeQuery = new TextField();
+		likeQuery.setMaxWidth(200);
+		FILTER_DATALIST.addAll(DATALIST);
+		likeQuery.textProperty().addListener(new ChangeListener<String>() {
+
+			@Override
+			public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+				FILTER_DATALIST.clear();
+				for (CoursePlan cp : DATALIST) {
+					if (cp.getCourseCode().indexOf(newValue) != -1 || cp.getCourseName().indexOf(newValue) != -1) {
+						FILTER_DATALIST.add(cp);
+					}
+		        }
+				TABLE.setItems(FILTER_DATALIST);
+			}
+			
+		});
+		
+		//删除课程
+		BTN_DELETE.setOnAction(new EventHandler<ActionEvent>() {
+
+			@Override
+			public void handle(ActionEvent event) {
+				deleteCoursePlan(stage);
+				
+			}
+		});
+		
+		
+		//导入
+		BTN_IMP.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent arg0) {
+				impPlanFunc(stage);
+			}
+		});
+		
+		//命题办指定学校
+		BTN_SPECIFIC.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent arg0) {
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				if (SELECTED_BATCH == null) {
+					DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次号!");
+					return;
+				}
+				if (DialogUtil.confirm("是否指定学校?", stage)) {
+					specificFunc(stage);
+				}
+			}
+		});
+		
+		//开启征集
+		BTN_START.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent arg0) {
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				if (SELECTED_BATCH == null) {
+					DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次号!");
+					return;
+				}
+				if (DialogUtil.confirm("是否开启征集?", stage)) {
+					startMtzjFunc(stage);//开启征集
+					if (SCHEDULE_FUTRUE == null) {
+						startSheduleThread(stage);//定时刷新线程是否开启
+					}
+				}
+			}
+		});
+		//最终发布
+		BTN_PUBLISH.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent arg0) {
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				if (SELECTED_BATCH == null) {
+					DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次号!");
+					return;
+				}
+				//只有征集中才能到发布
+				if (SELECTED_BATCH.getStatus() == BatchConst.STATUS_COLLECTING) {
+					if (DialogUtil.confirm("是否最终发布?", stage)) {
+						finalPublishFunc(stage, SELECTED_BATCH);
+					}
+				}
+			}
+		});
+		//导出最终征集结果
+		BTN_EXPORT.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				if (SELECTED_BATCH == null) {
+					DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次号!");
+					return;
+				}
+				if (DialogUtil.confirm("是否导出?", stage)) {
+					FileChooser fileChooser = new FileChooser();
+					fileChooser.setTitle("导出");
+					fileChooser.setInitialFileName(SELECTED_BATCH.getBatchNo() + ".xls");
+					fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XLS Files", "*.xls"));
+					File file = fileChooser.showSaveDialog(stage);
+					if(file != null) {
+						POIUtil.writeToXlsx(MtzjTab.DATALIST, file.getAbsolutePath());
+					}
+				}
+				
+			}
+		});
+		
+		//加入容器
+		gp.add(BTN_IMP, 1, 0);
+		gp.add(BTN_START, 2, 0);
+		gp.add(BTN_MANUAL, 3, 0);
+		gp.add(BTN_SPECIFIC, 4, 0);
+		gp.add(BTN_DELETE, 5, 0);
+		gp.add(BTN_PUBLISH, 6, 0);
+		gp.add(BTN_EXPORT, 7, 0);
+		gp.add(likeText, 8, 0);
+		gp.add(likeQuery, 9, 0);
+	}
+	
+	protected void deleteCoursePlan(final Stage stage) {
+		Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+		if (SELECTED_BATCH == null) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次号!");
+			return;
+		}
+		CoursePlan delObj = TABLE.getSelectionModel().getSelectedItem();
+		if (delObj == null) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选课程计划!");
+			return;
+		}
+		if (SELECTED_BATCH.getStatus() != BatchConst.STATUS_IMPORTED &&
+				SELECTED_BATCH.getStatus() != BatchConst.STATUS_COLLECTING) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "只能删除'已导入'和'征集中'计划!");
+			return;
+		}
+		if (DialogUtil.confirm("确认删除课程'" + delObj.getCourseCode() + "'?", stage)) {
+			MtzjTab.DATALIST.remove(delObj);
+			final List<CoursePlan> delList = new ArrayList<CoursePlan>();
+			delList.add(delObj);
+			final BizRequest req = new BizRequest();
+			req.setReqType(AppConst.SRV_DELETE_COURSE);
+			req.setSourceList(delList);
+			req.setBatchNo(SELECTED_BATCH.getBatchNo());
+			String json = JSON.toJSONString(req);
+			final Message msg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+			try {
+				//IFuture future = 
+				Object obj = Manage.CLIENT.requestTwoWay(msg);
+				//TODO
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private void createMtzjTable(final GridPane gp, final Stage stage) {
+		//表格宽度
+		TABLE.setMinWidth(stage.getWidth() - 70);
+		TABLE.setMinHeight(stage.getHeight() - 170);
+		
+		//表格列
+		final TableColumn<CoursePlan, Integer> coursePlanId = new TableColumn<CoursePlan, Integer>("序号");
+		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(30);
+		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(50);
+		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(30);
+		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);
+		//学校选择的课程IDs
+		final TableColumn<CoursePlan, String> schSelectIds = new TableColumn<CoursePlan, String>("选择课程ID");
+		schSelectIds.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("schSelectIds"));
+		schSelectIds.setVisible(false);
+		final TableColumn<CoursePlan, String> schSelectNames = new TableColumn<CoursePlan, String>("选择该课程学校");
+		schSelectNames.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("schSelectNames"));
+		schSelectNames.setMinWidth(120);
+		//该课程最后被学校选择的时间
+		final TableColumn<CoursePlan, String> schSelectTime = new TableColumn<CoursePlan, String>("选择时间");
+		schSelectTime.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("schSelectTime"));
+		schSelectTime.setMinWidth(120);
+		//命题办指定的学校ID
+		final TableColumn<CoursePlan, String> specificId = new TableColumn<CoursePlan, String>("命题办指定ID");
+		specificId.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("schoolId"));
+		specificId.setMinWidth(200);
+		specificId.setVisible(false);
+		
+		final TableColumn<CoursePlan, ComboBox<School>> comboBoxCol = new TableColumn<CoursePlan, ComboBox<School>>("命题办指定学校");
+		comboBoxCol.setCellValueFactory(new PropertyValueFactory<CoursePlan, ComboBox<School>>("comboBox"));
+		comboBoxCol.setMinWidth(250);
+		
+		//最终认定学校
+//		final TableColumn<CoursePlan, String> affirmSchoole = new TableColumn<CoursePlan, String>("最终认定学校");
+//		affirmSchoole.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("affirmSchooleName"));
+//		affirmSchoole.setMinWidth(150);
+	//	affirmSchoole.setVisible(false);
+		
+		final TableColumn<CoursePlan, String> confirmDescCol = new TableColumn<CoursePlan, String>("是否已确认");
+		confirmDescCol.setCellValueFactory(new PropertyValueFactory<CoursePlan, String>("confirmDesc"));
+		confirmDescCol.setMinWidth(90);
+		
+		
+		TABLE.setItems(DATALIST);
+		TABLE.getColumns().addAll(coursePlanId, courseCode, courseName, outlineName, bookName, 
+				bianZhu, press, publications, cengci, schSelectIds, schSelectNames, schSelectTime, specificId, comboBoxCol, //affirmSchoole,
+				confirmDescCol);
+		
+		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, 10, 1);
+	}
+	
+	//导入征集计划
+	private void impPlanFunc(final Stage stage) {
+		Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+		if (SELECTED_BATCH == null) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择批次号!");
+			return;
+		}
+		final FileChooser fileChooser = new FileChooser();
+		configureFileChooser(fileChooser);
+		final File excelFile = fileChooser.showOpenDialog(stage);
+		if (excelFile != null) {
+			try {
+				//解析excel文件并导入.
+				final Map<String, Object> map = POIUtil.readExcel(excelFile);
+				if (map.get("errMsg").equals("")) {
+					List<CoursePlan> coursePlanList = (List<CoursePlan>)map.get("data");
+					final List<School> schoolList = ShoolDateUtil.loadSchoolData();
+					for (CoursePlan course : coursePlanList) {
+						course.getComboBox().getItems().addAll(schoolList);
+					}
+					DATALIST.clear();
+					DATALIST.addAll(coursePlanList);
+					//save to file
+					CoursePlanDataUtil.writeCoursePlan(coursePlanList, SELECTED_BATCH.getBatchId());
+					//update batch status
+					BatchDataUtil.updateBatchStatus(SELECTED_BATCH.getBatchId(), BatchConst.STATUS_IMPORTED);
+					//更新下拉框显示-下拉框显示
+					updateComboShow(Integer.valueOf(BatchConst.STATUS_IMPORTED));
+					//提示
+			        DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "成功导入征集计划.");
+				} else {
+					//显示导入报错信息.
+					//TODO ...
+				}
+			} catch (OldExcelFormatException e) {
+				DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "当前格式为excel-95,请另存为97-2003格式后再导入!");
+			}
+        }
+	}
+	
+	private void configureFileChooser(final FileChooser fileChooser) {
+		    fileChooser.setTitle("选择征集计划.");
+		fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));                 
+		fileChooser.getExtensionFilters().addAll(
+			new FileChooser.ExtensionFilter("xls", "*.xls"),
+		    new FileChooser.ExtensionFilter("xlsx", "*.xlsx")
+		  
+		);
+	}
+	
+	//提交命题办干预-指定的学校,学校收到不能改
+	private void specificFunc(final Stage stage) {
+		final BizRequest req = new BizRequest();
+		req.setReqType(AppConst.SRV_SPECIFIC_COURSE);
+		final List<CoursePlan> specificList = new ArrayList<CoursePlan>();
+		for (CoursePlan courseplan : DATALIST) {
+			School specificSchool = courseplan.getComboBox().getSelectionModel().getSelectedItem();
+			if (specificSchool != null) {//命题办指定了学校,貌似初始值为0
+				courseplan.setSchoolId(specificSchool.getSchoolId());
+				courseplan.setSchoolName(specificSchool.getSchoolName());
+				specificList.add(courseplan);
+			}
+		}
+		if (specificList.size() == 0) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择学校!");
+			return;
+		}
+		req.setSourceList(specificList);
+		String json = JSON.toJSONString(req);
+		Message msg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+		try {
+			//IFuture future = 
+			Object obj = Manage.CLIENT.requestTwoWay(msg);
+			if (obj != null) {
+				//提示
+		        DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "成功指定学校.");
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			//TODO
+		}
+	}
+	
+	//发送数据到客户端.
+	private void startMtzjFunc(Stage stage) {
+		final BizRequest req = new BizRequest();
+		req.setZjStatus(BatchConst.STATUS_COLLECTING);
+		req.setReqType(AppConst.SRV_FRESH_INIT_LIST);
+		req.setSourceList(DATALIST);
+		String json = JSON.toJSONString(req);
+		Message msg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+		try {
+			//IFuture future = 
+			Object retObj = Manage.CLIENT.requestTwoWay(msg);
+			if (retObj.toString().equals("0")) {
+				//update batch status
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				BatchDataUtil.updateBatchStatus(SELECTED_BATCH.getBatchId(), BatchConst.STATUS_COLLECTING);
+				//更新下拉框显示-下拉框显示
+				updateComboShow(Integer.valueOf(BatchConst.STATUS_COLLECTING));
+				DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "已开启征集.");
+			} else {
+				//失败了,有客户端没有收到!
+				DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "部分失败", "有客户端没有收到征集!");
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	//定时刷新线程
+	private void startSheduleThread(final Stage stage) {
+		if (SystTab.FRSH_MINUTES > 0) {
+			Runnable command = new Runnable() {
+
+				@Override
+				public void run() {
+					try {
+						pushLastestZjDataFunc(stage);
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+				
+			};
+			int firstDelay = SystTab.FRSH_MINUTES;
+			SCHEDULE_FUTRUE = SCHEDULE_FRESH_SERVICE.scheduleAtFixedRate(command, firstDelay, SystTab.FRSH_MINUTES, TimeUnit.SECONDS);
+		}
+	}
+	
+	//推送最新选择情况到学校
+	private void pushLastestZjDataFunc(final Stage stage) {
+		final List<CoursePlan> notifyList = new ArrayList<CoursePlan>();
+		for (CoursePlan courseplan : DATALIST) {
+			if (courseplan.getSchSelectIds() != null && courseplan.getSchSelectIds().length() > 0) {
+				final CoursePlan cp = new CoursePlan();
+				cp.setCoursePlanId(courseplan.getCoursePlanId());
+				cp.setSchSelectIds(courseplan.getSchSelectIds());
+				notifyList.add(cp);
+			}
+		}
+		if (notifyList.size() > 0) {
+			final BizRequest req = new BizRequest();
+			req.setReqType(AppConst.SRV_TRIGGER_FRESH);
+			req.setSourceList(notifyList);
+			String json = JSON.toJSONString(req);
+			final Message msg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+			try {
+				//IFuture future = 
+				//Manage.CLIENT.requestToClient(msg).get();
+				Object obj = Manage.CLIENT.requestTwoWay(msg);
+				if (obj != null) {
+					DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "成功刷新客户端.");
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+				//TODO...
+			}
+		}
+	}
+	
+	//刷新后把征集下拉框中学校过滤显示
+	private void filterZjSelectList() {
+		for (CoursePlan cp : DATALIST) {
+			if (StringHelper.isNotEmpty(cp.getSchSelectIds())) {
+				cp.getComboBox().getItems().clear();
+				cp.getComboBox().getItems().addAll(cp.getMultiSelectList());
+			}
+		}
+		TABLE.refresh();
+	}
+	
+	//最终发布
+	private void finalPublishFunc(Stage stage, Batch selectBatch) {
+		for (CoursePlan courseplan : DATALIST) {
+			if (courseplan.getComboBox().getSelectionModel().getSelectedItem() == null
+					&& courseplan.getSchSelectIds() == null) {//命题办指定了学校,貌似初始值为0
+				DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "提示", 
+						"课程代码:" + courseplan.getCourseCode() + " 征集学校为空,请确认!");
+				return;
+			}
+			//存在多个学校选择情况
+			if (courseplan.getSchSelectIds() != null && courseplan.getSchSelectIds().contains("|")
+					&& courseplan.getComboBox().getSelectionModel().getSelectedItem() == null) {
+				DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "提示", 
+						"课程代码:" + courseplan.getCourseCode() + " 需指定学校!");
+				return;
+			}
+		}
+		final BizRequest req = new BizRequest();
+		req.setReqType(AppConst.SRV_FINAL_PUBLISH);
+		req.setSourceList(DATALIST);
+		req.setBatchNo(selectBatch.getBatchNo());
+		String json = JSON.toJSONString(req);
+		final Message msg = new Message(MsgTypeConst.TYPE_BIZ_RESP_TO_CLIENT, json);
+		try {
+			//IFuture future = 
+			Object obj = Manage.CLIENT.requestTwoWay(msg);
+			if (obj != null && obj.toString().equals("0")) {
+				//修改服务端数据库文件
+				for (CoursePlan courseplan : DATALIST) {
+					School specificSchool = courseplan.getComboBox().getSelectionModel().getSelectedItem();
+					if (specificSchool != null) {
+						courseplan.setSchoolId(specificSchool.getSchoolId());
+						courseplan.setSchoolName(specificSchool.getSchoolName());
+					}
+				}
+				//修改批次为已发布
+				//save to file
+				Batch SELECTED_BATCH = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+				CoursePlanDataUtil.updateCoursePlan(DATALIST, SELECTED_BATCH.getBatchId());
+				//更新批次文件
+				BatchDataUtil.updateBatchStatus(SELECTED_BATCH.getBatchId(), BatchConst.STATUS_PUBLISHED);
+				//更新下拉框显示-下拉框
+				updateComboShow(Integer.valueOf(BatchConst.STATUS_PUBLISHED));
+				//停止刷新
+				destroyScheduleFreshCommand();
+				//提示
+				DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "成功发布计划.");
+				disablePubBtn();
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			//TODO
+		}
+	}
+	//发布后修改按钮状态
+	private void disablePubBtn() {
+		//其他按钮disable
+		BTN_IMP.setDisable(true);
+		BTN_PUBLISH.setDisable(true);
+		BTN_START.setDisable(true);
+		BTN_SPECIFIC.setDisable(true);
+		BTN_MANUAL.setDisable(true);
+		BTN_DELETE.setDisable(true);
+		BTN_EXPORT.setDisable(false);
+	}
+	
+    private synchronized void destroyScheduleFreshCommand() {
+        try {
+            if (SCHEDULE_FUTRUE != null && !SCHEDULE_FUTRUE.isDone()) {
+            	SCHEDULE_FUTRUE.cancel(true);
+            	SCHEDULE_FRESH_SERVICE.purge();
+            	SCHEDULE_FUTRUE = null;
+            }
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+    }
+	
+	private void updateComboShow(final Integer status) {
+		int oldSelectIdx = BATCH_SELECT_BOX.getSelectionModel().getSelectedIndex();
+		Batch oldSelect = BATCH_SELECT_BOX.getSelectionModel().getSelectedItem();
+		oldSelect.setStatus(status);
+		oldSelect.setStatusDes(BatchDataUtil.getStatusCaption(status.byteValue()));
+		BATCH_COMBO_DATA_LIST.remove(oldSelectIdx);
+		BATCH_COMBO_DATA_LIST.add(oldSelect);
+		BATCH_SELECT_BOX.getSelectionModel().select(oldSelect);
+		//修改批次列表状态显示
+		for (Batch ba : BatchTab.DATALIST) {
+			if (ba.getBatchId().longValue() == oldSelect.getBatchId().longValue()) {
+				ba.setStatus(status);
+				ba.setStatusDes(oldSelect.getStatusDes());
+				break;
+			}
+		}
+		BatchTab.TABLE.refresh();
+	}
+	
+	/**
+	 * 新增学校后改变征集列表的 学校下拉框.
+	 * @param school
+	 */
+	public static void addSchoolToListCombo(final School school) {
+		for (CoursePlan courseplan : DATALIST) {
+			courseplan.getComboBox().getItems().add(school);
+		}
+	}
+	
+	/**
+	 * 删除学校后改变征集列表的 学校下拉框.
+	 * @param school
+	 */
+	public static void delSchoolToListCombo(final School school) {
+		for (CoursePlan courseplan : DATALIST) {
+			courseplan.getComboBox().getItems().remove(school);
+		}
+	}
+}

+ 295 - 0
src/com/hmsoft/bizpanel/SchoolTab.java

@@ -0,0 +1,295 @@
+package com.hmsoft.bizpanel;
+
+import java.util.Optional;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.common.util.ShoolDateUtil;
+import com.hmsoft.model.School;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+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;
+import javafx.scene.control.Button;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Label;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class SchoolTab extends Tab {
+
+	private Button btnAdd;
+	private Button btnDelete;
+	public static final TableView<School> SCH_TABLE = new TableView<School>();
+	public static final ObservableList<School> DATALIST = FXCollections.observableArrayList();
+	
+	public SchoolTab(final Stage stage) {
+		super();
+		this.setText("学校维护");
+		this.setId("schoolTab");
+		this.setClosable(false);
+		
+		//Grid容器
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		
+		//创建按钮
+		this.createBtn(stage, gp);
+		//创建表格
+		this.createSchoolTable(gp, stage);
+		//grid布局加到容器
+		this.setContent(gp);
+	}
+	
+	private void createBtn(final Stage stage, final GridPane gp) {
+		//按钮
+		btnAdd = new Button();
+		btnAdd.setText("添加学校");
+		
+		btnDelete = new Button();
+		btnDelete.setText("删除学校");
+		
+		Button btnPassReset = new Button();
+		btnPassReset.setText("密码重置");
+	
+		btnAdd.setOnAction(new EventHandler<ActionEvent>() {
+			
+			@Override
+			public void handle(ActionEvent event) {
+				btnAdd.setDisable(true);
+				showCreateWin();
+			}
+		});
+		
+		btnDelete.setOnAction(new EventHandler<ActionEvent>() {
+			
+			@Override
+			public void handle(ActionEvent event) {
+				deleteSchoolFunc(stage);
+			}
+		});
+		
+		btnPassReset.setOnAction(new EventHandler<ActionEvent>() {
+			
+			@Override
+			public void handle(ActionEvent event) {
+				resetPassFunc(stage, "123456");
+			}
+		});
+		
+		gp.add(btnAdd, 0, 0);
+		gp.add(btnDelete, 1, 0);
+		gp.add(btnPassReset, 2, 0);
+	}
+	
+	@SuppressWarnings("unchecked")
+	private void createSchoolTable(final GridPane gp, final Stage stage) {
+		//设置表格整体宽度
+		SCH_TABLE.setMinWidth(stage.getWidth() - 70);
+		SCH_TABLE.setMinHeight(stage.getHeight() - 170);
+		//表格列
+		final TableColumn<School, Integer> idCol = new TableColumn<School, Integer>("序号");
+		idCol.setCellValueFactory(new PropertyValueFactory<School, Integer>("id"));
+		idCol.setMinWidth(30);
+		final TableColumn<School, Integer> schoolIdCol = new TableColumn<School, Integer>("学校序号");
+		schoolIdCol.setCellValueFactory(new PropertyValueFactory<School, Integer>("schoolId"));
+		schoolIdCol.setMinWidth(30);
+		final TableColumn<School, String> schoolNameCol = new TableColumn<School, String>("学校名称");
+		schoolNameCol.setCellValueFactory(new PropertyValueFactory<School, String>("schoolName"));
+		schoolNameCol.setMinWidth(200);
+		final TableColumn<School, String> userNameCol = new TableColumn<School, String>("登录账号");
+		userNameCol.setCellValueFactory(new PropertyValueFactory<School, String>("userName"));
+		userNameCol.setMinWidth(150);
+		final TableColumn<School, String> statusCol = new TableColumn<School, String>("连接状态");
+		statusCol.setCellValueFactory(new PropertyValueFactory<School, String>("statusDesc"));
+		statusCol.setMinWidth(150);
+		final TableColumn<School, String> connectedTimeCol = new TableColumn<School, String>("开始连接时间");
+		connectedTimeCol.setCellValueFactory(new PropertyValueFactory<School, String>("connectedTime"));
+		connectedTimeCol.setMinWidth(150);
+		final TableColumn<School, String> disconnectTimeCol = new TableColumn<School, String>("断开时间");
+		disconnectTimeCol.setCellValueFactory(new PropertyValueFactory<School, String>("disconnectTime"));
+		disconnectTimeCol.setMinWidth(150);
+		//添加列到表格
+		SCH_TABLE.getColumns().addAll(idCol,schoolIdCol, schoolNameCol, userNameCol, statusCol, connectedTimeCol, disconnectTimeCol);
+		DATALIST.addAll(ShoolDateUtil.loadSchoolData());
+		SCH_TABLE.setItems(DATALIST);
+		//添加显示表格
+		final VBox vbox = new VBox();
+        vbox.setSpacing(5);
+        vbox.setPadding(new Insets(10, 0, 0, 10));
+        vbox.getChildren().addAll(SCH_TABLE);
+        
+        gp.add(vbox, 0, 1, 3, 1);
+	}
+	
+	/**
+	 * 弹出新建窗口.
+	 */
+	private void showCreateWin() {
+		final Stage addStage = new Stage();
+		addStage.setTitle("添加学校");
+		final GridPane grid = new GridPane();
+		grid.setAlignment(Pos.CENTER);
+		grid.setHgap(10);
+		grid.setVgap(10);
+		grid.setPadding(new Insets(50));
+		
+		//学校名称框
+		Label schoolName = new Label("学校名称: ");
+		grid.add(schoolName, 0, 1);
+		final TextField schoolNameField = new TextField();
+		grid.add(schoolNameField, 1, 1);
+		//登录账号框
+		Label schoolActNo = new Label("登录账号:");
+		grid.add(schoolActNo, 0, 2);
+		final TextField schoolActNoField = new TextField();
+		grid.add(schoolActNoField, 1, 2);
+		//登录密码
+		Label schoolActPassWord = new Label("登录密码:");
+		grid.add(schoolActPassWord, 0, 3);
+		final TextField schoolActPassWordField = new TextField();
+		schoolActPassWordField.setOnKeyPressed(new EventHandler<KeyEvent>() {
+			@Override
+			public void handle(KeyEvent event) {
+				if (event.getCode() == KeyCode.ENTER) {
+					createSchoolFunc(addStage, schoolNameField, schoolActNoField, schoolActPassWordField);
+				}
+			}
+		});
+		grid.add(schoolActPassWordField, 1, 3);
+		
+		final HBox hbBtn = new HBox(10);
+		grid.add(hbBtn, 1, 4);
+		
+		Button btnSave = new Button("保存");
+		Button btncancel = new Button("取消");
+		hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
+		hbBtn.getChildren().add(btnSave);
+		hbBtn.getChildren().add(btncancel);
+		
+		//新建窗口退出事件
+		addStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
+			@Override
+			public void handle(WindowEvent event) {
+				btnAdd.setDisable(false);
+			}
+		});
+		
+		btnSave.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				createSchoolFunc(addStage, schoolNameField, schoolActNoField, schoolActPassWordField);
+			}
+		});
+		
+		btncancel.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				btnAdd.setDisable(false);
+				addStage.close();
+			}
+		});
+		
+		Scene scene = new Scene(grid, 400, 220);
+		addStage.setScene(scene);
+		addStage.setResizable(false);
+		addStage.alwaysOnTopProperty();
+		addStage.show();
+	}
+	
+	/**
+	 * 删除批次.
+	 */
+	private void deleteSchoolFunc(Stage stage) {
+		School selected = SCH_TABLE.getSelectionModel().getSelectedItem();
+		if (selected == null) {
+			DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请选择学校!");
+			return;
+		}
+		Alert confirm = new Alert(Alert.AlertType.CONFIRMATION,
+				"", 
+				new ButtonType("否", ButtonData.NO),
+                new ButtonType("是", ButtonData.YES));
+		confirm.setTitle("请确认");
+		confirm.setHeaderText("是否删除?");
+		confirm.initOwner(stage);
+		Optional<ButtonType> btnType = confirm.showAndWait();
+		if (btnType.get().getButtonData().equals(ButtonData.YES)) {
+			ShoolDateUtil.deleteSchool(selected.getSchoolId());
+			DATALIST.remove(selected);//列表删除
+			//删除征集列表 学校下拉框对应学校
+			MtzjTab.delSchoolToListCombo(selected);
+		}
+	}
+	
+	private void createSchoolFunc(final Stage addStage, final TextField schoolNameField, 
+			final TextField schoolActNoField, final TextField schoolActPassWordField) {
+		try {
+			for (School school : DATALIST) {
+				if (school.getSchoolName().equals(schoolNameField.getText())) {
+					DialogUtil.showAlertDialog(addStage, Alert.AlertType.WARNING, "提示", "学校名称不能重复!");
+					return;
+				}
+				if (school.getUserName().equals(schoolActNoField.getText())) {
+					DialogUtil.showAlertDialog(addStage, Alert.AlertType.WARNING, "提示", "学校登录账号不能重复!");
+					return;
+				}
+			}
+			final School school = new School();
+			school.setSchoolId(System.currentTimeMillis());
+			school.setSchoolName(schoolNameField.getText());
+			school.setUserName(schoolActNoField.getText());
+			school.setPassWord(schoolActPassWordField.getText());
+			ShoolDateUtil.insertSchool(school);
+			DATALIST.add(school);
+			//修改征集列表 学校下拉框
+			MtzjTab.addSchoolToListCombo(school);
+		} catch(Exception e) {
+			e.printStackTrace();
+		}
+		addStage.close();
+		btnAdd.setDisable(false);
+	}
+	
+	private void resetPassFunc(final Stage addStage, final String passwd) {
+		School school = SCH_TABLE.getSelectionModel().getSelectedItem();
+		if (school == null) {
+			DialogUtil.showAlertDialog(addStage, Alert.AlertType.WARNING, "提示", "请选择学校!");
+			return;
+		}
+		if (DialogUtil.confirm("确认重置?", addStage)) {
+			ShoolDateUtil.resetPasswd(school.getSchoolId(), passwd);
+			changeTablePasswdData(school.getSchoolId(), passwd);
+			DialogUtil.showAlertDialog(addStage, Alert.AlertType.INFORMATION, "成功", "密码已重置.");
+		}
+	}
+	
+	public static void changeTablePasswdData(long schId, String passwd) {
+		for (School sch : SchoolTab.DATALIST) {
+			if (sch.getSchoolId().longValue() == schId) {
+				sch.setPassWord(passwd);
+				break;
+			}
+		}
+	}
+}

+ 154 - 0
src/com/hmsoft/bizpanel/StatisTab.java

@@ -0,0 +1,154 @@
+package com.hmsoft.bizpanel;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.common.util.POIUtil;
+import com.hmsoft.model.Course;
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.School;
+import com.hmsoft.model.StatisObj;
+
+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.Button;
+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.FileChooser;
+import javafx.stage.Stage;
+
+/**
+ * 统计
+ * @author zq
+ *
+ */
+public class StatisTab extends Tab {
+	public static final TableView<StatisObj> STATIS_TABLE = new TableView<StatisObj>();
+	public static final ObservableList<StatisObj> STATIS_DATALIST = FXCollections.observableArrayList();
+	
+	
+	public StatisTab(final Stage stage) {
+		super();
+		this.setText("征集统计");
+		this.setId("statisTabId");
+		this.setClosable(false);
+		//Grid容器
+		GridPane gp = new GridPane();
+		gp.setPadding(new Insets(20));
+		gp.setHgap(5);
+		gp.setVgap(5);
+		//创建按钮
+		this.createBtn(stage, gp);
+		//创建表格
+		this.createStatisTable(gp, stage);
+		//grid布局加到容器
+		this.setContent(gp);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void createStatisTable(final GridPane gp, final Stage stage) {
+		//设置表格整体宽度
+		STATIS_TABLE.setMinWidth(stage.getWidth() - 70);
+		STATIS_TABLE.setMinHeight(stage.getHeight() - 170);
+		//设置所有列
+		TableColumn<StatisObj, String> schoolName = new TableColumn<StatisObj, String>("学校名称");
+		schoolName.setCellValueFactory(new PropertyValueFactory<StatisObj, String>("schoolName"));
+		schoolName.setMinWidth(150);
+		TableColumn<StatisObj, Integer> count = new TableColumn<StatisObj, Integer>("课程数量");
+		count.setCellValueFactory(new PropertyValueFactory<StatisObj, Integer>("count"));
+		count.setMinWidth(100);
+		TableColumn<StatisObj, ComboBox<Course>> comboBox = new TableColumn<StatisObj, ComboBox<Course>>("课程名称");
+		comboBox.setCellValueFactory(new PropertyValueFactory<StatisObj, ComboBox<Course>>("comboBox"));
+		comboBox.setMinWidth(260);
+		
+		STATIS_TABLE.setItems(STATIS_DATALIST);
+		STATIS_TABLE.getColumns().addAll(schoolName, count, comboBox);
+		
+		final VBox vbox = new VBox();
+        vbox.setSpacing(5);
+        vbox.setPadding(new Insets(10, 0, 0, 10));
+        vbox.getChildren().addAll(STATIS_TABLE);
+        
+        gp.add(vbox, 0, 1, 2, 1);//第1行
+	}
+
+	private void createBtn(final Stage stage, final GridPane gp) {
+		Button btnFrs = new Button();
+		btnFrs.setText("刷新统计");
+		Button btnExport = new Button();
+		btnExport.setText("统计导出");
+		btnFrs.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				Map<Long, List<Course>> selectMap = new HashMap<Long, List<Course>>();
+				Map<Long, String> schoolMap = new HashMap<Long, String>();
+				for (School sch : SchoolTab.DATALIST) {
+					selectMap.put(sch.getSchoolId(), new ArrayList<Course>());
+					schoolMap.put(sch.getSchoolId(), sch.getSchoolName());
+				}
+				for (CoursePlan cp : MtzjTab.DATALIST) {
+					//指定的
+					if (cp.getSchoolId() != null && cp.getSchoolId() > 0) {
+						List<Course> list = selectMap.get(cp.getSchoolId());
+						list.add(new Course(cp.getCourseCode(), cp.getCourseName()));
+					} else {
+						//可能多学校选择
+						if (cp.getSchSelectIds() != null && cp.getSchSelectIds().length() > 0) {
+							String[] arr = cp.getSchSelectIds().split("\\|");
+							for (String idstr : arr) {
+								List<Course> list = selectMap.get(Long.valueOf(idstr));
+								list.add(new Course(cp.getCourseCode(), cp.getCourseName()));
+							}
+						}
+					}
+				}
+				//sort and show
+				List<StatisObj> statisList = new ArrayList<StatisObj>();
+				Set<Long> set = selectMap.keySet();
+				for (Long schoolId : set) {
+					List<Course> courList = selectMap.get(schoolId);
+					statisList.add(new StatisObj(schoolId, schoolMap.get(schoolId), courList.size(), courList));
+				}
+				Collections.sort(statisList);
+				//
+				STATIS_DATALIST.clear();
+				STATIS_DATALIST.addAll(statisList);
+			}
+		});
+		btnExport.setOnAction(new EventHandler<ActionEvent>() {
+
+			@Override
+			public void handle(ActionEvent event) {
+				if (DialogUtil.confirm("是否导出?", stage)) {
+					FileChooser fileChooser = new FileChooser();
+					fileChooser.setTitle("导出");
+					fileChooser.setInitialFileName("征集统计.xls");
+					fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XLS Files", "*.xls"));
+					File file = fileChooser.showSaveDialog(stage);
+					if(file != null) {
+						POIUtil.writeStatisToXlsx(STATIS_DATALIST, file.getAbsolutePath());
+						DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "导出成功.");
+					}
+				}
+			}
+			
+		});
+		gp.add(btnFrs, 0, 0);
+		gp.add(btnExport, 1, 0);
+	}
+}

+ 147 - 0
src/com/hmsoft/bizpanel/SystTab.java

@@ -0,0 +1,147 @@
+package com.hmsoft.bizpanel;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Properties;
+import com.hmsoft.common.FileConst;
+import com.hmsoft.common.util.DialogUtil;
+import com.hmsoft.common.util.RegexpUtils;
+import com.hmsoft.common.util.SysCfgUtil;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.RadioButton;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TextField;
+import javafx.scene.control.Toggle;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.layout.GridPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class SystTab extends Tab {
+
+	public static volatile Integer FRSH_MINUTES = 0;//默认为0-自动,>0-定时手动刷新,单位为分组
+	
+	public SystTab(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);
+		
+		try {
+			this.createTimeRadio(stage, gp);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		//grid布局加到容器
+		this.setContent(gp);
+	}
+
+	private void createTimeRadio(final Stage stage, final GridPane gp) throws FileNotFoundException, IOException {
+		final Button btnSave = new Button("保存");
+		final ToggleGroup group = new ToggleGroup();
+		
+		final RadioButton auto = new RadioButton("自动刷新");
+		final RadioButton shedule = new RadioButton("定时刷新");
+		final RadioButton manual = new RadioButton("手动刷新");
+		
+		Text sheduleStartText = new Text(" 每隔");
+		Text sheduleEndText = new Text("分钟刷新一次");
+		final TextField minutesField = new TextField();
+		minutesField.setMinWidth(60);
+		auto.setToggleGroup(group);
+		shedule.setToggleGroup(group);
+		manual.setToggleGroup(group);
+		
+		Properties props = SysCfgUtil.loadFromFile(FileConst.SYS_CFG_PATH);
+		Object obj = props.get(FileConst.FRSH_MINUTES_KEY);
+		if (obj == null || Integer.parseInt(obj.toString()) == 0) {
+			auto.setSelected(true);
+		} 
+		if (obj != null && Integer.parseInt(obj.toString()) > 0) {
+			shedule.setSelected(true);
+			minutesField.setText(obj.toString());
+			FRSH_MINUTES = Integer.parseInt(obj.toString());
+		}
+		if (obj != null && Integer.parseInt(obj.toString()) == -1) {
+			manual.setSelected(true);
+			FRSH_MINUTES = -1;
+			//
+			minutesField.setDisable(true);
+			minutesField.setText("");
+		}
+		gp.add(auto, 0, 0);
+		gp.add(shedule, 0, 1);
+		gp.add(sheduleStartText, 1, 1);
+		gp.add(minutesField, 2, 1);
+		gp.add(sheduleEndText, 3, 1);
+		gp.add(manual, 0, 2);
+		gp.add(btnSave, 0, 5);//上面空一行
+		//group
+		group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
+
+			@Override
+			public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
+				RadioButton newRB = (RadioButton)newValue;
+				if (newRB.getText().equals("自动刷新")) {
+					minutesField.setText("");
+					minutesField.setDisable(true);
+				} else if (newRB.getText().equals("定时刷新")) {
+					minutesField.setDisable(false);
+				} else if (newRB.getText().equals("手动刷新")) {
+					minutesField.setText("");
+					minutesField.setDisable(true);
+				}
+			}
+			
+		});
+		btnSave.setOnAction(new EventHandler<ActionEvent>() {
+			
+			@Override
+			public void handle(ActionEvent event) {
+				if (auto.isSelected()) {//自动
+					FRSH_MINUTES = 0;
+					MtzjTab.BTN_MANUAL.setDisable(true);
+				} else if (shedule.isSelected()) {//定时刷新
+					if (RegexpUtils.checkIsPositiveIntGtZero(minutesField.getText())) {
+						FRSH_MINUTES = Integer.parseInt(minutesField.getText());
+						MtzjTab.BTN_MANUAL.setDisable(true);
+					} else {
+						DialogUtil.showAlertDialog(stage, Alert.AlertType.WARNING, "提示", "请输入非0正整数!");
+						return;
+					}
+				} else if (manual.isSelected()) {//手动刷新
+					FRSH_MINUTES = -1;
+					MtzjTab.BTN_MANUAL.setDisable(false);
+				}
+				try {
+					final Properties pro = new Properties();
+					pro.setProperty(FileConst.FRSH_MINUTES_KEY, String.valueOf(FRSH_MINUTES));
+					SysCfgUtil.storeToFile(pro, FileConst.SYS_CFG_PATH);
+					DialogUtil.showAlertDialog(stage, Alert.AlertType.INFORMATION, "成功", "设置成功.");
+				} catch (FileNotFoundException e) {
+					e.printStackTrace();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		});
+	}
+}

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

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

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

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

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

@@ -0,0 +1,25 @@
+package com.hmsoft.common;
+
+import java.io.File;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class FileConst {
+	
+	public static final String FILE_NEWLINE = "\r\n";
+	public static String SYS_HOME = System.getenv("MTZJ_HOME") == null ? System.getProperty("user.dir") : System.getenv("MTZJ_HOME");
+	public static String SCHOOL_DATA_PATH = SYS_HOME + File.separator + "Data" + File.separator + "SchoolStore";
+	public static String Batch_DATA_PATH = SYS_HOME + File.separator + "Data" + File.separator + "BatchStore";
+	public static String COURSEPLAN_DATA_PATH = SYS_HOME + File.separator + "Data" + File.separator + "CoursePlanStore";
+	//server.properties
+	public static String CONF_SERVER_PATH =  SYS_HOME + File.separator + "Cfg" + File.separator + "server.properties";
+	public static String SYS_CFG_PATH =  SYS_HOME + File.separator + "Data" + File.separator + "SysCfg.properties";
+	//
+	public static String KEY_IP = "server.ip";
+	public static String KEY_PORT = "server.port";
+	//
+	public static String FRSH_MINUTES_KEY = "frsh.minutes";
+}

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

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

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

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

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

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

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

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

+ 30 - 0
src/com/hmsoft/common/cell/CheckBoxButtonTableCell.java

@@ -0,0 +1,30 @@
+package com.hmsoft.common.cell;
+
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.TableCell;
+
+public class CheckBoxButtonTableCell<S, T> extends TableCell<S, T> {
+
+	private CheckBox chebox;
+	private ObservableValue<T> ov;
+	
+    public CheckBoxButtonTableCell() {
+        this.chebox = new CheckBox();
+        setGraphic(chebox);
+    }
+    
+    @Override
+    protected void updateItem(T item, boolean empty) {
+        //System.out.println("empty---" + empty);
+        super.updateItem(item, empty);
+        if (empty) {
+            setText(null);
+            setGraphic(null);
+        } else {
+            chebox.setSelected(false);
+            setGraphic(chebox);
+           
+        }
+    }
+}

+ 34 - 0
src/com/hmsoft/common/cell/CombSelectTableCell.java

@@ -0,0 +1,34 @@
+package com.hmsoft.common.cell;
+
+
+import com.hmsoft.common.util.ShoolDateUtil;
+import com.hmsoft.model.School;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.TableCell;
+
+public class CombSelectTableCell<S, T> extends TableCell<S, T> {
+
+	private ComboBox<School> comboBox;
+	private ObservableValue<T> ov;
+	
+	public CombSelectTableCell() {
+        this.comboBox = new ComboBox<School>();
+        this.comboBox.setMinWidth(180);
+        this.comboBox.getItems().addAll(ShoolDateUtil.loadSchoolData());
+        setGraphic(comboBox);
+	}
+	
+    @Override
+    protected void updateItem(T item, boolean empty) {
+        //System.out.println("empty---" + empty);
+        super.updateItem(item, empty);
+        //this.setTextFill(Color.RED);
+        if (empty) {
+            setText(null);
+            setGraphic(null);
+        } else {
+            setGraphic(comboBox);
+        }
+    }
+}

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

@@ -0,0 +1,176 @@
+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.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import com.alibaba.fastjson.JSON;
+import com.hmsoft.common.BatchConst;
+import com.hmsoft.common.FileConst;
+import com.hmsoft.model.Batch;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class BatchDataUtil {
+
+	private BatchDataUtil() {
+		
+	}
+	
+	//load 
+	public static ObservableList<Batch> loadBatchData() {
+		final ObservableList<Batch> dataList = FXCollections.observableArrayList();
+		final File file = new File(FileConst.Batch_DATA_PATH);
+		try {
+			if (!file.exists()) {
+				file.createNewFile();
+			}
+			final List<String> list = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			for (String str : list) {
+				if (str == null || str.equals("")) {
+					continue;
+				}
+				Batch batch = JSON.parseObject(str, Batch.class);
+				dataList.add(batch);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return dataList;
+	}
+	
+	//新增
+	public static void insertBatch(final Batch batch) {
+		PrintWriter printwriter = null;
+		File file = new File(FileConst.Batch_DATA_PATH);
+		try {
+			if (!file.exists()) {
+				file.createNewFile();
+			}
+			List<String> oldList = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			batch.setStatusDes(getStatusCaption(batch.getStatus().byteValue()));
+			String jsonBatch = JSON.toJSONString(batch);
+			oldList.add(jsonBatch);
+			//清空文件,再添加
+			printwriter = new PrintWriter(file, "UTF-8");
+			for (String json : oldList) {
+				if (json == null || json.equals("")) {
+					continue;
+				}
+				printwriter.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (printwriter != null) {
+				printwriter.flush();
+				printwriter.close();
+			}
+		}
+	}
+	
+	//删除
+	public static void deleteBatch(final Long batchId) {
+		PrintWriter writer = null;
+		File file = new File(FileConst.Batch_DATA_PATH);
+		try {
+			List<String> oldList = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			List<String> newList = new ArrayList<String>();
+			for (String bat : oldList) {
+				Batch batch = JSON.parseObject(bat, Batch.class);
+				if (batch.getBatchId().longValue() != batchId.longValue()) {
+					newList.add(bat);
+				}
+			}
+			//清空文件,再添加
+			writer = new PrintWriter(file, "UTF-8");
+			writer.write("");
+			for (String json : newList) {
+				if (json == null || json.equals("")) {
+					continue;
+				}
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+	//修改
+	public static void updateBatchStatus(final Long batchId, final byte status) {
+		PrintWriter writer = null;
+		File file = new File(FileConst.Batch_DATA_PATH);
+		try {
+			final List<String> oldList = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			List<String> newList = new ArrayList<String>();
+			for (String str : oldList) {
+				Batch batch = JSON.parseObject(str, Batch.class);
+				if (batch.getBatchId().longValue() == batchId.longValue()) {
+					batch.setStatus(new Integer(status));
+					batch.setStatusDes(getStatusCaption(status));
+					if (status == BatchConst.STATUS_PUBLISHED) {
+						batch.setPublishTime(DateUtil.format(LocalDateTime.now()));
+					}
+					str = JSON.toJSONString(batch);
+				}
+				newList.add(str);
+			}
+			//删除之前内容,重新生成新内容
+			writer = new PrintWriter(file, "UTF-8");
+			writer.write("");
+			//new
+			for (String json : newList) {
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+	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;
+	}
+}

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

@@ -0,0 +1,109 @@
+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.School;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class CoursePlanDataUtil {
+
+	private CoursePlanDataUtil() {
+		
+	}
+	
+	//从指定文件中加载征集计划.
+	public static ObservableList<CoursePlan> loadCoursePlanData(final Long batchId) {
+		final ObservableList<CoursePlan> dataList = FXCollections.observableArrayList();
+		final File file = new File(FileConst.COURSEPLAN_DATA_PATH + "-" + batchId);
+		final List<School> schoolList = ShoolDateUtil.loadSchoolData();
+		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);
+					courseplan.getComboBox().getItems().clear();
+					courseplan.getComboBox().getItems().addAll(schoolList);
+					//设置下拉框之前的选中值,比如发布状态
+					if (courseplan.getSchoolId() != null && courseplan.getSchoolId().longValue() > 0) {
+						courseplan.getComboBox().getSelectionModel().select(new School(courseplan.getSchoolId(), courseplan.getSchoolName()));
+					}
+					courseplan.setConfirmDesc(courseplan.getConfirmStatus() == 1 ? "已确认" : "否");
+					dataList.add(courseplan);
+				}
+			}
+
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return dataList;
+	}
+	
+	//导入写到文件中.
+	public static void writeCoursePlan(final List<CoursePlan> coursePlanList, final Long batchId) {
+		PrintWriter writer = null;
+		File destFile = new File(FileConst.COURSEPLAN_DATA_PATH + "-" + batchId);
+		try {
+			writer = new PrintWriter(destFile, "UTF-8");
+			for (CoursePlan obj : coursePlanList) {
+				String json = JSON.toJSONString(obj);
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException | UnsupportedEncodingException e) {
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+	//清空之前内容,用新列表内容修改课程计划文件
+	public static void updateCoursePlan(final List<CoursePlan> coursePlanList, final Long batchId) {
+		PrintWriter writer = null;
+		File file = new File(FileConst.COURSEPLAN_DATA_PATH + "-" + batchId);
+		try {
+			List<String> newList = new ArrayList<String>();
+			for (CoursePlan course : coursePlanList) {
+				String json = JSON.toJSONString(course);
+				newList.add(json);
+			}
+			//删除之前内容,重新生成新内容
+			writer = new PrintWriter(file, "UTF-8");
+			writer.write("");
+			//new
+			for (String json : newList) {
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+}

+ 26 - 0
src/com/hmsoft/common/util/DateUtil.java

@@ -0,0 +1,26 @@
+package com.hmsoft.common.util;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class DateUtil {
+	
+	public final static String LongDateString = "yyyy-MM-dd HH:mm:ss";
+	public final static DateTimeFormatter LongDateFormatter = DateTimeFormatter.ofPattern(LongDateString);
+	
+	/***************************
+	 * 将时间转换为字符串
+	 */
+	public static String format(LocalDateTime value) {
+		try {
+			return value.format(LongDateFormatter);
+		} catch (Exception e) {
+		}
+		return null;
+	}
+}

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

@@ -0,0 +1,42 @@
+package com.hmsoft.common.util;
+
+import java.util.Optional;
+
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.ButtonBar.ButtonData;
+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;
+	}
+}

+ 328 - 0
src/com/hmsoft/common/util/POIUtil.java

@@ -0,0 +1,328 @@
+package com.hmsoft.common.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.hssf.OldExcelFormatException;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import com.hmsoft.model.CoursePlan;
+import com.hmsoft.model.School;
+import com.hmsoft.model.StatisObj;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class POIUtil {
+
+	private POIUtil() {}
+	
+	public static Map<String, Object> readExcel(final File excelFile) {
+		InputStream is = null;
+		try {
+			is = new BufferedInputStream(new FileInputStream(excelFile));
+			if (excelFile.getName().toLowerCase().endsWith("xls")) {
+				return readFromXls(is);
+				//return JxlUril.readFromXls(is);
+			} else if (excelFile.getName().toLowerCase().endsWith("xlsx")) {
+				return readFromXlsx(is);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (OldExcelFormatException e) {
+			throw e; 
+		}
+		finally {
+			if (is != null) {
+				try {
+					is.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return null;
+	}
+	
+	private static Map<String, Object> readFromXls(final InputStream is) {
+		HSSFWorkbook book = null;
+		final Map<String, Object> result = new HashMap<String, Object>();
+		final List<CoursePlan> list = new ArrayList<CoursePlan>();
+		final StringBuilder errorSb = new StringBuilder();
+		final Map<String, String> existCourseMap = new HashMap<String, String>();
+		try {
+			book = new HSSFWorkbook(is);
+        	final HSSFSheet sheet = book.getSheetAt(0);  
+            int lastRow = sheet.getLastRowNum();
+            for (int m=1; m<=lastRow; m++) {
+            	final HSSFRow row = sheet.getRow(m);
+            	String courseCode = row.getCell(0).getStringCellValue();
+            	String courseName = row.getCell(1).getStringCellValue();
+            	String outlineName = row.getCell(2).getStringCellValue();
+            	String bookName = row.getCell(3).getStringCellValue();
+            	String bianZhu = row.getCell(4).getStringCellValue();
+            	String press = row.getCell(5).getStringCellValue();
+            	String publications = row.getCell(6).getStringCellValue();
+            	String cengci = row.getCell(7).getStringCellValue();
+            	//
+            	final CoursePlan courplan = new CoursePlan();
+            	courplan.setBianZhu(bianZhu);
+            	courplan.setBookName(bookName);
+            	courplan.setCourseCode(courseCode);
+            	courplan.setCourseName(courseName);
+            	courplan.setCoursePlanId(m);
+            	courplan.setOutlineName(outlineName);
+            	courplan.setPress(press);
+            	courplan.setPublications(publications);
+            	courplan.setCengci(cengci);
+            	list.add(courplan);
+            	if (existCourseMap.get(courseCode) != null) {
+            		errorSb.append("第").append(m).append("行,课程编码:'" ).append(courseCode).append("'已经存在;");
+            	}
+            }
+		} catch (IOException e) {
+			errorSb.append("导入错误:").append(e.getMessage()).append(";");
+			e.printStackTrace();
+		} catch (OldExcelFormatException e) {
+			throw e; 
+		} finally {
+			if (book != null) {
+				try {
+					book.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		result.put("data", list);
+		result.put("errMsg", errorSb.toString());
+		return result;
+	}
+	
+	private static Map<String, Object> readFromXlsx(final InputStream is) {
+		XSSFWorkbook book = null;
+		final Map<String, Object> result = new HashMap<String, Object>();
+		final List<CoursePlan> list = new ArrayList<CoursePlan>();
+		final StringBuilder errorSb = new StringBuilder();
+		final Map<String, String> existCourseMap = new HashMap<String, String>();
+		try {
+            book = new XSSFWorkbook(is);
+            XSSFSheet sheet = book.getSheetAt(0);  
+            int lastRow = sheet.getLastRowNum();
+            // 将数据项存入map(除去表头即第一行)  
+            for (int m=1; m<=lastRow; m++) {
+            	final XSSFRow row = sheet.getRow(m);
+            	String courseCode = row.getCell(0).getStringCellValue();
+            	String courseName = row.getCell(1).getStringCellValue();
+            	String outlineName = row.getCell(2).getStringCellValue();
+            	String bookName = row.getCell(3).getStringCellValue();
+            	String bianZhu = row.getCell(4).getStringCellValue();
+            	String press = row.getCell(5).getStringCellValue();
+            	String publications = row.getCell(6).getStringCellValue();
+            	String cengci = row.getCell(7).getStringCellValue();
+            	//
+            	final CoursePlan courplan = new CoursePlan();
+            	courplan.setBianZhu(bianZhu);
+            	courplan.setBookName(bookName);
+            	courplan.setCourseCode(courseCode);
+            	courplan.setCourseName(courseName);;
+            	courplan.setCoursePlanId(m);
+            	courplan.setOutlineName(outlineName);
+            	courplan.setPress(press);
+            	courplan.setPublications(publications);
+            	courplan.setCengci(cengci);
+            	//add
+            	list.add(courplan);
+            	if (existCourseMap.get(courseCode) != null) {
+            		errorSb.append("第").append(m).append("行,课程编码:'" ).append(courseCode).append("'已经存在;");
+            	}
+            }
+		} catch (IOException e) {
+			errorSb.append("导入错误:").append(e.getMessage()).append(";");
+			e.printStackTrace();
+		} finally {
+			if (book != null) {
+				try {
+					book.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		result.put("data", list);
+		result.put("errMsg", errorSb.toString());
+		return result;
+	}
+	
+	/**
+	 * 导出计划.
+	 * @param path
+	 * @param planList
+	 */
+	public static void writeToXlsx(final List<CoursePlan> planList, final String path) {
+		HSSFWorkbook workbook = new HSSFWorkbook();
+		HSSFSheet sheet = workbook.createSheet("最终计划");
+		sheet.setDefaultColumnWidth((short) 15);
+		HSSFRow row = sheet.createRow(0);
+		HSSFCell cell0 = row.createCell(0);
+		HSSFCell cell1 = row.createCell(1);
+		HSSFCell cell2 = row.createCell(2);
+		HSSFCell cell3 = row.createCell(3);
+		HSSFCell cell4 = row.createCell(4);
+		HSSFCell cell5 = row.createCell(5);
+		HSSFCell cell6 = row.createCell(6);
+		HSSFCell cell7 = row.createCell(7);
+		HSSFCell cell8 = row.createCell(8);
+		HSSFCell cell9 = row.createCell(9);
+		cell0.setCellValue(new HSSFRichTextString("课程代码"));
+		cell1.setCellValue(new HSSFRichTextString("课程名称"));
+		cell2.setCellValue(new HSSFRichTextString("课程大纲"));
+		cell3.setCellValue(new HSSFRichTextString("教材名称"));
+		cell4.setCellValue(new HSSFRichTextString("编著"));
+		cell5.setCellValue(new HSSFRichTextString("出版社"));
+		cell6.setCellValue(new HSSFRichTextString("版次"));
+		cell7.setCellValue(new HSSFRichTextString("层次"));
+		cell8.setCellValue(new HSSFRichTextString("选择该课程学校"));
+		cell9.setCellValue(new HSSFRichTextString("命题办指定"));
+		for (int i=0; i<planList.size(); i++) {
+			HSSFRow datarow = sheet.createRow(i+1);
+			CoursePlan plan = planList.get(i);
+			HSSFCell datacell0 = datarow.createCell(0);
+			datacell0.setCellValue(new HSSFRichTextString(plan.getCourseCode()));
+			HSSFCell datacell1 = datarow.createCell(1);
+			datacell1.setCellValue(new HSSFRichTextString(plan.getCourseName()));
+			HSSFCell datacell2 = datarow.createCell(2);
+			datacell2.setCellValue(new HSSFRichTextString(plan.getOutlineName()));
+			HSSFCell datacell3 = datarow.createCell(3);
+			datacell3.setCellValue(new HSSFRichTextString(plan.getBookName()));
+			HSSFCell datacell4 = datarow.createCell(4);
+			datacell4.setCellValue(new HSSFRichTextString(plan.getBianZhu()));
+			HSSFCell datacell5 = datarow.createCell(5);
+			datacell5.setCellValue(new HSSFRichTextString(plan.getPress()));
+			HSSFCell datacell6 = datarow.createCell(6);
+			datacell6.setCellValue(new HSSFRichTextString(plan.getPublications()));
+			HSSFCell datacell7 = datarow.createCell(7);
+			datacell7.setCellValue(new HSSFRichTextString(plan.getCengci()));
+			HSSFCell datacell8 = datarow.createCell(8);
+			
+			String schName = "";
+			if (plan.getSchoolId() != null && plan.getSchoolId().longValue() != 0) {
+				schName = plan.getSchoolName();
+			} else {
+				schName = getSchoolName(Long.parseLong(plan.getSchSelectIds()));
+			}
+			datacell8.setCellValue(new HSSFRichTextString(schName));//为保持一致
+			HSSFCell datacell9 = datarow.createCell(9);
+			datacell9.setCellValue(new HSSFRichTextString(plan.getSchoolName()));
+		}
+		try {
+			FileOutputStream fos = new FileOutputStream(new File(path));
+			workbook.write(fos);
+			fos.close();
+			workbook.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+	
+	private static String getSchoolName(long schoolId) {
+		List<School> schools = ShoolDateUtil.getAllSchoolData();
+		for (School sch : schools) {
+			if (sch.getSchoolId().longValue() == schoolId) {
+				return sch.getSchoolName();
+			}
+		}
+		return "";
+	}
+	
+	//征集统计
+	public static void writeStatisToXlsx(final List<StatisObj> statisList, final String path) {
+		HSSFWorkbook workbook = new HSSFWorkbook();
+		HSSFSheet sheet = workbook.createSheet("统计");
+		sheet.setDefaultColumnWidth((short) 15);
+		sheet.setColumnWidth(3, 40*256);
+		HSSFCellStyle styleBold = workbook.createCellStyle();
+		HSSFCellStyle styleContent = workbook.createCellStyle();
+		styleContent.setWrapText(true);
+		HSSFFont font = workbook.createFont();
+		font.setBold(true);
+		styleBold.setFont(font);
+		
+		HSSFRow row = sheet.createRow(0);
+		
+		HSSFCell cell0 = row.createCell(0);
+		HSSFCell cell1 = row.createCell(1);
+		HSSFCell cell2 = row.createCell(2);
+		HSSFCell cell3 = row.createCell(3);
+		
+		cell0.setCellValue(new HSSFRichTextString("序号"));
+		cell0.setCellStyle(styleBold);
+		
+		cell1.setCellValue(new HSSFRichTextString("学校名称"));
+		cell1.setCellStyle(styleBold);
+		
+		cell2.setCellValue(new HSSFRichTextString("课程数量"));
+		cell2.setCellStyle(styleBold);
+		
+		cell3.setCellValue(new HSSFRichTextString("所选课程"));
+		cell3.setCellStyle(styleBold);
+		
+		//cell2.setCellStyle(new HSSFCellStyle().set);
+		int totalCourses = 0;
+		for (int i=0; i<statisList.size(); i++) {
+			HSSFRow datarow = sheet.createRow(i+1);
+			StatisObj stat = statisList.get(i);
+			
+			HSSFCell datacell0 = datarow.createCell(0);
+			datacell0.setCellValue(new HSSFRichTextString(String.valueOf(i + 1)));
+			
+			HSSFCell datacell1 = datarow.createCell(1);
+			datacell1.setCellValue(new HSSFRichTextString(stat.getSchoolName()));
+			
+			HSSFCell datacell2 = datarow.createCell(2);
+			datacell2.setCellValue(new HSSFRichTextString(stat.getCount().toString()));
+			
+			HSSFCell datacell3 = datarow.createCell(3);
+			datacell3.setCellStyle(styleContent);
+			datacell3.setCellValue(new HSSFRichTextString(stat.getCourseShow()));
+			
+			totalCourses += stat.getCount();
+		}
+		HSSFRow lastRow = sheet.createRow(statisList.size() + 1);
+		HSSFCell cellHzCaption = lastRow.createCell(0);
+		HSSFCell cellSchNum = lastRow.createCell(1);
+		HSSFCell cellCourseNum = lastRow.createCell(2);
+		cellHzCaption.setCellValue("汇总");
+		cellHzCaption.setCellStyle(styleBold);
+		cellSchNum.setCellValue(new HSSFRichTextString(String.valueOf(statisList.size())));
+		cellCourseNum.setCellValue(new HSSFRichTextString(String.valueOf(totalCourses)));
+		try {
+			FileOutputStream fos = new FileOutputStream(new File(path));
+			workbook.write(fos);
+			fos.close();
+			workbook.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 53 - 0
src/com/hmsoft/common/util/RegexpUtils.java

@@ -0,0 +1,53 @@
+package com.hmsoft.common.util;
+
+import java.util.regex.Pattern;
+
+/**
+ * 
+ * @author zhanq
+ *
+ */
+public class RegexpUtils {
+	/**
+	 * 包含0的整数
+	 */
+	public static String intRex = "^\\d+$";
+	/**
+	 * 非0正整数
+	 */
+	public static String intRexGtZero = "^[1-9]\\d*$";
+	
+	public static final String positiveIntRegx = "^[0-9]*[1-9][0-9]*$";
+	public static final String positiveFloatRegx = "([0-9]\\d*\\.?\\d*)";
+	
+	/**
+	 * 正整数,包括0.
+	 * @param intVal
+	 * @return
+	 */
+	public static boolean checkIsPositiveInt(final String intVal) {
+		return Pattern.matches(intRex, intVal);
+	}
+	
+	/**
+	 * 正整数,大于0.
+	 * @param intVal
+	 * @return
+	 */
+	public static boolean checkIsPositiveIntGtZero(final String intVal) {
+		return Pattern.matches(intRexGtZero, intVal);
+	}
+	
+	/**
+	 * 正整数,或正浮点数.
+	 * @param floatVal
+	 * @return
+	 */
+	public static boolean checkIsPositiveFloat(final String floatVal) {
+		return Pattern.matches(positiveFloatRegx, floatVal);
+	}
+	
+	public static void main(String[] args) {
+		System.err.println(checkIsPositiveIntGtZero("-1"));
+	}
+}

+ 154 - 0
src/com/hmsoft/common/util/ShoolDateUtil.java

@@ -0,0 +1,154 @@
+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.bizpanel.SchoolTab;
+import com.hmsoft.common.FileConst;
+import com.hmsoft.model.School;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class ShoolDateUtil {
+
+	private ShoolDateUtil() {
+		
+	}
+	
+	//load 
+	public static ObservableList<School> loadSchoolData() {
+		final ObservableList<School> dataList = FXCollections.observableArrayList();
+		final File file = new File(FileConst.SCHOOL_DATA_PATH);
+		try {
+			if (!file.exists()) {
+				file.createNewFile();
+			}
+			final List<String> list = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			int j=0;
+			for (String schoolStr : list) {
+				School school = JSON.parseObject(schoolStr, School.class);
+				int i = ++j;
+			    school.setId(Long.valueOf(i));	
+				dataList.add(school);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return dataList;
+	}
+
+	public static void insertSchool(School school) {
+		PrintWriter printwriter = null;
+		File file = new File(FileConst.SCHOOL_DATA_PATH);
+		try {
+			if (!file.exists()) {
+				file.createNewFile();
+			}
+			List<String> oldList = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			String jsonSchool = JSON.toJSONString(school);
+			oldList.add(jsonSchool);
+			//清空文件,再添加
+			printwriter = new PrintWriter(file, "UTF-8");
+			for (String json : oldList) {
+				if (json == null || json.equals("")) {
+					continue;
+				}
+				printwriter.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch(FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (printwriter != null) {
+				printwriter.flush();
+				printwriter.close();
+			}
+		}
+	}
+
+	public static void deleteSchool(Long schoolId) {
+		PrintWriter writer = null;
+		File file = new File(FileConst.SCHOOL_DATA_PATH);
+		try {
+			List<String> oldList = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			List<String> newList = new ArrayList<String>();
+			for (String sho : oldList) {
+				School school = JSON.parseObject(sho, School.class);
+				if (school.getSchoolId().longValue() != schoolId.longValue()) {
+					newList.add(sho);
+				}
+			}
+			//清空文件内容
+			writer = new PrintWriter(file, "UTF-8");
+			writer.write("");
+			for (String json : newList) {
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+	//修改
+	public static void resetPasswd(final Long schoolId, final String passwd) {
+		PrintWriter writer = null;
+		File file = new File(FileConst.SCHOOL_DATA_PATH);
+		try {
+			final List<String> oldList = IOUtils.readLines(new FileInputStream(file), Charset.forName("UTF-8"));
+			List<String> newList = new ArrayList<String>();
+			for (String str : oldList) {
+				School school = JSON.parseObject(str, School.class);
+				if (school.getSchoolId().longValue() == schoolId.longValue()) {
+					school.setPassWord(passwd);
+					str = JSON.toJSONString(school);
+				}
+				newList.add(str);
+			}
+			//删除之前内容,重新生成新内容
+			writer = new PrintWriter(file, "UTF-8");
+			writer.write("");
+			//new
+			for (String json : newList) {
+				writer.write(json + FileConst.FILE_NEWLINE);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (writer != null) {
+				writer.flush();
+				writer.close();
+			}
+		}
+	}
+	
+	public static List<School> getAllSchoolData() {
+		return SchoolTab.DATALIST;
+	}
+}

+ 157 - 0
src/com/hmsoft/common/util/StringHelper.java

@@ -0,0 +1,157 @@
+package com.hmsoft.common.util;
+
+import java.util.LinkedHashMap;
+
+/*********************
+ * 字符串复制类
+ * 
+ * @author zxq
+ * @create 2018-04-05 21:56:50
+ * @version 5.0.0
+ * @email: revisit@126.com
+ * @Company: www.hmsoft.cn
+ */
+public class StringHelper {
+	/*****************************
+	 * 判断字符串是否为空(空值或者空格都认为是空)
+	 */
+	public static boolean isEmpty(String value) {
+		return (value == null || value.trim().length() == 0);
+	}
+
+	/*****************************
+	 * 判断是否为空(空值或者空格都认为是空)
+	 */
+	public static boolean isEmpty(Object value) {
+		return (value == null || value.toString().trim().length() == 0);
+	}
+
+	/*****************************
+	 * 判断字符串是否不为空(空值或者空格都认为是空)
+	 */
+	public static boolean isNotEmpty(String value) {
+		return !isEmpty(value);
+	}
+
+	/*****************************
+	 * 判断是否不为空(空值或者空格都认为是空)
+	 */
+	public static boolean isNotEmpty(Object value) {
+		return !isEmpty(value);
+	}
+
+	/*****************************
+	 * 数据库字段名称转换为java类属性 CUST_NAME => CustName;
+	 */
+	public static String toClassName(String columnName) {
+		if (isEmpty(columnName))
+			return "";
+		StringBuilder sb = new StringBuilder();
+		for (String s : columnName.split("_")) {
+			if (s.length() > 0)
+				sb.append(s.substring(0, 1).toUpperCase());
+			if (s.length() > 1)
+				sb.append(s.substring(1).toLowerCase());
+		}
+		return sb.toString();
+	}
+
+	/**********************************
+	 * 数据库字段名称转换为java类属性 CUST_NAME => custName;
+	 */
+	public static String toFieldName(String columnName) {
+		return lowerFirstChar(toClassName(columnName));
+
+	}
+
+	/**********************************************
+	 * java类属性名称转换成数据库名称 custName=>CUST_NAME
+	 */
+	public static String toColumnName(String fieldName) {
+		if (isEmpty(fieldName))
+			return "";
+		char c = Character.toUpperCase(fieldName.charAt(0));
+		StringBuilder sb = new StringBuilder();
+		sb.append(c);
+		for (int i = 1; i < fieldName.length(); i++) {
+			if (Character.isUpperCase(fieldName.charAt(i)))
+				sb.append("_");
+			sb.append(Character.toUpperCase(fieldName.charAt(i)));
+		}
+		return sb.toString().toUpperCase();
+	}
+
+	/***************************************
+	 * 首字母大写
+	 */
+	public static String upperFirstChar(String source) {
+		if (isEmpty(source))
+			return "";
+		if (source.length() == 1)
+			return source.toUpperCase();
+		return source.substring(0, 1).toUpperCase() + source.substring(1);
+	}
+
+	/*****************************************
+	 * 首字母小写
+	 */
+	public static String lowerFirstChar(String source) {
+		if (isEmpty(source))
+			return "";
+		if (source.length() == 1)
+			return source.toUpperCase();
+		return source.substring(0, 1).toLowerCase() + source.substring(1);
+	}
+
+	/*****************************************
+	 * 分割不定长度的空白字符
+	 */
+	public static String[] splitWithSpace(String source) {
+		return source.split("\\s+");
+	}
+
+	/***************************************************
+	 * 左边补齐
+	 */
+
+	public static String leftPadding(String source, int length, char padding) {
+		if (source == null)
+			source = "";
+		String value = "";
+		for (int i = 0; i < length - source.length(); i++) {
+			value += padding;
+		}
+		return value + source;
+	}
+
+	/***************************************************
+	 * 右边补齐
+	 */
+
+	public static String rightPadding(String source, int length, String padding) {
+		if (source == null)
+			source = "";
+		String value = "";
+		for (int i = 0; i < length - source.getBytes().length; i++) {
+			value += padding;
+		}
+		return source + value;
+	}
+
+	/*************************************
+	 * 字符串到map 回车换行
+	 * 
+	 */
+	public static LinkedHashMap<String, String> toMap(String param_str) {
+		LinkedHashMap<String, String> paramMap = new LinkedHashMap<String, String>();
+		if (StringHelper.isNotEmpty(param_str)) {
+			for (String value : param_str.trim().split("\n")) {
+				String[] array = value.split(":");
+				if (array.length > 1) {
+					paramMap.put(array[0].trim(), array[1].trim());
+				}
+			}
+		}
+		return paramMap;
+	}
+}

+ 29 - 0
src/com/hmsoft/common/util/SysCfgUtil.java

@@ -0,0 +1,29 @@
+package com.hmsoft.common.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Properties;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class SysCfgUtil {
+
+	public static Properties loadFromFile(String path) throws FileNotFoundException, IOException {
+		Properties props = new Properties();
+		props.load(new FileInputStream(path));
+		return props;
+	}
+	
+	public static void storeToFile(Properties props, String path) throws IOException {
+		OutputStream oFile = new FileOutputStream(new File(path));
+		props.store(oFile, "Comment");
+		oFile.close();
+	}
+}

+ 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 com.hmsoft.common.util.BatchDataUtil;
+
+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、已发布-3)
+	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(BatchDataUtil.getStatusCaption(Integer.valueOf(status.get()).byteValue()))
+				.toString();
+	}
+	
+	public int hashCode() {
+	     return new HashCodeBuilder(17, 37).
+	       append(this.batchId.get()).
+	       append(this.batchNo.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())
+		                 .isEquals();
+	}
+	
+}

+ 34 - 0
src/com/hmsoft/model/Course.java

@@ -0,0 +1,34 @@
+package com.hmsoft.model;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class Course {
+
+	private String code;
+	private String name;
+	
+	public Course(String code, String name) {
+		this.code = code;
+		this.name = name;
+	}
+	
+	public String getCode() {
+		return code;
+	}
+	public void setCode(String code) {
+		this.code = code;
+	}
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	public String toString() {
+		return new StringBuilder().append(code).append("-").append(name).toString();
+	}
+}

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

@@ -0,0 +1,278 @@
+package com.hmsoft.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.scene.control.ComboBox;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class CoursePlan implements Serializable {
+
+	private static final long serialVersionUID = 5308384112992831269L;
+	
+	private IntegerProperty coursePlanId;
+	private StringProperty courseCode;
+	private StringProperty courseName;
+	private StringProperty outlineName;//教材大纲
+	private StringProperty bookName;//教材名称
+	private StringProperty bianZhu;//编著
+	private StringProperty press;//出版社
+	private StringProperty publications;//版次
+	private StringProperty cengci;//层次
+	private LongProperty schoolId;//命题办指定的学校ID(最终征集学校ID)
+	private StringProperty schoolName;//ָ命题办指定的学校名称(最终征集学校名称)
+	private StringProperty affirmSchooleName;//ָ最终认定学校名称)
+	
+	private StringProperty schSelectIds;//选择该课程的学校IDs
+	private StringProperty schSelectNames;//选择该课程的学校名称
+	private StringProperty schSelectTime;//最后一个选择该课程的学校选择时间
+	private IntegerProperty confirmStatus;//确认状态是在课程上面,同一个批次状态下每个学校都不一样可能,前面最终发布、征集中、已导入都是公用批次的状态
+	//待指定学校列表
+	private transient ComboBox<School> comboBox = new ComboBox<School>();
+	//记录瞬间校选择该课程的多个学校
+	private transient List<School> multiSelectList = new ArrayList<School>(10);
+	private transient StringProperty confirmDesc;//学校确认反馈
+	
+	public CoursePlan() {
+		this.coursePlanId = new SimpleIntegerProperty();
+		this.courseCode = new SimpleStringProperty();
+		this.courseName = new SimpleStringProperty();
+		this.outlineName = new SimpleStringProperty();
+		this.bookName = new SimpleStringProperty();
+		this.bianZhu = new SimpleStringProperty();
+		this.press = new SimpleStringProperty();
+		this.publications = new SimpleStringProperty();
+		this.cengci = new SimpleStringProperty();
+		this.schoolId = new SimpleLongProperty();
+		this.schoolName = new SimpleStringProperty();
+		this.schSelectIds = new SimpleStringProperty();
+		this.schSelectNames = new SimpleStringProperty();
+		this.schSelectTime = new SimpleStringProperty();
+		this.confirmDesc = 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 getAffirmSchooleName() {
+		return affirmSchooleName.get();
+	}
+	
+	public void setAffirmSchooleName(String affirmSchooleName) {
+		this.affirmSchooleName.set(affirmSchooleName);
+	}
+	
+
+	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 getSchSelectTime() {
+		return schSelectTime.get();
+	}
+
+	public void setSchSelectTime(String schSelectTime) {
+		this.schSelectTime.set(schSelectTime);;
+	}
+
+	public ComboBox<School> getComboBox() {
+		return comboBox;
+	}
+
+	public void setComboBox(ComboBox<School> comboBox) {
+		this.comboBox = comboBox;
+	}
+
+	public List<School> getMultiSelectList() {
+		return multiSelectList;
+	}
+
+	public void setMultiSelectList(List<School> multiSelectList) {
+		this.multiSelectList = multiSelectList;
+	}
+
+
+	public String getConfirmDesc() {
+		return confirmDesc.get();
+	}
+
+	public void setConfirmDesc(String confirmDesc) {
+		this.confirmDesc.set(confirmDesc);
+	}
+
+	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(" | ")
+				.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));
+	}
+}

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

@@ -0,0 +1,132 @@
+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;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class School implements Serializable {
+
+	private static final long serialVersionUID = -9178032507946351789L;
+	private LongProperty id;  //序号
+	private LongProperty schoolId;  //学校序号
+	private StringProperty schoolName;
+	//目前学校和账号一对一!
+	private StringProperty userName;
+	private StringProperty passWord;
+	//连接状态,不用序列化
+	private transient StringProperty statusDesc;//(已连接,未连接,已断开)
+	private transient StringProperty connectedTime;//开始连接时间
+	private transient StringProperty disconnectTime;//断开连接时间
+	
+	public School() {
+		this.id = new SimpleLongProperty();
+		this.schoolId =  new SimpleLongProperty();
+		this.schoolName = new SimpleStringProperty();
+		this.userName = new SimpleStringProperty();
+		this.passWord = new SimpleStringProperty();
+		this.statusDesc = new SimpleStringProperty();
+		this.connectedTime = new SimpleStringProperty();
+		this.disconnectTime = new SimpleStringProperty();
+	}
+	
+	public School(Long schooId, String schoolName) {
+		this.schoolId =  new SimpleLongProperty(schooId);
+		this.schoolName = new SimpleStringProperty(schoolName);
+	}
+	
+	public School(int id,int schooId, String schoolName, String userName, String passWord) {
+		this.id = new SimpleLongProperty(id);
+		this.schoolId =  new SimpleLongProperty(schooId);
+		this.schoolName = new SimpleStringProperty(schoolName);
+		this.userName = new SimpleStringProperty(userName);
+		this.passWord = new SimpleStringProperty(passWord);
+	}
+	
+	public Long getId() {
+		return id.get();
+	}
+	public void setId(Long id) {
+		this.id.set(id);
+	}
+	
+	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 getStatusDesc() {
+		return statusDesc.get();
+	}
+	public void setStatusDesc(String statusDesc) {
+		this.statusDesc.set(statusDesc);
+	}
+
+	public String getConnectedTime() {
+		return connectedTime.get();
+	}
+
+	public String getDisconnectTime() {
+		return disconnectTime.get();
+	}
+
+	public void setConnectedTime(String connectedTime) {
+		this.connectedTime.set(connectedTime);
+	}
+
+	public void setDisconnectTime(String disconnectTime) {
+		this.disconnectTime.set(disconnectTime);
+	}
+
+	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();
+	}
+}

+ 90 - 0
src/com/hmsoft/model/StatisObj.java

@@ -0,0 +1,90 @@
+package com.hmsoft.model;
+
+import java.util.List;
+
+import javafx.scene.control.ComboBox;
+
+/**
+ * 
+ * @author zq
+ *
+ */
+public class StatisObj implements Comparable<StatisObj> {
+
+	private Long schoolId;
+	private String schoolName;
+	private Integer count;
+	private String courseShow;
+	private List<Course> courseList;
+	private ComboBox<Course> comboBox = new ComboBox<Course>();
+	
+	public StatisObj(Long schoolId, String schoolName, Integer count, List<Course> courseList) {
+		this.schoolId = schoolId;
+		this.schoolName = schoolName;
+		this.count = count;
+		this.courseList = courseList;
+		this.comboBox.getItems().addAll(courseList);
+		this.comboBox.setMinWidth(255);
+		StringBuilder showSb = new StringBuilder();
+		for (int i=0; i<courseList.size(); i++) {
+			String courseStr = courseList.get(i).toString();
+			if (i < courseList.size() - 1) {
+				showSb.append(courseStr).append("\r\n");
+			} else {
+				showSb.append(courseStr);
+			}
+		}
+		if (showSb.length() > 0) {
+			showSb.substring(0, showSb.length() - "\r\n".length());
+		}
+		this.courseShow = showSb.toString();
+	}
+	
+	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 Integer getCount() {
+		return count;
+	}
+	public void setCount(Integer count) {
+		this.count = count;
+	}
+	public String getCourseShow() {
+		return courseShow;
+	}
+	public void setCourseShow(String courseShow) {
+		this.courseShow = courseShow;
+	}
+	public List<Course> getCourseList() {
+		return courseList;
+	}
+	public void setCourseList(List<Course> courseList) {
+		this.courseList = courseList;
+	}
+	
+	public ComboBox<Course> getComboBox() {
+		return comboBox;
+	}
+
+	public void setComboBox(ComboBox<Course> comboBox) {
+		this.comboBox = comboBox;
+	}
+
+	@Override
+	public int compareTo(StatisObj oth) {
+		if (this.count > oth.count) {
+			return 1;
+		}
+		return 0;
+	}
+	
+}

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

@@ -0,0 +1,92 @@
+package com.hmsoft.model.request;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import com.hmsoft.model.CoursePlan;
+
+/**
+ * 请求业务类.
+ * @author zq
+ *
+ */
+public class BizRequest implements Serializable {
+	
+	private static final long serialVersionUID = -8814212998506317013L;
+	protected Integer reqType; //1-学校选择课程,2-命题办通知其他学校课程被选择了,3-命题办指定该课程征集学校,4-学校取消选择的课程,5-命题办刷新课程列表数据到各个学校客户端
+	protected String batchNo;//批次编号
+	protected Long schoolId;
+	protected String schoolName;
+	protected String selectIds;//选择的课程代码,可以选多个
+	protected byte zjStatus;//0-征集中,1-最终发布
+	protected List<CoursePlan> sourceList;//服务端发送过来的列表
+	
+	public String getBatchNo() {
+		return batchNo;
+	}
+	public void setBatchNo(String batchNo) {
+		this.batchNo = batchNo;
+	}
+	public Integer getReqType() {
+		return reqType;
+	}
+	public List<CoursePlan> getSourceList() {
+		return sourceList;
+	}
+	public void setSourceList(List<CoursePlan> sourceList) {
+		this.sourceList = sourceList;
+	}
+	public void setReqType(Integer reqType) {
+		this.reqType = reqType;
+	}
+	public Long getSchoolId() {
+		return schoolId;
+	}
+	public void setSchoolId(Long schoolId) {
+		this.schoolId = schoolId;
+	}
+	public String getSchoolName() {
+		return schoolName;
+	}
+	public void setSchoolName(String schoolName) {
+		this.schoolName = schoolName;
+	}
+	public String getSelectIds() {
+		return selectIds;
+	}
+	public void setSelectIds(String selectIds) {
+		this.selectIds = selectIds;
+	}
+	public 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 = 8123141305576973084L;
+	protected String userName;
+	protected String passWord;
+	protected int status;//登录成功失败状态(0-成功,-1失败)
+	protected Long schoolId;
+	protected String schoolName;
+	
+	public LoginRequest() {
+		
+	}
+	
+	public LoginRequest(String userName, String passWord) {
+		this.userName = userName;
+		this.passWord = passWord;
+	}
+	
+	public String getUserName() {
+		return userName;
+	}
+	public String getPassWord() {
+		return passWord;
+	}
+	public void setUserName(String userName) {
+		this.userName = userName;
+	}
+	public void setPassWord(String passWord) {
+		this.passWord = passWord;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public Long getSchoolId() {
+		return schoolId;
+	}
+
+	public String getSchoolName() {
+		return schoolName;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public void setSchoolId(Long schoolId) {
+		this.schoolId = schoolId;
+	}
+
+	public void setSchoolName(String schoolName) {
+		this.schoolName = schoolName;
+	}
+	
+	
+}

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

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

+ 24 - 0
src/com/hmsoft/model/request/RegisterRequest.java

@@ -0,0 +1,24 @@
+package com.hmsoft.model.request;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 管理端注册请求.
+ * @author zq
+ *
+ */
+public class RegisterRequest implements Serializable {
+
+	private static final long serialVersionUID = -8135574694058205745L;
+
+	private LocalDateTime reqDateTime;
+
+	public LocalDateTime getReqDateTime() {
+		return reqDateTime;
+	}
+
+	public void setReqDateTime(LocalDateTime reqDateTime) {
+		this.reqDateTime = reqDateTime;
+	}
+}

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

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff