Reputation: 2286
I'm running tomcat 6.0.23, and according to the classloader documentation webapps should look for classes in this order:
I have a webapp that uses hibernate and the hibernate jars are in it's WEB-INF/lib directory. When running on it's own, all works fine.
I also have a jar file that needs to sit in the tomcat/lib directory as it contains some classes that need to be loaded on startup (an object factory and the objects it creates). These classes use toplink for their JPA implementation and here is where I get a problem.
I need to put the toplink jars where they can be accessed on tomcat startup, so I've put them in the tomcat/lib directory. According to the order of classloading listed above, when the webapp that uses hibernate wants the hibernate implementation classes, it should find them in it's WEB-INF/lib directory, but what actually happens is it finds the toplink implementation classes from the tomcat/lib directory, and I get a class cast exception.
Can anyone please explain why my webapp class loaders aren't finding what they need in their WEB-INF/lib directory, or suggest a way to debug the classpath at runtime?
Thanks.
Upvotes: 2
Views: 1070
Reputation: 128749
Be sure to read and understand the paragraph preceding the list you quoted:
"When a request to load a class from the web application's WebappX class loader is processed, this class loader will look in the local repositories first, instead of delegating before looking. There are exceptions. Classes which are part of the JRE base classes cannot be overriden. For some classes (such as the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be used."
What are these "hibernate implementation classes" that you're having problems with? (Hibernate's "implementation" classes would be completely different from Toplink's.) Are they javax.persistence classes? These may (or may not) fall under the category of "JRE base classes", which behave differently.
Edit: Based on your comment, this is just a typical cross-class-loader loading problem. Tomcat's class loading is working exactly as you expect it to. If you look into the JPA classes where this initialization is going on, you'll find a line like this:
Enumeration<URL> resources =
cl.getResources("META-INF/services/" + PersistenceProvider.class.getName());
That loads all PersistenceProviders from all ClassLoaders, including your Toplink one in the lib directory. It then immediately does this:
for ( PersistenceProvider provider : providers ) { ...
That's on line 77 of javax.persistence.Persistence, where your exception is coming from. That's because the PersistenceProvider class referred to on that line is from your webapp class loader, but the collection contains two instances: your Hibernate implementation from the same class loader and the Toplink one from a different class loader.
This global, static initialization is one major thing that's kept me from shifting to JPA. I still just use straight Hibernate because of problems like this.
Upvotes: 4