OpenLiberty数据库会话反序列化ClassNotFoundException问题求助
问题
使用配置了会话复制的OpenLiberty 24.0.0.6,技术栈为JSF+PrimeFaces,部分业务对象通过CDI的@SessionScoped注解存储在HTTP会话中。会话反序列化时抛出ClassNotFoundException,无法找到org.jboss.weld.module.web.HttpSessionBean$SerializableProxy类。
错误日志
FFDC日志
Exception = java.lang.ClassNotFoundException Source = com.ibm.ws.session.SessionLoader.loadObject probeid = 82 Stack Dump = java.lang.ClassNotFoundException: org.jboss.weld.module.web.HttpSessionBean$SerializableProxy at com.ibm.ws.classloading.internal.UnifiedClassLoader.findClass(UnifiedClassLoader.java:167) at com.ibm.ws.classloading.internal.ThreadContextClassLoader.findClass(ThreadContextClassLoader.java:145) at com.ibm.ws.classloading.internal.UnifiedClassLoader.loadClass0(UnifiedClassLoader.java:141) at com.ibm.ws.classloading.internal.UnifiedClassLoader$Delegation.loadClass(UnifiedClassLoader.java:87) at com.ibm.ws.classloading.internal.UnifiedClassLoader.loadClass(UnifiedClassLoader.java:106) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:527) at com.ibm.ws.serialization.DeserializationObjectInputStream.loadClass(DeserializationObjectInputStream.java:64) at com.ibm.ws.serialization.internal.DeserializationObjectInputStreamImpl.loadClass(DeserializationObjectInputStreamImpl.java:50) at com.ibm.ws.serialization.DeserializationObjectInputStream.resolveClass(DeserializationObjectInputStream.java:135) at com.ibm.ws.serialization.DeserializationObjectInputStream.resolveClass(DeserializationObjectInputStream.java:173) at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2003) at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1870) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2201) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1687) at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2496) at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2390) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2228) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1687) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:489) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:447) at java.base/java.util.Hashtable.readHashtable(Hashtable.java:1315) at java.base/java.util.Hashtable.readObject(Hashtable.java:1259) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1046) at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2357) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2228) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1687) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:489) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:447) at com.ibm.ws.session.utils.SessionLoader.loadObject(SessionLoader.java:64) at com.ibm.ws.session.store.db.DatabaseHashMap.getValue(DatabaseHashMap.java:1615)
服务器错误日志
[15.08.24, 16:03:13:605 MESZ] 0000007c com.ibm.ws.session.WASSession E SESN0051E: Attempting to deserialize a session object from the backend resulted in a ClassNotFoundException.. [15.08.24, 16:03:13:605 MESZ] 0000007c com.ibm.ws.session.WASSession E ID: n3KSj82AVIMrmIK9ZH8A0WH [15.08.24, 16:03:13:605 MESZ] 0000007c com.ibm.ws.session.WASSession E Exception: java.lang.ClassNotFoundException: org.jboss.weld.module.web.HttpSessionBean$SerializableProxy
服务器配置
server.xml
<?xml version="1.0" encoding="UTF-8"?> <server> <library id="SessionDriverLib"> <fileset dir="${shared.resource.dir}/session" includes="*.jar"/> </library> <dataSource id="SessionDS"> <jdbcDriver libraryRef="SessionDriverLib" /> <properties.postgresql serverName="${postgres_server}" portNumber="5432" databaseName="httpSession" user="session_user" password="***"/> </dataSource> <httpSessionDatabase id="SessionDB" dataSourceRef="SessionDS" tableName="sessions"/> <httpSession storageRef="SessionDB" cloneId="${clone_id}"/> </server>
启用特性
<featureManager> <feature>javaee-8.0</feature> <feature>microProfile-3.3</feature> <feature>monitor-1.0</feature> <feature>sessionDatabase-1.0</feature> </featureManager>
会话内容排查
发现会话中存在WELD代理属性:
attribute name: WELD_S#F_WELD%AbstractBuiltInBean%web_war_exploded#twl-web%HttpSession attribute value (from toString()) Bean: ForwardingBean null for WELD%AbstractBuiltInBean%web_war_exploded#web%HttpSession; Instance: org.jboss.weld.module.web.HttpSessionBean$SerializableProxy@1bbaee59; CreationalContext: org.jboss.weld.contexts.CreationalContextImpl@27396b8a
所有自定义会话对象均已实现Serializable接口,应用WAR包无WELD依赖,类路径配置正常。
解决思路与方案
问题根源
OpenLiberty以WELD作为CDI实现,当应用通过CDI注入HttpSession对象时,WELD会生成HttpSessionBean$SerializableProxy代理类并存储到会话中。该代理类属于WELD内部类,仅存在于服务器系统类加载器中,而会话反序列化使用应用类加载器,无法找到此类,引发异常。
具体方案
排除WELD代理与会话序列化
修改server.xml的httpSession配置,添加excludeAttributes属性过滤WELD相关属性:<httpSession storageRef="SessionDB" cloneId="${clone_id}" excludeAttributes="WELD_*"/>此配置会让会话复制时跳过所有以
WELD_开头的属性,避免存储WELD代理类。清理无效会话数据
数据库中已存储的含WELD代理的旧会话仍会触发异常,需手动清理sessions表数据,或等待这些会话自然过期。避免直接注入HttpSession(可选)
若业务允许,改用FacesContext.getCurrentInstance().getExternalContext().getSession()方式获取会话,减少WELD代理生成的场景。类加载器配置验证(不推荐)
确认OpenLiberty的WELD相关包(wlp/lib/features/weld-*目录下)正常加载,让应用类加载器能访问到org.jboss.weld.module.web.HttpSessionBean$SerializableProxy类。此方案会打破类加载隔离,增加潜在风险,仅作为备选。
内容的提问来源于stack exchange,提问作者Mark Hunt

