TP nº1: Découverte d'OSGi
Étape nº1: Installation de Knopflerfish
L'installation des Knopflerfish est assez some. Connectez votre navigateur Web sur http://www.knopflerfish.org et allez à la page de téléchargement. Je recommande de télécharger la version complète (environ 7 Mo, y compris toutes les sources et la documentation). Nous supposerons aussi que vous avez accès à un IDE tels que Eclipse (en passant, Eclipse est également un cadre OSGi). Ensuite, ouvrez le fichier JAR vous venez de télécharger avec WinZip (ou autre). Vous pouvez également changer tout simplement l'extension du fichier .jar en .zip et puis extraire le fichier dans un nouveau répertoire.Pour démarrer l'environnement d'exécution Knopflerfish, vous pouvez maintenant double-cliquer simplement sur le fichier
framework.jar
que vous trouverez dans ce répertoire:
/knopflerfish.org/OSGi/framework.jar
Je recommande de créer un fichier
startup.sh. Il suffit de copier la ligne
suivante dans un fichier appelé
startup.sh et le placer dans le même
répertoire que le fichier framework.jar:
java -jar framework.jar
Vous devriez voir une fenêtre de commande qui s'ouvre et peu de temps après cela le Knopflerfish OSGi Desktop démarre.
La fenêtre de commande de Knopflerfish.
L'interface Knopflerfish OSGi Desktop.
Étape nº2: Création de votre premier bundle
Dans la programmation OSGi, les éléments qui peuvent être installés dans un cadre sont appelés bundles. Les bundles sont simplement des fichiers .jar, qui contiennent généralement les fichiers de classes Java du service, les interfaces, leur mise en oeuvre et quelques informations supplémentaires dans un méta fichierMETAINF/manifest.mf. Les
services sont des interfaces Java et une fois que
votre bundle a enregistré un service dans
l'environnement OSGi, d'autres bundles
peuvent utiliser votre service publié. Dans un
premier temps, votre premier bundle se
contentera de créer un thread d'arrière-plan
qui affiche "Hello, World!" toutes les 5 secondes.
Création d'un projet pour votre bundle
Ouvrez votre IDE préféré (Eclipse, NetBeans...) et créez un nouveau projet Java. Nommez lesimplebundle. Assurez-vous d'importer le
fichier framework.jar dans le
classpath de votre projet (sinon vous
n'aurez pas accès aux classes et interfaces fournies
par OSGi).
Création du fichier manifest.mf
Ensuite, ajoutez un répertoireMETA-INF à votre
projet. Ce répertoire contiendra le fichier
manifest.mf qui décrira le contenu de
votre bundle. C'est ce fichier qui est
ensuite utilisé par l'environnement d'exécution OSGi
pour récupérer les informations nécessaires à son
déploiement. Créez le fichier
manifest.mf avec les informations
suivantes:
Manifest-Version: 1.0
Bundle-Name: simplebundle
Bundle-SymbolicName: simplebundle
Bundle-Version: 1.0.0
Bundle-Description: Demo Bundle
Bundle-Vendor: University of Lille 1
Bundle-Activator: simplebundle.impl.Activator
Bundle-Category: example
Import-Package: org.osgi.framework
Les propriétés les plus importantes sont
Bundle-Activator et
Import-Package.
Bundle-Activator indique à
l'environnement d'exécution quelle est la classe
d'activation du bundle à exécuter, c'est l'équivalent
de la méthode main() d'une application
Java. Dans ce TP, nous allons ensuite créer la classe
d'activation simplebundle.impl.Activator
qui sera appelée par l'environnement d'exécution OSGi
lors du déploiement de notre bundle.
La propriété
Import-Package indique à
l'environnement d'exécution que notre bundle
nécessite d'accéder aux classes du paquetage
org.osgi.framework, ce qui est le cas
généralement pour tous les bundles OSGi que vous
développerez. L'utilisation de cette propriété
protège votre environnement de déclencher des
exceptions du type
ClassNotFoundException.
Création du processus de compilation Ant
Nous utiliserons Ant pour compiler le projet (mais il est tout à fait possible d'utiliser Maven pour réaliser la compilation des bundles). Créez un fichierbuild.xml à la racine de votre
projet et ajoutez y les cibles suivantes:
<?xml version="1.0"?>
<project name="simplebundle" default="all">
<target name="all" depends="init,compile,jar"/>
<target name="init">
<mkdir dir="./classes"/>
<mkdir dir="./build"/>
</target>
<target name="compile">
<javac destdir = "./classes" classpath="PATH_TO_JAR/framework.jar" debug = "on" srcdir = "./src"/>
</target>
<target name="jar">
<jar basedir = "./classes" jarfile = "./build/simplebundle.jar"
compress = "true" includes = "**/*" manifest = "./meta-inf/MANIFEST.MF"/>
</target>
<target name="clean">
<delete dir = "./classes"/>
<delete dir = "./build"/>
</target>
</project>
Création de la classe d'activation
La plupart des bundles ont une classe d'activation spécifiée dans le fichiermanifest.mf du
bundle. Cette classe d'activation doit
implémenter l'interface BundleActivator
fournie par l'environnement OSGi. Cette interface
impose notamment l'implémentation de deux méthodes
start() et stop(), qui
seront appelées par l'environnement d'exécution pour
contrôler l'exécution du bundle.
Créez le paquetage
simplebundle.impl.
OSGi encourage à séparer les interfaces de leurs
implémentations. Comme notre premier bundle
ne va enregistrer aucun service pour l'instant, le
paquetage simplebundle sera vide. Le
sous-paquetage impl contiendra la class
Activator qui se chargera de démarrer et
stopper le bundle.
Créez la classe
Activator qui implémente
l'interface BundleActivator:
package simplebundle.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
public static BundleContext bc = null;
public void start(BundleContext bc) throws Exception {
Activator.bc = bc;
}
public void stop(BundleContext bc) throws Exception {
Activator.bc = null;
}}
Notez que les méthodes
start() et
stop() reçoivent un objet
BundleContext en paramètre. Vous devez
systématiquement stocker cet objet dans votre classe
d'activation et relâcher sa référence lorsque le
bundle est stoppé. Ensuite nous pouvons
créer une sous-classe de Thread qui se
chargera d'afficher le message «Hello, World!» toutes
les 5 secondes:
package simplebundle.impl;
public class HelloWorldThread extends Thread {
private boolean running = true;
public void run() {
while (running) {
System.out.println("Hello, World!");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("HelloWorldThread ERROR: " + e);
}}}
public void stopThread() {
this.running = false;
}}
Enfin, il ne nous reste plus qu'à créer un nouveau thread lorsque le bundle est démarré (via la méthode
start()). Ce
thread sera terminé quand le bundle
sera arrêté (via la méthode stop()). Les
messages supplémentaires permettent de visualiser
l'appel aux méthodes start() et
stop():
package simplebundle.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
public static BundleContext bc = null;
private HelloWorldThread thread = null;
public void start(BundleContext bc) throws Exception {
System.out.println("SimpleBundle starting...");
Activator.bc = bc;
this.thread = new HelloWorldThread();
this.thread.start();
}
public void stop(BundleContext bc) throws Exception {
System.out.println("SimpleBundle stopping...");
this.thread.stopThread();
this.thread.join();
Activator.bc = null;
}}
Compilation et Installation de votre bundle
Compilez votre projet en utilisant Ant. Vous devez maintenant avoir un fichiersimplebundle.jar dans le répertoire
build de votre projet. Ouvrez
l'interface Knopflerfish OSGi Desktop et
sélectionner le menu File > Open
Bundle. Sélectionner le bundle que
vous venez de compiler et installez le. Le
bundle est automatiquement démarré et vous
devez voir une icône apparaître dans la partie gauche
de la fenêtre. Toutes les 5 secondes, le
bundle affiche un nouveau message «Hello,
World!». Essayez d'arrêter et redémarrer le
bundle en utilisant les boutons du menu de
l'interface graphique.
Interface Knopflerfish OSGi Desktop après
l'installation du bundle.
Étape nº3: Création de votre premier service
Cette étape va vous permettre d'expérimenter la mise en œuvre d'un service OSGi. Pour cela, nous devons créer un bundle pour ce service, sauf que cette fois le paquetage associé contiendra non seulement la définition d'une interface mais aussi son implémentation.Dans un premier temps, créez une copie du bundle que vous avez développé à l'étape suivante. Assurez-vous de le renommer en
DateBundle puisque ce service nous
permettra de formater un object Date et
de le retourner via le service OSGi. Vérifiez que le
fichiers build.xml et
manifest.mf ont été correctement
renommés.
Mise à jour du fichier manifest.mf
Le fichiermanifest.mf doit être légèrement modifié
pour publier le service que vous allez développer.
Vous devez ajouter la propriété
Export-Package à la fin du fichier.
Cette propriété est nécessaire pour rendre publique
l'interface du service de manière à ce que d'autres
services puissent l'utiliser. Le contenu de votre
fichier manifest.mf doit donc ressembler
à:
Manifest-Version: 1.0
Bundle-Name: DateBundle
Bundle-SymbolicName: DateBundle
Bundle-Version: 1.0.0
Bundle-Description: Demo Bundle
Bundle-Vendor: University of Lille 1
Bundle-Activator: datebundle.impl.Activator
Bundle-Category: example
Import-Package: org.osgi.framework
Export-Package: datebundle
Création de l'interface du service
Créez une interfaceDateService dans le paquetage
datebundle avec la définition suivante:
package datebundle;
import java.util.Date;
public interface DateService {
String getFormattedDate(Date date);
}
Création de la classe d'implantation du service
Ensuite, vous pouvez implémenter le serviceDateService en créant la classe
DateServiceImpl dans le paquetage
impl. La mise en œuvre du service est
isolée de la définition du service pour éviter que
les autres bundles n'aient un accès direct à
l'implantation de notre service (principe
d'encapsulation).
package datebundle.impl;
import java.text.DateFormat;
import java.util.Date;
import datebundle.DateService;
public class DateServiceImpl implements DateService {
public String getFormattedDate(Date date) {
return DateFormat.getDateInstance(DateFormat.SHORT).format(date);
} }
L'implantation de ce service est relativement triviale mais c'est suffisant pour les besoins de cette étape.
Création de l'activateur de service
Finallement, il est nécessaire d'enregistrer la référence de notre service dans l'environnement d'exécution OSGi pour qu'il soit utilisable. Cette procédure est réalisée durant l'appel à la méthodestart() de la
classe d'activation du bundle. En
particulier, nous devons d'abord créer une instance
du service et ensuite enregistrer sa référence sous
le nom de l'interface. Les procédures
d'enregistrement sont gérées via les méthodes
fournies par l'objet BundleContext. Cet
objet joue donc le rôle d'interface entre le
bundle et l'environnement d'exécution OSGi.
package datebundle.impl;
import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import datebundle.DateService;
public class Activator implements BundleActivator {
public static BundleContext bc = null;
public void start(BundleContext bc) throws Exception {
System.out.println(bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME)+" starting...");
Activator.bc = bc;
DateService service = new DateServiceImpl();
ServiceRegistration registration = bc.registerService(DateService.class.getName(), service, new Hashtable());
System.out.println("Service registered: DateService");
}
public void stop(BundleContext bc) throws Exception {
System.out.println(bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME)+" stopping...");
Activator.bc = null;
} }
La méthode registerService de l'objet requiert 3 paramètres: le premier paramètre correspond à l'identifiant de l'interface du service. Le second paramètre correspond à la référence du service à enregistrer. Le dernier paramètre permet d'attacher des propriétés supplémentaires sous la forme de clés/valeurs et décrivant des caractéristiques du service.
Compilation et installation du service
Comme lors de l'étape précédente, compilez et installez le bundle DateBundle. Installez-le ensuite via l'interface Knopflerfish OSGi Desktop. Vous devez voir apparaître des messages de trace lors de l'installation du bundle. La prochaine étape vous montre comment utiliser ce service.Étape nº4: Déploiement automatique des bundles OSGi
Développez un nouveau
service OSGi qui surveille le contenu d'un répertoire
de votre système de fichier dans lequel des
bundles sont déposés (via un copier/coller
depuis le gestionnaire de fichiers). Quand un
bundle est copié dans le répertoire, le
service le déploie automatiquement dans
l'environnement d'exécution OSGi. Quand le
bundle est supprimé du répertoire, le
service désinstalle les services associés au bundle.
Enfin quand le bundle est remplacé par une
nouvelle version, le service met automatiquement à
jour le bundle associé.
Cet exercice doit être réalisé
individuellement et le code source du bundle est à
déposer sur l'interface PROF pour la
prochaine séance.