Thématiques principales

dimanche 3 mars 2019

OSGI : Les annotations

Nous avons vu comment déclarer un Activator/Factory/Tracker [ex-karaf], [uttu-felix] pour construire des bundles OSGI, ensuite nous avons vu comment tirer parti de la philosophie Spring pour alléger la définition des beans, des services  et leur utilisations avec SpringDM et Blueprint [bp-karaf], [uttu-bp]; dans ce dernier article (où presque sur OSGI), je vous propose de nous intéresser à une dernière façon de faire des bundles : en utilisant des annotations [scr-annot].

Bon la question ici n’est pas de faire un laïus sur les annotations, j’en ai déjà parlé dans un article [uttu-annot], donc on va aller directement dans le vif du sujet parce que l’on se doute bien que pour remplacer nos Activator and coe et/ou les description xml, il va nous falloir quelques concepts de base.

Les annotations

Alors en fait si vous ne l’aviez pas encore vraiment compris, avec OSGI, finalement il n’y à pas forcément beaucoup de concepts à mettre en oeuvre…. en effet finalement, on à des beans qui implémentent des services et qui vont être injecté dans d’autres beans…

Du coup dans notre contexte, on va rencontrer les annotations suivantes:
  • @Component: permet de déclarer un bean
  • @Activate/@Deactivate: permet de spécifier une méthode gérant une initialisation spécifique (où inversement pour l’annotation @Deactivate)
  • @Référence: permet de spécifier une donnée membre dans laquelle devra être injectée un autre bean (spécifié par @Component)



Alors il en existe d’autres des annotations bien sur et celles-ci possèdent divers propriétés pour permettre différentes configurations. Nous n’allons pas rentrer dans le détail, car il y à bien d’autres choses à dire avant!

Utilisations

Commençons déjà par définir nos beans et nos services. Nous n’allons pas faire compliqué… et reprendre simplement les classes faites durant les articles précédents….

Dans le provider:


public interface IWorldService {
 public String world();
}


@Component
public class WorldServiceImpl implements IWorldService
{
 @Activate
 public void start() throws Exception {
  System.out.println("Demarrage du bundle Provider via SCR"); 
 }

 @Deactivate
 public void deactivate() throws Exception {
  System.out.println("Fermeture du bundle Provider via SCR");
 }
 
 @Override
 public String world() {
  return "World";
 }
}

Dans le consumer:

@Component
public class HelloWorldConsumer {

 @Reference
 private IWorldService service;

 public HelloWorldConsumer() {
 }

 @Activate
 public void start() throws Exception {
  System.out.println("Demarrage du bundle Consumer via SCR");
  System.out.println("Hello " + service.world() + " SCR");
 }

 public IWorldService getService() {
  return service;
 }

 public void setService(IWorldService service) {
  this.service = service;
 }

 @Deactivate
 public void deactivate() throws Exception {
  System.out.println("Fermeture du bundle Consumer via SCR");
 }
}


Par contre bien sur pour utiliser les annotations il va falloir ajouter une dependance maven specifique:

<dependency>
 <groupId>org.osgi</groupId>
 <artifactId>osgi.cmpn</artifactId>
 <version>6.0.0</version>
</dependency>

Du coup maintenant la question importante… qu’est ce que vont permettre ces annotations? Et bien tout bêtement générer à la volé lors de la compilation et la construction du bundle un fichier xml équivalent à ceux que nous avons défini dans l’article sur SpringDM et Blueprint.

Alors la aussi on a de la chance, car le plugin maven maven-bundle-plugin [maven-bundle-plugin] se charge pour nous de charger le processeur d’annotation qui va bien et parser les differentes classes pour générer le fichier xml et le mettre dans OSGI-INF/Blueprint

Build

Vous voulez vois à quoi cela ressemble? Ok donc pour le provider cela donne ceci:


<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0" name="org.tc.osgi.bundle.hello.world.provider.annot.module.service.WorldServiceImpl" activate="start" deactivate="deactivate">
  <service>
    <provide interface="org.tc.osgi.bundle.hello.world.provider.annot.module.service.IWorldService"/>
  </service>
  <implementation class="org.tc.osgi.bundle.hello.world.provider.annot.module.service.WorldServiceImpl"/>
</scr:component>

Et pour le consommateur cela:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0" name="org.tc.osgi.bundle.hello.world.consumer.annot.HelloWorldConsumer" activate="start" deactivate="deactivate">
  <reference name="service" interface="org.tc.osgi.bundle.hello.world.provider.annot.module.service.IWorldService" field="service"/>
  <implementation class="org.tc.osgi.bundle.hello.world.consumer.annot.HelloWorldConsumer"/>
</scr:component>


À noter que pour voir ces fichiers, il vous faut soit ouvrir le jar contenant le fichier… soit ajouter une conf spécifique au plugin maven:


<plugin>
 <groupId>org.apache.felix</groupId>
 <artifactId>maven-bundle-plugin</artifactId>
 <version>4.1.0</version>
 <extensions>true</extensions>
 <inherited>true</inherited>
 <configuration>
  <exportScr>true</exportScr>
 </configuration>
</plugin>


(Retenez ceci car cela pourrait vous être utile si comme moi, vous utilisez un assembly pour faire vos jar plutôt que de passer par le plugin maven-jar-plugin….)

Voila, une fois le build réalisé, il reste à mettre nos bundles dans ServiceMix comme nous l’avons fait jusque la avec docker.

Déploiement

Il va nous falloir par contre considérer quelques différences à ce stade par rapport à ce que nous faisions jusque là lorsque nous lancions l’image docker. En effet jusque là nous embarquons le nécessaire aux traitements de nos bundles et l’ensemble des dépendances sont déjà présentes. La ca ne sera pas le cas car avec le mécanisme des annotations, il nous faut dans le server karaf une feature nommé “scr” (celle ci contenant les éléments pour traiter les fichiers xml issu des annotations dans nos bundles).

La vous devriez avoir bondi… et demander en criant? mais c’est quoi une feature? Alors effectivement on en a pas parlé car il s’agit d’une spécificité de packaging et de coordination de Karaf [doc-karaf] et pas forcément de la norme OSGI…  Du coup, on n'a pas évoqué ce point. Cependant sans entrer dans le détails il faut juste retenir qu’une feature est un fichier xml spécifiant un ensemble de bundle interdépendant facilitant donc l’installation de ces bundles et simplifiant leur démarrage (dans le bon ordre de la résolution des dépendances et des services)

Bref donc il va falloir installer la feature scr à l’aide de la commande suivante:


$ docker run -it --rm --name kraf smix-helloworld:latest
Please wait while Apache ServiceMix is starting...
100% [========================================================================]

Karaf started in 0s. Bundle stats: 9 active, 9 total
 ____                  _          __  __ _
/ ___|  ___ _ ____   _(_) ___ ___|  \/  (_)_  __
\___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| | \ \/ /
 ___) |  __/ |   \ V /| | (_|  __/ |  | | |>  <
|____/ \___|_|    \_/ |_|\___\___|_|  |_|_/_/\_\

  Apache ServiceMix (7.0.1)

Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or 'system:shutdown' to shutdown ServiceMix.

karaf@root>feature:install scr


Une fois l’installation faites, et comme nos bundles n’ont pas été installé correctement, on va alors les désinstaller puis les réinstaller manuellement puis les lancer et la magies ca fonctionne!


karaf@root>uninstall 9 10
karaf@root>install file:///apache-servicemix-7.0.1/deploy/tc-osgi-bundle-hello-world-provider-annot-0.2.0-SNAPSHOT-assembly.jar
Bundle ID: 228
karaf@root>install file:///apache-servicemix-7.0.1/deploy/tc-osgi-bundle-hello-world-consumer-annot-0.2.0-SNAPSHOT-assembly.jar
Bundle ID: 229
karaf@root>start 228
karaf@root>start 229
Demarrage du bundle Provider via SCR
Demarrage du bundle Consumer via SCR
Hello World SCR
karaf@root>stop 229
Fermeture du bundle Consumer via SCR
Fermeture du bundle Provider via SCR
karaf@root>


Alors on se dira que c’est du coup plus laborieux d’utiliser les annotations…

Non car il faut garder deux choses en têtes:
nous aurions pu créer une image docker intermédiaire dans laquelle la feature scr est déjà installé (et ne pas avoir de soucis)
nous aurions pu construire notre propre feature ayant pour dépendance scr afin de simplifier notre installation et l'exécution des bundles….

Enfin dans le deux cas, cela fait intervenir juste soit un peu d’organisation soit des spécificités techniques de karaf qui ne sont pas le propos de cet article (et honnêtement pas forcément intéressant… allez voir la doc, elle est bien faites…)

Conclusions

On vient donc de finir de faire le tour des approches de construction des bundles OSGI.
On en a vue trois ayant leurs avantages et leurs inconvénients. A mon sens, il faudra surtout faire des choix orientés selon vos affinités technologiques (par exemple personnellement, je ne suis pas fan de l’approche SpringDM) mais il faudra aussi prendre en compte l'implémentation du framework employé qui selon le cas (et sa version) fournira plus ou moins de moyen de simplifications (comme les features karaf)

Nous voilà donc maintenant au bout des bases essentielles sur OSGI, les prochaines articles sur le sujets ne seront donc probablement plus sur OSGI lui même mais plutôt sur son utilisation dans divers contextes: besoin fonctionnel spécifique (cas de l'implémentation d’un client serveur syslog) ou besoin technique spécifique (le cas de equinox-loader)

Edit*: depot git-hub du code de l'article https://github.com/collonville-tom/tc-hello-world

Références

[ex-karaf] https://github.com/apache/karaf/tree/master/examples/karaf-bundle-example
[bp-karaf] https://github.com/apache/karaf/tree/master/examples/karaf-blueprint-example/karaf-blueprint-example-client/src/main/java/org/apache/karaf/examples/blueprint/client
[uttu-bp] https://un-est-tout-et-tout-est-un.blogspot.com/2019/03/osgi-springdm-et-blueprint.html
[uttu-felix] https://un-est-tout-et-tout-est-un.blogspot.com/2019/02/osgi-felix.html
[scr-annot] https://github.com/apache/karaf/tree/master/examples/karaf-scr-example
[uttu-annot] https://un-est-tout-et-tout-est-un.blogspot.com/2018/03/les-annotations.html
[doc-karaf] http://karaf.apache.org/manual/latest/#_creating_bundles
[maven-bundle-plugin] http://felix.apache.org/documentation/faqs/apache-felix-bundle-plugin-faq.html

Aucun commentaire:

Enregistrer un commentaire