Modularizzare Java con JBoss Modules

II parte: Un esempio di integrazione con Springdi

Ci sono casi in cui occorre rendere modulare un progetto già esistente, che integra diverse tecnologie 'ereditate', come possono esse JSP, Spring , Spring MVC, e così via. In questo articolo vedremo come partire da un progetto basato su Maven che genera un unico WAR, e come possiamo trasformare le dipendenze di librerie di terze parti in moduli da caricare mediante modules, togliendole così dal WAR risultante.

Da dove iniziare

Prima di tutto occorre scaricarsi da internet gli strumenti che servono per iniziare a utilizzare jboss-modules.

Application Server

AS7: al momento in cui scrivo l'articolo, l'ultima versione stabile è la 7.1.1, ed è ovviamente il punto di partenza.

Esempi di prova

Quickstarts è un sito web [1] contenente esempi di prova di tutte le tecnologie JBoss, tra cui ci sono anche esempi su Java EE 6, integrazione con Spring e modules. Non è strettamente necessario dare un'occhiata a questo insieme di esempi, ma è un buon punto di inizio per avere delle linee guida su come utilizzare tutte le singole tecnologie messe a disposizione dal server di JBoss.

IDE

JBoss Developer Studio: è l'IDE di riferimento [2] realizzato da JBoss per utilizzare le loro tecnologie. Di fatto è una distribuzione di Eclipse fatta apposta da JBoss. Il Developer Studio si basa sulla distribuzione Java Enterprise di Eclipse, quindi utilizzando questo IDE non si perde nulla rispetto a una qualsiasi altra distro di Eclipse per il mondo Java. Inoltre è ovviamente possibile estendere il Developer Studio integrando altri plugin (p.e. SpringIDE, GWT, etc.). Durante l'installazione del Developer Studio, è bene fare attenzione a configurare correttamente dove si trova una distribuzione di JBoss AS7 sul proprio filesystem

 

 

Figura 1 - Download di JBoss AS7.1.1 e Quickstarts.

 

Creazione di un progetto EE basato su Spring

Iniziamo ora un progetto di un sito Web basato solamente sulla tecnologia Spring. In particolare il progetto dovrà integrare le seguenti tecnologie:

  • Maven: è il tool di compilazione;
  • Le librerie Spring: è il framework in sè e l'insieme di librerie da usare;
  • Hibernate, JPA e JTA per la parte business;
  • JSP con la libreria Spring MVC per la parte vista.

Tramite JBoss Developer Studio è possibile partire già da un progetto che integra assieme tutte queste tecnologie senza doverlo fare da soli a mano. Facendo partire il Developer Studio, comparirà automaticamente la vista del "JBoss Central", che contiene:

  • le news dal mondo JBoss;
  • link di wizard per la creazione di progetti Java EE;
  • link al JBoss Quickstarts;
  • update e installazione di pacchetti.

 

 

Figura 2 - JBoss Central.

 

Creiamo adesso un progetto basato su SpringMVC, cliccando sul wizard "Spring MVC Project". A questo punto l'IDE verificherà che abbiamo installato tutti i plugin necessari.

 

 

Figura 3 - Creazione del progetto Spring MVC.

 

Al passo successivo scegliamo il nome del progetto (in questo esempio il nome sarà quello di default: springmvc).

 

 

Figura 4 - Creazione del progetto Spring MVC.

 

A questo punto si completa il wizard e il progetto è pronto.

Adesso possiamo accendere JBoss AS7 da un'altra shell (possiamo anche farlo partire da dentro l'IDE, è sostanzialmente la stessa cosa). Per compilare e installare il progetto su JBoss AS7 è sufficiente lanciare il comando:

mvc clean package jboss-as:deploy

In questa maniera compiliamo e distribuiamo il WAR risultante su JBoss e il risultato è il classico esempio in cui gestiamo una semplice rubrica di indirizzi email messi in una tabella con la possiblità di aggiungere/togliere gli elementi selezionati:

 

 

Figura 5 - Welcome to JBoss!

L'esempio riportato quindi produce un classico WAR con dentro tutte le dipendenze espresse nel progetto. Quindi il progetto risultante sarà un WAR monolitico. Vediamo ora di renderlo modulare tramite jboss-modules.

Modularizzazione del progetto basato su Spring MVC

Per modularizzare un progetto di questo tipo dobbiamo andare per gradi. Prima di tutto occorre individuare quelle librerie esterne al nostro software, staccarle dalle dipendenze Maven, e creare al loro posto dei moduli jboss-modules e installarli dentro la nostra istanza di AS7.

Quello che faremo ora è quindi staccare tutte le librerie Spring, creare un modulo unico che contiene tutte le librerie Spring della distribuzione 3.2.0 e renderlo disponibile a tutte le applicazioni installate dentro la nostra istanza di AS7.

Creazione del pacchetto del modulo Spring

La scelta è quella di fare un unico modulo che contenga anche la parte MVC, e seguiamo questa via per semplicità: il modulo risultante verrebbe fuori molto grosso e "monolitico" a sua volta, ma questo è solo un esempio di come si deve procedere per portare dei vecchi progetti su jboss-modules.

Prima di tutto scarichiamoci dal sito di Spring l'ultima distribuzione (allo stato attuale è la 3.2.0). Scegliamo di dare il nome org.springframework al nuovo modulo main sotto la cartella dei moduli del server AS7.

cd ~/Applications/AS7/modules
mkdir -P org/springframework/main

A questo punto copiamo dentro questa cartella tutti i JAR che abbiamo preso dalla distribuzione Spring. La cartella dovrebbe quindi ora apparire così:

 

~/Applications/AS7/modules/org/springframework/main$ ls

aopalliance-1.0.jar                       spring-aspects-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar             spring-jms-3.2.0.RELEASE.jar
spring-web-3.2.0.RELEASE.jar              cglib-nodep-2.2.
spring-beans-3.2.0.RELEASE.jar            spring-expression-3.2.0.RELEASE.jar
spring-orm-3.2.0.RELEASE.jar              spring-webmvc-3.2.0.RELEASE.jar
commons-logging-1.1.1.jar                 spring-context-3.2.0.RELEASE.jar
spring-instrument-3.2.0.RELEASE.jar       spring-oxm-3.2.0.RELEASE.jar
spring-webmvc-portlet-3.2.0.RELEASE.jar   spring-aop-3.2.0.RELEASE.jar
spring-context-support-3.2.0.RELEASE.jar  spring-jdbc-3.2.0.RELEASE.jar
spring-tx-3.2.0.RELEASE.jar

 


Creiamo il descrittore di modulo module.xml, aggiungiamo le risorse che abbiamo scaricato dal sito di Spring e aggiungiamo le dipendenze dai moduli JBoss che gli servono per poter funzionare correttamente in un ambiente Java EE, per esempio i moduli per il parsing XML (dom4j), per le Servlet (javax.servlet.api con assieme JSP e JSTL), aggiungiamo una dipendenza da Hibernate, dal modulo per le transazioni, dai log (log4j e log di JBoss), da Java Assist per l'instrumentazione delle classi.

Il file diventa come segue:

       
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
       
       
              
              
              
              
              
              
              
              
              
              
              
              
              

Il modulo Spring è ora pronto: effettuiamo un undeploy del WAR che abbiamo appena fatto, spegniamo e riaccendiamo il server AS7. A questo punto vediamo che compariranno dei file .index all'interno della cartella del nuovo modulo: questo succede perchè modules indicizza i pacchetti che trova all'interno di ogni JAR, in modo da velocizzare la ricerca di dove si trova una classe da caricare.

Il modulo è ora pronto per essere utilizzato, dobbiamo solamente legarlo al progetto che abbiamo appena fatto.

Integrazione con il nuovo modulo Spring

Prima di tutto analizziamo bene il file di progetto Maven che abbiamo utilizzato: tutte le dipendenze dal framework Spring che troviamo nel POM non vanno rimosse, ma va aggiunto lo scope provided in modo tale da non avere errori a tempo di compilazione e da non aggiungere le librerie di spring nel WAR.


       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                               http://maven.apache.org/maven-v4_0_0.xsd">
       4.0.0
       org.moka.tools.example.springmvc
       springmvc
       war
       0.0.1-SNAPSHOT
       Getting Started with Spring on JBoss
       
              
              3.2.0.RELEASE
              
              1.0
 
              
              1.1.2
              1.1.1
 
              
              7.1.1.Final
       
       
…
       
       
       
       
              org.springframework
              spring-aop
              provided
       
       
              org.springframework
              spring-asm
              provided
       
       
              org.springframework
              spring-beans
              provided
       
       
              org.springframework
              spring-context
              provided
       
…
       
              org.springframework
              spring-tx
              provided
       
       
              org.springframework
              spring-web
              provided
       
       
               org.springframework
               spring-webmvc
               provided
       
 
              
…
       
…

Adesso che abbiamo tolto Spring dal deploy nel WAR dobbiamo aggiungere a questo WAR una dipendenza dal modulo Spring. Secondo le specifiche Java EE 6, il file descrittore specifico del vendor dell'Application Server che stiamo usando deve trovarsi dentro la directory WEB-INF del WAR; e il nome è ovviamente differente per ogni vendor. Per JBoss il nome è: jboss-deployment-structure.xml. Ecco come lo troviamo dall'esempio del Quickstarts:

       
              
                     
                     
                     
                     
              

Aggiungiamo ora la dipendenza dal modulo Spring in questo modo:

    
        
            
            
                
            
            
                
                    
                        
                
            
        

Come si può vedere, abbiamo aggiunto la dipendenza da org.springframework, ma abbiamo anche detto che importiamo tutto ciò che si trova sotto le directory META-INF e org; facciamo questo per poter importare tutti i file XML, i descrittori, etc. di cui Spring ha bisogno. Inoltre, in particolare, Spring MVC ha anche bisogno delle sue librerie di tag JSP, quindi dei file .tld che si trovano nel file spring-webmvc-3.2.0.RELEASE.jar.

Ricompiliamo il progetto ed effettuiamo il deploy su AS7. Il deploy sembra andare bene:

16:23:34,164 INFO [org.jboss.web] (MSC service thread 1-6) 
          JBAS018210: Registering web context: /springmvc

Un problema particolare

Se però andiamo con il browser a richiamare la home page del nostro sito troveremo il messaggio mostrato in figura 6

 

 

Figura 6 - Errore riscontrato nel progetto modularizzato.

 

E sul log di AS7, potremo leggere quanto segue:

16:32:25,628 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web]
                            .[default-host].[/springmvc].[jsp]] 
                             (http--127.0.0.1-8080-5) Servlet.service() 
                             for servlet jsp threw exception: 
                             org.apache.jasper.JasperException: 
                             The absolute uri: http://www.springframework.org/tags/form 
                             cannot be resolved in either web.xml 
                             or the jar files deployed with this application
    at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:51)
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:409) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:116) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.TagLibraryInfoImpl.generateTLDLocation(TagLibraryInfoImpl.java:239) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.TagLibraryInfoImpl.(TagLibraryInfoImpl.java:152) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Parser.parseTaglibDirective(Parser.java:386) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Parser.parseDirective(Parser.java:448) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Parser.parseElements(Parser.java:1398) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Parser.parse(Parser.java:130) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.ParserController.doParse(ParserController.java:255) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.ParserController.parse(ParserController.java:103) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:194) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Compiler.compile(Compiler.java:360) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Compiler.compile(Compiler.java:340) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.compiler.Compiler.compile(Compiler.java:327) 
           [jbossweb-7.0.13.Final.jar:]
    at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:607) 
           [jbossweb-7.0.13.Final.jar:]
…
16:32:25,646 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web]
                            .[default-host].[/springmvc].[jboss-as-kitchensink]] 
                             (http--127.0.0.1-8080-5) Servlet.service() 
                             for servlet jboss-as-kitchensink 
                             threw exception: 
                             org.apache.jasper.JasperException: 
                             The absolute uri: http://www.springframework.org/tags/form 
                             cannot be resolved in either web.xml 
                             or the jar files deployed with this application
     at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:51)
            [jbossweb-7.0.13.Final.jar:]
     at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:409) 
            [jbossweb-7.0.13.Final.jar:]
     at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:116) 
            [jbossweb-7.0.13.Final.jar:]
…

Come mai riscontriamo questo errore? Cerchiamo prima di tutto di capire che tipo di errore è. Da quello che leggiamo sembra che Catalina non riesca a trovare il file dove sono definite le librerie di tag di Spring (spring-form.tld e, a cascata, tutti gli altri, situati dentro la cartella META-INF di spring-webmvc.jar). Come mai dal WAR non riusciamo ad accedere al file TLD? Eppure nelle dipendenze di modulo abbiamo specificato di prendere tutti i file situati dentro le cartelle META-INF della famiglia di JAR di Spring.

Effettivamente jboss-modules riesce a servire questi files al nostro WAR, ma il file viene comunque ignorato perchè lo standard delle JSP asserisce che le librerie di tag devono obbligatoriamente stare dentro il WAR, e in particolare devono stare:

  • dentro un qualsiasi JAR all'interno della directory WEB-INF/lib del WAR;
  • dentro la directory WEB-INF.

Quindi è per questa ragione che non riusciamo a localizzare i nostri file TLD.

Deve essere chiaro che questo problema non dipende in alcun modo da jboss-modules: infatti avremmo ottenuto lo stesso risultato utilizzando OSGi. Si tratta di un caso specifico in cui non è possibile in alcun modo modularizzare una parte del progetto che stiamo facendo, a causa delle restrizioni dello standard. L'abbiamo presentato proprio per far capire che anche la modularizzazione ha i suoi problemi, che possono comunque essere superati.

La soluzione

La soluzione a questo problema risiede nell'iniettare a mano i file TLD dentro il WAR a tempo di compilazione. Per effettuare questo "hackeraggio" occorre modificare il file maven utilizzando il plugin maven-dependency-plugin nel seguente modo:

 
    org.apache.maven.plugins
    maven-dependency-plugin 
         
         
            extract-tld 
            generate-resources 
                 
                    unpack 
                 
         
             
                 
                         org.springframework 
                         spring-webmvc 
                         ${version.spring} 
                         src/main/webapp/WEB-INF 
                    META-INF/*.tld 
                 
             
         
     

Questo meccanismo può essere utilizzato in ogni occasione in cui sia necessario copiare dei file all'interno del WAR a causa di una non compatibilità di librerie/standard con la modularità appena introdotta.

Conclusioni

Abbiamo portato su JBoss AS7 una applicazione che usa Spring, modularizzandone le dipendenze. Portare comunque una vecchia applicazione non modulare in un ambiente modulare può incontrare dei problemi come quello risolto in questo articolo; il processo di risoluzione potrebbe non essere breve.

L'approccio più sensanto, quindi, è il seguente: per progetti web che partono da zero è sempre meglio utilizzare da subito tecnologie già pensate per essere modulari , come CDI/EJB, invece di tentare di integrare le nuove tecnologie con librerie più vecchie anche se ci sembrano "più comode"; alla lunga, questa scelta apparentemente comoda potrebbe dare più problemi che benefici.

Riferimenti

[1] Quickstarts, un sito contenente numerosi esempi

http://www.jboss.org/jdf/quickstarts/get-started/

 

[2] L'IDE JBoss Developer Studio

https://devstudio.jboss.com/download/6.x.html

 

 

 

Condividi

Pubblicato nel numero
182 marzo 2013
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…
Articoli nella stessa serie
Ti potrebbe interessare anche