Thématiques principales

samedi 2 mars 2019

OSGI : SpringDM et BluePrint

Nous revoilà avec OSGI. Nous parlions à la fin de l’article sur Karaf et Service-mix de SpringDM et BluePrint… mais qu’est ce que c’est?

SpringDM et Blueprint [gemini] sont des frameworks Java et XML permettant la simplification de la description et la gestion des services OSGI de nos Bundles.

SpringDM a été le précurseur et a été suivi par Blueprint grâce à une normalisation (plus une adhésion) au sein de la norme OSGI.

Le principe est d’utiliser les mécanismes de Spring pour réaliser la déclaration de Beans au sein des bundles qui seront chargé par un bundle context dédiés. Cela ne va pas nous empêcher de devoir déclarer nos services sous la forme d’interfaces et d’en réaliser des implémentation mais cela va nous permet de ne plus avoir à implémenter toute la partie Activator, Factory et Tracker que nous avions vu dans les précédents articles sur le sujet…

Quoi ? mais du coup il n’y à plus rien dans le Bundle?



Effectivement… à part des interfaces et des implémentations. Mais attention cela aura une contrepartie:

  • Le chargement des services se faisant via des beans, leur cycle de vie s’en trouve modifiée au profit des mécanismes de gestion des beans du framework Spring.
  • Les bundles sont chargé dans un bundle context spécifique et dédié. Même si celui ci communique avec celui de l’OSGI natif, leur synchronisation implique des restrictions. Entre autre s’il est facile de consommer avec SpringDM un service OSGI natif (ou également SpringDM) depuis un bundle déclaré dans le bundle context Spring, l’inverse n’est pas forcément vrai car il existe un temps de latence entre l’introduction du service dans le bundle contexte de Spring et sa déclaration pour exploitation dans le bundle context natif.


Donc comme toutes les technologies Spring, ca simplifie la vie mais il faut rester prudent….

Mettons en oeuvre! Alors comme le but est de montrer son utilisation, on ne va pas forcément s’amuser à refaire tout le Helloworld  vu précédemment mais on va juste faire un second consommateur au service World que nous avons déjà construit. Nous en ferons deux versions… une avec SpringDM et, une avec BluePrint mais sachez qu’il est possible de mixer les deux frameworks (oui on peut….)

Avec SpringDM


Bien du coup les exemples vont être court car grâce aux fichiers xml, beaucoup d'éléments que nous définissions avant vont disparaître [springdm].

Le pom par exemple ne fera plus que déclarer l’utilisation du felix-maven-plugin sans l’activator…. (ce pom sera le même que pour avec Blueprint)


<build>
 <plugins>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-jar-plugin</artifactId>
   <configuration>
    <archive>
     <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
    </archive>
   </configuration>
  </plugin>
  <plugin>
   <groupId>org.apache.felix</groupId>
   <artifactId>maven-bundle-plugin</artifactId>
  </plugin>
 </plugins>
</build>

<dependencies>
 <dependency>
  <groupId>org.tc.osgi.bundle.hello.world</groupId>
  <artifactId>tc-osgi-bundle-hello-world-provider</artifactId>
  <version>${hw.producer.version}${snapshot}</version>
  <classifier>assembly</classifier>
  <type>bundle</type>
 </dependency>
</dependencies>


Coté code, il nous faut une classe bean qui consumera notre service, on tâchera de mettre un flux sur la console identifiant que le service est bien utilisé


public class HelloWorldConsumer {

 private IWorldService service;

 public HelloWorldConsumer() {}

 public void start(){
  System.out.println("Demarrage du bundle Consumer via SpringDM");
  System.out.println("Hello "+ service.world() + " SpringDM");
 }
 
 public IWorldService getService() {
  return service;
 }

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


Enfin il faut faire le câblage entre le service offert par notre bundle provider et le notre qui va le consommer. Ainsi on va déclarer dans un fichier xml (dont l’extension est *context.xml et déposé dans META-INF/spring)  un bean issu de la classe définie précédemment puis on va y injecter via une référence le service IWorldService.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:osgi="http://www.springframework.org/schema/osgi"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd" default-lazy-init="true">
             
 <bean id="helloWorldConsumer" class="org.tc.osgi.bundle.hello.world.springdm.consumer.module.bean.HelloWorldConsumer" init-method="start" lazy-init="false">
  <property name="service" ref="worldServiceRef" />
 </bean>
 
 <osgi:reference id="worldServiceRef" interface="org.tc.osgi.bundle.hello.world.provider.module.service.IWorldService" />
 
</bean


Voilà maintenant on build tout ça et on reprend notre bonne vieille image docker de servicemix à laquelle on va ajouter nos bundles (voir article précédent). Et on lance l’image.


karaf@root>start 225
Demarrage du bundle Provider
Enregistrement du service World
karaf@root>start 224
karaf@root>Obtention du service WorldServiceFactory
Demarrage du bundle Consumer via SpringDM
Hello World SpringDM

Avec Blueprint

Bien on vient de voir que cela fonctionne magnifiquement bien avec SpringDM et que cela se fait très très facilement… Et avec Blueprint [blueprint] direz vous?

Et bien avec Blueprint c’est très simple aussi.

On va d’abord changer un peu notre consommateur du service de façon à le distinguer


public void start(){
 System.out.println("Demarrage du bundle Consumer via BluePrint");
 System.out.println("Hello "+ service.world() + " BluePrint");
}


Ensuite évidemment il faut changer le descripteur xml qui la sera dans le répertoire OSGI-ING/blueprint.


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy">
             
 <bean id="helloWorldConsumer" class="org.tc.osgi.bundle.hello.world.blueprint.consumer.module.bean.HelloWorldConsumer" init-method="start" activation="eager">
  <property name="service" ref="worldServiceRef" />
 </bean>
 
 <reference id="worldServiceRef" interface="org.tc.osgi.bundle.hello.world.provider.module.service.IWorldService" />
 
</blueprint>


Et de la même manière que précédemment, on construit une image docker avec ce nouveau bundle et on lance le bundle.


karaf@root>start 224
Demarrage du bundle Provider
Enregistrement du service World
karaf@root>start 225
Demarrage du bundle Consumer via BluePrint
Obtention du service WorldServiceFactory
Hello World BluePrint


Voila le résultat est sensiblement équivalent l’avantage étant surtout d’utiliser une technologie à jour puisque si SpringDM n’evolue plus, Blueprint en est maintenant à sa version 3.

Voilà nous avons fait un grand pas en avant dans la techno OSGI, il nous reste maintenant plus que de regarder comment exploiter les annotations pour encore plus diminuer la quantité de code à gérer.

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

Références

[gemini] https://www.eclipse.org/gemini/blueprint/documentation/reference/2.0.0.RELEASE/html/index.html
[springdm] https://docs.spring.io/spring-osgi/docs/current/reference/html/app-deploy.html
[blueprint] https://www.ibm.com/developerworks/library/os-osgiblueprint/index.html

Aucun commentaire:

Enregistrer un commentaire