Mise en place de JPA/Hibernate dans un bundle OSGI-Equinox

Dans le cadre d’un projet, j’avais besoin de mettre en place un bundle OSGI (implémentation EQUINOX) qui utilise JPA / Hibernate.

Les versions de départs sont les suivantes :

  • OSGI Equinox org.eclipse.osgi_3.5.2
  • Hibernate : hibernate-4.2.6.Final
  • Hibernate envers : hibernate-envers-4.2.6.Final

Ici : l’utilisation hibernate-envers n’est pas obligatoire

Suivi de la doc d’hibernate

Lorsque vous voulez mettre en place Hibernate dans un bundle vous avez plusieurs possibilités :

  • Container-Managed JPA
  • Unmanaged JPA
  • Unmanaged Native

(cf doc hibernate)

Pour mon cas, j’ai utilisé la solution du « Unmanaged JPA », pas besoin d’ajouter la couche « blueprint » pour l’instant.

Il y a des modifications à apporter au niveau du code (pour le chargement de l’entitymanager) ainsi qu’au niveau du MANIFEST.MF de votre bundle.

Pour le MANIFEST.MF il faut ajouter les Import-Package nécessaires et le chemin dans le Meta-Persistence :

[sourcecode language="plain" title="Fichier META-INF/MANIFEST.MF"]...
Export-Package: ...
Import-Package: ...
Meta-Persistence: META-INF/persistence.xml
[/sourcecode]

Voici le code pour pouvoir créer l’EntityManager dans le contexte d’un bundle OSGI. (Exemple de code issue de la doc d’hibernate).

[java title="Fichier Registry.java"]
package fr.mydevworld.webapp.util;

import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceProvider;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;

public class Registry {

    private static Registry instance;

    public  static Registry getInstance(){
        if(instance == null ) instance = new Registry();
        return instance;
    }

    private EntityManagerFactory emf;

    private Registry() {
        if ( this.emf == null ) {
            Bundle thisBundle = FrameworkUtil.getBundle( Registry.class );
            // Could get this by wiring up OsgiTestBundleActivator as well.
            BundleContext context = thisBundle.getBundleContext();

            ServiceReference serviceReference = context.getServiceReference( PersistenceProvider.class.getName() );
            PersistenceProvider persistenceProvider = (PersistenceProvider) context.getService( serviceReference );
            this.emf = persistenceProvider.createEntityManagerFactory( "mydevworld", null );
        }
    }

    public EntityManagerFactory getEmf() {
        return emf;
    }
}
[/java]

Dans le constructeur du Registry ; on récupère le contexte du bundle courant pour pouvoir récupérer les services enregistrés de type « javax.persistence.spi.PersistenceProvider ». Une fois le service récupérer, on va pouvoir créer notre entityManager définit dans le fichier META-INF/persistence.xml de notre bundle (voilà pour l’explication du code).

Première montée de version

La problématique que j’ai rencontrée suite à la modification du code est que la version d’hibernate que j’utilisais n’était plus compatible (packages manquants). J’ai du faire une montée de version d’hibernate 4.2.6.Final -> 4.3.0.Final.

La montée en version d’hibernate ma obligé à faire une montée de version d’Equinox de 3.5.2 -> 3.8.2.

Une fois qu’on a résolu toutes les dépendances, on obtient les versions principales suivantes :

  • OSGI Equinox org.eclipse.osgi_3.8.2
  • Hibernate : hibernate-4.3.0.Final
  • Hibernate OSGI: hibernate-osgi-4.3.0.Final
  • Hibernate envers : hibernate-envers-4.3.0.Final

Et l’on peut relancer notre bundle.
Attention : si vous avez un NullPointException au niveau de la résolution des services de référence dans votre registry, c’est que vous avez oublié de démarrer le bundle Hibernate-OSGI. En effet, c’est lui qui est en charge d’enregistrer les services de type « javax.persistence.spi.PersistenceProvider ».

Donc démarrer ce bundle (hibernate-osgi) avant le votre.

Après avoir redémarré dans le bon ordre les bundles, j’ai rencontré l’erreur suivante :

[sourcecode language="plain"]
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Lorg.hibernate.integrator.spi.Integrator;
at org.hibernate.osgi.OsgiPersistenceProvider.generateSettings(OsgiPersistenceProvider.java:126)[/sourcecode]

La piste de la solution est ici : Hibernate OSGi 4.3.0.CR1 can’t discover services

Seconde montée de version

Cette erreur, je l’ai résolue en faisant une dernière montée de version d’hibernate de 4.3.0.Final -> 4.3.1.Final.

Après cette montée de version j’ai pu accéder au fichier persistence.xml de mon bundle et créer mon entitymanager.

Mon environnement final étant celui-ci :

  • OSGI Equinox org.eclipse.osgi_3.8.2
  • Hibernate : hibernate-4.3.1.Final
  • Hibernate OSGI: hibernate-osgi-4.3.1.Final
  • Hibernate envers : hibernate-envers-4.3.1.Final

A noter : je rappelle que hibernate-envers n’est aucunement obligatoire, mais que si vous l’avez utilisé (comme moi) vous devez obligatoirement faire sa montée de version aussi en 4.3.1 car sinon, il y aura un problème de compatibilité.