Hibernate, the popular Java ORM implementation, has recently added OSGi support, enabling Hibernate to be used both as a standalone Jar and also in an OSGi runtime. Although generally adding OSGi support to a library is as simple as adding a few entries in the MANIFEST.MF, for libraries that perform reflection to look up classes the effort can be more challenging.
InfoQ caught up with Brett Meyer, a software engineer at Red Hat and a core developer of Hibernate ORM to find out what the difficulties were and how the feature evolved.
InfoQ: It looks like enabling OSGi for Hibernate is a recent bug, but the request goes back as far as 2008. Can you say how the fix came about?
Meyer: In many ways, this is simply how community-driven projects work. Someone's interest will be piqued around a particular subject and they will provide iterated solutions that eventually land on the end result. In this case, I had struggled in the past to get Hibernate to work in an OSGi environment. So when I joined the team in August 2012, it was on my short list of priorities. This lined up with a few community members starting some of the kick-off work, as well as contributing to the overall discussion.
InfoQ: Why is adding OSGi support for Hibernate more difficult than the average Java library?
Meyer: Nearly every difficulty, potentially unique to ORM implementations in OSGi, revolve around multiple ClassLoaders. Scanning, parsing entities, parsing mapping resources, indexing annotations, registering/using extension point implementations, delegation, statefulness, caching, and other considerations make this a frustratingly complex problem.
Throw in Hibernate's "family of bundles" (rather than one "uber-jar") and eventual support of dynamic persistence unit bundles, things get worse.
InfoQ: Is there anything new in Hibernate that has helped enable OSGi support?
Meyer: In Hibernate ORM 4, there were some new concepts that certainly made things easier. Multi-phased bootstrapping made it more simple to provide ClassLoaders and integration hooks prior to loading and processing resources. The integration hooks themselves made it easier to wire-in "extension point" implementations found in the persistence unit bundle.
However, there are still numerous static, band-aid fixes that have made issues in the current architecture much more apparent. The new metamodel work in Hibernate ORM 5 will greatly increase our support and capabilities.
InfoQ: How does the Hibernate library get a reference to the JPA classes?
Meyer: In order to support multiple persistence units, multiple instances of Hibernate ORM, and potentially multiple versions of both, we completely avoid
DynamicImport-Package
. Hibernate ORM OSGi provides a custom ClassLoader to Core when an EntityManagerFactory (JPA) or SessionFactory (Native) is requested. That ClassLoader is aware of only the instances of Hibernate bundles and the "requesting bundle" – the bundle containing the persistence unit. As usual, entities can be explicitly listed by the unit or discovered with scanning. We can also discover implementations of our "extension point" interfaces, such as Integrator. All this requires that the entire persistence unit (all mappings, entities, extension points) reside in the one single bundle requesting the EMF/SF.Requiring one unit bundle is not exactly ideal and we're working on improving that. Further, it should be noted that supporting multiple instances of Hibernate ORM or multiple versions is not yet fully supported. Unfortunately, as mentioned above, Hibernate ORM 4 is still very static. A more dynamic setup is being worked on for Hibernate ORM 5.
InfoQ: Can you restart a bundle that depends on Hibernate and have it pick up the new classes correctly?
Meyer: In theory, the "requesting bundle" ClassLoader architecture should allow this. The functionality is tied to the persistence unit bundle requesting an EMF/SF, so restarting should be possible. However, fully supporting this dynamic nature won't be a reality until Hibernate ORM 5, especially when it comes to gracefully cleaning up, etc.
InfoQ: How did you go about testing the OSGi support?
Meyer: I created an integration/unit test on Arquillian, using Apache Felix as the container. This brought up some really unique challenges, mostly related to ClassLoading. I had originally hoped to use ShrinkWrap or Tinybundles to dynamically create a persistence unit bundle in memory during test runtime. However, both solutions caused an entity's annotations to be stripped out when received by Hibernate ORM Core (different CLs used to provide InputStreams to ShrinkWrap and read them in Core). There were additional issues, such as the test runtime overriding the "requesting bundle" (described above), essentially giving it the CL the entire container. Any way around the problems introduced a slew of hacky band-aid fixes.
I eventually landed on having three separate source sets: test, testClientBundle, and testResult. The
test
set contains the actual Arquillian test case. ThetestClientBundle
set contains the persistence unit and the actual "unit tests", using Hibernate ORM OSGi services (both JPA and Native) and validating results. Any failures are set in an OSGi service reference, defined by an interface intestResult
. At the end of the run, the service is checked by the original test case. In addition to removing the CL issues, this closely models a real-world environment anyway. I eventually found out that this sort of intermediary service setup is fairly close to how the Aries JPA tests work. I should have checked there first...
InfoQ: Is there any documentation or examples which show how to use Hibernate with OSGi?
Meyer: The best place to start is our Developers' Guide. It describes the types of environments we support, implementation details, and caveats. There's also a Tutorial and QuickStart projects. For those wanting to read through the detailed discussions, start at the top-level OSGi task in JIRA, its subtasks, and linked pull requests.
OSGi support arrived in last week's Hibernate 4.2.3 release. What do you think of the new functionality?