Ce post a pour but de présenter OSGi et ses concepts dans un premier temps. Puis ensuite, de fournir un exemple basique de développement de composants OSGi à l’aide d’Apache Karaf 4. Enfin montrer comment peut-on écrire des tests d’intégration à l’aide de Pax Exam, afin de tester ses commandes.
Définition de OSGi
OSGi (Open Service Gateway Initiative) est une spécification permettant de concevoir une application basée sur une architecture modulaire. Chaque module, appelé bundle. Il possède une version et peut dépendre d’autres modules. OSGi gère différents classloader par bundle. Ceci permet une gestion dynamique des bundles, donc de déployer plusieurs versions d’un même composant. Le redéploiement partiel et à chaud de composants est donc ainsi possible, ce qui rend possible une mise à jour de versions applicatives sans coupure de service.
Présentation de Karaf
Karaf est un framework open source de la fondation Apache, basé sur OSGi. La dernière version (4.0.3) est téléchargeable sur le site ici. Il faut ensuite se positionner dans le répertoire bin/ et lancer la commande : karaf. La console shell de Karaf présente alors un prompt qui permet de lancer des commandes :
Karaf s’appuie sur Apache Aries pour fournir un mécanisme d’injection de dépendances pour OSGi. Un bundle est constitué d’un fichier Blueprint XML qui permet d’instancier des composants, d’injecter des dépendances entre ces composants, d’exposer des services à travers le Service Registry, d’injecter des références vers d’autres services d’autres bundles.
Le plugin maven maven-bundle-plugin va permettre de packager le bundle et de générer le fichier MANIFEST.MF définissant un nom Bundle-SymbolicName, une version Bundle-Version, les packages exportés Export-Package, et importés Import-Package, ou encore les packages contenant les commandes Karaf-Commands.
Un bundle peut être installé manuellement. Par exemple :
Mais, il peut être également installé par l’intermédiaire d’une feature. Une feature est constituée d’un fichier description XML qui liste les bundles que l’on souhaite installer au démarrage.
Karaf vient avec un ensemble de repositories de features pré-configurées. Les commandes, ci-dessous, permettent respectivement de lister les repositories de features, lister les features, installées ou à disposition pour installation, installer une feature.
Les bundles sont récupérés depuis internet et stockés dans le repository local. Les commandes, ci-dessous, permettent respectivement de démarrer / stopper un bundle, lister les bundles installés, de redéployer automatiquement un bundle compilé et installé dans le repository local.
Projet OSGi exemple
Le projet exemple ippon-osgi-sample montre comment développer des commandes OSGi. Il s’agit d’une application de type CRUD qui fournit la possibilité de lister les salariés de la société, d’ajouter et de supprimer un salarié d’une base de données. Cet exemple s’appuie sur un socle JPA/Hibernate et une base H2. Il est organisé en projet multi-modules maven :
- ippon-osgi-sample-ds contient la datasource vers la base H2, packagée sous forme de bundle pour être déployé simplement.
- ippon-osgi-sample-services contient les services et entités JPA. Ces services sont exposés en tant que services OSGi.
- ippon-osgi-sample-command contient les commandes OSGi qui pourront être invoquées depuis le shell de Karaf.
- ippon-osgi-sample-kar contient principalement le fichier features.xml qui permet de faire du provisionning et de packager notre application avec les bundles précedents pour déploiement dans Karaf.
- ippon-osgi-sample-ittests contient les tests d’intégration d’exécution de nos commandes.
Pour initialiser le projet, on peut utiliser différents archetypes maven. Le premier est un archetype maven destiné à créer des commandes. Le second permet d’initialiser un projet avec Blueprint XML. Le troisième est un archetype maven pour la création de feature, pour le provisionning et le packaging sous forme de fichier kar.
Une feature définit les différentes ressources via URLs (instances, bundles, fichiers de configuration). features.xml recense d’autres features que l’on souhaite installer et activer par défaut au démarrage du serveur. Voici les principaux dans notre cas : jdbc, hibernate, transaction, jpa, etc. Dans ce même fichier peuvent être ajouter nos propres bundles, comme par exemple, l’ajout du driver H2 ou des librairies Apache Commons. La commande feature:info hibernate liste l’ensemble des bundles dépendant de cette feature. Karaf peut demander le téléchargement d’artefact à partir de dépôts distants présent dans sa liste de repositories.
Un kar est donc un package sous forme d’archive zip, qui contient toutes les ressources décrites dans le fichier features XML. Il peut être déployé sans connexion internet. il est constitué d’un répertoire repository contenant une liste de features XML et l’ensemble des artefacts Maven. Voici les commandes pour installer / désinstaller un kar :
Service JNDI pour la Datasource
Le projet ippon-osgi-sample-ds produit à la compilation un bundle OSGi qui va enregistrer dans le registre de services notre datasource vers la base H2. Ce service est accessible à partir de son nom JNDI (jdbc/ippon-osgi)
Services et entités JPA
Le bundle ippon-osgi-sample-services contient les entités JPA et les services d’accès à la base de données. On définit le fichier persistence.xml en précisant bien l’URL vers le service qui expose la datasource.
Dans le fichier Blueprint XML, JPA est activé. Les beans de services sont déclarés et exposés comme services OSGi, afin de les utiliser dans d’autres bundles.
Il ne faut pas oublier aussi de préciser au niveau du plugin maven-bundle-plugin le chemin vers le fichier de configuration JPA <Meta-Persistence>META-INF/persistence.xml</Meta-Persistence> et d’exporter les packages contenant les services <Export-Package>fr.ippon.osgi.sample.services*</Export-Package>.
La configuration JPA est classique. Une entityManager est récupérée grâce à l’annotation @PersistenceContext(unitName = “ippon-pu”) et l’API Criteria JPA est utilisée pour construire les requêtes. (cf. EmployeeServiceImpl.java)
Développement de commandes Karaf
Karaf offre la possibilité d’étendre ses commandes shell de base. Nous allons créer nos propres commandes faisant référence à nos services Blueprint développés précédemment dans le bundle ippon-osgi-sample-services. Une commande est définie par un scope et un nom. Elle peut avoir en paramètre des options ou des arguments ; la complétion peut être activée.
Voici un exemple de commande faisant appel au service qui liste les salariés de la société.
Les annotations @Command @Service servent à déclarer cette classe comme commande Karaf. Cette commande est exposée comme un service OSGi. Il faut également dans le maven-bundle-plugin penser à définir les packages contenant les commandes Karaf <Karaf-Commands>fr.ippon.osgi.sample.command*</Karaf-Commands> dans les instructions.
L’annotation @Option permet de définir des paramètres pour la commande. @Argument permet de définir les arguments. @Completion permet de faire de la complétion et peut-être associée à une option ou un argument de commande.
L’annotation @Reference permet de récupérer les services OSGi exposés par le projet ippon-osgi-services. L’usage de ces annotations simplifie grandement la configuration, réalisée auparavant en XML dans les versions antérieures de Karaf (dans le fichier Blueprint XML du bundle). La méthode execute() contient le cœur d’exécution de la commande qui renvoie la liste des salariés.
Retourner tous les salariés de la société : ippon:list-employees
Retourner uniquement les architectes de la société : ippon:list-employees -j ARCHITECT
Retourner uniquement les architectes de la société dont le nom contient ‘Employee 3’: ippon:list-employees -j ARCHITECT -n ‘Employee 3’
D’autres commandes d’ajout et de suppression de salarié sont disponibles sur GitHub ici.
Packaging kar
Voici le feature du projet, il contient les features Karaf à activer, les bundles de librairies tierces et les bundles du projet (ippon-osgi-sample-*). Le plugin maven karaf-maven-plugin va génèrer le kar (cf. pom.xml).
Tests d’intégration des commandes avec PaxExam
PaxExam est un framework pouvant réaliser des tests d’intégration dans le cadre d’un environnement OSGi. Il est capable de démarrer un Karaf, de déployer des features, des bundles, de surcharger les propriétés de configuration et de lancer de commandes. Différentes stratégies permettent d’utiliser ou non la même configuration pour chaque tests unitaires.
Pour l’intégrer au projet Maven ippon-osgi-sample-ittests, il faut ajouter les dépendances suivantes :
Puis ajouter le plugin suivant, pour activer les fonctionnalités de versionAsInProject() sur les features (voir plus loin)
La classe de tests d’intégration des commandes Karaf doit posséder les annotations suivantes : @RunWith(PaxExam.class) pour activer PaxExam et @ExamReactorStrategy(PerClass.class) pour activer la configuration une et une seule fois pour tous les tests de cette même classe. La classe étendue KarafTestSupport fournit des fonctions permettant d’exécuter des commandes, vérifier l’installation d’un bundle ou d’une feature. Elle est fortement inspirée de celle des tests d’intégration du projet Karaf lui-même. https://github.com/apache/karaf/blob/master/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
La configuration dans la méthode probeConfiguration() autorise l’import dynamique de tous les packages de type provisional dans le features.xml du projet.
La configuration de la méthode configure() définit la distribution Karaf karafDistributionConfiguration() à déployer lors des lancements des tests, les features features() et bundles mavenBundle() à installer. streamBundle() permet de créer un bundle à la volée à partir d’une autre datasource de test datasource-h2-test.xml. editConfigurationFilePut() permet de surcharger les fichiers de configuration du serveur.
Il est alors possible de tester l’installation des features et bundles.
De tester l’exécution de nos commandes Karaf. Exemple :
Conclusion
Nous avons vu comment écrire nos propres commandes Karaf, comment les tester avec PaxExam. J’espère que ce tutoriel peut constituer une première approche pour qui souhaite débuter avec les concepts d’OSGi.
Le code source de l’application est disponible sur GitHub : https://github.com/sfoubert/ippon-osgi-sample
Références
https://www.osgi.org/developer/specifications/
https://ops4j1.jira.com/wiki/display/PAXEXAM4
https://ops4j1.jira.com/wiki/display/PAXEXAM4/Karaf+Test+Container+Reference
https://www.packtpub.com/application-development/apache-karaf-cookbook
https://github.com/jgoodyear/ApacheKarafCookbook