Introduction à OSGi

Introduction à OSGi

1. introduction

Plusieurs applications Java critiques et middleware ont des exigences technologiques strictes.

Certains doivent prendre en charge le déploiement à chaud, afin de ne pas perturber les services en cours d'exécution, tandis que d'autres doivent pouvoir utiliser différentes versions du même package afin de prendre en charge les systèmes hérités externes.

Les plates-formesOSGi représentent une solution viable pour prendre en charge ce type d'exigences.

The Open Service Gateway Initiative is a specification defining a Java-based component system. Il est actuellement géré par lesOSGi Alliance, et sa première version date de 1999.

Depuis lors, il s’est avéré être un excellent standard pour les systèmes de composants, et il est largement utilisé de nos jours. LeEclipse IDE, par exemple, est une application basée surOSGi.

Dans cet article, nous allons explorer quelques fonctionnalités de base deOSGi en tirant parti de l'implémentation fournie parApache.

2. Principes de base d'OSGi

Dans OSGi, un seul composant est appelé un bundle.

Logiquement,a bundle is a piece of functionality that has an independent lifecycle – ce qui signifie qu'il peut être démarré, arrêté et supprimé indépendamment.

Techniquement, un bundle est juste un fichier jar avec un fichierMANIFEST.MF contenant des en-têtes spécifiques à OSGi.

La plate-formeOSGi offre un moyen de recevoir des notifications sur les offres groupées devenant disponibles ou lorsqu'elles sont supprimées de la plate-forme. Cela permettra à un client correctement conçu de continuer à fonctionner, peut-être avec une fonctionnalité dégradée, même si un service dont il dépend est momentanément indisponible.

Pour cette raison, un bundle doit déclarer explicitement les packages auxquels il doit avoir accès et la plateformeOSGi ne le démarrera que si les dépendances sont disponibles dans le bundle lui-même ou dans d'autres bundles déjà installés sur la plate-forme.

3. Obtenir les outils

Nous allons commencer notre aventure dansOSGi en téléchargeant la dernière version deApache Karaf à partir dethis link. Apache Karaf est une plate-forme qui exécute les applications basées surOSGi-; il est basé sur l’implémentation parApache de la spécificationOSGi appeléeApache Felix.

Karaf offre des fonctionnalités pratiques en plus deFelix qui nous aideront à nous familiariser avecOSGi, par exemple, une interface de ligne de commande qui nous permettra d'interagir avec la plateforme.

Pour installerKaraf, vous pouvez suivre les instructions d'installation desofficial documentation.

4. Point d'entrée du paquet

Pour exécuter une application dans un environnement OSGi, nous devons l'empaqueter en tant que bundleOSGi et définir le point d'entrée de l'application, et ce n'est pas la méthode habituellepublic static void main(String[] args).

Commençons donc par créer une application "Hello World" basée surOSGi-.

Nous commençons à mettre en place une simple dépendance sur le noyauOSGi API:


    org.osgi
    org.osgi.core
    6.0.0
    provided

La dépendance est déclarée en tant queprovided car elle sera disponible dans le runtime deOSGi et le bundle n'a pas besoin de l'intégrer.

Écrivons maintenant la classe simpleHelloWorld:

public class HelloWorld implements BundleActivator {
    public void start(BundleContext ctx) {
        System.out.println("Hello world.");
    }
    public void stop(BundleContext bundleContext) {
        System.out.println("Goodbye world.");
    }
}

BundleActivator est une interface fournie parOSGi qui doit être implémentée par des classes qui sont des points d'entrée pour un bundle.

La méthodestart() est appelée par la plate-formeOSGi lorsque le bundle contenant cette classe est démarré. D'un autre côté,stop() est appelé juste avant l'arrêt du bundle.

Gardons à l'esprit que chaque bundle peut contenir au plus unBundleActivator. L'objetBundleContext fourni aux deux méthodes permet d'interagir avec le runtime deOSGi. Nous y reviendrons bientôt.

5. Créer un pack

Modifions lespom.xml et en faisons un véritable bundle OSGi.

Tout d'abord, nous devons déclarer explicitement que nous allons créer un bundle, pas un pot:

bundle

Ensuite, nous exploitons lemaven-bundle-plugin, gracieuseté de la communautéApache Felix, pour empaqueter la classeHelloWorld comme un bundleOSGi:


    org.apache.felix
    maven-bundle-plugin
    3.3.0
    true
    
        
            
                ${pom.groupId}.${pom.artifactId}
            
            ${pom.name}
            ${pom.version}
            
                com.example.osgi.sample.activator.HelloWorld
            
            
                com.example.osgi.sample.activator
            
        
    

Dans la section des instructions, nous spécifions les valeurs des en-têtesOSGi que nous voulons inclure dans le fichier MANIFEST du bundle.

Bundle-Activator est le nom complet de l’implémentation deBundleActivator qui sera utilisée pour démarrer et arrêter le bundle, et il fait référence à la classe que nous venons d’écrire.

Private-Package n'est pas un en-tête OSGi, mais il est utilisé pour indiquer au plugin d'inclure le package dans le bundle mais de ne pas le rendre disponible pour les autres. Nous pouvons maintenant construire le bundle avec la commande habituellemvn clean install.

6. Installation et exécution du bundle

CommençonsKaraf en exécutant la commande:

/bin/karaf start

<KARAF_HOME> est le dossier dans lequelKaraf est installé. Lorsque l'invite de la consoleKaraf apparaît, nous pouvons exécuter la commande suivante pour installer le bundle:

> bundle:install mvn:com.example/osgi-intro-sample-activator/1.0-SNAPSHOT
Bundle ID: 63

Ceci indique à Karaf de charger le paquet à partir du référentiel Maven local.

En retour, Karaf imprime l'ID numérique attribué à l'ensemble, qui dépend du nombre d'ensembles déjà installés et peut varier. Le paquet est maintenant juste installé, nous pouvons maintenant le démarrer avec la commande suivante:

> bundle:start 63
Hello World

«Hello World» apparaît immédiatement dès que le paquet est démarré. Nous pouvons maintenant arrêter et désinstaller le paquet avec:

> bundle:stop 63
> bundle:uninstall 63

«Goodbye World» apparaît sur la console, conformément au code de la méthodestop().

7. Un service OSGi

Continuons à écrire un simple serviceOSGi, une interface qui expose une méthode pour saluer les gens:

package com.example.osgi.sample.service.definition;
public interface Greeter {
    public String sayHiTo(String name);
}

Écrivons-en une implémentation qui est également unBundleActivator, afin que nous puissions instancier le service et l'enregistrer sur la plate-forme au démarrage du bundle:

package com.example.osgi.sample.service.implementation;
public class GreeterImpl implements Greeter, BundleActivator {

    private ServiceReference reference;
    private ServiceRegistration registration;

    @Override
    public String sayHiTo(String name) {
        return "Hello " + name;
    }

    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Registering service.");
        registration = context.registerService(
          Greeter.class,
          new GreeterImpl(),
          new Hashtable());
        reference = registration
          .getReference();
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Unregistering service.");
        registration.unregister();
    }
}

Nous utilisons lesBundleContext comme moyen de demander à la plateformeOSGi d'enregistrer une nouvelle instance du service.

Nous devons également fournir le type de service et une carte des paramètres de configuration possibles, qui ne sont pas nécessaires dans notre scénario simple. Passons maintenant à la configuration desmaven-bundle-plugin:


    org.apache.felix
    maven-bundle-plugin
    true
    
        
            
                ${project.groupId}.${project.artifactId}
            
            
                ${project.artifactId}
            
            
                ${project.version}
            
            
                com.example.osgi.sample.service.implementation.GreeterImpl
            
            
                com.example.osgi.sample.service.implementation
            
            
                com.example.osgi.sample.service.definition
            
        
    

Il convient de noter que seul le packagecom.example.osgi.sample.service.definition a été exporté cette fois, via l'en-têteExport-Package.

Grâce à cela,OSGi permettra aux autres bundles d'appeler uniquement les méthodes spécifiées dans l'interface de service. Le packagecom.example.osgi.sample.service.implementation est marqué comme privé, donc aucun autre bundle ne pourra accéder directement aux membres de l'implémentation.

8. Un client OSGi

Écrivons maintenant le client. Il recherche simplement le service au démarrage et l'invoque:

public class Client implements BundleActivator, ServiceListener {
}

Implémentons la méthodeBundleActivator start():

private BundleContext ctx;
private ServiceReference serviceReference;

public void start(BundleContext ctx) {
    this.ctx = ctx;
    try {
        ctx.addServiceListener(
          this, "(objectclass=" + Greeter.class.getName() + ")");
    } catch (InvalidSyntaxException ise) {
        ise.printStackTrace();
    }
}

La méthodeaddServiceListener() permet au client de demander à la plate-forme d'envoyer des notifications sur le service conforme à l'expression fournie.

L'expression utilise une syntaxe similaire à celle de LDAP et, dans notre cas, nous demandons des notifications sur un serviceGreeter.

Passons à la méthode de rappel:

public void serviceChanged(ServiceEvent serviceEvent) {
    int type = serviceEvent.getType();
    switch (type){
        case(ServiceEvent.REGISTERED):
            System.out.println("Notification of service registered.");
            serviceReference = serviceEvent
              .getServiceReference();
            Greeter service = (Greeter)(ctx.getService(serviceReference));
            System.out.println( service.sayHiTo("John") );
            break;
        case(ServiceEvent.UNREGISTERING):
            System.out.println("Notification of service unregistered.");
            ctx.ungetService(serviceEvent.getServiceReference());
            break;
        default:
            break;
    }
}

Lorsqu'une modification impliquant le serviceGreeter se produit, la méthode est notifiée.

Lorsque le service est enregistré sur la plate-forme, nous en recevons une référence, nous le stockons localement, puis nous l'utilisons pour acquérir l'objet de service et l'appeler.

Lorsque le serveur est ultérieurement désenregistré, nous utilisons la référence précédemment stockée pour le supprimer, ce qui signifie que nous indiquons à la plate-forme que nous n'allons plus l'utiliser.

Il ne nous reste plus qu'à écrire la méthodestop():

public void stop(BundleContext bundleContext) {
    if(serviceReference != null) {
        ctx.ungetService(serviceReference);
    }
}

Là encore, nous dégageons le service pour couvrir le cas dans lequel le client est arrêté avant que le service ne soit arrêté. Jetons un dernier regard sur les dépendances dans lespom.xml:


    com.example
    osgi-intro-sample-service
    1.0-SNAPSHOT
    provided


    org.osgi
    org.osgi.core
    6.0.0

9. Client et service

Installons maintenant les offres client et service dans Karaf en procédant comme suit:

> install mvn:com.example/osgi-intro-sample-service/1.0-SNAPSHOT
Bundle ID: 64
> install mvn:com.example/osgi-intro-sample-client/1.0-SNAPSHOT
Bundle ID: 65

Gardez toujours à l'esprit que les numéros d'identifiant attribués à chaque groupe peuvent varier.

Commençons maintenant le groupe client:

> start 65

Par conséquent, rien ne se passe car le client est actif et il attend le service, avec lequel nous pouvons commencer:

> start 64
Registering service.
Service registered.
Hello John

Ce qui se passe, c'est que dès que le service BundleActivator démarre, le service est enregistré sur la plate-forme. Cela, à son tour, informe le client que le service qu’il attendait est disponible.

Le client obtient ensuite une référence au service et l'utilise pour appeler l'implémentation fournie via le bundle de services.

10. Conclusion

Dans cet article, nous avons exploré les fonctionnalités essentielles d'OSGi avec un exemple simple indiquant qu'il suffit de comprendre le potentiel d'OSGi.

En conclusion, OSGi peut être une solution viable dès lors que nous devons garantir qu’une seule application doit être mise à jour sans inconvénient.

Le code de ce message peut être trouvéover on GitHub.