com.paic.pafa.core.exceptions.PafaRuntimeException: error unmarshalling arguments; nested exception is:
java.io.OptionalDataException
at com.paic.pafa.app.web.servlet.mvc.AbstractController.dispatchService(AbstractController.java:235)
at com.palic.egis.endorsement.processing.web.controller.GetPlanDetailController.formBackingObject(GetPlanDetailController.java:73)
java.io.OptionalDataException的异常产生的过程大概是这样的:
Java的对象传输是在一个keepalive连接上传输的,就是说建立一个网络连接后可以传输很多很多的对象实例。
譬如:A跟B通讯,A端write对象a,write对象b,write对象c;B端就要read对象a,read对象b,read对象c。
但是B端面对的是源源不断的数据,怎么才能区分出a和b的分界点呢,这个就是应用协议要解决的问题了。通用的解决方法是在write a前用一个商定的协议头,在协议头里描述数据的长度,通过这个长度来进行数据的定界。如果在write协议头的时候,序列化的a对象被修改了,那协议头的标识的长度和正式长度就不一致了,这样导致了a对象的部分数据会被read b的时候当作协议头,这样这个连接管道原来有序的数据分界就乱了,只有重置这个连接才能恢复。怎样才能重置这个连接呢,重启是最简单的办法了,重启连接的任何一端都可以。
什么情况下会出现“如果在write协议头的时候,序列化的a对象被修改”的情况呢,在并发环境下,一个线程在进行write对象a,另外一个线程在修改对象a就会导致这种情况。这种情况一般是因为应用使用非线程安全的类,譬如:HashMap,并且应用自身并发控制不严引起的。
定位问题的办法:
修改JDK 的library里的ObjectInputStream类,当出现StreamCorruptedException时把Stream里的前4K字节写到console ouput中。可以通过这个方法找到第一次破坏协议的序列化的内容,以这个序列化的内容作为线索找出应用问题发生的地方。在出现问题的系统上加上这个补丁。这个补丁需要使用-Xbootclasspath来设置,影响JVM boot ClassLoader.