Visiteur non identifié. Spherick
« Je le vois, vous demandez moins à être distingués par vos concitoyens, que vous ne cherchez à être distingués de vos concitoyens »Abbé Sièyes, Essai sur les privilèges, 1788
Jan
Fév
Mar
Avr
Mai
Jui
Jui
Aou
Sep
Oct
Nov
Déc
Accueil > Blog Eric > Hobbies > Informatique > OSGi : compiler et installer à la main

Public  OSGi : compiler et installer à la main

Marre de chercher...

mardi 4 mars 2008, lu 975 fois

Doc

Quelques infos sur les services OSGi

(Traduction (un peu aménagée) du tutoriel de Knoplerfich)

Qu’est-ce qu’un service ?

Un service OSGi est une instance d’un objet Java enregistré dans un framework OSGi avec un ensemble de propriétés. N’importe quel objet Java peut être enregistré comme un service, mais ils implémentent le plus souvent une interface bien connue.

La lecture du chapitre 4.10 des spécifications R3 d’OSGi est chaudement recommandée, de même que la javadoc sur le BundleContext, qui contient un tas d’infos.

Le client d’un service est toujours un bundle OSGi, c’est à dire un bout de code qui implémente l’interface BundleActivator, ce qui permet de l’exécuter dans le framework d’OSGi.

Chaque bundle peut ou non déclarer des services, et peut aussi (ou non) utiliser les services d’autres bundles. Il n’y a pas de limite au nombre de services déclarés et/ou utilisés, à part les limites liées à la mémoire disponible. Cependant, la publication/déclaration et l’utilisation des services peuvent toutes deux être limitées grâce à la gestion de sécurité de Java.

Déclaration d’un objet de classe Long comme un service :

public class monBundle implements BundleActivator {
   [...]
   public void start(BundleContext context) {
       Long      i     = new Long(20);
       Hashtable props = new Hashtable();
       props.put("description", "This an long value");
       bc.registerService(Long.class.getName(), i, props);
   }
   [...]
}

Remarque : a service can also be registered as several interfaces. In this case, the object must implement all of the interfaces. [Nota : moi j’ai pas compris le sens de cette phrase, ou plutôt je comprends les mots, je comprends l’ensemble des mots, mais je vois pas ce que signifie concrètement "être déclaré comme plusieurs interfaces"...]

Qu’est-ce qu’une factory de service ?

La factory d’un service OSGi est une classe spéciale ServiceFactory, qui peut créer des instances individuelles d’objets services pour différents bundles.

Parfois, un service a besoin d’être configuré différemment selon le bundle qui l’utilise. Par exemple, le service de log a besoin de pouvoir mémoriser l’id du bundle qui l’utilise, faute de quoi il serait difficile de s’y retrouver.

Une factory de service est déclarée exactement de la même façon qu’un service normal, en utilisant registerService. La seule différence est qu’il y a une "étape d’indirection" avant que le service soit effectivement pris en compte.

Le client qui va utiliser le service n’aura pas besoin, et ne devrait pas, s’occuper de savoir si un objet est généré par une factiry ou par un objet normal.

Déclaration d’une factory de service :

class LongFactory implements ServiceFactory {
   public Object getService(Bundle bundle, ServiceRegistration reg) {
      // each bundle gets a Long with it's own id
      return new Long(bundle.getBundleId());
   }
   void ungetService(Bundle bundle, ServiceRegistration reg, Object service) {
      // nothing needed in this case
   }
 }
 
 ServiceFactory factory = new LongFactory();
 bc.registerService(Long.class.getName(), factory, new Hashtable());

Remarque : le framework dissimulera les objets service générés. C’est pourquoi un objet service au plus peut être généré par bundle client.

A quoi peuvent servir les services ?

Le service est une notion qui permet de définir des outils très divers, dont voici quelques exemples :

- Exporter des fonctionnalités d’un bundle à un autre
- Importer les fonctionnalités d’autres bundles
- Surveiller les évènements en provenance d’autres bundles
- Rendre accessibles des outils externes à d’autres bundles
- Rendre accessible du code Java tournant sous OSGi à un réseau externe
- Configurer des bundles, via le Configuration Manager

Globalement, les services sont la méthode par laquelle les bundles devraient communiquer les uns avec les autres.

Comment accéder aux services ?

On accède toujours aux services par ServiceReferences, qui est le seul à pouvoir atteindre un objet service.

Pour accéder à un service, la procédure suivante doit toujours être utilisée (avec la possibilité de l’encapsuler dans un code) :

  1. Obtenir un ServiceReference
  2. Obtenir le service de cet objet, en utilisant BundleContext.getService()

Les ServiceReference sont retrouvés par :

  1. une utilisation explicite de BundleContext.getService(). Ceci renvoie soit le service de plus grand rang de la classe, soit null
  2. une utilisation explicite de BundleContext.getServices(), qui renvoie soit la liste de tous les services correspondants, soit null
  3. le retour d’un appel à ServiceListener
  4. une utilisation de la classe "utilitaire" ServiceTracker

Dans tous les cas, sauf le premier, le client peut préciser un ensemble de caractéristiques que le service doit avoir. Ces propriétés sont spécifiées par des filtres LDAP. Aucun ou plusieurs services peuvent correspondre à ces critères.

Un filtre typique contient un nom de classe et un jeu de caractéristiques ? L’exemple ci-dessous sélectionne un service HTTP ayant une propriété "port" dont la valeur est 80.

(&(objectclass=org.osgi.service.http.HttpService)(port=80))

Dès qu’un ServiceReference est disponible, l’objet service peut être accédé grâcé à un cast :

HttpService http = (HttpService)bc.getService(sr) ;

(bc = le BundleContext reçu dans les paramètres de la méthode, sr étant je suppose le ServiceReference qui correspond aux critères)

Ensuite, l’objet service peut être utilisé comme n’importe quel objet Java. En fait, c’est un objet Java totalement normal, ou, pour être plus précis, c’est le même objet que celui qui a été créé par le bundle qui propose le service.

Relacher les services

Dès qu’un service n’est plus utile, le client devrait le relacher en utilisant BundleContext.ungetService() :

bc.ungetService(sr) ;

Tous les services d’un bundle client sont automatiquement relachés quand ce bundle s’arrête. Aucun "nettoyage" des services n’est donc nécessaire dans la méthode BundleActivator.stop().

Un service ne devrait être relaché que lorsqu’il est vraiment devenu inutile. Certains services peuvent conserver un statut pour chacun des bundles qui les utilisent, et relacher le service peut donc réinitialiser ce statut, ce qui peut ou pas être le but. Un exemple est le HttpService, qui va supprimer toutes les servlets et ressources du client quand il est relâché.

[...]

Exemple 1

L’activator fait tout le travail

Démarche

- Se placer dans mestests (CD C :\Users\Eric\Documents\mestests) où il y a equinox.jar
- Créer un dossier Alerteur
- Aller dedans (cd Alerteur)
- Créer une classe AlerteurActivator.java, dans le package PAlerteur
- La compiler en plaçant le class dans un sous-répertoire PAlerteur
- Créer un manifeste (.../Alerteur/toto.mf)
- Générer le Jar
- Installer dans OSGi

-mestests/Alerteur/AlerteurActivator.java-
package PAlerteur;

import org.osgi.framework.*;

public class AlerteurActivator implements BundleActivator {
 public void start(BundleContext context) {
            System.out.println("Test n° 5");
 }

 public void stop(BundleContext context) {
mport java.util.*;
    System.out.println("Fin du test 5");
 }
}
C :\...\Alerteur> javac -d . -classpath C :\...\mestests\equinox.jar ;. AlerteurActivator.java
-mesTests/Alerteur/toto.mf-
Manifest-Version: 1.0
Bundle-Name: Alerteur
Bundle-Activator: PAlerteur.AlerteurActivator
Bundle-SymbolicName: Alerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
C :\...\Alerteur> jar -cfm Test5.jar toto.mf PAlerteur/*.class
osgi> install file:Alerteur/Test6.jar

Osgi répond : bundle id is 64.

osgi> start 64

Test n° 5

osgi> stop 64

Fin du test 5

osgi> uninstall 64

Exemple 2

l’activator appelle une classe du même package

- Se placer dans mestests (bêtement avec cd C :\Users\Eric\Documents\mestests) où il y a equinox.jar
- Créer un dossier Alerteur
- Aller dedans (cd Alerteur)
- Créer une classe Alerteur.java dans un package PAlerteur
- La compiler en plaçant le class dans un sous-répertoire PAlerteur
- Créer une classe AlerteurActivator.java, dans le même package
- La compiler aussi, toujours en mettant le .class dans PAlerteur
- Créer un manifeste (.../Alerteur/toto.mf)
- Générer le Jar
- Installer dans OSGi

-mestests/Alerteur/Alerteur.java-
package PAlerteur;

public class Alerteur {
        public void Alerter(String unMessage) {
                System.out.print(unMessage) ;
        }
}
C :\...\Alerteur> javac -d . Alerteur.java
-mestests/Alerteur/AlerteurActivator.java-
package PAlerteur;

import org.osgi.framework.*;

public class AlerteurActivator implements BundleActivator {
 public void start(BundleContext context) {
            System.out.println("Test n° 6");
         Alerteur myAlerteur = new Alerteur() ;
         myAlerteur.Alerter("Ceci est un message affiché par Alerteur");
 }

 public void stop(BundleContext context) {
   System.out.println("Fin du test 6");
 }
}
C :\...\Alerteur> javac -classpath C :\...\mestests\equinox.jar ;. AlerteurActivator.java
-mesTests/Alerteur/toto.mf-
Manifest-Version: 1.0
Bundle-Name: Alerteur
Bundle-Activator: PAlerteur.AlerteurActivator
Bundle-SymbolicName: Alerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
C :\...\Alerteur> jar -cfm Test6.jar toto.mf PAlerteur/*.class
osgi> install file:Alerteur/Test6.jar

Osgi répond : bundle id is 65.

osgi>osgi> start 65

Test n° 6
Ceci est un message affiché par Alerteur

osgi>osgi> stop 65

Fin du test 6

osgi>osgi> uninstall 65

Exemple 3

l’activator appelle une classe qui implémente une interface, tout dans le même package

- Se placer dans mestests (CD C :\Users\Eric\Documents\mestests) où il y a equinox.jar
- Créer un dossier Alerteur
- Aller dedans (cd Alerteur)
- Créer une interface IAlerteur.java dans un package PAlerteur
- La compiler en plaçant le class dans un sous-répertoire PAlerteur
- Créer une classe Alerteur.java qui implémente IAlerteur, dans un package PAlerteur
- La compiler en plaçant le class dans un sous-répertoire PAlerteur
- Créer une classe AlerteurActivator.java qui utilise Alerteur, dans le même package
- La compiler aussi, toujours en mettant le .class dans PAlerteur
- Créer un manifeste (.../Alerteur/toto.mf)
- Générer le Jar
- Installer dans OSGi

-mestests/Alerteur/IAlerteur.java-
package PAlerteur;

public interface IAlerteur {
        void Alerter(String unMessage) ;
}
C :\...\Alerteur> javac -d . IAlerteur.java
-mestests/Alerteur/Alerteur.java-
package PAlerteur;

public class Alerteur implements IAlerteur {
        public void Alerter(String unMessage) {
                System.out.print(unMessage) ;
        }
}
C :\...\Alerteur> javac -d . Alerteur.java
-mestests/Alerteur/AlerteurActivator.java-
package PAlerteur;

import org.osgi.framework.*;

public class AlerteurActivator implements BundleActivator {
 public void start(BundleContext context) {
            System.out.println("Test n° 8");
         Alerteur myAlerteur = new Alerteur() ;
         myAlerteur.Alerter("Ceci est un message affiché par Alerteur");
 }

 public void stop(BundleContext context) {
   System.out.println("Fin du test 8");
 }
}
C :\...\Alerteur> javac -d . -classpath C :\...\mestests\equinox.jar ;. AlerteurActivator.java
-mesTests/Alerteur/toto.mf-
Manifest-Version: 1.0
Bundle-Name: Alerteur
Bundle-Activator: PAlerteur.AlerteurActivator
Bundle-SymbolicName: Alerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
C :\...\Alerteur> jar -cfm Test7.jar toto.mf PAlerteur/*.class
osgi> install file:Alerteur/Test7.jar

Osgi répond : bundle id is 67.

osgi> start 67

Test n° 8
Ceci est un message affiché par Alerteur

osgi> stop 67

Fin du test 8

osgi> uninstall 67

Exemple 4

l’activator appelle une classe d’un autre package

- Se placer dans mestests (CD C :\Users\Eric\Documents\mestests) où il y a equinox.jar
- Créer un dossier Alerteur
- Aller dedans (cd Alerteur)
- Créer une interface IAlerteur.java dans un package PIAlerteur
- La compiler en plaçant le class dans un sous-répertoire PIAlerteur
- Créer le manifeste du 1er bundle (.../Alerteur/IAlerteur.mf)
- Installer le 1er bundle dans OSGi
- Créer une classe Alerteur.java, dans un package PAlerteur, et qui implémente IAlerteur
- La compiler en plaçant le class dans un sous-répertoire PAlerteur
- Créer une classe AlerteurActivator.java qui utilise Alerteur, dans le même package
- La compiler aussi, toujours en mettant le .class dans PAlerteur
- Créer un manifeste (.../Alerteur/Alerteur.mf)
- Générer le Jar
- Installer dans OSGi

-mestests/Alerteur/IAlerteur.java-
package PIAlerteur;

public interface IAlerteur {
        void Alerter(String unMessage) ;
}
C :\...\Alerteur> javac -d . IAlerteur.java
-mestests/Alerteur/IAlerteur.mf-
Manifest-Version: 1.0
Bundle-Name: IAlerteur
Bundle-SymbolicName: IAlerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
Export-Package: PIAlerteur;version="1.0.0"
C :\...\Alerteur>jar -cfm IAlerteur.jar IAlerteur.mf PIAlerteur/*.class
osgi> install file:Alerteur/IAlerteur.jar
-mestests/Alerteur/Alerteur.java-
package PAlerteur;

import package PIAlerteur.* ;

public class Alerteur implements IAlerteur {
        public void Alerter(String unMessage) {
                System.out.print(unMessage) ;
        }
}
C :\...\Alerteur> javac -d . Alerteur.java
-mestests/Alerteur/AlerteurActivator.java-
package PAlerteur;

import org.osgi.framework.*;
import package PIAlerteur.* ;

public class AlerteurActivator implements BundleActivator {
 public void start(BundleContext context) {
            System.out.println("Test n° 9");
         Alerteur myAlerteur = new Alerteur() ;
         myAlerteur.Alerter("Ceci est un message affiché par Alerteur");
 }

 public void stop(BundleContext context) {
   System.out.println("Fin du test 9");
 }
}
C :\...\Alerteur> javac -d . -classpath C :\...\mestests\equinox.jar ;. AlerteurActivator.java
- mesTests/Alerteur/Alerteur.mf-
Manifest-Version: 1.0
Bundle-Name: Alerteur
Bundle-Activator: PAlerteur.AlerteurActivator
Bundle-SymbolicName: Alerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework , PIAlerteur;version="[1.0.0, 2.0.0)"
C :\...\Alerteur> jar -cfm Alerteur.jar Alerteur.mf PAlerteur/*.class
osgi> install file:Alerteur/Alerteur.jar

Osgi répond : bundle id is 72.

osgi> start 72

Test n° 9
Ceci est un message affiché par Alerteur

Exemple 5

idem exemple 4 avec déclaration du service Alerteur dans OSGi

Les opérations sont identiques, on change juste AlerteurActivator.java

-mestests/Alerteur/AlerteurActivator.java-
package PAlerteur;

import org.osgi.framework.*;
import package PIAlerteur.* ;
import java.util.*;

public class AlerteurActivator implements BundleActivator {
        private ServiceRegistration registration;

        public void start(BundleContext context) {

                System.out.println("Exemple n°5");
                 Alerteur myAlerteur = new Alerteur() ;
                 myAlerteur.Alerter("Ceci est un message affiché par Alerteur");

                Dictionary props = new Properties();
                props.put("category", "misc");

                registration = context.registerService(
                              Alerteur.class.getName(),
                              myAlerteur, props);
        }

        public void stop(BundleContext context) {
                System.out.println("Fin de l'exemple 5");
                registration.unregister();
        }
}
C :\...\Alerteur> javac -d . -classpath C :\...\mestests\equinox.jar ;. AlerteurActivator.java
C :\...\Alerteur> jar -cfm Alerteur.jar Alerteur.mf PAlerteur/*.class
osgi> install file:Alerteur/Alerteur.jar

Osgi répond : bundle id is 74.

osgi> start 74

Exemple n°5
Ceci est un message affiché par Alerteur

osgi> ss

id State Bundle
0 Active org.eclipse.osgi_3.3.1.R33x_v20070828
69 RESOLVED IAlerteur_1.0.0
74 ACTIVE Alerteur_1.0.0

osgi> services (objectClass=*Alerteur)
{PAlerteur.Alerteur}={category=misc, service.id=21}
     Registrered by bundle : file:Alerteur/Alerteur.jar [74]
     No bundles using service.

Exemple 6

Création d’un bundle qui utilise le service Alerteur

EN COURS DE CREATION !!!!

Pour l’instant, tout ça c’est des hypothèses !!!

On veut créer un package qui va utiliser les services du bundle Alerteur (package PAlerteur).

Nous allons donc créer simplement un activator qui appelera ce service.

Il va donc instancier la classe Alerteur du package PAlerteur.

Mais pour cela il faudra que cette classe soit accessible dans PAlerteur au sein d’OSGi.

Or, dans Alerteur.mf nous n’avons pas déclaré ce package comme exporté : OSGi ne saura pas résoudre l’import.

Il faut donc commencer par modifier Alerteur.mf pour ajouter PAlerteur comme package exporté.

- mesTests/Alerteur/Alerteur.mf-
Manifest-Version: 1.0
Bundle-Name: Alerteur
Bundle-Activator: PAlerteur.AlerteurActivator
Bundle-SymbolicName: Alerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework , PIAlerteur;version="[1.0.0, 2.0.0)
Export-Package: PAlerteur;version="1.0.0"

Il reste à regénérer Alerteur.jar puis à le réinstaller sous OSGi. Au lieu de faire uninstall n / install file :... on peut utiliser la commande

osgi>update n

où n est l’id du bundle Alerteur (obtenu par la commande ss si besoin). Il reste à faire start n pour redémarrer le service Alerteur.

Pour le bundle de test, on a le code suivant :

-mestests/Alerteur/AlerteurActivator.java-
package CAlerteur;

import java.util.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import PAlerteur.*;

public class TestAlerteur implements BundleActivator {
        public void start(BundleContext context) throws Exception {

                System.out.println("TestAlerteur.start, tentative d'utiliser le service " + Alerteur.class.getName());

                ServiceReference sr  =  context.getServiceReference(Alerteur.class.getName());

                if(sr == null) {
                        System.out.println("Impossible de trouver un ServiceReference") ;
                } else {
                        System.out.println("Un ServiceReference a été trouvé") ;

                        Alerteur myAlerteur = (Alerteur) context.getService(sr);

                        if(myAlerteur == null) {
                                System.out.println("Impossible d'instancier le service Alerteur.") ;
                        } else {
                                myAlerteur.Alerter("Message émis par TestAlerteur et affiché par Alerteur.");
                        }
                }
        }

        public void stop(BundleContext context) {
                System.out.println("TestAlerteur.stop");
        }
}
C :\...\Alerteur> javac -d . -classpath C :\Users\Eric\Documents\mestests\equinox.jar ;. TestAlerteur.java
-mestests/Alerteur/CAlerteur.mf-
Manifest-Version: 1.0
Bundle-Name: CAlerteur
Bundle-Activator: CAlerteur.TestAlerteur
Bundle-SymbolicName: Test Alerteur
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework , PAlerteur;version="[1.0.0, 2.0.0)
C :\...\Alerteur> jar -cfm CAlerteur.jar CAlerteur.mf CAlerteur/*.class

On l’installe sous OSGi, on le démarre et... on obtient le message suivant :

Je cherche une réponse

Je n’ai jamais réussi à faire fonctionner ServiceTracker sous OSGi/Equinox... J’ai pris modèle sur un tutoriel, mais OSGi me répond quand je démarre mon bundle que la classe ServiceTracker est introuvable, j’ai vérifié, elle est bien dans equinox.jar, avec le bon chemin...

Il y a sans doute une grosse connerie, mais j’ai pas vu quoi !

-mestests/Alerteur/AlerteurActivator.java-
package CAlerteur;

import java.util.*;
import PAlerteur.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;

public class TestAlerteur implements BundleActivator {
        private ServiceTracker finderTracker;
        private ServiceRegistration listerReg;
        public void start(BundleContext context) {

                // Recherche le service
                finderTracker = new ServiceTracker(context, Alerteur.class.getName(), null);
                finderTracker.open();

                Alerteur myAlerteur = (Alerteur) finderTracker.getService() ;
                if (myAlerteur == null) {
                        System.out.println("Impossible d'instancier le service Alerteur.") ;
                }
                else {
                        myAlerteur.Alerter("Message ‚mis par TestAlerteur ... Alerteur.");               
                }
        }

        public void stop(BundleContext context) {
                finderTracker.close();
        }
}
Et il me reste d’autres questions

Dans les exemples de client de service, on implémente Alerteur et pas IAlerteur, et ça me surprend...

Vous pouvez noter cet article, lu 975 fois
0 vote


Qui êtes-vous ?
Votre message

Ce formulaire accepte les raccourcis SPIP [->url] {{gras}} {italique} <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.