Skip to content

ClassNotFoundException when deserializing nested objects under a custom ClassLoader #107

@skopf

Description

@skopf

Abstract:
When deserializing an object, the classes of nested objects (e.g. objects contained in an ArrayList) are loaded with the sun.misc.Launcher$AppClassLoader that has loaded spring-loaded itself instead of the classloader of the calling code.

Background:
When deserializing objects, the ObjectInputStream uses sun.misc.VM.latestUserDefinedLoader() to get the ClassLoader to resolve the classes that are read from the input stream.
If the class that invoked deserialization is loaded by a custom class loader, e.g. the WebappClassLoader within a web application, this class loader is used to load the classes of the object to be deserialized.
The contract of sun.misc.VM.latestUserDefinedLoader() is:

Returns the first non-null class loader up the execution stack,
or null if only code from the null class loader is on the stack.

Under spring-loaded, reflective method calls are intercepted. So when ObjectStreamClass.invokeReadObject() uses reflection to call ArrayList<E>.readObject(), this call is intercepted by ReflectiveInterceptor.jlrMethodInvoke(). When ObjectInputStream.resolveClass() is then invoked by ArrayList<E>.readObject(), the first non-null class loader on a stack frame is the sun.misc.Launcher$AppClassLoader that has been used to load ReflectiveInterceptor.jlrMethodInvoke().

Here is a sample call stack that illustrates this issue. The class loader in each stack frame is listed in square brackets at the end of the line. Stack Frames without a ClassLoader at the end use the null class loader (i.e. system loader).

    ObjectInputStream.resolveClass(ObjectStreamClass) line: 623 
    ObjectInputStream.readNonProxyDesc(boolean) line: 1610  
    ObjectInputStream.readClassDesc(boolean) line: 1515 
    ObjectInputStream.readOrdinaryObject(boolean) line: 1769    
    ObjectInputStream.readObject0(boolean) line: 1348   
    ObjectInputStream.readObject() line: 370 [local variables unavailable]  
    ArrayList<E>.readObject(ObjectInputStream) line: 771    
    GeneratedMethodAccessor417.invoke(Object, Object[]) line: not available 
    DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
    Method.invoke(Object, Object...) line: 606  
    ReflectiveInterceptor.jlrMethodInvoke(Method, Object, Object...) line: 1270 [sun.misc.Launcher$AppClassLoader]
    GeneratedMethodAccessor339.invoke(Object, Object[]) line: not available 
    DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
    Method.invoke(Object, Object...) line: 606  
    ObjectStreamClass.__sljlrmi(Method, Object, Object[]) line: not available   
    ObjectStreamClass.invokeReadObject(Object, ObjectInputStream) line: 1017    
    ObjectInputStream.readSerialData(Object, ObjectStreamClass) line: 1891  
    ObjectInputStream.readOrdinaryObject(boolean) line: 1796    
    ObjectInputStream.readObject0(boolean) line: 1348   
    ObjectInputStream.readObject() line: 370    
    SerializationUtils.deserialize(InputStream) line: 230 [org.springsource.loaded.ChildClassLoader]
    SerializationUtils.deserialize(byte[]) line: 273    [org.springsource.loaded.ChildClassLoader]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions