I have two different webapps, and each load the same class A with different classloader. When I put one instance in the session and then get it from the other webapp, a ClassCastException
is thrown.
For example, in webapp A, I store a
in the session, then in webapp B, I get the a
from the session and cast it to A, the ClassCastException
is thrown.
Is there a way to resolve this?
A class is always identified using its fully qualified name (package. classname). So when a class is loaded into JVM, you have an entry as (package, classname, classloader). Therefore the same class can be loaded twice by two different ClassLoader instances.
There can be multiple classloaders in a normal Java program. The one that loads your main class, ClassLoader , is the default one, and from your code, you can create and use as many classloaders as you like.
When the JVM is started, three class loaders are used: Bootstrap class loader. Extensions class loader. System class loader.
As we can see, there are three different class loaders here: application, extension, and bootstrap (displayed as null). The application class loader loads the class where the example method is contained. An application or system class loader loads our own files in the classpath.
Is there a way to resolve this?
Basically no.
As far as the JLS is concerned, the types are different types, and there is no way that the JVM will allow you to pretend otherwise. For instance, the classes could have different code and different object layouts. If you could trick the JVM into treating the types as the same, you would be able to blow away JVM runtime safety. That way lies insanity.
The solution is to make sure that you don't have two different class loaders loading the same class. In the context of Tomcat, this means that if two or more webapps need to share instances of a class, then that class must be defined in a classloader that is common to both; e.g. put the JAR file in the $CATALINA_HOME/lib
or $CATALINA_HOME/common
directory.
If there is a technical reason why the classes have to be loaded by different classloaders (maybe because the classes really are different), then you could work around the problem by defining an interface that both versions of the class implement, and then programming to the interface rather than the implementation class. Of course, the interface needs to be loaded by a shared classloader ... or else you run into the same problem again.
You should avoid this situation, basically - either put both bits of functionality in the same webapp, or move the library containing class A into an appropriate location such that only one classloader will be used. Two classes loaded by different classloaders are entirely distinct in the JVM - you simply won't be able to cast between them.
See the Tomcat classloader documentation for more details about the various classloaders used. It looks like you'd want to put this common class into the common classloader area. As the documentation notes, this is pretty unusual, but if you really want to share an object between two webapps (which is also unusual) it's probably the easiest way forward.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With