自从我安装了 openjdk-1.8.0-312 补丁后,我在所有使用
HashMap
和 HashSet
的 JBoss-RMI 调用中都遇到了这个错误。
这是使用 openjdk-1.8.0 并运行 JBoss 作为 6.1.1:
2022-05-05 10:30:19,761 ERROR [STDERR] ... 100 more
2022-05-05 10:30:19,761 ERROR [STDERR] Caused by: org.jboss.serial.exception.SerializationException: Excepted to be String
2022-05-05 10:30:19,761 ERROR [STDERR] at org.jboss.serial.objectmetamodel.DataContainer$DataContainerInput.readUTF(DataContainer.java:1120)
2022-05-05 10:30:19,761 ERROR [STDERR] at org.jboss.serial.persister.ObjectInputStreamProxy.readUTF(ObjectInputStreamProxy.java:196)
2022-05-05 10:30:19,761 ERROR [STDERR] at org.jboss.serial.objectmetamodel.FieldsContainer.readField(FieldsContainer.java:147)
2022-05-05 10:30:19,761 ERROR [STDERR] at org.jboss.serial.objectmetamodel.FieldsContainer.readMyself(FieldsContainer.java:218)
2022-05-05 10:30:19,761 ERROR [STDERR] at org.jboss.serial.persister.ObjectInputStreamProxy.readFields(ObjectInputStreamProxy.java:224)
2022-05-05 10:30:19,761 ERROR [STDERR] at java.util.HashSet.readObject(HashSet.java:298)
2022-05-05 10:30:19,763 ERROR [STDERR] ... 104 more
2022-05-05 10:30:19,763 ERROR [STDERR] Caused by: java.lang.ClassCastException
有人遇到过这个错误吗?关于如何解决这个问题的任何线索(不从方法中删除 HashSet 和 HashMap)?
谢谢
尝试切换回以前版本的 openjdk。
我知道我迟到了两年,但我花了几天时间才弄清楚这个确切的错误,所以我们开始吧。我有一个古老的 JBoss EAP 5.x 服务器,我必须保持运行足够长的时间,以便开发团队完成将服务从其中移出。 (为什么是的,我在企业工作,你怎么知道?)
这里的根本原因是 JBoss 序列化。早在 Java 4 左右的日子里,JBoss 创建了自己的序列化实现,比 JVM 中的序列化实现更快。这种方法一直运行良好,直到后来的 Java 8 版本中出现了针对反序列化的安全修复,从而破坏了该库。这就是为什么即使最初是为 Java 8 构建的 EAP 5.2.0 和 6.x 也会崩溃,以及回滚到 JVM 的早期补丁版本有效的原因。
在我看来,JBoss 序列化实际上不再需要了,因此这里的简单修复方法就是禁用它。在
/deploy/ejb3-connectors-bean.xml
中,找到 invokerLocator
的 RemotingConnector
属性,并将 serializationType 参数添加到调用 URL,如下所示:
<bean name="org.jboss.ejb3.RemotingConnector"
class="org.jboss.remoting.transport.Connector">
<property name="invokerLocator">
<value-factory bean="ServiceBindingManager"
method="getStringBinding">
<parameter>
jboss.remoting:type=Connector,name=DefaultEjb3Connector,handler=ejb3
</parameter>
<parameter>
<null />
</parameter>
<parameter>socket://${jboss.bind.address}:${port}?timeout=300000&invokerDestructionDelay=5000&serializationType=java</parameter>
<parameter>
<null />
</parameter>
<parameter>3873</parameter>
</value-factory>
</property>
<property name="serverConfiguration">
<inject bean="ServerConfiguration" />
</property>
</bean>
/deploy/remoting-jboss-beans.xml
中也有类似的定义。我很确定不再使用它,但在那里添加参数也没有什么坏处:
<bean name="UnifiedInvokerConfiguration" class="org.jboss.remoting.ServerConfiguration">
<constructor>
<!-- transport: Others include sslsocket, bisocket, sslbisocket, http, https, rmi, sslrmi, servlet, sslservlet. -->
<parameter>socket</parameter>
</constructor>
<!-- Parameters visible to both client and server -->
<property name="invokerLocatorParameters">
<map keyClass="java.lang.String" valueClass="java.lang.String">
<!-- Other map entries ... -->
<entry><key>serializationType</key><value>java</value></entry>
</map>
</property>
</bean>
这应该禁用所有远程 EJB 调用的 JBoss 序列化。 然而,我确实发现了拦截器的另一个问题。
jboss-ejb3-core
库包含两个 AOP 拦截器,旨在检查服务是否意外通过远程绑定调用自身,并将该调用转换为本地调用。表面上是个好主意,但这些拦截器没有相当于 serializationType
参数。它们是硬编码的并且将始终使用 JBoss 序列化。我个人认为这是一个错误,但 JBoss 5.x 不会很快得到补丁!
有两个非常简单的选项可以解决这个问题。您当然可以更新您的服务以not调用自己并正确使用本地绑定。这将是首选选项,但显然作为服务器管理员,很难预测这些问题出现在哪里,我们不想破坏当前有效的东西。
第二个选项是您只需禁用拦截器。这将对任何错误使用自己的远程绑定的服务产生负面性能影响,因为这些调用实际上会被分派到远程服务器,但好处是远程服务器现在已正确配置为使用 JVM 序列化!要禁用拦截器,请打开
/deploy/ejb3-interceptors-aop.xml
并注释掉拦截器堆栈中的 IsLocalInterceptor
和 ClusteredIsLocalInterceptor
:
<stack name="ServiceClientInterceptors">
<!-- <interceptor-ref name="org.jboss.ejb3.remoting.IsLocalInterceptor"/> -->
<interceptor-ref name="org.jboss.ejb3.security.client.SecurityClientInterceptor"/>
<interceptor-ref name="org.jboss.aspects.tx.ClientTxPropagationInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.InvokeRemoteInterceptor"/>
</stack>
<stack name="StatelessSessionClientInterceptors">
<!-- <interceptor-ref name="org.jboss.ejb3.remoting.IsLocalInterceptor"/> -->
<interceptor-ref name="org.jboss.ejb3.security.client.SecurityClientInterceptor"/>
<interceptor-ref name="org.jboss.aspects.tx.ClientTxPropagationInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.InvokeRemoteInterceptor"/>
</stack>
<stack name="StatefulSessionClientInterceptors">
<!-- <interceptor-ref name="org.jboss.ejb3.remoting.IsLocalInterceptor"/> -->
<interceptor-ref name="org.jboss.ejb3.security.client.SecurityClientInterceptor"/>
<interceptor-ref name="org.jboss.aspects.tx.ClientTxPropagationInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.InvokeRemoteInterceptor"/>
</stack>
<stack name="ClusteredStatelessSessionClientInterceptors">
<!-- <interceptor-ref name="org.jboss.ejb3.remoting.ClusteredIsLocalInterceptor"/> -->
<interceptor-ref name="org.jboss.ejb3.security.client.SecurityClientInterceptor"/>
<interceptor-ref name="org.jboss.aspects.tx.ClientTxPropagationInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.ClusterChooserInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.InvokeRemoteInterceptor"/>
</stack>
<stack name="ClusteredStatefulSessionClientInterceptors">
<!-- <interceptor-ref name="org.jboss.ejb3.remoting.ClusteredIsLocalInterceptor"/> -->
<interceptor-ref name="org.jboss.ejb3.security.client.SecurityClientInterceptor"/>
<interceptor-ref name="org.jboss.aspects.tx.ClientTxPropagationInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.ClusterChooserInterceptor"/>
<interceptor-ref name="org.jboss.aspects.remoting.InvokeRemoteInterceptor"/>
</stack>
显然这个修复是针对 JBoss 5.x 的,但希望它能为阅读本文的任何人提供一个在何处查看更高版本的起点。我在网上发现很多人遇到这个问题,但没有具体的解决办法,所以直到回复我希望它仍然对某人有用。