Blog

Partager l’entityManager JPA/Hibernate entre plusieurs bundles OSGI-Equinox

Contexte du projet

J’ai une web application qui consomme des services métiers, mis à disposition grâce à OSGI (Implémentation Equinox).

J’ai un premier bundle « A » qui fournit un ensemble de services métiers et permet de mettre en place la persistance de mon application.

Dans mon bundle « A » j’ai :

  • Mon registry : il permet de créer mon entityManager via mon fichier persistence.xml
  • Des classes annotées @Entity : elles définissent une première partie de mon modèle
  • Des DAOs
  • Des services métiers

Voici les versions actuelles de l’environnement OSGI :

  • 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
  • Bundle a : com.oxiane.a-1.0.0
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	version="2.0">
	<persistence-unit name="oxiane">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<non-jta-data-source>jdbc/db</non-jta-data-source>
		<exclude-unlisted-classes>false</exclude-unlisted-classes>
		<properties>
			<property name="hibernate.archive.autodetection" value="class" />
			<property value="org.hibernate.dialect.MySQLDialect" name="hibernate.dialect" />
			<property value="com.mysql.jdbc.Driver" name="hibernate.connection.driver_class"/>
			<property value="true" name="hibernate.format_sql" />
			<property value="true" name="hibernate.show_sql" />
		</properties>
	</persistence-unit>
</persistence>
Manifest-Version: 1.0
Build-Jdk: 1.6.0_04
Built-By: cheronju
Bundle-Activator: com.oxiane.a.Activator
Bundle-Localization: plugin
Bundle-ManifestVersion: 2
Bundle-Name: Bundle a
Bundle-SymbolicName: com.oxiane.a
Bundle-Version: 1.0.0
Export-Package: ...
Import-Package: ...
 org.hibernate.envers;version="[4.3,5)",
 org.hibernate;version="[4.3,5)",
 org.osgi.framework
...
Meta-Persistence: META-INF/persistence.xml

Problématique

La problématique est la suivante, mon bundle « A » doit servir de socle applicatif de ma web application. L’idée est de mettre en place par la suite des bundles B, C, D, etc, qui permettrons d’apporter leurs lots de services et de modèles métiers.
Et pour la persistance du modèle je souhaite réutiliser l’entityManager du bundle « A ».

Donc je crée mon bundle « B », celui-ci contient :

  • Des classes annotées @Entity
  • Des DAOs
  • Des services métiers
Manifest-Version: 1.0
Build-Jdk: 1.6.0_04
Built-By: cheronju
Bundle-Activator: com.oxiane.b.Activator
Bundle-ManifestVersion: 2
Bundle-Name: Bundle b
Bundle-SymbolicName: com.oxiane.b
Bundle-Version: 1.0.0
Export-Package: ...
Import-Package: ... 
com.oxiane.a.*;version="1.0.0",
...

Pour tester, je met en place un Activator « simple » qui permet de persister une entity définit dans mon bundle B (une instance de GestimeModule).

package com.oxiane.b;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import com.oxiane.b.dao.GestimeModuleDao;
import com.oxiane.a.enumeration.StatutModule;
import com.oxiane.b.model.entity.GestimeModule;

public class Activator implements BundleActivator {


	public void start(BundleContext context) throws Exception {
 		GestimeModule gm = new GestimeModule();
 		gm.setStatut(StatutModule.ACTIVE);
 		GestimeModuleDao.getInstance().merge(gm);
	}

	public void stop(BundleContext context) throws Exception {
		//...
	}
}

Je démarre mon bundle « B » et je me heurte directement à une belle exception :

org.hibernate.MappingException: Unknown entity: com.oxiane.b.model.entity.GestimeModule

En effet, les DAOs du bundle « B » héritent d’un AbstractDao définit dans le bundle A qui lui même utilise le registry qui définit mon entityManager.
Hors l’entityManager ne peut pas résoudre l’entity GestimeModule puisqu’elle ne fait pas partie du classloader du bundle « A ».

Une Solution

Une solution est donc de faire comprendre au bundle « A », que s’il ne trouve pas l’entity à partir du bundle « A » il faut qu’il regarde dans le bundle « B ».

Hors actuellement, le bundle « A » n’est pas conscient de l’existence du bundle « B »… Donc comment faire ?!

La solution est au niveau de l’entête Eclipse-BuddyPolicy et Eclipse-RegisterBuddy (Aide Eclipse Equinox ) semble t’il que cette entête n’est disponible que pour l’implémentation OSGI d’Equinox.

A savoir que pour chaque bundles osgi une suite de classloader est mis à disposition pour résoudre les dépendances des classes et cela passe par les entêtes du MANIFEST.MF

On connait : Export-package, Import-package mais il y a aussi Require-Bundle et Eclipse-RegisterBuddy.

Un article qui m’a aidé et qui permet de présenter un peu mieux ces entêtes : Ici

Alors les modifications à mettre en place pour résoudre ce problème sont les suivantes.

Dans un premier temps vous devez modifier le MANIFEST.MF du bundle hibernate-osgi-4.3.1.Final.jar en ajoutant ceci :

...
Eclipse-BuddyPolicy: registered
...

Ensuite il faut modifier le MANIFEST.MF du bundle com.oxiane.a-1.0.0.jar en ajoutant ceci :

...
Eclipse-BuddyPolicy: registered
Eclipse-RegisterBuddy: org.hibernate.osgi
....
Require-Bundle: org.hibernate.osgi,com.oxiane.b;resolution:=optional
...

Attention : ici le ;resolution:=optional est important car il permet de pouvoir lancer le bundle « A » sans obligatoirement avoir le bundle « B » de disponible. Cette solution n’est pas parfaite car elle met en place une forte granularité entre les bundles, mais dans mon cas cela ne pose pas de problème en soit puisque l’idée est de travailler avec un nombre de bundle fini et connu.

Et enfin.

...
Eclipse-RegisterBuddy: com.oxiane.a,org.hibernate.osgi
...
Require-Bundle: org.hibernate.osgi
...

Ces modifications m’ont permis de résoudre mon problème de chargement d’Entity, puisqu’elles ajoutent des classloader « amis » dans lesquels le bundle vont pouvoir chercher les classes manquantes.

admin

Written by

The author didnt add any Information to his profile yet