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?
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.