Search code examples
javatomcatclassloader

Classloaders and sharing .jar files with Apache Tomcat


If I have classes that need to be shared between my webapp and Tomcat (e.g. a custom realm and principal), where should the .jar file containing those classes go?

Currently I'm putting the .jar in ${CATALINA_HOME}/lib. This result is a ClassCastException when assigning references from classes of the same type. Here's an example:

MyCustomPrincipal principal = (MyCustomPrincipal)FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();

The method above throws a ClassCastException. The method returns an actual MyCustomPrincipal type (since that's what my custom realm gave Tomcat when it performed authentication) that, apparently, was created by a different classloader. How do I fix this so both Tomcat and my webapp can use MyCustomPrincipal?

http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

Any help is appreciated. Andrew


Solution

  • It looks like you have 2 copies loaded, once in tomcat and once in your WEB-INF/lib jars or other classpath of your deployed application.

    The reason you get classpath exception lies in the way a WAR looks for classes. Contrary to the normal Java rules, a war first looks inside the war for a class and only then passes the request to teh parent classloader.

    A class's identity is dependent of the classloader and the same class loaded in 1 classloader will generate a classcast exception when it is casted in the other classloader.

    The solution is to make sure that the war does not contain the classes which should be provided by the container. If you use maven you can mark these dependencies as 'provided', if you use ant, you have to split your classpath list in 2 and compile against both, but use only the ones you need for constructing the war.