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