In questo articolo parleremo delle specifiche OSGI e di come sviluppare applicazioni modulari,  dette “bundle”, attinenti a tali specifiche. In particolare svilupperemo un semplice progetto di esempio per illustrare il meccanismo di collaborazione tra differenti moduli.

Concetti di base

Per quelli completamente a digiuno sull’argomento, diciamo soltanto che le specifiche OSGI definiscono un modello standard e modulare per il ciclo di vita del software mettendo a disposizione un ambiente comune per consentire la cooperazione tra i vari bundle.
Grande attenzione viene data alla risoluzione delle dipendenze, che avviene in maniera dinamica e in modo tale che un bundle non venga nemmeno installato se tutti i moduli correlati non sono presenti e disponibili. Sfruttando le potenzialità messe a disposizione per il ciclo di vita del modulo è possibile installare, avviare, stoppare ed aggiornare un determinato modulo senza la necessità di effettuare alcun riavvio.

In rete e tra i link di riferimento in basso trovate abbastanza materiale per poter approfondire l’argomento.

Vediamo adesso di fissare alcuni concetti che ci torneranno utili nella nostra discussione:

  • Un bundle è un applicazione attinente alle specifiche OSGI installato all’interno di un application server che espone e supporta un framework OSGI. Per i miei esempi ho usato JBoss 7 all’interno del quale troviamo Apache Felix. Oltre a Felix esistono altre 3 implementazioni delle specifiche OSGI: Equinox, Knoplerfish e Spagic3.
  • Un bundle può esporre dei servizi e dei package per mettere a disposizione alcune delle sue funzioni all’ambiente di cooperazione OSGI. In pratica un bundle può utilizzare i servizi offerti da un altro bundle installato sullo stesso application server.
  • La definizione del bundle avviene aggiungendo alcuni metadati all’interno del file MANIFEST.MF indispensabile per questo tipo di progetti.
  • Se implementiamo all’interno del bundle una classe di tipo BundleActivator, tramite i metodi di start e stop potremo definire il comportamento del nostro modulo durante la fase di avvio.

Esempio pratico

Veniamo quindi ad un esempio pratico che ci permetterà di capire un pò più a fondo le dinamiche OSGI.

Implementeremo un semplice memory monitor per controllare periodicamente lo stato di utilizzo della memoria da parte del nostro application server. Se la memoria in uso supera un valore di soglia predefinito ipotizziamo di spedire una mail di alert.

Svilupperemo due moduli bundle separati:

  • BundleMailSender, espone l’interfaccia “sendMail” che consente la spedizione delle mail mediante il server di posta.
  • BundleMemoryMonitor, si occupa del lavoro effettivo di controllo della memoria e se necessario sfrutta il servizio sendMail offerto dal precedente bundle per inviare la mail di allarme.

>> Nota1: Questa divisione logica e modulare non introduce niente di nuovo ovvero anche senza utilizzare le specifiche OSGI potremmo realizzare due librerie, distribuite come archivi jar e utilizzate da un progetto più grande di tipo web o enterprise. Il valore aggiunto dei moduli bundle OSGI sta nella gestione del ciclo di vita che ci consente “a caldo” di aggiornare i singoli moduli differentemente da quanto succederebbe con i file jar per i quali dovremmo forse riavviare il server o probabilmente effettuare nuovamente il deploy dell’intera applicazione che li utilizza.

>> Nota2: Per realizzare i progetti bundle ho utilizzato il wizard di base di Eclipse relativo ai “Plugin project”. In alternativa un semplice progetto java contenente la cartella META-INF con il file Manifest ed esportato come jar funziona ugualmente bene ma all’aumentare della complessità risulta sicuramente comodo usare un’ambiente assistito. Altre possibilità sono date dai vari plugin maven realizzati ad hoc, ad esempio per il nostro caso specifico si potrebbe usare il plugin maven per Felix.

Il BundleMailSender

Veniamo quindi al primo dei 2 moduli, il MailSender.
Il progetto presenta:

 

  • la cartella META-INF contenente il nostro file MANIFEST.MF.
  • la cartella src all’interno della quale andremo a mettere il nostro codice.
  • la cartella lib, creata appositamente, contenente il file mail.jar con le librerie Java Mail appositamente scaricate.

Concentriamoci adesso sul codice da implementare.
Abbiamo detto che il nostro BundleMailSender deve esporre un’interfaccia all’esterno per fornire un metodo per l’invio della mail.
Le best practice di java ci dicono che la cosa più sensata è quella di creare un’interfaccia ad hoc che implementeremo successivamente con una classe concreta che rappresenta il servizio effettivo.

package it.lcianci.test.osgi.util;

import java.util.List;

public interface MailSender {
public void sendMail(String sender, String subject, String message, List recipients);

}

Occupiamoci adesso della classe concreta che implementa la nostra interfaccia:

package it.lcianci.test.osgi.util;

import java.util.List;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class MailSenderImpl implements MailSender {
@Override
public void sendMail(String sender, String subject, String message, List recipients) {
// Codice per invio mail

System.out.println(“Mail di allarme a “+recipients.get(0).toString());
}
}

Il codice Java Mail esula dallo scopo di questo articolo e rendeva il tutto più complicato da leggere quindi l’ho volutamente eliminato.

A questo punto, dato che vogliamo registrare il servizio “sendMail” all’interno dell’ambiente di collaborazione dei bundle OSGI, ci serve implementare un BundleActivator così come previsto dalle specifiche OSGI.
L’interfaccia BundleActivator espone i due metodi start e stop. Questi metodi vengono invocati all’occorrenza dall’application server.

package it.lcianci.test.osgi.util;

import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class MailSenderActivator implements BundleActivator {

private static BundleContext context;

static BundleContext getContext() {
return context;
}

public void start(BundleContext bundleContext) throws Exception {
MailSenderActivator.context = bundleContext;
MailSender ms = new MailSenderImpl();
Hashtable props = new Hashtable();
props.put(“nome”,”MailSender”);
props.put(“descrizione”,”Il mio bel MailSender”);
bundleContext.registerService(MailSender.class.getName(), ms, props);
}

public void stop(BundleContext bundleContext) throws Exception {
MailSenderActivator.context = null;
}
}
 
All’interno dei due metodi abbiamo la possibilità di interagire con il BundleContext che rappresenta appunto l’ambiente di collaborazione condiviso tra i vari bundle e gestito per noi dal framework OSGI.
All’interno del metodo start andiamo quindi a registrare con “registerService” una classe di tipo MailSender avendo anche la possibilità di aggiungere delle meta informazioni a corredo all’interno di un HashMap di proprietà specifiche.

Resta soltanto da definire correttamente il file manifest che nel nostro caso conterrà le seguenti informazioni:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: BundleMailSender
Bundle-SymbolicName: BundleMailSender
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: it.lcianci.test.osgi.util.MailSenderActivator
Bundle-Vendor: Luca Cianci
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version=”1.3.0″
Bundle-ClassPath: lib/,src/,.
Export-Package: it.lcianci.test.osgi.util;uses:=”org.osgi.framework”

A parte le informazioni di servizio come nome, autore e altro, risultano particolarmente importanti le proprietà in grassetto:

  • Bundle-SymbolicName e Bundle-Version sono importanti perchè ci permettono di ricavare il nome logico da utilizzare per recuperare un bundle da codice, se necessario, usando questa sintassi name:version (quindi useremmo “BundleMailSender:1.0.0”).
  • Bundle-Activator serve per dire al framework OSGI quale classe utilizzare in fase di start/stop, il nostro BundleActivator appunto.
  • Export-Package ci serve per fare in modo che il servizio esposto sia utilizzabile anche dagli altri Bundle.

A questo punto non resta che esportare il progetto in formato jar.
Se avete usato il wizard dei plugin di Eclipse usate “export, deployable plugin and fragment” altrimenti esportate il jar normalmente avendo cura però di specificare il nostro file manifest appena creato.
Una volta esportato il bundle possiamo effettuarne il deploy su JBoss mediante la console di amministrazione alla porta 9990 dalla sezione “Deployments/Manage Deployments” e poi avviarla. Fatto questo, sempre dalla console di amministrazione, spostandoci sulla sezione “Runtime Operations/OSGI” troverete in elenco il nostro Bundle in stato attivo.

Per il momento mi fermo. Nella seconda parte del tutorial vedremo la parte relativa al BundleMemory Monitor.

Riferimenti:

http://www.osgi.org/Technology/WhatIsOSGi
http://www.osgi.org/Technology/HowOSGi
http://it.wikipedia.org/wiki/OSGi
http://www.vogella.com/articles/OSGi/article.html
http://www.javabeat.net/2011/11/writing-an-osgi-web-application/
https://docs.jboss.org/author/display/AS7/Helloworld+OSGi+quickstart