在 Weblogic 12c 中反序列化时发生 ClassNotFoundException
ClassNotFoundException while deserializing in Weblogic 12c
我有一个在客户端保存状态的网络应用程序。状态被累积成一个 Object []
Object state[] = new Object[10];
state[0] = _parent_s_state_array
state[1] = _some_int;
state[2] = _some_POJO;
..
..
那就是
- 序列化
- Base64 编码
- 作为隐藏输入包含在响应中
在下一个请求中,这个序列化状态被提交回来,我们逆向步骤在服务器端重建状态。此策略适用于数百页。
但是对于使用具有少量 String 属性的特定 POJO 的特定页面,我在反序列化时得到 ClassNotFoundException
。需要注意的是,同一 class 的对象是在同一 JVM 会话中创建和序列化的,因此我们可以排除 Class 不存在于 class 路径中。
更多观察
- 此错误仅在 Weblogic 12c 中的生产和测试平台中出现,而在 运行 码头 8 或 9 的开发中从未出现过。所以我猜这在某种程度上与 Weblogic 的 class 加载有关。
- 该问题在该特定页面中始终重复出现。
- 在同一平台上还有其他页面使用相同的问题class没有任何问题。
- Web 应用程序是从展开的 war 目录部署的,有问题的 class 存在于
WEB-INF/classes/com/xxx/yyy/
中
有人可以提供一些 pointers/strategy 来 war 调试这个问题。
提前致谢。
异常
ERROR [[ACTIVE] ExecuteThread: '5' for queue: 'weblogic.kernel.Default (self-tuning)'] ClassNotFoundException occured restoring StateManager state from serialized form
java.lang.ClassNotFoundException: com.xxx.yyy.TimeParameters
at java.net.URLClassLoader.run(URLClassLoader.java:366) ~[na:1.7.0_65]
at java.net.URLClassLoader.run(URLClassLoader.java:355) ~[na:1.7.0_65]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_65]
at java.net.URLClassLoader.findClass(URLClassLoader.java:354) ~[na:1.7.0_65]
at java.lang.ClassLoader.loadClass(ClassLoader.java:425) ~[na:1.7.0_65]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) ~[na:1.7.0_65]
at java.lang.ClassLoader.loadClass(ClassLoader.java:358) ~[na:1.7.0_65]
at java.lang.Class.forName0(Native Method) [na:1.7.0_65]
at java.lang.Class.forName(Class.java:270) [na:1.7.0_65]
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:625) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1612) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) ~[na:1.7.0_65]
at org.apache.commons.collections.map.AbstractHashedMap.doReadObject(AbstractHashedMap.java:1212) ~[weblogic.server.merged.jar:12.1.3.0.0]
at org.apache.commons.collections.map.CaseInsensitiveMap.readObject(CaseInsensitiveMap.java:149) ~[weblogic.server.merged.jar:12.1.3.0.0]
Class
package com.xxx.yyy;
public class TimeParameters implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5384703411813811209L;
private String yr;
private String qtr;
private String mth;
private String analysisType;
// Getters and Setters
...
...
}
Java 序列化不是完成任务的最佳方式。您最好将其序列化为(例如)JSON,然后根据需要对其进行压缩。 google GSON 是不错的选择。或者 XStream 与 JSON 驱动程序。或者杰克逊...
TLDR;
来自 Apache Commons 集合的 class 在序列化过程中的某个地方被涉及。由于 apache-commons-collections 是从 Weblogic jar 加载的,因此在 Application classloader 中搜索 required class 找不到它。修复是将 org.apache.commons.collections.*
添加到 weblogic.xml
中的 prefer-application-packages
详情
我做了一个远程调试器会话来跟踪对 Class.forName0(String className, boolean, ClassLoader loader)
的调用。
发现当需要的class可以加载时,通过的class加载器层次更深
this contains all classes in WEB-INF/classes --> weblogic.utils.classloaders.ChangeAwareClassLoader@6437e3af finder: weblogic.utils.classloaders.CodeGenClassFinder@2cd01c0c annotation: mi.8200@MedicalIntelligence
weblogic.utils.classloaders.FilteringClassLoader@6413df9c finder: weblogic.utils.classloaders.CodeGenClassFinder@54bdae18 annotation:
weblogic.utils.classloaders.GenericClassLoader@5919d25d finder: weblogic.utils.classloaders.CodeGenClassFinder@1265cb83 annotation:
java.net.URLClassLoader@5a88cbff
Application classloader, contains classes from weblogic bundled jars --> sun.misc.Launcher$AppClassLoader@2a9a42ef
sun.misc.Launcher$ExtClassLoader@75a06ec2
当它抛出 ClassNotFoundException
时,它被缩短了——只出现了最后两个条目
sun.misc.Launcher$AppClassLoader@2a9a42ef <-- Hierarchy starts at Application classloader
sun.misc.Launcher$ExtClassLoader@75a06ec2
然后,我注意到堆栈跟踪包含这些以 ~[weblogic.server.merged.jar:12.1.3.0.0]
结尾的行(编辑问题以添加更多堆栈跟踪)
at org.apache.commons.collections.map.AbstractHashedMap.doReadObject(AbstractHashedMap.java:1212) ~[weblogic.server.merged.jar:12.1.3.0.0]
at org.apache.commons.collections.map.CaseInsensitiveMap.readObject(CaseInsensitiveMap.java:149) ~[weblogic.server.merged.jar:12.1.3.0.0]
所以 Apache commons 集合是从 weblogic 捆绑的 jar 中加载的,即 sun.misc.Launcher$AppClassLoader@2a9a42ef
这可能会触发 错误 classloader 进一步向下传递.所以在 weblogic.xml
中将 org.apache.commons.collections.*
添加到 prefer-application-packages
<prefer-application-packages>
...
<package-name>org.apache.commons.collections.*</package-name>
</prefer-application-packages>
有用的资源
- Weblogic 提供的类加载器分析工具 (CAT):
http(s)://<server-hostname:port>/wls-cat
,显示类加载器树
- Understanding Classloading in Weblogic
- java.lang.NoClassDefFoundError: How to resolve series
我有一个在客户端保存状态的网络应用程序。状态被累积成一个 Object []
Object state[] = new Object[10];
state[0] = _parent_s_state_array
state[1] = _some_int;
state[2] = _some_POJO;
..
..
那就是
- 序列化
- Base64 编码
- 作为隐藏输入包含在响应中
在下一个请求中,这个序列化状态被提交回来,我们逆向步骤在服务器端重建状态。此策略适用于数百页。
但是对于使用具有少量 String 属性的特定 POJO 的特定页面,我在反序列化时得到 ClassNotFoundException
。需要注意的是,同一 class 的对象是在同一 JVM 会话中创建和序列化的,因此我们可以排除 Class 不存在于 class 路径中。
更多观察
- 此错误仅在 Weblogic 12c 中的生产和测试平台中出现,而在 运行 码头 8 或 9 的开发中从未出现过。所以我猜这在某种程度上与 Weblogic 的 class 加载有关。
- 该问题在该特定页面中始终重复出现。
- 在同一平台上还有其他页面使用相同的问题class没有任何问题。
- Web 应用程序是从展开的 war 目录部署的,有问题的 class 存在于
WEB-INF/classes/com/xxx/yyy/
中
有人可以提供一些 pointers/strategy 来 war 调试这个问题。
提前致谢。
异常
ERROR [[ACTIVE] ExecuteThread: '5' for queue: 'weblogic.kernel.Default (self-tuning)'] ClassNotFoundException occured restoring StateManager state from serialized form
java.lang.ClassNotFoundException: com.xxx.yyy.TimeParameters
at java.net.URLClassLoader.run(URLClassLoader.java:366) ~[na:1.7.0_65]
at java.net.URLClassLoader.run(URLClassLoader.java:355) ~[na:1.7.0_65]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_65]
at java.net.URLClassLoader.findClass(URLClassLoader.java:354) ~[na:1.7.0_65]
at java.lang.ClassLoader.loadClass(ClassLoader.java:425) ~[na:1.7.0_65]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) ~[na:1.7.0_65]
at java.lang.ClassLoader.loadClass(ClassLoader.java:358) ~[na:1.7.0_65]
at java.lang.Class.forName0(Native Method) [na:1.7.0_65]
at java.lang.Class.forName(Class.java:270) [na:1.7.0_65]
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:625) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1612) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) ~[na:1.7.0_65]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) ~[na:1.7.0_65]
at org.apache.commons.collections.map.AbstractHashedMap.doReadObject(AbstractHashedMap.java:1212) ~[weblogic.server.merged.jar:12.1.3.0.0]
at org.apache.commons.collections.map.CaseInsensitiveMap.readObject(CaseInsensitiveMap.java:149) ~[weblogic.server.merged.jar:12.1.3.0.0]
Class
package com.xxx.yyy;
public class TimeParameters implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5384703411813811209L;
private String yr;
private String qtr;
private String mth;
private String analysisType;
// Getters and Setters
...
...
}
Java 序列化不是完成任务的最佳方式。您最好将其序列化为(例如)JSON,然后根据需要对其进行压缩。 google GSON 是不错的选择。或者 XStream 与 JSON 驱动程序。或者杰克逊...
TLDR;
来自 Apache Commons 集合的 class 在序列化过程中的某个地方被涉及。由于 apache-commons-collections 是从 Weblogic jar 加载的,因此在 Application classloader 中搜索 required class 找不到它。修复是将 org.apache.commons.collections.*
添加到 weblogic.xml
prefer-application-packages
详情
我做了一个远程调试器会话来跟踪对 Class.forName0(String className, boolean, ClassLoader loader)
的调用。
发现当需要的class可以加载时,通过的class加载器层次更深
this contains all classes in WEB-INF/classes --> weblogic.utils.classloaders.ChangeAwareClassLoader@6437e3af finder: weblogic.utils.classloaders.CodeGenClassFinder@2cd01c0c annotation: mi.8200@MedicalIntelligence
weblogic.utils.classloaders.FilteringClassLoader@6413df9c finder: weblogic.utils.classloaders.CodeGenClassFinder@54bdae18 annotation:
weblogic.utils.classloaders.GenericClassLoader@5919d25d finder: weblogic.utils.classloaders.CodeGenClassFinder@1265cb83 annotation:
java.net.URLClassLoader@5a88cbff
Application classloader, contains classes from weblogic bundled jars --> sun.misc.Launcher$AppClassLoader@2a9a42ef
sun.misc.Launcher$ExtClassLoader@75a06ec2
当它抛出 ClassNotFoundException
时,它被缩短了——只出现了最后两个条目
sun.misc.Launcher$AppClassLoader@2a9a42ef <-- Hierarchy starts at Application classloader
sun.misc.Launcher$ExtClassLoader@75a06ec2
然后,我注意到堆栈跟踪包含这些以 ~[weblogic.server.merged.jar:12.1.3.0.0]
结尾的行(编辑问题以添加更多堆栈跟踪)
at org.apache.commons.collections.map.AbstractHashedMap.doReadObject(AbstractHashedMap.java:1212) ~[weblogic.server.merged.jar:12.1.3.0.0]
at org.apache.commons.collections.map.CaseInsensitiveMap.readObject(CaseInsensitiveMap.java:149) ~[weblogic.server.merged.jar:12.1.3.0.0]
所以 Apache commons 集合是从 weblogic 捆绑的 jar 中加载的,即 sun.misc.Launcher$AppClassLoader@2a9a42ef
这可能会触发 错误 classloader 进一步向下传递.所以在 weblogic.xml
org.apache.commons.collections.*
添加到 prefer-application-packages
<prefer-application-packages>
...
<package-name>org.apache.commons.collections.*</package-name>
</prefer-application-packages>
有用的资源
- Weblogic 提供的类加载器分析工具 (CAT):
http(s)://<server-hostname:port>/wls-cat
,显示类加载器树 - Understanding Classloading in Weblogic
- java.lang.NoClassDefFoundError: How to resolve series