7/9/10

Creating custom Hibernate OSGi bundles for JPA 2.0

Edit: I am more than happy that this post is now completely obsolete. Hibernate is now OSGi ready, Yay!


Prologue
I was trying to migrate an application that uses JPA 2.0 / Hibernate to OSGi. I found out that hibernate does not provide OSGi bundles. There are some Hibernate bundles provided in the Spring Enterprise Bundle repository, however they are none available for Hibernate 3.5.x which implements JPA 2.0. So I decided to create them myself and share the experience with you.

This post describes how to OSGi-fy Hibernate 3.5.2-Final with EhCache and JTA transaction support. The bundles that were created were tested on Felix Karaf, but they will probably work in other containers too.

Introduction
A typical JPA 2.0 application with Hibernate as persistence provider will probably require among other the following dependencies
  • hibernate-core
  • hibernate-annotations
  • hibernate-entitymanager
  • hibernate-validator
  • ehcache
Unfortunately, at the time this post was written none of the above was available as OSGi bundle. To make OSGi bundles for the above one needs to overcome the following problems
  • Cyclic dependencies inside Hibernate artifacts.
  • 3rd party dependencies (e.g. Weblogic/Websphere Transaction Manager).
  • Common api / impl issues for validation api and hibernate cache.
The last bullet which may not be that clear points to a problem where an api loads classes from the implementation using Class.forName() or similar approaches. In the OSGi world that means that the api must import packages from the implementation.

Hibernate cyclic dependencies
The creation of an OSGi bundle for each hibernate artifact is possible. However, when the bundles get deployed to an OSGi container, they will fail to resolve due to cyclic package imports.

The easiest way to overcome this issue is to merge hibernate core artifacts into one bundle. Below I am going to provide an example of how to use maven bundle plug-in to merge hibernate-core, hibernate-annotations & hibernate-entitymanager into one bundle.

A common way to use the maven-bundle-plugin to merge jars into artifacts is to instruct it to embed the dependencies of a project into a bundle. However, this is not very handy in cases where you need to add custom code into the final bundle. In that case you can use the maven dependency plug-in to unpack the dependencies, bundle plug-in to create the manifest and jar plug-in to instruct it to use the generated manifest in the package phase.

  
   org.apache.maven.plugins
   maven-dependency-plugin
   
    
     unpack
     generate-resources
     
      unpack
     
     
      
       
        org.hibernate
        hibernate-core
        false
        target
       
       
        org.hibernate
        hibernate-annotations
        false
        target
       
       
        org.hibernate
        hibernate-entitymanager
        false
        target
       
      
      false
      true
     
    
   
  
  
   org.apache.felix
   maven-bundle-plugin
   2.0.1
   true
   
    
     create-manifest
     process-classes
     
      manifest
     
    
   
   
    
     jar
     bundle
    
    ${unpack.bundle}
    
     ${artifactId}
     ${groupId},${artifactId}
     ${project.name}
     *
     *
    
   
  
  
   maven-jar-plugin
   
    
     target/META-INF/MANIFEST.MF
    
   
  
 

Hibernate & 3rd Party dependencies
Hibernate has a lot of 3rd party dependencies. Some of them are available as OSGi bundles, some need to be created and some can be excluded.

Examples of 3rd party dependencies that are available as OSGi bundle in the Spring Enterprise Repository are:
  • antlr
  • dom4j
  • cglib
Dependencies that are not available are:
  • jacc (javax.security.jacc)
 Dependencies that can be excluded vary depending on the needs. In my case I could exclude Weblogic/Websphere transaction manager, since I didn't intend to use them. To exclude a dependency just add the packages that are to be excluded in the import packages section using the ! operator (e.g. !com.ibm.*,*)

Hibernate validator and Validation API
As mentioned above the validation api provides a factory that build the validator by loading the implementing class using Class.forName(). This issue can be solved with 2 ways
  • Use dynamic imports in the API bundle to import the Implementation at runtime.
  • Make the implementation OSGi Fragment that will get attached to the API.
In this example the validation api is the one provided by the Spring Enterprise Bundle Repository, so the second approach was easier to apply.

More details on this issue can be found at this excellent blog post:
Having “fun” with JSR-303 Beans Validation and OSGi + Spring DM

Hibernate & EhCache
More or less the same applies to EhCache. Hibernate provides an interface which is implemented by EhCache. Hibernate loads that implementation in runtime. We will do exactly the same thing  we did for hibernate validator. We will convert ehcache jar to fragement bundle so that it gets attached to the merged hibernate bundle.

Hibernate & JTA Transactions
I kept for last the most interesting part. This part describes what needs to be added inside the bundle so that it can support JTA transactions.

For JTA transactions Hibernate needs a reference to the transaction manager. That reference is returned by the TransactionManagerLookup class specified in the persistence.xml. In a typical JEE container the lookup class just performs a JNDI to get the TransactionManager. In an OSGi container the transaction manager is very likely to be exported as an OSGi service.

This section describes how to build an OSGi based TransactionManagerLookup class. The solution presented is very simple and uses only the OSGi core framework (no blueprint implementation required).

We will add to the hibernate bundle 3 new classes:
  • TransactionManagerLocator (Service Locator).
  • OsgiTransactionManagerLookup (Lookup implementation).
  • Activator (Hibernate Bundle Activator).
TransactionManagerLocator is a ServiceLocator that uses OSGi's ServiceTracker to get a reference to the TransactionManager service.
package org.hibernate.transaction;

import javax.transaction.TransactionManager;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

/**
 *
 * @author iocanel
 */
public class TransactionManagerLocator {

    private final String lookupFilter = "(objectClass=javax.transaction.TransactionManager)";
    private static BundleContext context;
    private static TransactionManagerLocator instance;
    private ServiceTracker serviceTracker;

    //Constructor
    private TransactionManagerLocator() throws Exception {
        if (context == null) {
            throw new Exception("Bundle Context is null");
        } else {
            serviceTracker = new ServiceTracker(context, context.createFilter(lookupFilter), null);
        }
    }

    public static synchronized TransactionManagerLocator getInstance() throws Exception {
        if (instance == null) {
            instance = new TransactionManagerLocator();
        }
        return instance;
    }


    public static void setContext(BundleContext context) {
        TransactionManagerLocator.context = context;
    }

    public TransactionManager getTransactionManager() {
        return (TransactionManager) serviceTracker.getService();

    }
}

OsgiTransactionManagerLookup is an implementation of Hibernates TransactionManagerLookup that delegates the look
up to the TransactionManagerLocator.
package org.hibernate.transaction;

import java.util.Properties;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.hibernate.HibernateException;

/**
 *
 * @author iocanel
 */
public class OsgiTransactionManagerLookup implements TransactionManagerLookup {

    public TransactionManager getTransactionManager(Properties props) throws HibernateException {
        try {
            return TransactionManagerLocator.getInstance().getTransactionManager();        } catch (Exception ex) {
            throw new HibernateException("Failed to lookup transaction manager.", ex);
        }
    }

    public String getUserTransactionName() {

        return "java:comp/UserTransaction";
    }

    public Object getTransactionIdentifier(Transaction transaction) {
        return transaction;
    }
}

Activator is just a bundle activator. Its role is to pass a static reference of the bundle context to the TransactionManagerLocator (the bundle context is required by the service tracker).
package org.hibernate;

import org.hibernate.transaction.TransactionManagerLocator;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

/*
 *
 * @author iocanel
 */
public class Activator implements BundleActivator {

    public void start(BundleContext bc) throws Exception {
        TransactionManagerLocator.setContext(bc);
    }

    public void stop(BundleContext bc) throws Exception {
    }
} 
Example use of the bundle & bundle source code.
An example web application that uses the custom hibernate bundles can be found in this  post.

If you feel tired of reading and just want to use the bundles. You can download them from here. All the custom bundles are included in the maven project under the bundles folder (as seen in the picture).



The example application uses Wicket and can be easily deploy in Karaf.
Final Thoughts
I hope you found it useful.

Any feedback is more than welcome.

15 comments:

  1. Great! Could you please provide the bundles you made?

    ReplyDelete
  2. The full source of the bundles, together with a simple example on how to use the bundles can be found in the follow up post:

    http://iocanel.blogspot.com/2010/07/wicket-spring-3-jpa2-hibernate-osgi.html

    ReplyDelete
  3. http://iocanel.blogspot.com/2010/07/wicket-spring-3-jpa2-hibernate-osgi.html states:
    "The first part of this guide is Creating custom Hibernate 3.5 OSGi bundles. This part describes how to use the custom hibernate bundles in order to build a wicket, spring 3, hibernate 3.5 jpa 2 and deploy it to Karaf"

    ...which is this part... didn't see any link to download OSGi bundled Hibernate.

    It would be great if you could either provide the bundle(s) or a ready-to-use structure to unpack where to put hibernate in order to get it bundled more or less automatically ;)

    ReplyDelete
  4. @JPC: The bundled hibernate is included inside the demo project. Link to the demo project can be found at the bottom of the page http://iocanel.blogspot.com/2010/07/wicket-spring-3-jpa2-hibernate-osgi.html.

    Sorry, if that wasn't very clear. I will update both pages.

    ReplyDelete
  5. Thank you very much. It's very usefull.
    But I can't bundles get work. database-tier starts and fail with error:
    ...
    Failed to convert property value of type [org.springframework.orm.jpa.LocalEntityManagerFactoryBean] to required type [javax.persistence.EntityManagerFactory] for property 'entityManagerFactory'
    ...

    may be something wrong in contex.xml? I stuck with this many hours. Could you please help?

    ReplyDelete
  6. It's rather sad to see that Hiberate still does not care about OSGi out of the box and that manual osgification is still as troublesome as it used to be with Hibernate 3.2.6.
    (See http://hwellmann.blogspot.com/2008/11/hibernate-and-osgi-elaborate-solution.html.)

    Leaving OSGi aside, Hibernate has a number of bugs and omissions in the JPA 2.0 area. I'm really a lot happier with OpenJPA, both for OSGi and JPA 2.0 support.

    Cheers,
    Harald

    ReplyDelete
  7. What if I want to deploy a jar and not a war? With what do I change the jee container that loads the web.xml to arrive to the wicket SpringAppFactory and the first spring beans xml? Any clue?

    ReplyDelete
  8. @idella: I guess that the response was intended to go to the next spot (wicket hibernate osgi).

    I am not sure I understand your question. You can change the war and deploy it as jar (webbundle), if this is what you mean.

    ReplyDelete
  9. Hey, when building with maven, it fails on retrieving "jacc".

    Any ideas ?

    Thanks

    ReplyDelete
  10. Hi,

    we are facing problem when using datanucleus with apache karaaf. I saw that you submitted a patch for the datanucleus. Do you have a simple datanucleus jpa example that can run on a osgi container like karaaf or virgo.

    ReplyDelete
  11. @Sunil: That was while back and I don't have something handy.
    Can you provide me with some more details and see if I can give a hand?

    ReplyDelete
  12. Is there an official release of hibernate osgi now?
    Anybody knows?
    Now im with service mix, i suppose that aries is the best chance that we have ... am i wrong?

    ReplyDelete
  13. Not yet!
    Aries will help you but you'll still need an osgi bundle for hibernate. There is a somwhat older hibernate-jpa feature inside jboss-fuse (formerly known as fuse-esb).

    ReplyDelete
  14. Just to correct my previous comment. It seems that there has been an official hibernate release which is OSGi ready. You'll need to wrap a couple of its dependencies as bundles (jandex and classmate) but it seems that it does work.

    ReplyDelete
  15. Hi Iocanel, maybe it is interesting a little example about how we can use hibernate with jpa 2.0 nowadays for example with Equinox!! I was searching just a simple example.

    ReplyDelete