JBoss-Modules è un’implementazione stand-alone di un sistema modulare di caricamento ed esecuzione di classi Java, molto veloce, semplice ed estendibile. In questa serie affronteremo il tema della modularizzazione in Java, con riferimento particolare alla piattaforma Enterprise.
Introduzione
JBoss-Modules [1] è un’implementazione stand-alone di un sistema modulare di caricamento ed esecuzione di classi Java molto veloce, semplice ed estendibile. In altre parole, all’interno di una applicazione, piuttosto che utilizzare un singolo class loader flat che carica tutti i JAR, si utilizza un class loader diverso per ogni modulo da cui dipende l’applicazione.
I class load non risultano quindi organizzati ne’ in maniera gerarchica, ne’ in un unico class loader che contiene tutto.
Nel dizionario un “modulo” è definito come “…composto da unità di costruzione semplice, flessibile e standardizzato…” . Il concetto di modularità è universale e ha trovato impiego in molti svariati ambiti, come attività manifatturiere, commercio, finanza, ingegneria, etc. I produttori di materiali utilizzano da sempre il concetto di modularità per aumentare la produzione, migliorare la qualità e ridurre la pressione dei costi. Già nell’industria dell’automobile è così da oltre un secolo: gli elementi dell’auto sono tutti pezzi standard e intercambiabili che permettono l’abbattimento sia del costo di produzione sia del tempo di produzione.
Anche nell’industria dell’informatica l’adozione di una architettura modulare è la forza per eccellenza della crescita rapida: sistemi informatici molto complessi e potenti possono essere concepiti, scritti e rilasciati, in tempi brevi con una elevata qualità proprio grazie alla divisione in sottosistemi intercambiabili che formano un dispositivo software/hardware molto complesso.
La modularità nel mondo Java
JBoss-Modules è quindi una libreria che implementa un sistema modulare per applicazioni Java sia in ambito EE che SE. Ma JBoss-Modules non è l’unico strumento di questo tipo: la ricerca della modularità è iniziata già da tantissimo tempo ed è stata intrapresa da tante organizzazioni: basti pensare che oggi esistono un buon numero di librerie per la modularità sia closed source che open source, e molti di questi prodotti sono stati scritti per uso interno (p.e. Atlassian Jira si basa su un proprio sistema proprietario di modularità).
Possiamo ritenere un sistema modulare già il sistema IoC implementato da diversi framework come Spring: tramite l’Inversion of Control è possibile dividere una propria applicazione in moduli separati e collegati da interfacce (ma caricati tutti nello stesso class loader). Qualcuno ha anche implementato un sistema per rendere pienamente modulare un software basato su Spring (Impala, su Google Code).
Un altro sistema modulare è anche quello egli EJB, per lo stesso discorso: i beans implementati in un modulo possono interagire con le classi presenti in altri moduli EE tramite il meccanismo del class loading del contenitore utilizzato.
La soluzione di OSGi
Dal 1999 è nato invece l’unico stardard Java destinato a imporsi su tutti gli altri, creato da aziende come Sun, Ericsson, IBM e Motorola che si proponevano come obiettivo quello di fornire un framework dinamico e completo. Il framework si chiama OSGi e, dal 1999 ad oggi, è sicuramente il sistema modulare più diffuso e più conosciuto nel mondo Java (JSR8 e JSR 291). L’organizzazione che detiene lo standard è OSGi Alliance, fondata appunto nel 1999.
OSGi ha indubbiamente avuto successo grazie al porting che l’IBM ha fatto del suo IDE, Eclipse, da un proprio sistema proprietario di modularizzazione ad OSGi, nella lontana versione 3.0. Per molti anni OSGi è rimasto, però, “relegato” nel mondo Eclipse.
Dynamic Module Server
Poi c’è stato il primo tentativo sperimentale di portare la programmazione modulare basata su OSGi nel mondo Java EE: qualche anno fa SpringSource ha implementato il suo DM-Server (Dynamic Module Server): era un server completamente basato su OSGi che permetteva di sviluppare applicazioni Java EE realmente modulari. Il DM-Server era un prodotto estremamente elegante, efficiente e completo che permetteva di portare OSGi nel mondo web implementando:
- moduli OSGi fatti apposta per il web (moduli per fare servlet, per pagine JSP…);
- un repository centrale OSGi per librerie per il web da cui il server attingeva automaticamente;
- porting di numerose librerie (se non quasi tutte le librerie più famose) del mondo Java EE nel proprio repository OSGi (p.e. il porting di Hibernate, oltre che quello di tutti i moduli dell’ecosistema Spring, o di tanti drivers JDBC).
Nonostante l’elevata qualità del prodotto, il risultato fu un insuccesso: il server DM-Server non fu accettato benevolmente dalla comunità Java EE perchè a prima vista OSGi poneva più svantaggi che vantaggi. Infatti SpringSource sviluppò il proprio server per due sole versioni, e poi lo donò alla comunità di Eclipse (oggi si chiama Virgo).
Il DM-Server, seppur molto elegante e semplice da usare, portava con sè moltissimi problemi dovuti alla definizione modulare delle proprie librerie nel mondo Java EE: in pratica molti sviluppatori si ritrovarono a dover risolvere numerosi ClassNotFoundException o ClassCastException dovuti proprio al sistema modulare di OSGi. Inoltre i moduli OSGi non erano semplici da fare e quindi tutto l’ecosistema DM sembrava eccessivamente complesso per quello a cui serviva (fare un sito web!).
La stessa SpringSource proponeva di utilizzare TC-Server (sostanzialmente Tomcat) per progetti medio-piccoli, mentre per progetti grandi proponeva di utilizzare il DM-Server perchè alla lunga avrebbe dato più benefici che inconvenienti e in cui OSGi permette veramente di non fare crescere la complessità in maniera esplosiva.
Nonostante tutto, il DM-Server rimase inutilizzato dalla stragrande comunità di sviluppatori che continuarono a preferire la modularità offerta dal framework EJB, molto più semplice e immediato; infatti presenta una curva di apprendimento molto meno ripida, ma è uno strumento anche meno completo rispetto a Spring+OSGi.
Il primo tentativo di portare la programmazione modulare nel mondo EE è risultato quindi un fallimento: o la programmazione modulare non è adatta al web, oppure OSGi è un po’ troppo complicato per determinati scopi.
La modularità nel RTE
Nel frattempo, all’orizzonte, si profila un altro problema: il RTE di Java comincia a essere estremamente grande: per un sistema web (per esempio per un server), può anche andare bene, ma per un’applicazione client stand-alone, sia che si tratti di una applet, sia che si tratti di una applicazione desktop, è veramente troppo grande: richiede un tempo di start up inaccettabile e un consumo di memoria elevato. Se la Oracle vuole “rivitalizzare” le applet (si vedano tutti gli sforzi fatti per JavaFX 2) e l’utilizzo di Java come tecnologia client in generale, allora è veramente necessario creare finalmente uno standard per la modularità e applicarlo al RTE.
La modularità nell’RTE permetterebbe alle applicazioni “pesanti” di non caricarsi tutte quelle librerie che non sono tipiche di un client, per esempio le librerie di accesso a un database non dovrebbero mai essere caricate all’interno di una applet. Inoltre le applet caricheranno il RTE “a pezzi”, quindi solo on-demand; in questa maniera si avrà un grosso risparmio di tempo di start up e di consumo di memoria.
Ecco quindi che la Oracle prende il testimone dalla Sun continuando lo sviluppo del progetto Jigsaw (JSR 294), prima pensato per Java 7, poi posticipato a Java 8. Siamo arrivati quindi ad oggi; ricapitoliamo cosa abbiamo nell’ecosistema SE e EE:
- OSGi: utilizzato prettamente da Eclipse per scrivere plugin e applicazioni Rich Client;
- Netbeans-Modules: utilizzato prettamente da Netbeans;
- Jigsaw per Java 8: pensato prettamente per rendere modulare il RTE di Java, e comunque pronto solamente verso la fine del 2013 (se va bene…);
- tanti altri framework, abbastanza datati, che hanno avuto più o meno successo e che oggi sono grosso modo o abbandonati o utilizzati solamente all’interno delle IT che li hanno progettati;
- il framework Spring: che riesce a rendere modulare una applicazione monolitica solo dal punto di vista “logico”, grazie al pattern IoC, ma poi tutta l’applicazione tipicamente viene installata su Tomcat in modo monolitico con un WAR, e occorre fare dei cambiamenti strutturali per evitare questo inconveniente, oppure abbracciare OSGi;
- il framework EJB, che riesce a rendere modulare una applicazione monolitica EE senza, tuttavia, introdurre concetti importanti come il caricamento di moduli on-demand, filtri di export ed import di classi/pacchetti, dipendenze esplicite opzionali/mandatori tra moduli, etc.
Tra tutti questi tentativi di portare la programmazione modulare nel mondo EE, sicuramente la soluzione migliore è quella degli EJB: anche se il framework Spring è molto più flessibile, leggero e ha numerosi plugin verso librerie di terze parti (forse più completo del corrispettivo framework EJB), però la modularità definita e realizzata dallo standard EE è nettamente più semplice e produttiva rispetto alla soluzione Spring + OSGi + Tomcat.
Per quanto riguarda la “pesantezza” di un framework EJB, c’è da dire che generalmente ogni application server dà la possibilità di definire delle configurazioni statiche fatte apposta far partire solo i servizi che abbiamo intenzione di usare evitando di caricare tutto il superfluo.
Arriva JBoss-Modules
Ma oggi, a fianco di tutte queste soluzioni e dei numerosi tentativi che si sono susseguiti negli anni, dal 2011, ha fatto il suo ingresso un ulteriore framework, JBoss-Modules, con l’obiettivo integrare lo standard EE con concetti di modularità che prima erano assenti.
Questo framework è molto promettente ed estremamente efficace: può dare benefici sia a progetti vecchi da “modularizzare” sia in progetti nuovi senza alcuno sforzo, senza le complessità del mondo OSGi e portando in dote la semplicità e potenza del nuovo framework EE6: CDI e EJB 3.1.
Motivazioni
Ma in generale, che bisogno c’era di creare un ulteriore framework modulare? E perchè JBoss non ha optato per:
- utilizzare OSGi e scrivere il proprio server AS7 [2] basandolo su questo standard (come già fatto, peraltro, nel passato per molteplici AS)?
- aspettare l’uscita di Jigsaw e utilizzarlo come base per il proprio server (a questo punto “AS8” per inizio 2014)?
La scelta è stata quella di anticipare i tempi implementando un proprio sistema modulare e, sopra questo sistema modulare estremamente semplice e performante, implementare anche il proprio modulo OSGi [3].
Basandosi sul proprio framework, oggi tutto il sistema JBoss AS è basato su Modules: ogni modulo EE installato su JBoss è implicitamente un modulo JBoss Modules, inoltre ogni modulo OSGi è un modulo JBoss Modules (mentre non è vero il contrario). La nuova libreria è semplice, veloce, dinamica e non comporta l’utilizzo di complicati file di configurazione come avviene in OSGi.
È stata una scelta vincente?
In questa serie di articoli provero’ a rispondere a questa domanda descrivendo in modo concreto il meccanismo di funzionamento di JBoss Modules, facendo vedere come può essere utilizzato tanto all’interno di AS7, quanto come base per una propria applicazione standalone modulare a plugin (JBoss Modules è del tutto indipendente dall’ecosistema di AS7: è vero anzi il contrario, tutto il server JBoss AS7 è basato su JBoss Modules e su MCS, il Module Container Service).
Per inciso, anche altri hanno intrapreso questa strada: la Oracle ha implementato Glassfish attorno a un proprio framework modulare, 100HK2, che rende il server estremamente veloce. Questo framework è comunque scarsamente documentato; inoltre ad oggi è relegato solo all’interno di Glassfish. Infine 100HK2 non è stato usato per implementare il servizio OSGi di Glassfish, ma il servizio OSGi viene assicurato tramite Apache Felix.
JBoss-Modules invece, essendo estremamente flessibile e potente, è stato utilizzato per implementare lo standard OSGi all’interno di JBoss AS7.
Performance e consumo di memoria
Le versioni precendenti di JBoss AS7 non erano state scritte per essere veramente modulari, e infatti il tempo di startup era molto alto cosi come il consumo di memoria: tutti i servizi venivano fatti partire anche se non venivano mai richiesti.
Si era reso quindi necessario rimettere a posto il server basandolo su un sistema modulare che potesse permettergli di:
- partire in pochissimo tempo;
- consumare poca memoria;
- caricare i servizi on demand;
- essere esteso facilmente.
In sostanza gli ingegneri di JBoss si sono resi conto che era venuto il momento di riscrivere il proprio server in maniera modulare per renderlo più leggero e veloce. Quindi la ricerca di una struttura modulare all’interno del server JBoss appare del tutto necessaria e tempestiva per “battere la concorrenza”: non era possibile aspettare il nuovo standard e AS7 doveva essere estremamente veloce e per niente esoso di risorse.
Figura 1 – Un confronto sul consumo di risorse all’avvio.
Le ragioni di una scelta
Rimane ora da rispondere alla domanda che ci siamo già posti: perchè JBoss ha implementato un proprio meccanismo modulare e non ha usato OSGi [4]? I motivi possono essere riassunti nei seguenti punti:
- è molto più semplice di OSGi: la libreria è composta da un solo JAR;
- è minimale: non implementa i service layer di OSGi e nessun altro servizio di alto livello, fa una sola cosa e la fa bene;
- pur essendo minimale è molto flessibile: JBoss-OSGi è una implementazione dello standard basato proprio su JBoss Modules e MSC;
- a valle di tutto ciò si potrebbe pensare che Modules diventi un altro framework modulare relegato solo nel mondo dove è nato, ma non è così:
- JBoss Modules è stato pensato per poter tradurre una applicazione stand-alone in una applicazione modulare in modo facile, veloce ed intuitivo;
- JBoss-Modules è formato da un solo JAR indipendente: non è necessario portarsi dietro il mondo JBoss se non se ne ha bisogno;
- se occorre scrivere un programma a linea di comando, è possibile farlo a plug-in e modulare mediante JBoss-Modules senza dover scrivere un nuovo framework fatto in casa, senza complicati file di configurazione come con OSGi, e riuscendo ad essere produttivi da subito; si noti il progetto JBoss Forge [8]: è un generatore di progetto EE fatto a linea di comando, orientato a plugin, è completamente basato su Modules;
- per quanto riguarda il mondo EE, una applicazione non può che avere enormi benefici utilizzando il meccanismo di “module isolation” definito mediante le dipendenze tra i moduli in maniera semplice (un modulo EE da quale JAR dipende?, quali classi si importano e quali si escludono?, quale versione si importa?, quali classi si re-esportano ad altri moduli?… tutte possibilità che non ci sono nello standard EE).
Features di JBoss Modules
JBoss Modules è stato disegnato per lavorare con qualsiasi libreria già esistente: è sostanzialmente un piccolo wrapper per eseguire una applicazione in un ambiente modulare. JBoss Modules non fa altro che organizzare i class loaders per moduli, in maniera non gerarchica nè flat, e da quando l’applicazione dell’utente prende il controllo del processo, JBoss Modules non fa nient’altro che caricare le classi solo quando servono, risolvendole tramite le dipendenze modulari in tempo O(1). Tra l’altro i moduli vengono sempre caricati on-demand, quindi permette un consumo minimale di memoria e un tempo di startup migliore.
Altro aspetto importante di JBoss-Modulese è che è estendibile: è possibile definire diverse strategie di class-loading tramite plugin. Permette inoltre di caricare dei moduli “locali”, cioè messi in una directory utilizzando una struttura molto semplice (come raffronto, OSGi invece ha un servizio complesso di discovery che permette il caricamento anche di moduli remoti, con annesse soluzioni efficaci a problemi di sicurezza che ciò comporta: Modules non implementa niente di tutto questo, non implementa alcun container e ciò lo rende particolarmente semplice e veloce).
Infine, Modules, permette di caricare librerie native filtrandole in base al nome dell’architettura e del Sistema Operativo e offre delle API per caricare e creare dei moduli a run-time.
Come sono organizzati i moduli
Un modulo è una unità isolata di funzionalità che implementa un determinato contratto, ogni modulo viene caricato nel proprio class loader e due moduli non verranno mai caricati nello stesso class loader.
Un modulo si trova all’interno di un repository, è univoco, ed è composto
- da una cartella dove ci sono delle risorse (JARs, classi, files di proprietà…) che sono da aggiungere al classpath di questo class loader;
- un descrittore XML che istruisce JBoss-modules nel modo in cui deve caricare il modulo.
In particolare, questo descrittore XML porta le seguenti informazioni:
- Il nome e lo slot del modulo (lo slot può essere visto come un versionamento, lo vediamo più avanti). La coppia (nome, slot) identifica il modulo e deve essere univoca all’interno del repository.
- Una lista di risorse (JAR, files…) da caricare nel class path del modulo. Le risorse puntate dai vari resource-root possono anche stare fuori dalla cartella del modulo.
- Una lista di moduli da cui questo modulo dipende (le risorse esportate dai moduli da cui questo modulo dipende vengono rese visibili e caricati in O(1). Quindi Modules risulta estremamente efficiente grazie a una mappa che si precarica allo startup, nella fase in cui fa lo scan di tutti i moduli definiti e si registra dove si trovano tutte le classi/risorse. In questa fase di “scan”, Modules non carica i moduli, ma li analizza solamente.
- Delle regular expression che filtrano quali risorse esportare tra quelle definite, e quali risorse importare da ogni singolo modulo. Inoltre definisce se una dipendenza va ri-esportata all’esterno (per default le dipendenze di un modulo sono nascoste all’esterno, così si rispetta il principio di isolamento dei moduli).
Nota sui profili EE6
Utilizzando un server completamente modulare, che senso hanno i profili Web introdotti nello standard Java EE 6?
I profili web diventano sostanzialmente superflui: non c’è più bisogno di definire staticamente quali debbano essere i servizi da caricare e quali no; con AS7 ogni instanza caricherà sempre e solo i servizi che vengono effettivamente utilizzati dagli applicativi utente. Praticamente è come se creasse ogni volta un profilo ad-hoc per noi.
Migrazione di un programma stand-alone a JBoss Modules
Vediamo adesso, come esempio, come migrare un progetto stand-alone non modulare a JBoss Modules. Prima di tutto per lanciare un programma modulare basta lanciare la JVM con jboss.modules.jar come JAR eseguibile:
java -jar jboss-modules.jar -mp path/to/modules my.main.module.name
Il parametro -mp indica la directory dove andare a cercare i moduli richiesti (è il repository dei moduli, lo vediamo in seguito); my.main.module.name indica il modulo principale, il main module, che deve essere caricato per primo (che, con ogni probabilità, conterrà la main class).
Per un programma stand-alone ci dovrà essere almeno un modulo eseguibile, cioè un modulo che promuove a eseguibile una classe che implementa il metodo statico main(…). Vediamo ora un esempio di un descrittore XML da usare per il modulo principale:
Da questo descrittore si vede che la classe eseguibile da lanciare è la main-class my.main.class. In ogni caso non è detto che questa classe debba essere per forza contenuta all’interno del modulo principale: è però vero che deve essere visibile al modulo principale per essere eseguita.
Poi abbiamo l’elenco delle risorse da caricare, sono appunto i resource-root, che vengono aggiunti al class path del modulo. In questo esempio abbiamo aggiunto come primo resource root il JAR che contiene proprio le classi della libreria (praticamente il JAR prodotto dalla compilazione Maven) e come secondo resource root una libreria di terze parti che non abbiamo “promosso” a modulo, ma l’abbiamo legato a questo modulo come fosse una libreria interna, di utilità, necessaria per far funzionare il modulo.
Infine il tag dependencies contiene i nomi dei moduli da caricare come dipendenze, che possono anche essere opzionali.
Quindi trasformare una propria applicazione in una applicazione modulare (quanto meno a “grandi linee”) è fattibile in breve tempo: basta dividerla in “macro-pezzi” logicamente diversi, “promuoverli” a moduli e legarli tutti assieme con delle dipendenze che ne descrivono le visibilità delle classi.
Il repository dei moduli
I moduli devono essere tutti contenuti all’interno di un repository gerarchico (directory). Il path dove leggere il modulo è ottenuto convertendo il nome del modulo in directory e i punti ” . ” in slash ” / ” e aggiungendo una ulteriore directory con il nome dello slot di riferimento (lo slot può essere visto come un versionamento del modulo, anche se lo slot non è nè rappresentato da un numero e non è neanche possibile effettuare alcun tipo di ordinamento tra slot, almeno per questa prima versione di Modules).
Per convenzione, il layout di un repository assomiglia molto ad un repository Maven semplificato:
- repository
- organization name
- library name
- slot name
- module.xml
- risorse (JARs, classi, files)
dove organization name e library name formano assieme il nome del modulo, che sarà: organization.library e dove slot name è un simbolo che indica la versione della libreria (il default è main).
Per esempio, il modulo getopt con versione main è registrato all’interno di AS7 come:
gnu.getopt.main
All’interno della directory gnu/getopt/main ci sono il descrittore module.xml e il JAR:
gnu/ gnu/getopt gnu/getopt/main gnu/getopt/main/module.xml gnu/getopt/main/getopt-1.0.13.jar
Il descrittore è:
Per inciso, si trova sotto la directory
jboss-as-7.1.1.Final/modules
che è il repository di AS7.
Il descrittore di moduli
I tags principali
Un modulo può essere definito o come alias di un altro modulo, oppure come modulo “regolare”. Un modulo regolare, oltre agli attributi name e slot, contiene i seguenti tag:
- exports: è un filtro (regexp) che indica quali risorse/classi sono da esportare ad altri moduli, per default ogni cosa definita in questo modulo viene esportato.
- dependencies: è un elenco di dipendenze, a loro volta suddivise in module (dipendenze espresse in base al nome del modulo, possono essere opzionali o obbligatorie); system (dipendenze espresse in base al system class path;
- resources: indica un elenco di resources root che sono visibili al class path del modulo;
- main-class: è la classe eseguibile;
- properties: sono definizioni di proprietà valide all’interno del modulo.
Figura 2 – I tags principali.
Le dipendenze da un modulo
Il tag di tipo moduleDependencyType definisce la dipendenza da un modulo (espresso con il nome del modulo, non con un percorso sul file system); ecco gli attributi del tag module:
- name: è il nome del modulo;
- slot: è la versione slot del modulo;
- export: è di default false; indica se questo modulo deve essere ri-esportato all’esterno, cioè se è visibile anche ai moduli che dipendono dal modulo corrente; si noti che per default è false: le dipendenze da moduli generalmente non vanno ri-esportate perchè un modulo dovrebbe esporre solo le sue classi pubbliche come interfaccia “esportabile” e nascondere le dipendenze;
- services: specifica se i servizi SPI (Service Provider Interface di Java) definiti in questa dipendenza possono essere importati, e una volta importati, se possono essere ri-esportati (none, import, export);
- optional: se la dipendenza è facoltativa o obbligatoria.
Figura 3 – moduleDependencyType
Filtro imports
In aggiunta, una dipendenza definisce anche il tag imports: un path definito con una regular expression che filtra che risorse/classi importare.
Figura 4 – Il filtro imports
Ecco un esempio di filtro imports con cui non importiamo le classi di test:
Moduli alias
I moduli alias non sono nient’altro che “un nome” alternativo a un altro modulo già caricato da JBoss Modules. Il modulo alias definisce il proprio name e il proprio slot, in aggiunta definisce il target-name e il target-slot, che sono proprio il nome e la versione del modulo a cui fanno riferimento.
JBoss Modules e JARs
Un JAR installato dentro JBoss AS7 come modulo Java EE è, per definizione, un Modulo JBoss. Qualunque modulo EE installato dentro JBoss AS7 è un Modulo JBoss “implicito”. Di conseguenza, per semplificare la creazione dei JAR, JBoss Modules accetta la definizione di dipendenze anche se scritte dentro il manifest file [5]. In questa maniera è possibile integrare facilmente la tecnologia Modules con strumenti di build come Maven.
Per esempio, per definire le dipendenze di un modulo con Maven si fa come segue:
org.apache.maven.plugins maven-jar-plugin 2.3.1 org.some.module1, org.some.module2:3.2.0, org.some.module3:main optional org.some.module4:1.0 optional export
I flags optional ed export indicano se il modulo è opzionale e se deve essere re-esportato.
JBoss Modules e lo standard EE
Class loading e Namespace Isolation
In base alle specifiche EE [6][7], ogni componente EE viene associato a un namespace univoco e caricato in un class loader apposito: quindi ad ogni namespace corrisponde un class loader, salvo diverse eccezioni.
Questo principio è noto come “namespace isolation” (EE5 specifications, 8.4). Per esempio, di regola, tutti i moduli di un EAR (JARs e WARs) vengono caricati in class loaders differenti; ma è anche possibile fare in modo che vengano caricati tutti all’interno di un unico class loader (scelta prevista ma sconsigliata dalle specifiche). Quindi, visto che ogni componente viene caricato in un class loader differente, le classi appartenenti a moduli diversi non sono visibili tra loro, a meno che non vengano definite delle dipendenze in maniera esplicita nel Manifest file del JAR/WAR usando la chiave Class-Path: .
Esistono poi altre dipendenze implicite previste dallo standard:
- come le librerie incluse nella directory comune dell’Application Server (sono visibili a tutti);
- le librerie incluse nella directory comune di un EAR (sono visibili all’interno di tutti i moduli dell’EAR stesso);
- le librerie dentro WEB-INF/lib di un WAR, sono visibili a tutto il WAR e non vengono caricate in class loaders differenti: tutto ciò che sta dentro un WAR è caricato dentro un solo class loader.
Figura 5 – Moduli EE e class loader diversi.
Per quanto riguarda JBoss AS7, invece che scrivere le dipendenze all’interno dei Manifest file di ogni JAR, è possibile descrivere le dipendenze tra moduli in un unico file jboss-deployment-structure.xml posizionato in App.ear/MANIFEST-INF:
true
Sempre da specifica (paragrafo: 8.4.f), ogni applicazione può caricare solamente una versione per ogni classe: se una applicazione dipende da due differenti moduli che dipendono entrambi da una stessa libreria ma con versioni diverse, allora l’applicazione potrebbe non essere deployabile.
Pro e contro delle regole di caricamento
Vediamo che vantaggi e svantaggi comportano queste regole di caricamento di classi e come è possibile migliorarle tramite JBoss-Modules.
Un grosso vantaggio di questo meccanismo è questo: un modulo EE può caricare una classe appartenente ad un altro modulo by reference, senza alcun bisogno di serializzazione/deserializzare l’oggetto.
Un altro vantaggio è il redeploy a caldo: siccome ogni modulo è caricato con un class loader diverso, allora ri-deployare un modulo sarà semplice: basta scaricare e ricaricare tutte le classi caricate con quel class loader.
Il vincolo di avere una sola versione per classe/libreria semplifica il meccanismo di class loading, ma comporta un problema: che succede se includo in una mia applicazione una libreria già contenuta nell’Application Server?
Se volessimo usare le JSF 1.2 quando invece il server fa uso delle 2.0 (la libreria quindi è inclusa nel class path comune a tutti), che risultato darebbe il deploy della mia applicazione, visto che la mia EAR ha visibilità di due classi/librerie uguali ma di versioni diverse?
In realtà non è vietato dallo standard installare uno stesso modulo EE più volte con versioni diverse: basta che abbiano nomi diversi (namespace) e non siano visibili l’uno all’altro e non ci sia nessun altro modulo che ha visibilità di entrambi.
Quindi, se volessimo utilizzare una versione aggiornata di una qualche libreria comune dell’Application Server, dovremmo trovare un modo per rimuovere la dipendenza dalla libreria ereditata implicitamente: con JBoss AS7 è immediato, basta escludere la dipendenza nel deployment descriptor:
Cosa cambia con JBoss-Modules
Siamo arrivati alla conclusione di tutto il discorso che abbiamo fatto: quali vantaggi ci dà JBoss-Module per i moduli EE? Alla luce di tutto quello che abbiamo visto in questo articolo riassumiamo ciò che possiamo fare con JBoss-Modules e che prima non si poteva fare, oppure era estremamente difficile da fare.
Module Isolation
Ogni modulo EE può decidere quale classe esportare e quale no, e quali classi importare dalle proprie dipendenze.Questo aspetto è molto importante: se dobbiamo integrare una libreria di terze parti all’interno di una nostra applicazione, e questa libreria contiene delle classi che vanno in contrasto con il nostro codice, possiamo escluderle dalle dipendenze tramite i filtri excludes/includes; questa possibilità ci aiuta notevolmente quando dobbiamo integrare diverse librerie potenzialmente incompatibili (p.e. usano dei parser XML che non vogliamo usare).
Stessa libreria, slot diversi
Grazie all’organizzazione modulare è possibile installare una stessa libreria con slot (versioni) diversi: le dipendenze espresse mediante JBoss-Modules indicano nome-modulo.slot, quindi anche la versione; mentre le dipendenze espresse come da specifica EE5 non includono alcun versionamento. Poi, se una stessa applicazione dipende da una libreria caricata due volte con due versioni diverse, può funzionare e non dare problemi: basta isolare correttamente le dipendenze e renderle valide solo dove serve. Certamente questo modo di procedere rimane molto pericoloso, ma a volte non evitabile se siamo costretti a integrare codice di terze parti che vogliono delle particolari versioni di librerie (questo meccanismo viene espressamente vietato dallo standard EE, ma grazie a Modules è possibile realizzarlo in AS7).
Repository centrale
Si definisce un repository centrale di moduli non visibili automaticamente a tutti (AS7/modules); nel repository è possibile installare drivers, librerie di utilità, anche con versioni diverse e renderle disponibili solo ai moduli interessati.
Uso di Spring
Con JBoss AS7 è possibile scrivere moduli che utilizzano Spring, deployare moduli Spring “a pezzi”, e non in un unico WAR, esportare gli Spring beans mediante il meccanismo SPI di Java, e tali beans saranno visibili all’esterno come oggetti managed di CDI. Inoltre è anche vero il contrario: oggetti CDI/EJB sono integrabili facilmente all’interno di codice Spring.
Conclusioni
In questo primo articolo della serie dedicata a JBoss-Modules abbiamo visto lo stato dell’arte dei framework per la modularità, abbiamo visto come OSGi sia la scelta migliore: seppure presenti una curva di apprendimento abbastanza ripida e abbia dei file di configurazione che utilizzano uno stile obsoleto rispetto ad oggi, rimane comunque uno strumento completo ed efficace per risolvere il problema della modularità.
Dopo di ciò abbiamo visto la scelta di JBoss, quella di scrivere “in casa” un nuovo framework su cui basare il nuovo server AS7. Abbiamo visto come OSGi sia comunque alla base di tanti altri Application Servers (come JonAS che offre le stesse feature di AS7 ma tramite OSGi), ma abbiamo comunque evidenziato come JBoss-Modules riesce a fare, in sostanza, le cose “che servono veramente” ma in modo molto più semplice.
Abbiamo visto come Jigsaw sia il nuovo standard Java, ma pronto solo per Java 8. Infine abbiamo visto quali vantaggi può dare una organizzazione modulare di una applicazione complessa EE rispetto alla modularità “strettamente permessa” dallo standard.
Abbiamo voluto sottolineare come scrivere una applicazione stand-alone basata su Modules sia fattibile in breve tempo e permette di creare programmi efficienti con un tempo di start up ragionevole.
Nelle prossime puntate, approfondiremo l’argomento.
Riferimenti
[1] JBoss Modules, pagina di documentazione principale
https://docs.jboss.org/author/display/MODULES/Home
[2] JBoss AS7 home page
[3] JBoss-OSGi home page
http://www.jboss.org/jbossas/osgi
[4] Discussione sulla modularizzazione per il team di JBoss e confronto con OSGi
http://planet.jboss.org/view/post.seam?post=modularized_java_with_jboss_modules
[5] Giovanni Puliti, “JBoss 7 Application Server”, MokaByte 178, novembre 2012
https://www.mokabyte.it/cms/article.run?articleId=DVV-SOM-XAS-RPI_7f000001_13046033_faa84870
[6] Specifiche EE5, si veda i capitoli 8.3 e 8.4 per il class loading, Java™ Platform, Enterprise Edition (Java EE) Specification, v7
[7] Giovanni Puliti, “Applicazioni enterprise, application server e classloader”, MokaByte 110, settembre 2006
https://www.mokabyte.it/cms/article.run?articleId=K7R-YIV-CD7-8ZV_7f000001_22398300_a790ffd6
[8] Home Page di JBoss Foge
Michele Mazzei si è laureato in Scienze dell’Informazione nell’ormai lontano 1998. Si occupa di progettazione e scrittura di software in Java/Java EE e in C/C++ sul mondo Linux. Lavora a Roma in ambito spaziale maturando esperienze in ambito OGC, GIS, Map Server, Payload Data Ground Segment (PDGS). Si interessa di tecnologie web basate su Java/Java EE, ma anche di tecnologie C/C++ del mondo Linux.