-
Notifications
You must be signed in to change notification settings - Fork 38.5k
Proper load-time weaving support for Hibernate 5 [SPR-13886] #18459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Juergen Hoeller commented I'm afraid our Hibernate 5.0 support in Spring 4.2 is somewhat basic still, with load-time weaving not having been fully tested and not really supported yet in the first place. Native Tomcat weaving support is also pretty recent. We'll make sure to close any such gaps for 4.3. Juergen |
Andrei Ivanov commented Hmm, should I open a separate issue for the inability to inject a specific LTW into a component that is |
Juergen Hoeller commented It looks like we'll have to expose a Excluding the |
Juergen Hoeller commented I've committed a revision where we expose an |
Andrei Ivanov commented Made a quick test, doesn't work :(
Btw, there's a PR attached to the ticket that should help reproduce the problem. |
Juergen Hoeller commented Indeed, it would probably work if the throwaway class loader was actually used for those purposes! It seems that Hibernate only uses it for identifying candidate classes but then goes on registering its class transformer in a very broad way... without filtering for entity classes first, like it did up until 4.3 and like OpenJPA and EclipseLink still do. The transformer just unconditionally goes right into its enhancer code path: Aside from choking on some classes, it also sets up a lot of stuff for every single transform attempt on the application class loader, even for completely unrelated classes loaded after the persistence bootstrap phase... Frankly, this looks like misbehavior on Hibernate's side: The first thing a transformer should do is to check whether it actually applies! With JPA persistence metadata, this is quite straightforward... if the persistence provider itself does it. From our perspective, we don't have hard information on this: In particular, we don't know the provider's scanning results. Filtering based on exclusions, like in your workaround above, is not efficient there on the application class loader itself at all. We really need the transformer to do it based on its own metadata, immediately backing out if it encounters a non-entity class that it doesn't apply to. |
Juergen Hoeller commented It seems that the root of this particular problem is not in As a consequence, I'm afraid this will have to be reported to the Hibernate project instead: |
Juergen Hoeller commented There is a trick applied by OpenJPA's class transformer that we could possibly apply at the level of our At the same time, it still makes a lot of sense for Hibernate to narrow the applicability of its transformer implementation. |
Andrei Ivanov commented I would create a ticket for Hibernate, but I would also have to create a test to demonstrate the issue, and that will probably be much more complicated than for this one :( Seems to work now without my workaround :) |
Andrei Ivanov opened SPR-13886 and commented
Trying to upgrade Hibernate from 4.3.x to 5.0 throws this nice exception:
It hides the actual
ClassCircularityError
, thrown here:MetadataBuildingProcess.handleTypes(MetadataBuildingOptions) line: 327 -> triggers loading of
org.hibernate.type.BasicTypeRegistry
1st. call to EnhancingClassTransformerImpl.transform(ClassLoader, String, Class<?>, ProtectionDomain, byte[]) line: 44 triggers the loading of
org.hibernate.bytecode.enhance.spi.Enhancer
2nd. call triggers the
ClassCircularityError
Investigating the issue, I see that
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl
callspersistenceUnit.getTempClassLoader()
,which ends up in
org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver#getThrowawayClassLoader
, invokingorg.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader#getThrowawayClassLoader
.In theory, this should handle this scenario.
There are 2 problems here:
org.apache.catalina.loader.WebappClassLoader
, which is not aDecoratingClassLoader
, so the exclude package (org.hibernate
) is not set:SpringPersistenceUnitInfo#getNewTempClassLoader
Even if it did, just
org.hibernate
wouldn't be enough here.EntityManagerFactoryBuilderImpl
, on line 227, pushes the class transformer but 3 lines after (230) it sets the temp classloader to null.After this,
EntityManagerFactoryBuilderImpl#build
is called, and that triggers the error.Now for the workaround.
Inspired by the
AspectJClassBypassingClassFileTransformer
, I've created my own:As you can see in the code, ignoring just the Hibernate classes isn't enough, the Javassist classes must also be ignored.
The problem with the workaround so far is that it cannot injected:
My LTW initially gets injected, but then the
LoadTimeWeaverAwareProcessor
comes along and injects back the original LTW (DefaultContextLoadTimeWeaver
).I even tried with a
DefaultPersistenceUnitManager
, but that also implementsLoadTimeWeaverAware
.So, to get my LTW injected, I've created my own processor:
This finally makes it run.
So, did I miss something in the configuration?
Is this supposed to run using standard Spring configuration?
Or maybe Hibernate + bytecode enhancement wasn't tested thoroughly?
The app is running inside a Tomcat 8.0.30 with LTW active.
Affects: 4.2.4
Issue Links:
@Bean
method as wellReferenced from: pull request spring-attic/spring-framework-issues#117, and commits bdb9473, 836a321, b82df14
0 votes, 5 watchers
The text was updated successfully, but these errors were encountered: