JBoss 7 è uscito ormai da diverso tempo: vale la pena cominciare ad avvicinarsi e ammirare se le promesse degli sviluppatori sono state mantenute! Già da subito si rimane sbalorditi dalla velocità con cui si avvia: tutto tempo e soprattutto stress risparmiato! Tra le diverse novità, emerge anche la nuova gestione del ClassLoader che passa dalla classica versione gerarchica a quella per moduli. A fronte di maggior sicurezza e isolamento, siamo costretti a stare più attenti a come “confezioniamo” l’EAR delle nostre applicazioni: pena una bella serie di ClassNotFoundException
! Ma andiamo per gradi e cominciamo dall’inizio.
JBoss Tools
Chi ha già sviluppato su JBoss sa che per lavorare insieme ad Eclipse sono necessari i JBoss Tools che forniscono un adapter per il server e una serie di strumenti di supporto allo sviluppo. E’ possibile scaricarli e installarli direttamente dall’Eclipse MarketPlace, attendere un po’ (non pochissimo a dire il vero) e al riavvio dell’IDE ci troviamo davanti una bella paginetta con una serie di shortcut alle funzionalità principali abilitate dai tools.
Il mio primo progetto
La modularità nello sviluppo è importante, infatti JavaEE porta a lavorare proprio in quella direzione: non a caso gli “Enterprise Archive (EAR)” sono composti da diversi moduli. Normalmente, in un EAR ci aspettiamo di trovare almeno un:
- WAR (Web Archive): modulo che contiene l’interfaccia web;
- EJB-JAR (Enterprise Java Bean): modulo che implementa la logica di dominio;
- JPA-JAR (Java Persistence API): modulo che contiene le entità del dominio;
Vediamo come questa struttura si riflette in Eclipse. Cominciamo col creare un “Enterprise Application Project“: File -> New -> Enterprise Application Project:
Se non abbiamo ancora creato un server clicchiamo su “New Runtime” e puntiamo alla cartella dove risiede JBoss. Dal wizard inoltre sarà possibile creare anche i moduli principali, ovvero un nuovo progetto Web e uno EJB.
Al termine, ci troveremo nel workspace i seguenti progetti.
- MyEnterpriseApp
- MyEnterpriseAppWeb
- MyEnterpriseAppEJB
E possibile a questo punto creare un progetto EJB Client che conterrà le sole interfacce degli EJB: click destro sul progetto EJB -> Java EE Tools -> Create EJB Client Jar. Verrà creato il progetto MyEnterpriseAppEJBClient.
Adesso non ci rimane che il modulo JPA. File -> New -> JPA Project: scegliamo di chiamarlo per esempio MyEnterpriseAppJPA e aggiungiamolo all’EAR MyEnterpriseApp (come in figura): seguiamo poi il wizard e scegliamo di creare un progetto JPA Generico.
Al termine, il workspace sarà arricchito di due progetti:
- MyEnterpriseAppEJBClient
- MyEnterpriseAppJPA
Cerchiamo ora di capire come legare i cinque progetti che abbiamo creato in modo da risolvere tutte le dipendenze sia in compilazione che in esecuzione.
Legare i progetti tra di loro: Compile Time vs Run Time
Questa è la fase che di solito crea più confusione. Certe volte non è chiaro come mai in fase di scrittura del codice le dipendenze vengano risolte bene, mentre a runtime ci imbattiamo con eccezioni del tipo ClassNotFoundException
. Distinguiamo quindi dipendenze a
- Compile Time
- sono le dipendenze necessarie per compilare i progetti. Si impostano tramite la voce Build Path, che si trova tra le proprietà del progetto;
- Runtime
- sono le librerie che verranno incluse nell’EAR e che saranno necessarie all’Application Server per far girare la nostra applicazione. Da Eclipse Helios in poi, vengono gestite dalla voce Deployment Assembly delle proprietà del progetto, che sostituisce la vecchia Java EE Module Dependencies (effettivamente meno chiara).
Modulo EJB
Cominciamo quindi dal modulo EJB Client: Click destro sul progetto -> Build Path… -> Configure Build Path. Compariranno una serie di tab:
- nel tab “Projects” aggiungiamo il progetto JPA (MyEnterpriseAppJPA);
- spostiamoci sul tab “Order and Export” e selezioniamo il progetto appena aggiunto: da adesso in poi, ogni progetto che importerà EJB Client, farà anche automaticamente riferimento al progetto JPA in compile time.
Modulo Web
Passiamo adesso a considerare la parte web. Per accedere alla logica di dominio che implementeremo negli EJB, basta legare il modulo Web a quello EJB Client, in modo da separarlo dalle implementazioni: questa configurazione permette di rendere le implementazioni degli EJB, siano essi remoti o locali, trasparenti al modulo web. Il Build Path sarà:
Per quanto detto prima, il modulo Web risolverà automaticamente anche le dipendenze con il modulo JPA.
Archivio Enterprise
Non ci resta che vedere come organizzare i moduli all’interno dell’EAR: controlliamo il suo Deployment Assembly dalle proprietà del progetto:
Dovrebbe apparire così come in figura: tutti i moduli a livello di root, ad eccezione di quello JPA che viene archiviato nella cartella lib.
Primi preparativi
Ammettiamo che la nostra prima applicazione sia un gestionale di ordini: avremo bisogno per esempio dell’entità “ordine” da creare nel progetto JPA:
@Entity @Table(name="orders") public class Order implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String description; //Getters and Setters }
e di un EJB capace di salvare un ordine e di recuperarlo in base al suo id
@Stateless public class OrderEJB implements OrderEJBLocal { @PersistenceContext(unitName = "MyEnterpriseAppJPA") private EntityManager entityManager; @Override public Order findById(long id) { return this.entityManager.find(Order.class, id); } @Override public void save(Order order) { this.entityManager.persist(order); } }
molto molto semplici a scopo di test. Ricordiamo che l’interfaccia OrderEJBLocal
sta nel progetto EJB Client. Se lasciate creare gli EJB al wizard di Eclipse, ci penserà lui a dividere le classi tra i progetti EJB in modo opportuno.
Per la parte web infine, basta una semplice pagina (JSF per esempio) di test con pochi campi necessari a creare l’ordine.
Ultimi preparativi: un persistence.xml tipo il seguente
jdbc/MySQL
e il JDBC driver (MySQL in questo esempio) da copiare nella cartella di deploy del server (eh si, proprio accanto al vostro EAR pubblicato…). Non dimentichiamoci infine del Data Source! E’ facilissimo crearlo dalla console di amministrazione del server (di default su localhost:8080 a server attivo): basta selezionare il driver (che comparirà tra quelli disponibili una volta copiato nella cartella di deploy), dare un nome JNDI (jdbc/MySQL in questo caso) e poche altre informazioni!
Si parte… o quasi: problemi di ClassLoader!
A questo punto siamo pronti per partire: avviamo il server, deployamo l’applicazione e… ecco la prima sorpresa!! L’entità Order
non viene risolta dal modulo EJB Client durante l’avvio!!!!
Colpa del classloader… Come avevamo accennato all’inizio, JBoss 7 non implementa più un classloader gerarchico, ma diviso per moduli: l’eccezione è il chiaro sintomo che il modulo EJB Client e quello JPA sono stati caricati da due classloader differenti! Visto che fino a JBoss 6 questa configurazione funzionava correttamente come fare?! Dopo aver approfondito un po’ l’argomento, prima di cominciare a sviluppare è bene tenere presente alcuni concetti base:
- All’interno dell’EAR, ogni modulo è caricato con un classloader differente.
- All’interno dell’EAR, il/i moduli Web hanno accesso al/ai moduli EJB, i quali possono avere accesso tra di loro. I moduli EJB però non hanno accesso al modulo Web.
- Tutto il contenuto del WAR (ovvero WEB-INF/classes e WEB-INF/lib) è visto come modulo unico, caricato quindi con lo stesso classloader.
- Tutto il contenuto della cartella EAR/lib è caricata con lo stesso classloader.
- L’EAR mette a disposizione dei suoi moduli l’accesso alla cartella lib.
- Al momento del deploy, a seconda del tipo di modulo, viene dato accesso alle dipendenze del server (Automatic Dependencies).
Alla luce di tutto ciò, torniamo a considerare il Deployment Assembly dell’EAR visto in precedenza: per come sono impostati, è evidente che EJB Client e JPA hanno classloader diverso! Come mai però il modulo JPA è finito nella cartella lib?! Controlliamo le facets del progetto: click destro sul progetto JPA -> Properties -> Project Facets:
Tra le varie cose, troviamo il check su “Utility Module“: il progetto sarà quindi referenziabile dai moduli all’interno dell’EAR. E’ per questo che viene automaticamente inserito nella cartella lib! Per risolvere il problema, allora basta marcare come “Utility Module” anche il progetto EJB Client in modo che venga messo nella cartella lib e venga caricato con lo stesso classloader del progetto JPA. Andiamo a controllare le sue facets e tristemente scopriamo che il check ce l’ha già 🙁
Eppure è strano che non sia stato messo nella cartella lib… Non è che ci ha giocato un brutto scherzo Eclipse? Torniamo nel Deployment Assembly dell’EAR e facciamo la prima cosa che un informatico farebbe: rimuoviamo il progetto EJB Client e rimettiamolo: come per magia adesso verrà messo nella cartella lib!!!
Della serie quando il vecchio trucco spegni e riaccendi funziona sempre…
Pronti? Avviamo nuovamente il server e questa volta vedremo che le dipendenze verranno risolte correttamente!
Pingback: ()