users@glassfish.java.net

Re: Java Persistence and ClassLoaders

From: Sahoo <Sahoo_at_Sun.COM>
Date: Tue, 11 Dec 2007 21:33:49 +0530

See inline...

glassfish_at_javadesktop.org wrote:
> 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.
>
>
This is correct, because you have probably installed your provider in
domains/domain1/lib. Thread.currentThread().getContextClassLoader() in
the context of createEMF call will return the class loader that's
capable of loading application classes.
> 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)
>
Since you generate source code, why don't you do it during development
as opposed to runtime? That will solve all your problems, only thing is
the application would be non-portable as it would not contain classes
which are specific to your provider.
> 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
>
How does EMF know about these generated class to instantiate them?
Reflection?
> - Application code creates entity classes which in turn create auxilliary classes
> - Auxilliary classes are not available on any classpath
>
Correct, there is no API exposed to extend classpath of our class loader.
> 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).
defineClass is a protected method. How did you call it? If you called it
from your own subclass, then that's not going to make it available in
the namespace of its delegate.
> 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.
>
>
I don't think any of the above would work.
> 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.
>

AFAIK, there is no exposed API to allow external module to write to our
deployment area.

By writing some custom, GlassFish specific code, it is possible to
extend GlassFish to integrate with your persistence provider such that
the code generation happens only when an application is deployed. The
generated classes can then be automatically made available to runtime
class loader every time application is loaded in the system. Take a look
at PersistenceProcessor.java [1] to see how we use a similar trick to
generate DDLs (a.k.a. Java2DB) using TopLink.

Thanks,
Sahoo

[1]
http://fisheye5.cenqua.com/browse/glassfish/cmp/support/ejb/src/com/sun/jdo/spi/persistence/support/ejb/ejbc/PersistenceProcessor.java