Let’s assume you use inter ServletContext communication mechanism to transfer an Object created by one web application to another web application. In order to make this object fully available to your target web application, you must make sure that those two web applications use same ClassLoader to load that object’s class.
This can be easily achieveable if you put related classes to a shared location of your servlet container, like common/lib/ext folder in Tomcat. However this approach cannot be always possible as those classes/jars to be put into the shared folder might get conflict with those which are already there, or you may come accross with class loading problems, like those shared classes are loaded instead of classes which exist in one of your other web application’s WEB-INF/lib folder. In short, it is much more preferable to keep application specific classes or jars isolated in each web application’s private classpath. Unfortunately, you will face with another problem in this case.
In java, two classes are same if they have same fully qualified name in addition to they are both loaded by same ClassLoader. In our case, an object is instantiated in one of our web application which uses its own ClassLoader to load that object’s class, and then uses inter ServletContext communication to pass that object, possibly putting it into current request as attribute, to another web application. At this time, if the second application tries to cast that object into its exact type it will get a ClassCastException. This is because that object’s class is loaded by another ClassLoader, and here we try to cast it to a type which is also loaded by the second application’s private ClassLoader. Our object is simply not castable to its type!
How can we solve this problem? I use a simple trick to overcome such a problem; object serialization/deserialization. Let’s look at below code snippet:
Object obj = req.getAttribute("myObject"); ByteArrayOutputStream bout = new ByteArrayOutputStream(); out = new ObjectOutputStream(bout); out.writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); in = new ObjectInputStream(bin); obj = in.readObject();
We get our object from request, and then stream it into a byte array using ObjectOutputStream. Afterwards, we deserialize it using ObjectInputStream from that byte stream, and obtain same object whose class is loaded by our second web application’s ClassLoader. In order for this solution to be workable, we need to mark our object’s class as Serializable. Some may argue that streaming an object to byte array, and then reverse it back to an object might create a performance issue, but I am sure that cost is tolerable when compared to class loading issues which might possibly occur if you put many application specific classes or jars into a common location, especially if you have several other applications having with dependencies to those shared classes/libraries.