Deserializing objects using custom class loaders


Introduction


Object serialization and de-serialization is pretty straightforward in Java. One serialiazes objects by creating an ObjectOutputStream and then writing objects to it. One deserializes objects by opening an ObjectInputStream to read from the serialized bytes of the object, followed by reading objects from the ObjectInputStream. All this is well and good, until one encounters the problem, during deserialization, that the objects to be desrialized belong to classes that are known only to a custom class loader. In this case, attempting to deserialize objects results in ClassNotFoundExceptions.


This article shows a technique that allows desrialization of objects belonging to classes defined in custom class loaders. But first, let us take a look at the problem.


The problem


Let us assume that we create a "Point" class to model two dimensional points. The "Point" class is a java bean class containing two "int" fields named "x" and "y" (representing the "x" and "Y" co-ordinates of a two dimensional point, respectively).


We create the 'Point" class dynamically using the technique describe in an earlier blog positing about dynamic java beans. Next, we instantiate a couple of Point objects:



Next, we serialize the point objects p1 and p2 to a byte array:




Having obtained the byte array bytes, we can demonstrate the problem we are trying to solve by attempting to deserialize the objects contained therein using the following code snippet:




To our chagrin, this throws a ClassNotFoundException trying to read the first object. It should be pretty obvious why this exception is thrown: essentially, the objects being read belong to a class ("Point") about which the application class loader knows nothing. This is so because the "Point" class has been defined within the context of a custom class loader.


The solution


What we need during the deserialization process is a way to tell the ObjectInputStream to use a custom class loader which "knows" about the "Point" class. In other words, if we had a class loader with us at the point we invoked the deserialization code above that was able to "find" the "Point" class, we need some way to tell the ObjectInputStream to use this class loader as it attempted to read the objects from the stream. Looking at the deserialization API, however, there does not appear to be an obvious way to do this.


This article uses the following technique to accomplish the solution:


Create a CustomObjectInputStream by extending ObjectInputStream. CustomObjectInputStream accepts an array of custom ClassLoaders to use during deserialization.


Override the "resolveClass" method of ObjectInputStream in CustomObjectInputStream.


The "resolveClass" method accepts an ObjectStreamClass object and returns a Class object corresponding to this object (see the Java doc of ObjectInputStream). CustomObjectStream performs attempts to first use the application class loader top load the class corresponding to the name of the ObjectStreamClass. If this fails, it attempts to use each of its custom class loaders one by one to load the class. If even this fails, the over ridden resolveClass throws a ClassNotFoundException:



Putting it all together


The following code snippet puts all of the above together. Note that we define the functionality of the CustomClassLoader anonymously in the code snippet below:



Source code for this article is provided in the form a JUnit test. It is available for download here.


 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments
  • No comments exist for this post.
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.