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
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 :
... Export-Package: ... Import-Package: ... Meta-Persistence: META-INF/persistence.xml
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).
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; } }
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 :
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Lorg.hibernate.integrator.spi.Integrator; at org.hibernate.osgi.OsgiPersistenceProvider.generateSettings(OsgiPersistenceProvider.java:126)
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é.