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 - et d'autres doivent être capables de travailler avec différentes versions du même package dans le but de prendre en charge les systèmes hérités externes.

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

L' Open Service Gateway Initiative est une spécification définissant un système de composants basé sur Java. Il est actuellement géré par l' OSGi Alliance et sa première version remonte à 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. L' IDE Eclipse , par exemple, est une application basée sur OSGi .

Dans cet article, nous explorerons certaines fonctionnalités de base d' OSGi en tirant parti de l'implémentation fournie par Apache .

2. Principes de base d'OSGi

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

Logiquement, un bundle est une fonctionnalité qui a un cycle de vie indépendant - 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 fichier MANIFEST.MF contenant des en-têtes spécifiques à OSGi.

La plate-forme OSGi fournit 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 des fonctionnalités dégradées, même lorsqu'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 plate-forme OSGi le démarrera uniquement 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 commencerons notre voyage dans OSGi en téléchargeant la dernière version d' Apache Karaf à partir de ce lien. Apache Karaf est une plate-forme qui exécute des applications basées sur OSG ; il est basé sur l' implémentation Apache de la spécification OSGi appelée Apache Felix .

Karaf propose des fonctionnalités pratiques en plus de Felix qui nous aideront à nous familiariser avec OSGi , par exemple, une interface de ligne de commande qui nous permettra d'interagir avec la plate-forme.

Pour installer Karaf , vous pouvez suivre les instructions d'installation de la documentation officielle.

4. Point d'entrée du bundle

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

Commençons donc par créer une application «Hello World» basée sur OSG .

Nous commençons à configurer une simple dépendance sur l' API OSGi principale :

 org.osgi org.osgi.core 6.0.0 provided 

La dépendance est déclarée comme fournie car elle sera disponible dans l' environnement d' exécution OSGi et le bundle n'a pas besoin de l'intégrer.

Écrivons maintenant la classe HelloWorld simple :

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 par OSGi qui doit être implémentée par des classes qui sont des points d'entrée pour un bundle.

La méthode start () est appelée par la plateforme OSGi lorsque le bundle contenant cette classe est démarré. D'autre part, stop () est appelé juste avant l'arrêt du bundle.

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

5. Créer un lot

Modifions le pom.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 le plugin maven-bundle-plugin, gracieuseté de la communauté Apache Felix , pour empaqueter la classe HelloWorld comme un bundle OSGi :

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

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

Bundle-Activator est le nom complet de l' implémentation de BundleActivator qui sera utilisé 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 à d'autres. Nous pouvons maintenant construire le bundle avec la commande habituelle mvn clean install .

6. Installation et exécution du bundle

Commençons Karaf en exécutant la commande:

/bin/karaf start

est le dossier dans lequel Karaf est installé. Lorsque l'invite de la console Karaf apparaît, nous pouvons exécuter la commande suivante pour installer le bundle:

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

Cela demande à Karaf de charger le bundle à partir du référentiel Maven local.

In return Karaf prints out the numeric ID assigned to the bundle that depends on the number of bundles already installed and may vary. The bundle is now just installed, we can now start it with the following command:

> bundle:start 63 Hello World

“Hello World” immediately appears as soon the bundle is started. We can now stop and uninstall the bundle with:

> bundle:stop 63 > bundle:uninstall 63

“Goodbye World” appears on the console, accordingly to the code in the stop() method.

7. An OSGi Service

Let's go on writing a simple OSGi service, an interface that exposes a method for greeting people:

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

Let's write an implementation of it that is a BundleActivator too, so we'll be able to instantiate the service and register it on the platform when the bundle is started:

package com.baeldung.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(); } }

We use the BundleContext as a mean of requesting the OSGi platform to register a new instance of the service.

We should also provide the type of the service and a map of the possible configuration parameters, which aren't needed in our simple scenario. Let's now proceed with the configuration of the maven-bundle-plugin:

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

It's worth noting that only the com.baeldung.osgi.sample.service.definition package has been exported this time, through the Export-Package header.

Thanks to this, OSGi will allow other bundles to invoke only the methods specified in the service interface. Package com.baeldung.osgi.sample.service.implementation is marked as private, so no other bundle will be able to access the members of the implementation directly.

8. An OSGi Client

Let's now write the client. It simply looks up the service at startup and invokes it:

public class Client implements BundleActivator, ServiceListener { }

Let's implement the BundleActivator start() method:

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(); } }

The addServiceListener() method allows the client to ask the platform to send notifications about the service that complies with the provided expression.

The expression uses a syntax similar to the LDAP's one, and in our case, we're requesting notifications about a Greeter service.

Let's go on to the callback method:

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; } }

When some modification involving the Greeter service happens, the method is notified.

When the service is registered to the platform, we get a reference to it, we store it locally, and we then use it to acquire the service object and invoke it.

When the server is later unregistered, we use the previously stored reference to unget it, meaning that we tell the platform that we are not going to use it anymore.

We now just need to write the stop() method:

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

Here again, we unget the service to cover the case in which the client is stopped before the service is being stopped. Let's give a final look at the dependencies in the pom.xml:

 com.baeldung osgi-intro-sample-service 1.0-SNAPSHOT provided   org.osgi org.osgi.core 6.0.0 

9. Client and Service

Let's now install the client and service bundles in Karaf by doing:

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

Always keep in mind that the identifier numbers assigned to each bundle may vary.

Let's now start the client bundle:

> start 65

Therefore, nothing happens because the client is active and it's waiting for the service, that we can start with:

> start 64 Registering service. Service registered. Hello John

What happens is that as soon as the service's BundleActivator starts, the service is registered to the platform. That, in turn, notifies the client that the service it was waiting for is available.

Le client obtient ensuite une référence au service et l'utilise pour appeler l'implémentation fournie via le groupe 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, chaque fois que nous devons garantir qu'une seule application doit être mise à jour sans aucun mauvais service, OSGi peut être une solution viable.

Le code de ce message se trouve à l'adresse over sur GitHub.