Issue Details (XML | Word | Printable)

Key: HHH-2317
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: Unassigned
Reporter: Jason Novotny
Votes: 5
Watchers: 6
Operations

If you were logged in you would be able to see more operations.
Hibernate Core

CGLIB unable to lazy load classes in diff. classloader than hibernate jars

Created: 19/Dec/06 01:14 PM   Updated: 27/Apr/09 03:35 AM
Component/s: core
Affects Version/s: 3.2.1
Fix Version/s: None

Time Tracking:
Not Specified

File Attachments: 1. Text File hibernate-3.1.patch (1 kB)
2. Text File HibernateSpecifyClassLoaderToCGLIB.patch (0.5 kB)

Issue Links:
Duplicate
 

Bug Testcase Reminder (view):
REMINDER: Bug reports should generally be accompanied by a test case
Participants: Jason Novotny, Martin Kneissl, Max Rydahl Andersen, Mike Meessen and Pietu Pohjalainen


 Description  « Hide

Please see http://forum.hibernate.org/viewtopic.php?p=2334617#2334617 for more details and possible fix



Max Rydahl Andersen added a comment - 19/Dec/06 04:33 PM

what exactly does not work here ? testcase?

I load classes constantly from a different classloader; namely the one that is set via the current context thread classloader.


Jason Novotny added a comment - 19/Dec/06 10:20 PM

My test case is a bit complex. I'm using Tomcat and I have my hibernate libs in the WEB-INF/lib of my web application. However I have a "service factory" that lives in the Tomcat shared/lib classpath which maintains an instance of my hibernate persistence wrapper. The persistence wrapper passes itself to the service factory along with its classloader. Now in web application B, I get an instance of my persistence wrapper and I use it to load in the hibernate mapping files in webapp B. As long as lazy="false" it works fine. Otherwise if lazy is true, I get this stack trace:

net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.getProxyFactory(CGLIBLazyInitializer.java:121)
at org.hibernate.proxy.pojo.cglib.CGLIBProxyFactory.postInstantiate(CGLIBProxyFactory.java:43)


Max Rydahl Andersen added a comment - 20/Dec/06 01:01 AM

And why is it not enough for you to set the current thread context classloader before and after the calls ?

But in any case having both hibernate in server lib and your web app sounds like a really bad idea.


Jason Novotny added a comment - 20/Dec/06 03:28 AM

I don't think I explained well enough-- here's the actual structure

webappA/WEB-INF/lib
+ hibernate-3.2.jar, cglib-x.jar, asm-*.jar, etc
+ PersistenceServiceImpl which acts as a wrapper around hibernate-- responsible for loading in mapping files

webappB/
+ contains hibernate mapping files (no other jars)

shared/lib
+ PersistenceService interface

So webappB has access to the PersistenceService interface since it's in shared/lib, but the actual impl is in webappA and has webapp A's classloader. To recap, if lazy is false,then webappB can use the concrete PersistenceServiceImpl to load in the mapping files in B. However, if lazy is true, then the proxies need to be created.

I then tried experimenting with Configuration.addResource(mappingFile, classLoader) in the PersistenceServiceImpl so that it could use webapp B's classloader to locate its mapping file and was greeted with this error:

Caused by: java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy
at java.lang.ClassLoader.defineClass1(Native Method)

which seems in line with the reporter of http://opensource.atlassian.com/projects/hibernate/browse/HHH-2318

In a nutshell I am trying to figure out any possible way where the hibernate and co. jars do not need to be placed in every single webapp.

Thanks, Jason


Max Rydahl Andersen added a comment - 20/Dec/06 03:50 AM

so remove the hibernate jars you have in webapp's and just use the one in the server lib.

But only load classes when you actually are inside the scope of the webapp! Nothing weird about this.

But it is definitly not recommended to use libs from server libs since it very often (always?) result in redeployment
being a pain since you can't unload those classes.


Pietu Pohjalainen added a comment - 15/Jan/08 06:16 AM

Here's a patch against hibernate 3.1.3, which changes the order of interfaces so that the class HibernateProxy is located as the last one.
This way, when the cglib enhancer selectes the classloader, it chooses the POJO's classloader instead of HibernateProxy's classloader.
This fixed my error where the hibenate classes were located at web server common libraries, and POJOs in the web application itself.

This problem seems to be present in hibernate 3.2.5ga as well; I guess that this fix would also apply there.


Martin Kneissl added a comment - 24/Apr/09 09:40 AM

We seem to have this issue, too, with Hibernate 3.2.6. The hibernate-3.1.patch solves the problem for us.

Specific details:

  • We have the Hibernate 3.2.6 jars in the JBoss/server/xxx/lib directory. Let's call the class loader handling this directory "J" (for JBoss).
  • We have an enterprise app to deploy as EAR with separate class loader. The hibernate session factory for this application is configured using an MBean from a SAR within the EAR. Let's call the application class loader "A" (for application).

Problem: Hibernate is not able to load the CGLIB generated proxy class. Our analysis shows that the CGLIB generated byte code that references the application class interfaces cannot be loaded. This is because hibernate does not tell CGLIB which class loader to use and CGLIB implicitly takes the one from the first interface of the generated proxy. This often is a hibernate provided interface (HibernateProxy), coming from class loader "J". This class loader has no access to the classes from class loader "A", where the application interfaces come from, so CGLIB cannot load the proxy class.

The following alternatives have been evaluated and rejected:

  • Putting hibernate into the EAR and removing it from JBoss/server/xxx/lib. This prevents using EJB3 because the EJB3 deployer uses class loader "J" and has dependencies on hibernate that would now reside in class loader "A".
  • Not using a separate class loader for the EAR. This prevents redeployment since the classes used from the hibernate mapping would remain in the one and only relevant class loader "J" when the EAR is undeployed.

The patch provided by Pietu above (hibernate-3.1.patch) solves the problem, we would be glad to see it in a future hibernate release.

I still have the feeling that the ClassLoader for the generated proxy classes should be defined explicitly by hibernate.
Ideas:
a) Perhaps the class loader should be defined via the SessionFactory as a strategy and then used by the CGLIBLazyInitializer to define the class loader in CGLIB's Enhancer.
b) Or, a little less general and less invasive, use the class loader of the persistentClass aka entityClass of the proxy to be created. I consider it more likely that the entity class comes from the application class loader than that all of the interfaces come from the application class loader.

A patch for 3.2.6 to implement b) will follow, soon.


Mike Meessen added a comment - 27/Apr/09 03:35 AM

Attached the patch in agreement with Martin Kneissl: HibernateSpecifyClassLoaderToCGLIB.patch

This patch worked for us and is as unobtrusive as it can be.