In questo articolo racconteremo qualcosa di Karaf, un progetto Apache che nasce per partenogenesi da ServiceMix, l’ESB open source della stessa fondazione. Karaf è un contenitore runtime di Java modulare, fondato sullo standard OSGi. Non ci soffermeremo sui dettagli specifici, che demandiamo a un articolo di prossima pubblicazione, ma ne vedremo piuttosto gli aspetti che lo pongono al centro dell’architettura di applicazioni e che permettono di usarlo per esprimere la nostra creatività.
Introduzione
Sono entrato in contatto con ServiceMix nel 2008 quando fui coinvolto per dare un parere su quale ESB utilizzare per un nuovo progetto avviato dai quartieri generali francesi. Io votai per OpenESB perche’ era l’unico progetto open source su cui fossi riuscito a trovare un libro, ma (fortunatamente) prevalse lo stesso l’idea di seguire la strada Apache, o meglio della sua versione con supporto commerciale, FuseESB, curata a quel tempo da Iona.
A seguito di questa scelta aziendale, anche io ho cominciato ad avvicinarmi a tale prodotto e nel 2010 abbiamo cominciato a usarlo anche nel nostro team per un progetto di ricerca finanziato dalla regione Toscana.
All’inizio non è stato facile, ma oggi usiamo ServiceMix in pianta stabile per i nostri progetti su base Java, non solo per fare integrazione tramite Apache Camel, ma soprattutto usiamo il suo kernel come backend per ospitare i moduli dei nostri servizi. Siamo così appassionati di questa architettura che recentemente ci siamo presi la briga di realizzare un esempio open source che speriamo aiuti altri sviluppatori a entrare in contatto con questa piattaforma e con le sue potenzialità e fornire nuovi strumenti per realizzare applicazioni modulari in Java.
Karaf (ossia ServiceMix Kernel)
Vi starete sicuramente chiedendo come mai nella introduzione si è parlato solo di ServiceMix e non di Karaf: perche’ Karaf nasce per partenogenesi da ServiceMix [1] ed è l’evoluzione di quello che era il sottoprogetto “Kernel” di ServiceMix.
Gli sviluppi di ServiceMix iniziano nel 2005 col fine di realizzare una implementazione open source delle specifiche Java Business Integration (JBI), la JSR 208. Per cercare però di risolvere proprio problemi dovuti a requisiti della JBI, nel 2008 il team decide di provare la strada OSGi [2] e quindi cominciano gli sviluppi del ServiceMix Kernel, ovvero un container basato su OSGi che sarà poi la base di ServiceMix dalla successiva major release, la 4.0. Ben presto questo nuovo container cresce a tal punto da meritare attenzione più generale, assume il nome di Karaf e si sposta fino a diventare un progetto Apache di primo livello.
Architettura e caratteristiche di Karaf
Così come viene definito sul suo sito, Karaf è un “ambiente runtime basato su OSGi che fornisce un container leggero nel quale poter installare componenti e applicazioni” in Java. Questa criptica frase vuol dire per noi che si possono scrivere delle applicazioni o delle librerie, impacchettarle dentro ad un JAR (che in OSGi sono chiamati ufficialmente Bundle) e installarle dentro a Karaf come se fosse un sistema operativo. La cosa più simile a cui riesco a paragonare questo sistema, è un ambiente server classico, Windows o Unix, dove si installano i servizi o i demoni che a loro volta utilizzano le librerie dinamiche, .DLL o .SO, installate nel sistema. Ma tutto questo, con Karaf, lo si fa in Java!
Figura 1 – Si può paragonare Karaf ai servizi di Windows.
In realtà le funzionalità elencate fino ad ora sono merito di OSGi, ma Karaf completa il quadro aggiungendo strumenti pragmaticamente indispensabili per amministrare un sistema di questo genere. Tra gli strumenti più importanti forniti da Karaf citiamo la possibilità di installare a caldo nuovi bundle, la configurazione dinamica (che offre un registro di sistema per i moduli), l’accesso remoto tramite SSH direttamente alla console OSGi ed una console web attraverso la quale amministrare il sistema.
Figura 2 – Architettura di Karaf.
OSGi possiede inoltre un classloader speciale il quale rispetto al classloader di sistema della JVM fornisce una soluzione per risolvere problemi di conflitti di versioni tra dipendenze a runtime, permettendovi di installare moduli che dipendono dalla stessa libreria ma di versioni diverse. In Java questo problema doveva essere affrontato dal progetto Jigsaw, che avrebbe dovuto venir rilasciato insieme alla Java 7 e che invece è slittato e non sarà presente nemmeno in Java 8, mentre OSGi esiste dal 2000.
Non voglio qui dilungarmi oltre sulla importanza di scrivere software modulare per la quale vi rimando all’intervista di Kirk Knoernschild [3], che è autore di un ottimo libro sull’argomento [4], ma il suo consiglio è che oggigiorno a scrivere software modulare debba essere chiunque “scriva codice per vivere”.
Tecnologie di contorno
Insieme a Karaf troviamo inclusi altri framework: si potrebbe usare Karaf semplicemente per realizzare applicazioni OSGi, ma anche volendo seguire questa come strada iniziale per fare i primi passi, si rischierebbe di trovarsi poi a dover ristrutturare completamente l’architettura una volta arrivati a maturità e quindi consigliamo di partire dall’alto. Inoltre, almeno lato Java, a noi piace non avere dipendenze dirette da OSGi.
Per poter sfruttare al massimo la piattaforma, è quindi necessario capire fin da subito che ruolo hanno alcune delle tecnologie più significative tra quelli presenti. Oggi vogliamo raccontarvi di Spring/Blueprint, Pax Web e Camel.
Spring e Blueprint
Spring è il famoso framework open source che fa quasi tutto, ma in questo caso ci interessa la sua base, ossia la Dependency Injection e in particolare la possibilità di istanziare le classi della nostra applicazione Java a partire da file di configurazione XML.
Karaf esplora i JAR che vengono installati alla ricerca di questi file e ne crea i bean che vi sono definiti dentro quando li trova. Per chi non conosce Spring, immaginate di creare una web application dove le servlet sono definite nel file web.xml: il meccanismo di Spring è simile, ma generalizzato e non limitato al contesto web, dato che in un file di configurazione dei bean si possono istanziare classi di ogni tipo.
Spring in Karaf è esteso con Spring DM (Dynamic Modules) che permette di interagire con il mondo OSGi senza però doverne importare le API nel nostro codice: in questo modo il codice risulta più pulito e le classi che scriviamo ovvero i nostri moduli possono essere riutilizzati anche su altre piattaforme.
Vediamo un breve esempio di un file Spring così come si potrebbe trovare in Karaf:
xmlns_osgi="http://www.springframework.org/schema/osgi"> init-method="start" destroy-method="stop" > interface="net.cristcost.MyBeanInterface" />
Quando Karaf individua questo file, istanzierà la nostra classe (net.cristcost.MyBean) e la avvierà chiamandone il metodo start. Il tag successivo invece fa parte di Spring DM e pubblica la nostra classe come servizio OSGi rendendola così accessibile da altri moduli.
In Karaf, Spring ha oltretutto due forme: lo Spring classico oppure Blueprint che è una copia di Spring, ma standardizzata da OSGi nella versione 4.2. A seconda delle nostre preferenze, è possibile usare uno o l’altro, con vantaggi e svantaggi in ognuna delle due soluzioni.
Il seguente esempio utilizza Blueprint ed è equivalente all’esempio precedente:
init-method="start" destroy-method="stop" > interface="net.cristcost.MyBeanInterface" />
La dependency injection gioca un ruolo molto importante nello sviluppo di applicazioni modulari [5], e la disponibilità di Spring in Karaf ci offre un ambiente favorevole ad implementarne i principi.
Web Application con Pax Web
Non è il principale motivo per cui qualcuno dovrebbe passare a Karaf ma, nell’ottica di dare un quadro degli strumenti disponibili per esprimere appunto la nostra creatività, è importante far sapere che Karaf include al suo interno un Web Server e che su si esso si possono installare delle applicazioni web. Non suggerirei a nessuno di adottare Karaf semplicemente come sostituto di Tomcat, Glassfish o JBoss poiche’ i vantaggi che possono giustificare una scelta di questo tipo derivano dal seguire un approccio modulare che significa adottare una architettura drasticamente influenzata da questa scelta.
Il framework Pax Web fornisce a Karaf il supporto per fare web: insieme ad un Web Server Jetty integrato, Pax Web implementa le specifiche HTTP Service definite in OSGi Compendium [7] e le specifiche Web Application definite in OSGi Enterprise [8]. Similmente a quanto fa Spring DM, Pax Web esplora i Bundle installati in Karaf alla ricerca del file web.xml. Quando lo trova, Pax Web avvia l’applicazione creandone le servlet, i filtri e inizializzandone i servlet context così come definiti nel web deployment descriptor.
In queste servlet è possibile accedere a servizi OSGi, direttamente o indirettamente attraverso meccanismi di dependency injection, e quindi siamo in grado di separare la nostra applicazione Web in moduli. La regola principale è dividere almeno in bundle diversi la web application dall’accesso ai dati, quindi avere uno strato di presentazione nel quale le servlet non operano logica di business ma si occupano prevalentemente di scambiare informazioni tra il browser e i servizi sottostanti. È in queste condizioni, quando il sistema è sviluppato ad-hoc e segue questa strutturazione che può valere la pena valutare Karaf come ambiente di deployment per il Web.
Questo stile si sposa bene qualora si utilizzino dei framework JavaScript MVC come GWT o AngularJS, dove l’interfaccia utente si implementa sul browser e il backend usa servizi Ajax come interfaccia: in questo caso lo strato server della web application si assottiglia e il vero sviluppo avviene negli altri bundle o nel codice per il browser. Il progetto della web application rimane maggiormente stabile nel tempo e richiede minore manutenzione.
Integrazione con Apache Camel
Apache Camel non è disponibile direttamente insieme a Karaf ma sono usati molto spesso insieme visto che quest’ultimo gli offre un ambiente di esecuzione ideale. Camel è un toolkit per sviluppare integrazione ovvero “scambiare informazioni tra applicazioni” ed entra in gioco là dove queste utilizzano protocolli, formati dati e interfacce non compatibili tra di loro. Il framework si basa sul paradigma dei messaggi, scambiati tra due endpoint attraverso delle rotte lungo le quali il messaggio è processato, trasformato, adattato e instradato.
Figura 3 – Rappresentazione di una rotta che integra due applicazioni.
Camel fornisce un vasto insieme di componenti predefiniti che supportano protocolli di ogni genere, dai quelli applicativi comuni come HTTP, FTP e SMTP, ai protocolli di più basso livello per comunicare in TCP o UDP attraverso socket, fino componenti specifici per interfacciarsi ai servizi di Amazon o Facebook [9]. Le rotte si definiscono attraverso uno speciale Domain Specific Language (DSL), disponibile sia in forma Java che in forma XML.
Prendiamo il servizio OSGi che pubblica un Bean definito via Spring nell’esempio precedente e immaginiamo di dovergli inviare informazioni da uno smartphone che parla con JSON via HTTP. In Camel questo si può implementare con la rotta definita utilizzando la sintassi Spring DSL nel seguente modo:
interface="net.cristcost.MyBeanInterface" /> unmarshalTypeName="net.cristcost.PhoneData"/>
Non si intende spiegare qui i dettagli tecnici, per approfondire i quali si rimanda all’ottimo libro su Camel di Claus Ibsen [10], ma soffermiamoci sul significato del codice che vedete sopra: grazie a queste righe di XML si riesce a integrare i dati senza inquinare la nostra classe con dipendenze da librerie di JSON, di HTTP o di codice di trasformazione del formato. Il vantaggio è che la nostra classe rimane semplice, può essere testata in modo completo con JUnit senza bisogno di test di integrazione ed è quindi possibile focalizzarsi meglio sullo sviluppo degli aspetti funzionali.
Tornando infine a ServiceMix, al giorno d’oggi JBI è diventata una tecnologia ormai obsoleta e l’approccio che questo ESB propone per fare integrazione si basa oramai completamente su Apache Karaf e su Apache Camel; anzi, volendo sintetizzar, si può dire che ServiceMix non è altro che la combinazione di questi due. Ufficialmente per completare il quadro si devono includere anche Apache CXF ed Apache ActiveMQ, ma per quanto riguarda la nostra esperienza personale l’utilizzo di questi ultimi due è subordinato alla gestione degli stessi tramite componenti di Camel.
La tecnologia non basta
Ci sono due importanti termini relativi a quanto è stato detto finora che però sono stati deliberatamente evitati: queste keyword sono SOA (Service Oriented Architecture) ed EIP (Enterprise Integration Patterns). Soprattutto il termine SOA negli ultimi anni ha illuso prima e disatteso poi le aspettative di molti sviluppatori sparsi per il globo e nell’articolo volevamo focalizzare il racconto su Karaf come uno strumento senza incorrere in pregiudizi di alcun tipo.
Tuttavia, il legame è forte: Camel è dichiaratamente un framework per implementare EIP [11], si ispira ai principi di questi, usa la stessa notazione e terminologia. Il nesso tra Karaf e le SOA va invece ricercato nel significato dell’acronimo OSGi, ovvero Open Service Gateway initiative. I principi dietro ad OSGi sono molto orientati sul concetto di servizio, il quale è usato come elemento per l’interazione tra i moduli. No, non entrano in gioco i Web Service almeno fino a questo punto, ma come ogni buon libro sull’argomento può raccontare [12], i Web Service sono solo uno dei tanti modi in cui si può implementare una Architettura Orientata ai Servizi. OSGi ne è un altro.
Progettare servizi che rispettano i principi delle SOA è molto importante per applicazioni che hanno l’ambizione di essere modulari. Questi principi aiutano a definire i confini più appropriati dei nostri moduli, e indirizzano su una strada che permette agli sviluppatori di collaborare più efficacemente insieme, anche nel semplice Java.
Gli EIP invece non sono utili solo quando si deve integrare sistemi esterni, ma anzi ci hanno aiutato molto anche nel combinare tra di loro i moduli delle nostre applicazioni: bisogna riconoscere che un modulo ben progettato è un sottosistema da integrare. Quindi, sebbene inizialmente possa sembrare inusuale, gli EIP trovano una legittima applicazione anche in un contesto locale dove le classi Java sono in esecuzione nella stessa JVM.
Nel nostro caso usiamo Camel come bus interno a ServiceMix per connettere tra di loro i servizi OSGi. Questo è utile soprattutto quando il sistema supera una certa complessità, e in più ci dà la flessibilità di far comunicare tra loro i moduli, direttamente attraverso la JVM oppure su rete distribuendo servizi su macchine diverse, con JMS o con Web Service a seconda delle esigenze.
Un esempio concreto: “SensorMix”
Per capire cosa si può fare con questa architettura vi proponiamo un esempio. Lo abbiamo chiamato SensorMix, in assonanza con ServiceMix, e lo abbiamo pubblicato su Github [13].
Figura 4 – ServiceMix con dentro i bundle che da cui è composto SensorMix.
SensorMix è una applicazione modulare sviluppata per Karaf/ServiceMix che si compone di 4 moduli principali più altri moduli accessori. Tra i moduli principali ne troviamo uno che usiamo come riferimento per implementare il pattern Canonical Data Model (Data Model API), un modulo che si occupa della persistenza delle informazioni (Data Service), una web application di amministrazione (Admin Webapp) ed un modulo che integra le informazioni dai sistemi esterni (Integration).
Figura 5 – SensorMix: architettura esterna.
SensorMix raccoglie informazioni da dispositivi esterni, eterogenei tra di loro e che usano protocolli e formati dati differenti. Abbiamo usato Arduino per campionare misure di luminosità e temperatura e inviarle tramite UDP con formato dati JSON. Da Android invece recuperiamo le informazioni di posizionamento GPS e i livelli di segnale WiFi e li inviamo questa volta via HTTP. Una applicazione per iOS usa la telecamera per contare il numero di volti ripresi nella inquadratura, e inviare questa informazione tramite socket TCP e Protocol Buffer come formato.
SensorMix pubblica sul Web Server integrato una applicazione GWT che permette di visualizzare i campioni registrati nel Data Service.
Figura 6 – SensorMix: integrazione delle informazioni dai sensori.
L’integrazione dei sensori esterni è realizzata tramite Apache Camel, del quale abbiamo utilizzato i componenti camel-jetty, camel-mina2 e camel-netty per integrare protocolli HTTP, UDP e TCP e i formati dati camel-xmljson, camel-xslt, camel-jaxb e camel-protobuf per trasformare i campioni inviati dai sensori al formato Java del Canonical Data Model.
Figura 7 – SensorMix: architettura della web application.
Infine il progetto della Web Application GWT è composto da codice prevalentemente destinato alla esecuzione su browser. Lo strato server è molto sottile e si limita a una unica Servlet GWT RPC che non fa altro che da tramite tra i servizi OSGi e il browser.
In questo modo abbiamo implementato una Service Oriented Front-End Architecture (SOFEA), uno stile imparentato alle SOA che non si è diffuso molto negli ultimi anni per colpa soprattutto dell’assenza di piattaforme integrate che lo supportassero, ma che non per questo manca di validità e che secondo noi trova un campo ideale di applicazione proprio con Karaf, Pax-Web e certi framework JavaScript che stanno riscontrando molto successo in questo momento.
Conclusioni
SensorMix è stato sviluppato da un team di colleghi/amici che si sono suddivisi i compiti e le responsabilità degli sviluppi sulla base dei moduli del sistema. Questa strutturazione dà il suo meglio in un contesto di team working e probabilmente una architettura di questo tipo non porta vantaggi specifici a sviluppatori individuali, anzi probabilmente sarebbe troppo complessa da gestire.
Con Karaf poi non è tutto rose e fiori e ci sono alcuni aspetti che sono complicati da affrontare, specialmente relativi a OSGi, come ad esempio la gestione dei package-import e package-export dei bundle dietro alla quale si rischia di perdere diverso tempo. Tuttavia l’architettura è flessibile, offre un alto grado di personalizzazione e fornisce molti spunti per implementare nuove idee.
Quindi, se riuscite a risolvere con soluzioni più semplici i vostri problemi software, seguite pure la vostra strada; ma laddove questo non sia possibile o riscontriate limiti con le infrastrutture usuali e magari abbiate bisogno di lavorare con un team di sviluppatori, allora vi consigliamo di valutare l’utilizzo della piattaforma formata da Karaf e delle tecnologie che orbitano intorno.
Riferimenti
[1] Jamie Goodyear, “A Brief History of Apache Karaf”
http://icodebythesea.blogspot.it/2011/01/brief-history-of-apache-karaf.html
[2] Guillame Nodet, “Thoughts about ServiceMix”
http://gnodet.blogspot.it/2010/12/thoughts-about-servicemix.html
[3] “OSGi vs. Jigsaw: Kirk Knoernschild on Modularity”
http://java.dzone.com/articles/osgi-vs-jigsaw-kirk
[4] Kirk Knoernschild, “Java Application Architecture Modularity Patterns with Examples Using OSGi”, Addison Wesley, 2012
[5] Dhanji R. Prasanna, “Dependency Injection: Design patterns using Spring and Guice”, Manning, 2009
[6] OSGi Service Platform Release 4 Version 4.2 Core Specification
http://www.osgi.org/Download/Release4V42
[7] OSGi Service Platform Release 4 Version 4.2 Compendium Specification
http://www.osgi.org/Download/Release4V42
[8] OSGi Service Platform Release 4 Version 4.2 Enterprise Specification
http://www.osgi.org/Download/Release4V42
[9] Apache Camel: lista dei componenti supportati
http://camel.apache.org/components.html
[10] Claus Ibsen – Jonathan Anstey, “Camel in Action”, Manning, 2010
[11] Gregor Hohpe – Bobby Woolf, “Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions”, Addison Wesley, 2003
[12] Nicolai M. Josuttis, “SOA in Practice: The Art of Distributed System Design”, O’Reilly, 2007
[13] Cristiano Costantini – Giuseppe Gerla – Michele Ficarra – Sergio Ciampi – Stefano Cigheri, “SensorMix”
https://github.com/cristcost/sensormix
Cristiano Costantini fin da piccolo usa i computer come mezzi per realizzare nuove idee; si è laureato in Ingegneria delle Telecomunicazioni ed è attualmente software architect e team leader presso Thales nella sede di Firenze, dove guida un gruppo di affiatati colleghi nel rivoluzionare gli schemi aziendali verso tecniche e tecnologie più produttive, più efficienti e innovative.