users@glassfish.java.net

Re: Java Persistence and ClassLoaders

From: <glassfish_at_javadesktop.org>
Date: Mon, 10 Dec 2007 17:25:19 PST

Thanks Sahoo, this was very helpful, but I'm still not clear how to ensure that my classes load correctly.

Looking at the classloaders available when PersistenceProvider.createContainerEntityManagerFactory is called and relating this to Siva's blog we have:

Returned from persistenceUnitInfo.getClassLoader (which I referred to as the entity classloader) the hierarchy is:
com.sun.enterprise.web.WebModuleListener$InstrumentableWebbappClassLoader (The Web classloader?)
org.apache.catalina.loader.WebappClassLoader (The EJB Classloader?)
com.sun.enterprise.util.ConnectorClassLoader (The Connector ClassLoader)
java.net.URLClassLoader (The Common ClassLoader)
com.sun.appserv.server.util.ASRLClassLoader (The System ClassLoader)
sun.misc.Launcher$AppClassLoader (Bootstrap ClassLoader 2)
sun.misc.Launcher$ExtClassLoader (Bootstrap ClassLoader 1)

However this.getClass().getClassLoader() on the PersistenceProvider (which I referred to as the application classloader) returns the Common ClassLoader i.e. just the last four entries in the above list.

In my JPA implementation the following steps happen in the org.hydrateframework.ejb.PersistenceProviderImpl createContainerEntityManagerFactory call:
1. search for and parse annotated entity classes
2. generate code based on interpreted annotations including:
  - classes containing code that will enhance the existing entity classes
  - auxilliary classes that support ORM
3. compile generated code into a temporary location (not on the classpath)
4. install a class transformer for the entity classes and any classes that access member fields directly

The class loader challenge arises because:
 - EntityManagerFactory creates auxilliary classes which in turn create entity classes
 - Application code creates entity classes which in turn create auxilliary classes
 - Auxilliary classes are not available on any classpath

My first solution was to force a pre-load all the auxilliary classes using the classloader from the persistence unit info (by calling defineClass directly on this class). Unfortunately, I just saw class not found exceptions for the auxilliary classes because they were not visible when loaded from the EntityManagerFactory. I then switched to using the common classloader to force load the aux classes, but these classes were now unable to load the entity classes. I could get the app to work by also force loading transformed entity classes, but this is less than ideal because these are then shared across all apps and in any case would not now be able to construct any other web-app deployed classes that might also be needed.

I've now reworked the code so that the EntityManagerFactory loads any necessary auxilliary classes using the classloader from the persistence unit info, however it's still having problems seeing the pre-loaded auxilliary classes even though I've confirmed that:
 - the classes I'm trying to load were in fact preloaded
 - the classloader used to load the auxilliary classes was WebModuleListener$InstrumentableWebappClassLoader
 - the same classloader fails to find these classes when EntityManagerFactory uses it with the loadClass method.

Short of finding a way of ensuring that the generated classes are written into the same directory/jar as the entity classes, I'm at a loss what to try next.
[Message sent by forum member 'dawidcha' (dawidcha)]

http://forums.java.net/jive/thread.jspa?messageID=249431