Classloader Issues with Envers in OSGi

260 Views Asked by At

I've successfully started Hibernate in an OSGi context, and now I want to add Envers.

The documentation claims that's possible. I don't believe it anymore. There is no documentation of any kind for this subject and nobody seems to have actually done it. Moreover even with a Blueprint implementation I had to hack the classloader to make Hibernate even find Envers:

osgiClassLoader = new org.hibernate.osgi.OsgiClassLoader();
osgiClassLoader.addBundle(requestingBundle);
osgiClassLoader.addBundle(FrameworkUtil.getBundle(SessionFactory.class));
osgiClassLoader.addBundle(FrameworkUtil.getBundle(HibernateEntityManagerFactory.class));
osgiClassLoader.addBundle(FrameworkUtil.getBundle(EnversService.class));

Thread.currentThread().setContextClassLoader(osgiClassLoader);

(I feel like I should be asking: "Is Envers in OSGi even possible?" So if you have a definite answer for that question, please let me know. I've been spending way too much time on these issues.)

However the actual problem has nothing to do with Hibernate / Envers and all with OSGi. Both want to access the entities and enums used. With reflection nonetheless. Of course they can't. And of course I can't add Import-Packages.

The relevant stack trace looks something like this:

Caused by: java.lang.ClassNotFoundException: org.acme.project.MyEnum cannot be found by org.hibernate.core_5.1.0.Final
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:461)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364)
    at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.hibernate.internal.util.ReflectHelper.classForName(ReflectHelper.java:151)
    at org.hibernate.type.EnumType.setParameterValues(EnumType.java:105)
    ... 62 more

Normally with OSGi-agnostic frameworks I'd just add something like this to the MANIFEST.MF:

// to org.hibernate.core
Eclipse-BuddyPolicy: registered
// to org.acme.project
Eclipse-RegisterBuddy: org.hibernate.core

However I can't add anything to Hibernate's manifest. I tried contributing the line via fragment, but that did not work either.

I even tried adding the entire Hibernate dependencies as JAR into a plug-in to add the above. It won't work.

How to resolve these classloader issues?

2

There are 2 best solutions below

1
On BEST ANSWER

If your @Audited class references an Enum, org.hibernate.core needs to import the classes package. The best way to do this is to register a fragment bundle.

Also, it is wise to make sure your start level for hibernate bundles is lower than the start levels for entity bundles.

Here is a snippet from a sample Gradle script to create a fragment bundle. Make sure your change the org.hibernate.core bundle version to the one you are using, and also update the 'your.package' to the one that contains your Enum.

/*
When using Envers, entities that reference Enum classes must be imported (Import-Package) by the org.hibernate.core bundle.
 */

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:3.2.0'
    }
}

apply plugin: 'biz.aQute.bnd.builder'

jar {
    manifest {
        attributes(
                'Fragment-Host': 'org.hibernate.core;' + 'bundle-version=' + '5.2.9.Final',
                'Import-Package': 'your.package'
        )
    }
}
4
On

Late to the party, but throwing this here in case anyone else needs it:

Hibernate bootstrapping looks fairly different in OSGi than it does in SE/EE land. We have numerous tickets in JIRA tracking enhancements, especially to make things more dynamic and to reduce the brittleness of startup ordering (Steven is absolutely correct that hibernate-core, hibernate-envers, etc. must currently be started first prior to your bundle).

I'd highly advise against the bundle fragment approaches, but I'll comment why on Steven's answer.

Our hibernate-demos project has a few OSGi quickstarts, all of which include Envers setup. This one is probably more of what you're looking for: https://github.com/hibernate/hibernate-demos/tree/master/hibernate-orm/osgi/unmanaged-native