Continuiamo a illustrare i concetti di modularizzazione presenti in JBoss. Uno dei moduli più interessanti è quello OSGi, che risulta molto interessante poiché il framework OSGi definisce un ciclo di vita per ogni modulo, permette di esporre anche versioni differenti dello stesso modulo che possono anche convivere contemporaneamente nello stesso contenitore OSGi. Dopo l’introduzione di questa parte, nel prossimo articolo vedremo un esempio in pratica.
Introduzione
Le specifiche OSGi [1] definiscono un ambiente di servizi modulari che permettono di implementare servizi in maniera standardizzata seguendo l’approccio component-oriented. Si tratta dunque di uno standard che permette di implementare dei servizi definiti mediante dei moduli che espongono solamente il codice inerente il contratto da implementare.
Non tutte le classi/interfacce pubbliche possono essere esportare agli altri moduli: è la definizione del modulo stesso che determina cosa esportare e cosa deve rimanere privato; inoltre il framework OSGi definisce un ciclo di vita per ogni modulo, permette di esporre anche versioni differenti dello stesso modulo che possono anche convivere contemporaneamente nello stesso contenitore OSGi.
Ogni modulo definisce inoltre delle dipendenze da altri moduli mediante nomi di pacchetti o mediante nomi di moduli e versioni; le dipendenze vengono risolte dal contenitore che permette così di isolare correttamente le classi al loro interno e di gestire la convivenza di stesse classi di versioni diverse nello stesso runtime.
In questo articolo, vedremo una panoramica su OSGi e nel prossimo articolo presenteremo un esempio.
Il framework OSGi
Il framework OSGi è diviso in moduli, ognuno dei quali offre dei servizi. I layer (livelli) in cui è suddiviso sono i seguenti:
- Security Layer (opzionale)
- Module Layer
- Life Cycle Layer
- Service Layer
- Actual Services
Figura 1 – La struttura del framework OSGi.
Security layer
Il livello per la sicurezza è opzionale, e non è stato implementato nel framework JBoss-OSGi.
Questo livello permette di determinare se un modulo può essere caricato o meno basandosi sulla firma digitale dei moduli o anche sulla locazione dove si trovano (locali/remoti e su quale macchina girano). Inoltre per ogni modulo può essere specificato che tipo di operazioni possono fare (per esempio solo controllare il ciclo di vita di altri moduli).
Questo livello non è stato implementato in JBoss; inoltre il container OSGi può caricare solo codice (moduli) in locale e non in remoto, di conseguenza questo layer può effettivamente risultare superfluo.
Module layer
In questo layer vengono definiti cosa sono i moduli e come possono interagire tra loro.
Secondo lo standard OSGi, un modulo viene chiamato bundle, e è composto da classi Java e altre risorse, tra cui il descrittore di modulo.
Il descrittore contiene una serie di informazioni sul modulo, tra i quali i seguenti.
-
Bundle activator: è la classe che implementa il ciclo di vita del bundle;
Bundle symbolic name: è il nome simbolico del bundle; - Bundle version: è la versione di questo bundle;
- Export/import packages: sono le proprietà che indicano quali sono i pacchetti che questo modulo esporta ad altri moduli, e da quali pacchetti dipende. Inoltre per ogni pacchetto da importare vengono specificate anche le versioni valide, cioè un range di valori numerici accettati dal bundle.
In un recente articolo su JBossModules, abbiamo parlato di dipendenze tra moduli specificate in base al nome dei moduli: per esempio un modulo A definisce una sua dipendenza da un modulo B (con una certa versione).
Quando si parla di modularità offerta da OSGi si parla invece di pacchetti e non di moduli; il motivo è semplice: definendo la dipendenza di un modulo da un certo numero di pacchetti si permette di disaccoppiare in maniera pressocchè assoluta due moduli tra loro. L’idea che sta dietro questa scelta è di dire da quali servizi dipende un modulo, indipendentemente da quali siano i moduli che implementano tali servizi.
Inoltre le versioni vengono specificate con un range di valori validi, per esempio
[versioneMinima, versioneMassima)
dove le parentesi tonde vengono usate per indicare un limite non incluso, mentre la parentesi quadra un limite incluso. Ecco un esempio di dipendenza:
Import-Package: org.moka.osgi;version="[1.2, 4.0)",
org.moka.jboss;version="[2.0, 4.0]"
La modularità espressa da OSGi è quindi molto più flessibile di quella offerta da JBossModules, permette quindi di poter sostituire a run-time dei moduli a patto che esportino gli stessi pacchetti in modo tale da poter sempre validare le dipendenze di altri moduli. Questo meccanismo è molto utile quando si vuole testare un modulo senza dover installare i moduli di produzione e quindi utilizzando delle classi di test.
In questo esempio si vede come le dichiarazioni di Import-Package ed Export-Package permettono di risolvere le dipendenze:
Modulo A: Import-Package: org.moka.osgi;version=[1.2, 4.0)
Modulo B: Export-Package: org.moka.osgi;version=1.4.1
Modulo C: Export-Package: org.moka.osgi;version=3
Da qui si vede che il modulo A importa il servizio espresso dal modulo B e non dal modulo C.
Figura 2 – Interazioni fra i servizi OSGi.
Life cycle layer
Questo livello definisce il ciclo di vita di ogni bundle: l’oggetto Bundle Activator risponde alle callback definite in questo livello (se presente nel modulo).
Gli stati in cui si può trovare il bundle sono:
- installed: il bundle è installato nel contenitore;
- resolved: il contenitore ha verificato le sue dipendenze e il modulo è pronto a partire;
- starting: il modulo viene fatto partire;
- active: il modulo è attivo, in “running”;
- stopping: il modulo è stato fermato;
- uninstalled: il modulo è stato cancellato dal contenitore.
Figura 3 – Il ciclo di vita di un bundle.
Service layer
Il service layer si occupa di implementare quei servizi che devono essere interrogati quando cambia lo stato di un qualsiasi servizio senza dover interrogare direttamente il servizio stesso.
Il service layer è un servizio registrato nel contenitore OSGi e può essere interrogato proprio per ispezionare dinamicamente lo stato dei bundle installati nel contenitore. JBoss implementa non solo questo layer (che è obbligatorio), ma implementa anche due servizi aggiuntivi che semplificano di molto l’utilizzo del service layer, e che sono:
- OSGi Declarative services;
- OSGi Blueprint services.
Questi due servizi sono opzionali, e vanno sotto il nome di OSGi Service Compendium, fanno parte di tutta una serie di servizi opzionali che hanno lo scopo di integrare OSGi con Java EE e di definire (in maniera più generica) dei servizi di base che qualsiasi sviluppatore vorrebbe avere.
OSGi Service Compendium
Quindi nel compendium sono definiti una serie di servizi “standardizzati” di base che semplificano l’utilizzo di OSGi.
Log Service
Consiste in due servizi, uno per effettuare il logging e un altro per catturare il log efettuato.
Http Service
Permette di definire dei servizi HTTP all’interno di un bundle.
Configuration Admin Service
Definisce un servizio di amministrazione del container.
Metatype Service
Definisce un meccanismo standard per permettere d uno sviluppatore di bundle di definire dei metadati di un proprio servizio; in questa maniera un servizio può definire delle proprietà da esporre al servizio di amministrazione (Configuration Admin Service).
User Admin Service
Consiste in un servizio che permette di autenticare e autorizzare un bundle.
Declarative Services Specification (DS)
Questo servizio descrive un modello usato per creare e consumare dei servizi OSGi senza usare direttamente le API di OSGi stesso. In questa maniera un servizio utilizza dei servizi importati mediante semplicemente l’injection degli oggetti al proprio interno.
Blueprint Specification
Il Blueprint Specification (BS) è un servizio del tutto simile al DS; la realizzazione del BS è stata influenzata fortemente dal framework Spring. La differenza più importante tra BS e DS sta nel modo in cui il servizio consumatore di altri servizi si comporta quando il servizio produttore non è più visibile: nel caso del DS il servizio consumatore verrà fermato quando le sue dipendenze non sono più verificate, mentre nel caso del Blueprint, il servizio consumatore viene mantenuto e verrà di nuovo informato quando il servizio produttore sarà di nuovo disponible. Nel prossimo articolo analizzeremo più da vicino (con degli esempi) questi due servizi aggiuntivi.
Event Admin Service
È un servizio di produzione-consumazione di eventi. Si tratta di un servizio che implementa il modello publish-subscribe tra bundle.
Remote Service Specification
Ogni servizio definito all’interno di un container può essere solamente locale: questo servizio permette invece di poter risolvere un servizio anche se installato in un contentore remoto e quindi permette di standardizzare la definizione di un servizio di distributing computing per OSGi.
JTA Specification
È un servizio che permette di utilizzare il servizio JTA all’interno di OSGi.
JMX Specification
È un servizio che definisce una serie di Mbean per la gestione del contenitore OSGi.
JDBC Specification
Definisce un servizio di integrazione di OSGi con driver JDBC di database.
JNDI Specification
Definisce un servizio che permette di accedere a servizi OSGi mediante JNDI.
JPA Specification
Definisce come utilizzare JPA all’interno di un modulo OSGi.
WebApplications Specifications
Definisce un WAB, cioè un Web Archive (WAR) definito come Bundle (integrazione Servlet-OSGi).
Service Tracker Specifications
Definisce un servizio di tracciamento di tutti gli eventi del ciclo di vita dei bundle senza dover utilizzare le API OSGi, ma in maniera più semplice.
Figura 4 – OSGi a runtime.
JBoss e OSGi
JBoss [2] implementa un contenitore OSGi e i servizi del Compendium [4]. Il modulo OSGi è già incluso nel server AS7, e non c’è bisogno di scaricarsi ne’ di installare nient’altro.
In ogni modo JBoss mette a disposizione un pacchetto che si chiama jboss-osgi-installer che, contrariamente a quanto si possa pensare, non contiene una implementazione di OSGi, ma la documentazione e una serie di esempi sull’utilizzo dei servizi del Compendium. Si tratta quindi del punto di partenza per chiunque volesse utilizzare OSGi all’interno di JBoss.
Inoltre, il tool di test Arquillan [3] è perfettamente integrato con OSGi: possiamo utilizzarlo per creare dei micro deploy di bundle OSGi e testarli allo stesso modo con cui si verificano i moduli Java EE.
Conclusioni
In questo articolo su OSGi abbiamo visto le definizioni principali dello standard OSGi, abbiamo visto i servizi opzionali che vengono offerti da JBoss e l’integrazione con Java EE e Arquillan.
Nel prossimo articolo faremo degli esempi su come utilizzare OSGi in un ambiente Java EE: quindi faremo vedere come integrare i due mondi, come accedere da un Session Bean a un modulo OSGi, come testare un bundle con Arquillan e, soprattutto, faremo degli esempi sui servizi Declarative Services e Blueprint services [5] [6].
Riferimenti
[0] Wiki page ufficiale di OSGi Alliance
http://wiki.osgi.org/wiki/Main_Page
[1] OSGi home page
http://www.osgi.org/Main/HomePage
[2] JBoss OSGi home page
http://www.jboss.org/jbossas/osgi
[3] Integrazione Arquillan con bundle OSGi
https://docs.jboss.org/author/display/JBOSGI/Arquillian+Test+Framework
[4a] Servizi implementati da JBoss-OSGi
http://docs.jboss.org/osgi/jboss-osgi-1.0.0.Beta9/userguide/html/ChapProvidedBundles.html
[4b] Esempi di utilizzo dei servizi OSGi
https://docs.jboss.org/author/display/JBOSGI/Provided+Examples
[5a] OSGi Blueprint Service
http://jbossosgi.blogspot.it/2009/04/osgi-blueprint-service-rfc-124.html
[5b] Come iniettare servizi OSGi dentro un modulo EE tramite il Blueprint
http://stackoverflow.com/questions/13836212/how-to-inject-osgi-service-to-ejb-with-blueprint
[5c] Wiki page sul servizio Blueprint
http://wiki.osgi.org/wiki/Blueprint
[6a] Un esempio con il servizio Declarative Service (DS)
https://docs.jboss.org/author/display/JBOSGI/Provided+Examples#ProvidedExamples-DeclarativeServices
[6b] Wiki page sui Declarative Services
http://wiki.osgi.org/wiki/Declarative_Services