Continuiamo il nostro percorso sui moduli con un esempio di modularizzazione applicato a una web app. Con le tecnologie Java EE 6, la modularizzazione è piuttosto semplice, ma grazie a quanto abbiamo appreso in precedenza su JBoss Modules, nell’articolo vedremo come sarà relativamente facile modularizzare anche una applicazione web basata su Spring MVC.
Introduzione
JBoss Modules [1] è un’implementazione stand-alone di un sistema modulare di caricamento ed esecuzione di classi Java: è molto veloce, semplice ed estendibile. Ne abbiamo infatti parlato nei primi due articoli della serie, e adesso presenteremo due esempi di come modularizzare una applicazione web in due ambiti diversi: nel primo caso farò un esempio su una applicazione web basata su Spring MVC, nel secondo caso con una applicazione web basata su CDI-EJB-JSF.
Dopo aver presentato JBoss Modules e dopo aver visto un esempio di creazione di un modulo complesso che permette integrare Spring con JBoss Modules, vedremo come scrivere una applicazione web basata su Spring MVC senza dover inglobare tutto il codice “business” all’interno del WAR stesso. Tutto ciò sarà possibile farlo in modo semplice e veloce, grazie alla semplicità JBoss Modules.
In questa maniera avremo dei benefici enormi in termini di tempo nel ciclo di deploy-test-redeploy-test. Inoltre il deploy di un solo “pezzo” dell’applicazione è ovviamente più veloce rispetto al deploy di tutta l’applicazione intera.
Per quanto riguarda invece le applicazioni web basate sullo standard EE6, la modularità è già garantita dallo standard stesso [2], e quindi non ci soffermeremo su una applicazione web basata su CDI-EJB-JSF, ma proprio su una applicazione web non basata sullo standard nuovo, ma per esempio su Spring MVC. L’esempio presentato in questo articolo è scaricabile come “Allegato” dal link a destra.
Definizione del servizio da esporre a una pagina JSP
Utilizziamo quindi la tecnologia JBoss Modules per definire un servizio “HelloWorld” che verrà utilizzato da una pagina JSP basata su Spring MVC. Questo servizio dovrà solamente essere in grado di restituire un messaggio di saluto dato un nome in input.
Quindi creiamo prima di tutto due progetti Java per la parte “business”:
- un progetto che contenga solamente le definizioni delle interfacce del servizio (in questo caso una sola interfaccia con un solo metodo);
- un progetto che conterrà una implementazione di default del servizio.
Entrambi i progetti Java genereranno due JAR che poi diventeranno due moduli JBoss Modules da installare.
L’interfaccia del servizio
Il primo progetto lo chiameremo hello-world-api e contiene solamente l’interfaccia del servizio HelloWord:
package org.mokabyte; public interface HelloWorld { String sayHello(String message); }
Definiamo ora il descrittore del modulo in questa maniera:
name="org.mokabyte.hello-world-api" slot="main">
Impacchettiamo il tutto e installiamo questo modulo dentro il repository di AS7 come descritto negli articoli precedenti di questa serie [2].
L’implementazione di default del servizio
Adesso definiamo una implementazione di “default” del servizio. Creiamo quindi il secondo progetto, hello-world-service, che deve dipendere dal progetto precedente; infatti ha una dipendenza di compilazione: l’interfaccia HelloWorld.
Quindi scriviamo la classe che implementa il servizio:
package org.mokabyte; public class HelloWorldService implements HelloWorld { public String sayHello(String message) { return "Hello " + message; } }
Seguendo le specifiche Java per la definizione di “Servizi”, creiamo un file di testo (nelle risorse) che leghi il servizio implementato HelloWorldService con la sua interfaccia HelloWorld [3].
Quindi creiamo nella cartella META-INF delle risorse un file con il nome dell’interfaccia: org.mokabyte.HelloWorld che contiene una sola riga:
org.moka.HelloWorldService
Questa definizione permette di utilizzare il framework dei servizi di Java indicando quale classe implementa il servizio HelloWorld.
Infine aggiungiamo il descrittore per questo modulo:
name="org.mokabyte.hello-world-service" slot="main">
Impacchettiamo anche questo modulo e installiamo dentro la nostra istanza di AS7. A questo punto abbiamo due moduli installati: uno che definisce solo il “contratto” del servizio, un altro è invece una sua possibile implementazione.
Il controller
Dopo aver installato i due moduli, adesso creiamo un @Controller che utilizzi il servizio hello-world-api per visualizzare il messaggio di saluto nella JSP.
Vediamo come è implementato il controller basato su Spring MVC:
@Controller
@RequestMapping("/hello.html")
public class HelloController {
@Autowired
private HelloWorld service;
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public String handleGet() {
return service.sayHello("World!");
}
}
Come si vede in questo codice, il Controller riesce a risolvere (@Autowired) il servizio HelloWorld anche se, potenzialmente, HelloWorld non viene implementato da un servizio basato su Spring.
Il servizio HelloWorld viene caricato dal contesto di Spring perch� è stato esportato come servizio Java dal modulo hello-world-service, e possiamo quindi referenziarlo utilizzando il ServiceFactoryBean di Spring in questo modo:
Infine aggiungiamo la dipendenza di questo modulo web con il modulo che implementa il servizio hello-world-service e l’interfaccia tramite il deployment descriptor di jboss:
Una nota sulle dipendenze
Nei tre progetti che abbiamo fatto, abbiamo utilizzato due tipi di dipendenze: una è la dipendenza di compilazione, ed utilizziamo lo strumento di compilazione più usato che è Maven, che definisce dei moduli di progetto con le loro dipendenze.
Il secondo tipo di dipendenza è invece quella di run-time, in cui dobbiamo effettivamente legare un modulo a un altro modulo che implementa un servizio; in questo caso parliamo invece di JBoss Modules, non di “Maven modules”.
Di conseguenza le dipendenze tra progetti Java espresse mediante Maven non sono delle dipendenze modulari, anche se i moduli Maven gli assomigliano molto.
Quindi potremmo avere delle dipendenze molto diverse tra moduli Maven e moduli JBoss: ecco perchè ad oggi RedHat non ha prodotto alcun tool che automaticamente genera un modulo JBoss da un modulo Maven: si tratta di due diversi tipi di modularità. In un caso, la modularità viene espressa solamente per risolvere problemi di compilazione, e infatti le dipendenze Maven sono anche transitive: se un modulo Maven A dipende da B che dipende da C, allora il modulo A vede le classi di C.
Altro discorso invece vale per i moduli JBoss: di regola non dovrebbe mai essere usata la dipendenza transitiva tra moduli diversi; se un modulo JBoss A dipende da B che dipende da C, A non può vedere le classi di C perch� altrimenti romperemmo il concetto di isolamento tra moduli.
Questa differenza è molto importante: infatti il modulo Web ha definito una dipendenza dal modulo Maven hello-world-api, mentre ha una dipendenza dal modulo hello-world-service per quanto riguarda la parte runtime, cioè i moduli JBoss.
Conclusioni
Abbiamo visto come con JBoss Modules sia possibile integrare facilmente applicazioni web anche non basate sullo standard EE6, renderle facilmente modulari in modo da risolvere molti problemi di redeploy (fare il redeploy di tutto il WAR può anche richiedere molto tempo) e questo è un grosso vantaggio rispetto alla soluzione di utilizzare un semplice servlet container come Tomcat.
Inoltre scegliere JBoss AS7 permette di utilizzare molti servizi e prodotti di RedHat per AS7 che possono semplificare molti aspetti di un progetto web moderno.
Infine, come abbiamo già detto più volte, l’uso di un Application Server basato su un sistema modulare semplice e ben documentato è un grosso vantaggio rispetto all’utilizzo di altri server che utilizzano un sistema molto complesso come OSGi o di altri sistemi modulari scarsamente documentati.
Riferimenti
[1] JBoss Modules, pagina di documentazione principale
https://docs.jboss.org/author/display/MODULES/Home
[2] Serie di articoli su Mokabyte su EE6
https://www.mokabyte.it/cms/article.run?articleId=P2O-MCA-Q8N-XJL_7f000001_3893887_329f578b
[3] Java Service
http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html