Vous pouvez trouver ce billet sur mon nouveau blog avec les informations mises a jour.
Dans le billet précédant [step2] nous avons créé le Bundle OSGi org.akrogen.gestcv.domain et préparé l'environnement OSGi (Target Platform). Dans ce billet nous allons créer les 2 Bundles OSGi Services org.akrogen.gestcv.services, et Client org.akrogen.gestcv.simpleosgiclient et gérer leur dépendances via leur fichier MANIFEST.MF.
Voici un schéma de ce que nous allons effectuer dans ce billet :

Ce schéma met en évidence plusieurs notions :
En fin du billet nous reprendrons les 2 problèmes souléves avec le Java build Path classique et qui seront résolus avec OSGi:
Vous pouvez télécharger les projets org.akrogen.gestcv_step3.zip et org.akrogen.gestcv_step3-commons-lang.zip (zip qui contient les Bundle OSGi qui utilisent 2 versions de la librairie Apache commans-lang*.jar et qui montre en évidence le problème de ClassLoader résolu) présentés dans ce billet.
Ici nous allons transformer le projet Java classique org.akrogen.gestcv.services en Bundle OSGi. Les sources Java de notre Bundle seront identiques à celui du projet Java classique. Créez le Bundle org.akrogen.gestcv.services avec les paramètres suivants :
Ajoutez la dépendance (avec Require-Bundle) au Bundle org.akrogen.gestcv.domain

Sauvegardez et vous pourrez vérifier que dans le MANIFEST.MF, il y a la dépendance Require-Bundle :
Require-Bundle: org.akrogen.gestcv.domain;bundle-version="1.0.0"
Créez l'interface org.akrogen.gestcv.services.UserService comme suit :
package org.akrogen.gestcv.services;
import java.util.Collection;
import org.akrogen.gestcv.domain.User;
public interface UserService {
Collection<User> findAllUsers();
}
Ce code ne compile pas car il n'arrive pas à résoudre la classe org.akrogen.gestcv.domain.User. Nous avons pourtant mis une dépendance sur le Bundle org.akrogen.gestcv.domain via Require-Bundle. Ceci s'explique par le fait que le Bundle org.akrogen.gestcv.domain n'expose aucune classes et que par conséquent la classe org.akrogen.gestcv.domain.User n'est pas accéssible. En effet par défaut un Bundle n'expose aucune classes. Pour indiquer les classes que l'on souhaite exposer à d'autres Bundles, ceci s'effectue en exportant les packages des classes que l'on souhaite rendre accéssibles.
Ici nous allons rendre accéssible les classes du package org.akrogen.gestcv.domain du Bundle org.akrogen.gestcv.domain en exportant le package org.akrogen.gestcv.domain. Cette information sera contenu dans le fichier MANIFEST.MF (balise Export-Package).
Nous allons modifier le MANIFEST.MF du Bundle org.akrogen.gestcv.domain via l'onglet Runtime de l'editor PDE:

Cliquez sur le bouton Add... qui ouvre la boite de dialogue qui propose tous les packages du Bundle.

Sélectionnez le package org.akrogen.gestcv.domain, puis cliquez sur OK. Le liste Export packaged doit être renseigné avec le package org.akrogen.gestcv.domain. Sauvegardez et l'interface UserService doit compiler à nouveau :

Vous pouvez vérifiez que cette action d'export package a mis à jour le fichier MANIFEST.MF avec ce contenu :
Export-Package: org.akrogen.gestcv.domain
Maintenant que nous avons exportés le package org.akrogen.gestcv.domain dans le Bundle org.akrogen.gestcv.domain, n'importe quel Bundle peut accéder aux classes du package org.akrogen.gestcv.domain qui à ce stade sont :
Le Bundle Activator ne doit pas être accéssible via un autre Bundle. Cette classe doit juste être accéssible par le conteneur OSGi. Hors à ce stade, il est possible d'accéder à la classe org.akrogen.gestcv.domain.Activator, dans des classes d'un autre Bundle. Pour vous rendre compte de ce problème, tappez Activator dans la classe UserService et vous pourrez constater qu'il est possible d'accéder à org.akrogen.gestcv.domain.Activator :

Pour régler, ce problème il faut mettre cette classe Activator dans un package protégé. Les plugins d'Eclipse utilise souvent le package internal pour mettre les classes que le Bundle ne doit pas exposer. Nous allons procéder de la même manière et mettre la classe Activator dans le package org.akrogen.gestcv.domain.internal.
Ici nous allons effectuer un refactoring pour mettre la classe org.akrogen.gestcv.domain.Activator dans le package org.akrogen.gestcv.domain.internal. Le fait de passer par un refactoring Eclipse, permettra de mettre à jour aussi le fichier MANIFEST.MF (Bundle-Activator).
Sélectionnez la classe org.akrogen.gestcv.domain.Activator, puis cliquez sur le bouton droit de la souris pour accéder au menu contextuel. Accédez à l'item Refactor->Move... :

La fenêtre de dialogue Move s'ouvre :

Cliquez sur le bouton Create Package..., la fenêtre de création de packages s'ouvre :

Saisissez dans le champs Name, la valeur org.akrogen.gestcv.domain.internal, puis cliquez sur OK, puis sur le bouton Finish . Le refactoring de changement de package s'est effectué et le MANIFEST.MF a été modifie aussi avec la valeur suivante :
Bundle-Activator: org.akrogen.gestcv.domain.internal.Activator
Exporter le package org.akrogen.gestcv.services du Bundle org.akrogen.gestcv.services pour exposer uniquement aux autres Bundles l'interface UserService et la factory ServicesFactory. Le fichier MANIFEST.MF du bundle org.akrogen.gestcv.services doit avoir ce contenu ajouté :
Export-Package: org.akrogen.gestcv.services
Modifiez le package de la classe Activator du Bundle org.akrogen.gestcv.services dans le package org.akrogen.gestcv.services.internal. Le fichier MANIFEST.MF du bundle org.akrogen.gestcv.services doit avoir ce contenu ajouté :
Bundle-Activator: org.akrogen.gestcv.services.internal.Activator
Modifiez le code de cette classe comme suit pour tracer le démarrage/stoppage du Bundle :
public class Activator implements BundleActivator {
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
System.out.println("Start Bundle [" + context.getBundle().getSymbolicName() + "]");
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
System.out.println("Stop Bundle [" + context.getBundle().getSymbolicName() + "]");
}
}
Créez la classe org.akrogen.gestcv.services.impl.UserServiceImpl comme suit :
package org.akrogen.gestcv.services.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.akrogen.gestcv.domain.User;
import org.akrogen.gestcv.services.UserService;
public class UserServiceImpl implements UserService {
private Collection<User> users = null;
public Collection<User> findAllUsers() {
if (users == null) {
users = createUsers();
}
return users;
}
private Collection<User> createUsers() {
List<User> users = new ArrayList<User>();
users.add(new User("angelo", ""));
users.add(new User("djo", ""));
users.add(new User("keulkeul", ""));
users.add(new User("pascal", ""));
return users;
}
}
Créez la classe org.akrogen.gestcv.services.ServicesFactory comme suit :
package org.akrogen.gestcv.services;
import org.akrogen.gestcv.services.impl.UserServiceImpl;
public class ServicesFactory {
private static ServicesFactory INSTANCE = new ServicesFactory();
private UserService userService = null;
private ServicesFactory() {
}
public static ServicesFactory getInstance() {
return INSTANCE;
}
public UserService getUserService() {
if (userService == null) {
userService = createUserService();
}
return userService;
}
private UserService createUserService() {
UserService userService = new UserServiceImpl();
return userService;
}
}
Ici nous allons transformer le projet Java classique org.akrogen.gestcv.simplemainclient en Bundle OSGi. Les sources Java de notre Bundle seront identiques à celui du projet Java classique. Créez le Bundle org.akrogen.gestcv.simpleosgiclient avec les paramètre suivants :
avec les paramètres suivants :
Ajoutez les dépendances (avec Require-Bundle) aux Bundles org.akrogen.gestcv.domain et org.akrogen.gestcv.services.
Modifiez le code de la classe org.akrogen.gestcv.simpleosgiclient.internal.Activator comme suit pour tracer le démarrage/stoppage du Bundle :
package org.akrogen.gestcv.simpleosgiclient.internal;
import java.util.Collection;
import org.akrogen.gestcv.domain.User;
import org.akrogen.gestcv.services.ServicesFactory;
import org.akrogen.gestcv.services.UserService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
/*
* (non-Javadoc)
*
* @see
* org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext
* )
*/
public void start(BundleContext context) throws Exception {
System.out.println("Start Bundle ["
+ context.getBundle().getSymbolicName() + "]");
UserService userService = ServicesFactory.getInstance()
.getUserService();
Collection<User> users = userService.findAllUsers();
for (User user : users) {
System.out.println("User [login=" + user.getLogin() + ", password="
+ user.getPassword() + "]");
}
}
/*
* (non-Javadoc)
*
* @see
* org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
System.out.println("Stop Bundle ["
+ context.getBundle().getSymbolicName() + "]");
}
}
Avant de relancer, vérifier que les 3 bundles OSGi sont cochés dans la configuration GestCV OSGi :

Relancez (via GestCV OSGi ) Equinox et la console OSGi doit afficher ceci :
osgi> Start Bundle [org.akrogen.gestcv.domain]
Start Bundle [org.akrogen.gestcv.services]
Start Bundle [org.akrogen.gestcv.simpleosgiclient]
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]
Les Traces de type "Start Bundle...." montre que la méthode start de chaque BundleActivator a été appelé.
Dans la classe Activator du Bundle org.akrogen.gestcv.simpleosgiclient, si vous tappez User, la complétion ne propose plus la classe UserServiceImpl, car cette classe n'est pas dans un package qui est exporté par le Bundle :

Ici nous allons effectuer les mêmes tests que ceux effectués dans le billet [step1] avec des projets Java classiques (dépendances classiques Java Build Path). Nous allons dans un premier temps montrer que chaque Bundle OSGi a son propre ClassLoader. Pour s'en rendre compte nous allons afficher le ClassLoader dans la console Eclipse.
Modifiez les 3 classes Activator en modifiant la méthode start comme suit :
public void start(BundleContext context) throws Exception {
System.out.println("Start Bundle [" + context.getBundle().getSymbolicName() + "], ClassLoader= "+ Activator.class.getClassLoader());
}
Relancez (via GestCV OSGi ) Equinox et vous verrez dans la console OSGi :
osgi> Start Bundle [org.akrogen.gestcv.domain], ClassLoader= org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@b5dac4
Start Bundle [org.akrogen.gestcv.services], ClassLoader= org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@adb1d4
Start Bundle [org.akrogen.gestcv.simpleosgiclient], ClassLoader= org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1484a05
...
qui montre que chaque Bundle OSGi a son propre ClassLoader.
Avec les dépendances classiques Java Build Path, nous avons vu qu'il y avait un ClassLoader unique et nous avons mis en évidence ce problème avec l'utilisation de versions différentes de la librairie Apache commons-lang*.jar. Nous allons effectuer la même chose avec nos Bundle OSGi :

Ce schéma montre que :
Vous pouvez télécharger le zip org.akrogen.gestcv_step3-commons-lang.zip qui contient les Bundles avec les librairies commans-lang*.jar.
Pour rappel, la différence entre ces 2 versions de commans-lang*.jar est que la classe utilitaire org.apache.commons.lang.StringUtils possède une méthode StringUtils#isBlank(String String) dans la version 2.4 et pas dans la version 1.0. Nous souhaitons utiliser StringUtils#isBlank(String String) dans la couche Services.
Ici nous allons ajouter au Bundle org.akrogen.gestcv.services la librairie jar commons-lang-2.4.jar. Cette dépendance se retrouvera au final dans le MANIFEST.MF du Bundle (Bundle-ClassPath). Ouvrez le fichier MANIFEST.MF du Bundle et cliquez sur l'onglet Runtime :

Cliquez sur le bouton Add..., la fenêtre de sélection de JAR s'ouvre :

Sélectionnez le JAR lib/commons-lang-2.4.jar, puis cliquez sur OK, la librairie est ajoutée :

Sauvegardez et vous pourrez voir que le MANIFEST.MF a été modifié en ajoutant ce contenu :
Bundle-ClassPath: lib/commons-lang-2.4.jar,
.
Modifiez la méthode start de la classe org.akrogen.gestcv.services.internal.Activator, BundleActivator de la couche Services pour utiliser la méthode utilitaire StringUtils#isBlank(String str) de commons-lang-2.4.jar :
public void start(BundleContext context) throws Exception {
StringUtils.isBlank("");
System.out.println("commons-lang-2.4 : StringUtils#isBlank() called.");
System.out.println("Start Bundle ["+ context.getBundle().getSymbolicName() + "], ClassLoader= " + Activator.class.getClassLoader());
}
Ajoutez la librairie commons-lang-1.0.jar dans le Bundle org.akrogen.gestcv.simplemainclient
Modifiez la méthode start de la classe org.akrogen.gestcv.simpleosgiclient.internal.Activator, BundleActivator de la couche Cliente pour utiliser la méthode utilitaire StringUtils#isEmpty(String str) de commons-lang-1.0.jar :
public void start(BundleContext context) throws Exception {
StringUtils.isEmpty("");
System.out.println("commons-lang-1.0.jar : StringUtils#isEmpty() called.");
System.out.println("Start Bundle ["+ context.getBundle().getSymbolicName() + "], ClassLoader= " + Activator.class.getClassLoader());
}
Relancez (via GestCV OSGi ) Equinox, la console OSGi affiche ceci :
osgi> Start Bundle [org.akrogen.gestcv.domain], ClassLoader= org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@51052d
commons-lang-2.4 : StringUtils#isBlank() called.
Start Bundle [org.akrogen.gestcv.services], ClassLoader= org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@2a6f16
commons-lang-1.0.jar : StringUtils#isEmpty() called.
...
ce qui montre qu'il est possible d'avoir une version d'une librairie par Bundle sans avoir de conflit.
A cette étape nous avons vu que les Bundle OSGi gérent leur dépendances (avec d'autres Bundles, avec des JAR internes au Bundle) via le fichier MANIFEST.MF avec Require-Bundle ou Import-Package. Un Bundle expose ses classes qu'il souhaite rendre visible aux autres Bundles via Export-Package qui est aussi une information contenu dans le MANIFEST.MF. Un Bundle a son propre ClassLoader. Les Bundles sont gérés par un conteneur OSGi (Equinox) qui fournit une console OSGi qui permet d'arrêter/stopper à chaud des Bundles.
Cet article n'a pas de Commentaires/Pingbacks pour le moment...
Vous devez être identifié pour poster un commentaire.
| Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 |
Copyright © 2000-2012 - www.developpez.com