4. Applicazione di esempio – 02: rdvmedecins-jsf2-spring
Ora ci proponiamo di trasferire l'applicazione precedente in un ambiente Spring / Tomcat:
![]() |
Si tratta effettivamente di un porting. Partiremo dall’applicazione precedente e la adatteremo al nuovo ambiente. Commenteremo solo le modifiche. Esse sono di tre tipi:
- il server non è più Glassfish ma Tomcat, un server leggero che non dispone di un contenitore EJB,
- per sostituire i EJB, useremo Spring, il principale concorrente dei EJB e [http://www.springsource.com/],
- l'implementazione JPA utilizzata sarà Hibernate al posto di EclipseLink.
Poiché effettueremo molte operazioni di copia/incolla tra il vecchio progetto e quello nuovo, manterremo aperti i progetti precedenti in NetBeans:
![]() |
L’utilizzo del framework Spring richiede alcune conoscenze che si trovano in [ref7] (cfr. pagina 166).
4.1. I livelli [DAO] e [JPA]
![]() |
4.1.1. Il progetto NetBeans
Creiamo un progetto Maven di tipo [Java Application]:
![]() | ![]() | ![]() |
![]() |
- in [1], il progetto creato,
- in [2], lo stesso progetto senza i pacchetti di [Source Packages] e [Test Packages] e senza la dipendenza [junit-3.8.1].
La parte più difficile nei progetti Maven è individuare le dipendenze corrette. Per questo progetto Spring / JPA / Hibernate, sono le seguenti:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-rdvmedecins-spring-dao-jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-rdvmedecins-spring-dao-jpa</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibernate.java-persistence</groupId>
<artifactId>jpa-api</artifactId>
<version>2.0.Beta-20090815</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
</dependencies>
</project>
- righe 18-29: per Hibernate,
- righe 30-34: per il driver JDBC di MySQL,
- righe 35-41: per il test JUnit,
- righe 42-51: per il pool di connessioni Apache Commons DBCP. Un pool di connessioni è un insieme di connessioni aperte. Quando l'applicazione ha bisogno di una connessione, la richiede al pool. Quando non ne ha più bisogno, la restituisce. Le connessioni vengono aperte all’avvio dell’applicazione e rimangono aperte per tutta la durata dell’applicazione. Ciò evita il costo delle ripetute aperture e chiusure delle connessioni. Questo tipo di pool esisteva già in Glassfish, ma il suo utilizzo era trasparente per noi. Sarà così anche in questo caso, ma dobbiamo installarlo e configurarlo,
- righe 52-75: per Spring.
Aggiungiamo queste dipendenze e compiliamo il progetto:
![]() |
- in [1]; la compilazione del progetto costringerà Maven a scaricare le dipendenze,
- in [2], queste appariranno quindi nel ramo [Dependencies]. Sono molto numerose, poiché i framework Hibernate e Spring hanno a loro volta moltissime dipendenze. Anche in questo caso, grazie a Maven, non dobbiamo preoccuparci di queste ultime. Vengono scaricate automaticamente.
Ora che disponiamo delle dipendenze, incolliamo il codice del progetto EJB del livello [dao] nel progetto Spring del livello [dao]:
![]() |
- in [1], si copia nel progetto di origine,
- in [2], si incolla nel progetto di destinazione,
- in [3], il risultato.
Una volta effettuata la copia, è necessario correggere gli errori.
4.1.2. Il pacchetto [exceptions]
![]() |
La classe [RdvMedecinsExceptions] [1] presenta degli errori a causa del pacchetto [javax] alla riga 4, che non esiste più. Si tratta di un pacchetto specifico di EJB. L'errore alla riga 6 deriva da quello alla riga 4. Si eliminano entrambe le righe. In questo modo si eliminano gli errori relativi a [2].
4.1.3. Il pacchetto [jpa]
![]() |
- in [1], la classe [Creneau] presenta un errore a causa della mancanza del pacchetto di validazione della riga [5]. Si sarebbe potuto aggiungere questo pacchetto nelle dipendenze del progetto. Tuttavia, durante i test, Hibernate genera un'eccezione a causa di esso. Poiché non è indispensabile per la nostra applicazione, lo abbiamo rimosso. Per correggere la classe, è sufficiente eliminare tutte le righe errate [2]. Si procede in questo modo per tutte le classi errate.
4.1.4. Il pacchetto [dao]
Siamo giunti al punto seguente:
![]() |
- in [1], i due pacchetti corretti,
- in [2], il pacchetto [dao]. Poiché non esiste più EJB, non esiste più nemmeno il concetto di interfaccia remota e locale di EJB. Li eliminiamo [3].
![]() |
- in [1], gli errori della classe [DaoJpa] hanno due cause:
- l’importazione di un pacchetto collegato a EJB (righe 6-8);
- l'utilizzo delle interfacce locale e remota che abbiamo appena rimosso.
Eliminiamo le righe errate e utilizziamo l'interfaccia [IDao] al posto delle interfacce locale e remota [2].
![]() |
Nel progetto EJB, la classe [DaoJpa] era un singleton e i suoi metodi venivano eseguiti all’interno di una transazione. Vedremo che la classe [DaoJpa] sarà un bean gestito da Spring. Per impostazione predefinita, ogni bean Spring è un singleton. Questo per quanto riguarda la prima proprietà. La seconda si ottiene con l’annotazione @Transactional di Spring [3]:
![]() |
A questo punto, il progetto non presenta più errori [4].
4.1.5. Configurazione del livello [JPA]
Nel progetto EJB avevamo configurato il livello [JPA] con il file [persistence.xml]. Qui abbiamo un livello [JPA] e quindi dobbiamo creare questo file. Nel progetto EJB lo avevamo generato con Glassfish. Qui lo creiamo manualmente. Il motivo principale è che parte della configurazione del file [persistence.xml] viene trasferita nel file di configurazione di Spring stesso.
Creiamo il file [persistence.xml]:
![]() |
con il seguente contenuto:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="spring-dao-jpa-hibernate-mysqlPU" transaction-type="RESOURCE_LOCAL">
<class>rdvmedecins.jpa.Client</class>
<class>rdvmedecins.jpa.Creneau</class>
<class>rdvmedecins.jpa.Medecin</class>
<class>rdvmedecins.jpa.Rv</class>
</persistence-unit>
</persistence>
- riga 3: si assegna un nome all'unità di persistenza,
- riga 3: il tipo di transazione è RESOURCE_LOCAL. Nel progetto EJB era JTA per indicare che le transazioni erano gestite dal contenitore EJB. Il valore RESOURCE_LOCAL indica che l’applicazione gestisce autonomamente le proprie transazioni. In questo caso ciò avverrà tramite Spring,
- righe 4-7: i nomi completi delle quattro entità JPA. È facoltativo poiché Hibernate le cerca automaticamente nel ClassPath del progetto.
Questo è tutto. Il nome del provider JPA, le sue proprietà e le caratteristiche JDBC della fonte dati sono ora presenti nel file di configurazione di Spring.
4.1.6. Il file di configurazione di Spring
Abbiamo detto che la classe [DaoJpa] è un bean gestito da Spring. Ciò avviene tramite un file di configurazione. Questo file conterrà anche la configurazione dell’accesso al database e la gestione delle transazioni. Deve trovarsi nel ClassPath del progetto. Lo inseriamo nel ramo [Other sources]:
![]() |
Il file [spring-config-dao.xml] è il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- livelli applicativi -->
<bean id="dao" class=" " />
<!-- EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<!--
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
-->
</bean>
</property>
</bean>
<!-- la fonte dei dati DBCP -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/dbrdvmedecins2" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<!-- gestore delle transazioni -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- traduzione delle eccezioni -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- persistenza -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
Si tratta di un file compatibile con Spring 2.x. Non abbiamo cercato di utilizzare le nuove funzionalità delle versioni 3.x.
- righe 2-4: il tag radice <beans> del file di configurazione. Non commentiamo i vari attributi di questo tag. Si raccomanda di effettuare un copia/incolla perché un errore in uno di questi attributi provoca errori talvolta difficili da comprendere,
- riga 7: il bean "dao" è un riferimento a un'istanza della classe [rdvmedecins.dao.DaoJpa]. Verrà creata un'unica istanza (singleton) che implementerà il livello [dao] dell'applicazione,
- righe 24-29: viene definita una fonte di dati. Essa fornisce il servizio di “pool di connessioni” di cui abbiamo parlato. Qui viene utilizzato [DBCP] del progetto Apache Commons DBCP [http://jakarta.apache.org/commons/dbcp/],
- righe 25-28: per creare connessioni con il database di destinazione, la fonte dati deve conoscere il driver JDBC utilizzato (riga 25), il nome del database URL (riga 26), l'utente della connessione e la sua password (righe 27-28),
- righe 10-21: configurano il livello JPA,
- riga 10: definisce un bean di tipo [EntityManagerFactory] in grado di creare oggetti di tipo [EntityManager] per gestire i contesti di persistenza. La classe istanziata [LocalContainerEntityManagerFactoryBean] è fornita da Spring. Per essere istanziata, necessita di una serie di parametri, definiti nelle righe 11-20,
- riga 11: la fonte dati da utilizzare per ottenere connessioni a SGBD. Si tratta della fonte [DBCP] definita alle righe 24-29,
- righe 12-20: l'implementazione JPA da utilizzare,
- riga 13: definisce Hibernate come implementazione JPA da utilizzare,
- riga 14: il dialetto SQL che Hibernate deve utilizzare con il target SGBD, in questo caso MySQL5,
- riga 16 (commentata): richiede che i comandi SQL eseguiti da Hibernate vengano registrati nella console,
- riga 17 (commentata): richiede che all’avvio dell’applicazione venga generato il database (drop e create),
- riga 32: indica che le transazioni sono gestite con annotazioni Java (avrebbero potuto essere dichiarate anche in spring-config.xml). Si tratta in particolare dell'annotazione @Transactional presente nella classe [DaoJpa],
- righe 33-35: definiscono il gestore di transazioni da utilizzare,
- riga 33: il gestore delle transazioni è una classe fornita da Spring,
- riga 34: il gestore delle transazioni di Spring deve conoscere la classe EntityManagerFactory che gestisce il livello JPA. Si tratta di quella definita alle righe 10-21,
- riga 41: definisce la classe che gestisce le annotazioni di persistenza di Spring,
- riga 38: definisce la classe Spring che gestisce, in particolare, l'annotazione @Repository, la quale rende una classe così annotata idonea alla conversione delle eccezioni native del driver JDBC da SGBD in eccezioni generiche Spring di tipo [DataAccessException]. Questa conversione incapsula l’eccezione nativa JDBC in un tipo [DataAccessException] che presenta diverse sottoclassi:

Questa traduzione consente al programma client di gestire le eccezioni in modo generico, indipendentemente dal SGBD di destinazione. Nel nostro codice Java non abbiamo utilizzato l’annotazione @Repository. Pertanto, la riga 38 è superflua. L’abbiamo lasciata solo a scopo informativo.
Abbiamo terminato con il file di configurazione di Spring. È stato tratto dalla documentazione di Spring. Il suo adattamento a diverse situazioni si riduce spesso a due modifiche:
- quella relativa al database di destinazione: righe 24-29,
- quella dell'implementazione JPA: righe 12-20.
All'esecuzione del codice, tutti i bean del file di configurazione verranno istanziati. Vedremo come.
4.1.7. La classe di test JUnit
![]() |
Avevamo testato il livello [DAO] del progetto EJB con un test JUnit. Facciamo lo stesso per il livello [DAO] del progetto Spring:
- nei progetti [1] e [2], copiando e incollando il test JUnit tra i due progetti,
- in [3], il test importato presenta degli errori nel nuovo ambiente.
![]() |
L'errore segnalato [1] riguarda l'interfaccia remota di EJB, che non esiste più. Inoltre, il codice di inizializzazione del campo [dao] alla riga 19 era una chiamata JNDI specifica per EJB (righe 25-28). Per istanziare il campo [dao] della riga 19, dobbiamo utilizzare il file di configurazione di Spring. Ciò avviene nel modo seguente:
![]() |
- riga 21: il tipo dell’interfaccia è diventato [IDao],
- riga 28: istanzia tutti i bean dichiarati nel file [spring-config-dao.xml], in particolare questo:
<bean id="dao" class="rdvmedecins.dao.DaoJpa" />
- la riga 29 richiede al contesto Spring della riga 28 un riferimento al bean con id="dao". Si ottiene quindi un riferimento al singleton [DaoJpa] (class sopra) che Spring ha istanziato.
Le righe 28-29 costruiscono i seguenti blocchi (linee tratteggiate rosa):
![]() |
Quando vengono eseguiti i test del client JUnit, il livello [DAO] è stato istanziato. È quindi possibile testarne i metodi. Si noti che non è necessario alcun server per eseguire questo test, a differenza del test su EJB [DAO], che aveva richiesto il server Glassfish. In questo caso, tutto viene eseguito all’interno dello stesso JVM.
Ora è possibile eseguire il test JUnit. È necessario che il server MySQL sia in esecuzione. I risultati sono i seguenti:
![]() |
Il test JUnit è andato a buon fine. Esaminiamo i log del test come è stato fatto durante il test di EJB:
- righe 1-4: log di Spring,
- righe 5-10: log di Hibernate,
- riga 11: Spring segnala tutti i bean che ha istanziato. Il primo che troviamo è il bean [dao],
- righe 12 e seguenti: i log del test JUnit,
- righe 60-65: si vede chiaramente l’eccezione causata dall’aggiunta di un appuntamento già presente nel database. Ricordiamo che con il test EJB non si era verificata questa eccezione a causa di un problema di serializzazione.
Il livello [dao] è operativo. Ora stiamo realizzando il livello [métier].
4.2. Il livello [métier]
![]() |
Procediamo allo stesso modo del livello [DAO], copiando e incollando dal progetto EJB al progetto Spring.
4.2.1. Il progetto NetBeans
Creiamo un nuovo progetto Maven di tipo [Java Application], ripulito da tutto ciò che non vogliamo conservare ([1]):
![]() |
4.2.2. Le dipendenze del progetto
Nell’architettura:
![]() |
il livello [métier] si basa sul livello [dao]. Aggiungiamo quindi una dipendenza dal progetto precedente:
![]() |
- in [1] e [2], si aggiunge una dipendenza dal progetto del livello [dao],
- in [3], questa dipendenza ha comportato altre dipendenze, quelle del progetto del livello [dao].
![]() |
- in [1] e [2], si copiano i sorgenti Java del progetto EJB nel progetto Spring,
- in [3]; i sorgenti importati presentano degli errori nel nuovo ambiente.
Si inizia eliminando le interfacce remote e locali del livello [métier] che non esistono più in [4]:
![]() |
- in [5], gli errori della classe [Metier] hanno diverse cause:
- l'utilizzo del pacchetto [javax.ejb], che non esiste più;
- l'utilizzo dell'interfaccia [IDaoLocal], che non esiste più;
- l'utilizzo delle interfacce [IMetierRemote] e [IMetierLocal], che non esistono più.
Noi
- eliminiamo tutte le righe errate relative al pacchetto [javax.ejb],
- sostituiamo l'interfaccia [IDaoLocal] con l'interfaccia [IDao],
- sostituiamo le interfacce [IMetierRemote] e [IMetierLocal] con l'interfaccia [IMetier].
![]() |
- in [6], la classe così corretta,
- In [7] non ci sono più errori.
Abbiamo eliminato i riferimenti a EJB, ma ora dobbiamo recuperare le relative proprietà:
![]() |
- riga 22: avevamo un singleton. Questa caratteristica si otterrà trasformando la classe in un bean gestito da Spring,
- riga 23: ogni metodo veniva eseguito all’interno di una transazione. Ciò si otterrà con l’annotazione Spring @Transactional,
- righe 27-28: il riferimento al livello [DAO] veniva ottenuto tramite iniezione dal contenitore EJB. Utilizzeremo un'iniezione Spring.
Il codice della classe [Metier] del progetto Spring viene quindi modificato come segue:
![]() |
Questo è tutto per quanto riguarda il codice Java. Il resto avviene nel file di configurazione di Spring.
4.2.3. Il file di configurazione di Spring
Copiamo il file di configurazione di Spring dal progetto del livello [DAO] nel progetto del livello [métier]. Iniziamo creando il ramo [Other Resources] nel progetto del livello [métier], se non esiste:
![]() |
- in [1], nella scheda [Files], si crea una sottocartella della cartella [main],
- in [2], deve chiamarsi [resources],
- in [3], nella scheda [Projects], è stato creato il ramo [Other Sources].
Possiamo ora procedere al copia/incolla del file di configurazione di Spring:
![]() |
- in [1] si copia il file dal progetto [DAO] nel progetto [métier] [2],
- in [3], il file copiato.
Il file di configurazione che è stato copiato configura il livello [DAO]. Aggiungiamo un bean per configurare il livello [métier]:
- riga 2: il bean del livello [DAO],
- righe 3-5: il bean del livello [métier],
- riga 3: il bean si chiama métier (attributo id) ed è un'istanza della classe [rdvmedecins.metier.service.Metier] (attributo class). Questo bean verrà istanziato come gli altri all'avvio dell'applicazione.
Ricordiamo il codice del bean [rdvmedecins.metier.service.Metier]:
package rdvmedecins.metier.service;
...
public class Metier implements IMetier, Serializable {
// livello DAO
private IDao dao;
public Metier() {
}
- riga 8: il campo [dao] verrà istanziato da Spring contemporaneamente al bean métier. Torniamo alla definizione di questo bean nel file di configurazione di Spring:
- riga 4: il tag <property> serve a inizializzare i campi del bean istanziato. Il nome del campo è indicato dall'attributo name. Sarà quindi il campo dao della classe [rdvmedecins.metier.service.Metier] a essere istanziato. Ciò avverrà tramite un metodo setDao che deve esistere. Il valore che gli verrà assegnato è quello dell’attributo ref. Tale valore è, in questo caso, il riferimento al bean dao della riga 2.
In parole più semplici, nel codice:
package rdvmedecins.metier.service;
...
public class Metier implements IMetier, Serializable {
// livello DAO
private IDao dao;
public Metier() {
}
Il campo dao della riga 19 verrà inizializzato da Spring con un riferimento al livello [dao]. È proprio quello che volevamo. Il campo dao verrà inizializzato da Spring tramite un setter che dobbiamo aggiungere:
// setter
public void setDao(IDao dao) {
this.dao = dao;
}
Rinominiamo il file di configurazione di Spring per tenere conto delle modifiche:
![]() |
Ora siamo pronti per un test. Riprendiamo il test da console utilizzato per testare EJB [Metier].
4.2.4. Test del livello [métier]
Il test verrà eseguito con la seguente architettura:
![]() |
Copiamo il test da console del progetto EJB nel progetto Spring:
![]() |
- in [1] e [2], il copia/incolla tra i due progetti,
- in [3], il codice importato presenta degli errori.
![]() |
Il codice importato presenta due tipi di errore:
- riga 13: l'interfaccia [IMetierRemote] è stata sostituita dall'interfaccia [IMetier],
- righe 24-27: l'istanziazione del livello [métier] non avviene più tramite una chiamata a JNDI, ma tramite l'istanziazione dei bean presenti nel file di configurazione di Spring.
Corregiamo questi due punti:
![]() |
- riga 22: viene utilizzato il file [spring-config-metier-dao.xml]. Tutti i bean di questo file vengono quindi istanziati. Tra questi vi sono i seguenti:
<!-- livelli applicativi -->
<bean id="dao" class="rdvmedecins.dao.DaoJpa" />
<bean id="metier" class="rdvmedecins.metier.service.Metier">
<property name="dao" ref="dao"/>
</bean>
Questi due bean rappresentano i livelli [DAO] e [métier] dell’architettura di test:
![]() |
A questo punto, il test può essere eseguito:
![]() |
I log del test sono quindi i seguenti:
- righe 1-4: i log di Spring e Hibernate,
- riga 5: i bean istanziati da Spring. Si notino i bean DAO e di business,
- righe 6-53: i log del test. Sono conformi a quanto ottenuto con il test del progetto EJB. Si rimanda il lettore ai commenti relativi a tale test (paragrafo 3.5.3).
Abbiamo realizzato il livello [métier]. Passiamo ora all’ultimo livello, il livello [web].
4.3. Il livello [web]
![]() |
Per creare il livello [web], procederemo allo stesso modo degli altri due livelli, copiando e incollando dal livello [web] del progetto EJB.
4.3.1. Il progetto NetBeans
Per prima cosa creiamo un progetto web:
![]() |
- in [1], creiamo un nuovo progetto,
- in [2], un progetto Maven di tipo [Web Application],
- in [3], gli diamo un nome,
![]() |
- in [4], questa volta si sceglie il server Tomcat anziché Glassfish, utilizzato per il progetto EJB,
- in [5], il progetto ottenuto,
- in [6], il progetto dopo l’eliminazione di [index.jsp] e del pacchetto di [Source Packages].
4.3.2. Le dipendenze del progetto
Diamo un'occhiata all'architettura del progetto:
![]() |
Il livello [web] richiede i livelli [métier], [DAO] e [JPA]. Questi fanno parte dei due progetti che abbiamo appena creato. Da qui deriva una dipendenza da ciascuno di questi progetti:
![]() |
- in [1], aggiungiamo la dipendenza dal progetto Spring / business,
- in [2], è stato aggiunto il progetto Spring / business. Poiché esso stesso presentava una dipendenza dal progetto Spring / DAO / JPA, quest’ultimo è stato automaticamente aggiunto alle dipendenze di [3].
Torniamo alla struttura della nostra applicazione:
![]() |
Il livello web è un livello JSF. Abbiamo quindi bisogno delle librerie di Java Server Faces. Il server Tomcat non le possiede. La dipendenza non avrà quindi l’ambito (scope) [provided], come era stato con il server Glassfish, ma l’ambito [compile], che è l’ambito predefinito quando non se ne specifica uno.
Aggiungiamo queste dipendenze direttamente nel codice di [pom.xml]:
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-rdvmedecins-spring-metier</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
- le righe 7-16 sono state aggiunte al file [pom.xml]. Si tratta delle dipendenze relative a JSF. Sono quelle utilizzate nel progetto EJB / Glassfish. Si noti che non presentano il tag <scope>. Di conseguenza, hanno per impostazione predefinita l’ambito [compile]. La libreria JSF verrà quindi inclusa nell’archivio [war] del progetto web.
Dopo aver aggiunto queste dipendenze al file [pom.xml], compiliamo il progetto affinché vengano scaricate.
4.3.3. Trasferimento del progetto JSF / Glassfish al progetto JSF / Tomcat
Copiamo l’intero codice dal progetto JSF / Glassfish al progetto JSF / Tomcat:
![]() |
- [1, 2, 3]: copia delle pagine web dal vecchio progetto a quello nuovo,
![]() |
- [1, 2, 3]: copia dei codici Java dal vecchio progetto al nuovo. Ci sono degli errori. È normale. Li correggeremo,
![]() |
- in [1], nella scheda [Files] di NetBeans, si crea una sottocartella [resources] all'interno della cartella [main],
- questo crea, nella scheda [Projects], il ramo [Other Sources] [3],
![]() |
- [1, 2, 3]: si copiano i file dei messaggi dal vecchio progetto al nuovo progetto.
4.3.4. Modifiche al progetto importato
Abbiamo segnalato che il codice Java importato presentava degli errori. Esaminiamoli:
![]() |
- in [1], solo il bean [Application] è errato,
- in [2], l’errore è dovuto esclusivamente all’interfaccia [IMetierLocal] che non esiste più. In questo caso, può sorprendere che la riga 20 non venga segnalata come errata. L'annotazione @EJB fa esplicito riferimento a EJB e in questo caso viene riconosciuta. Ciò è dovuto alla presenza della dipendenza [javaee-web-api-6.0] [3]. Java EE 6 ha introdotto un'architettura che consente di distribuire un'applicazione web basata su EJB senza interfaccia remota, su server privi di un container EJB. È sufficiente che il server fornisca la dipendenza [javaee-web-api-6.0]. Si nota infatti che questa ha l’ambito [provided] [3].
In questo caso non utilizzeremo la dipendenza [javaee-web-api-6.0]. La eliminiamo [1]:
![]() |
![]() |
Questo genera nuovi errori [2]. Inizieremo con quelli relativi al bean [Form]:
![]() |
- in [1], le righe errate sono legate alla mancanza del pacchetto [javax]. Le eliminiamo tutte da [2]. Le righe errate trasformavano la classe [Form] in un bean con ambito di sessione (righe 18-20 di [1]). Inoltre, il bean [Application] veniva iniettato alla riga 25. Queste informazioni verranno trasferite nel file di configurazione di JSF e [faces-config.xml].
Passiamo ora al bean [Application]:
![]() |
Si eliminano tutte le righe errate da [1] e si modifica l'interfaccia da [IMetierLocal] a [IMetier] nelle righe 13 e 21. In [2] non ci sono più errori. In [1], abbiamo eliminato le righe 15-16 che definivano la classe [Application] come bean con ambito application.. Questa informazione verrà trasferita nel file di configurazione di JSF [faces-config.xml]. Abbiamo inoltre eliminato la riga 20 che inseriva un riferimento al livello [métier] nel bean. Ora questo verrà inizializzato da Spring. Abbiamo già il file di configurazione necessario: è quello del progetto Spring / Métier. Lo copiamo:
![]() |
- in [1, 2]; copiamo il file di configurazione di Spring dal progetto Spring / Métier al progetto Spring / JSF,
![]() |
In [3], ecco il risultato.
Nel bean [Application], è necessario utilizzare questo file di configurazione per ottenere un riferimento al livello [métier]. Ciò avviene nel suo metodo [init]:
package beans;
...
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application {
// livello di business
private IMetier metier;
...
public Application() {
}
@PostConstruct
public void init() {
try {
// istanza del livello [métier]
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-metier-dao.xml");
metier = (IMetier) ctx.getBean("metier");
// si memorizzano nella cache medici e clienti
...
} catch (Throwable th) {
...
}
...
}
- riga 20: vengono istanziati i bean del file di configurazione di Spring,
- riga 21: si richiede un riferimento al bean di business, quindi al livello [métier].
In generale, l’istanziazione dei bean di Spring va effettuata nel metodo init del bean con ambito applicativo. Esiste un altro metodo in cui l’istanziazione dei bean viene effettuata da un servlet di Spring. Ciò comporta la modifica del file [web.xml] e l’aggiunta di una dipendenza dall’artefatto [spring-web]. In questo caso non l’abbiamo fatto per rimanere in linea con quanto utilizzato nei codici precedenti.
Abbiamo rimosso le annotazioni nelle classi [Application] e [Form] che le definivano come bean JSF. Queste classi devono rimanere bean JSF. Al posto delle annotazioni, si utilizza quindi il file di configurazione di JSF [WEB-INF / faces.config.xml] per dichiarare i bean.
![]() |
Questo file è ora il seguente:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<!-- il file dei messaggi -->
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
<!-- il bean applicationBean -->
<managed-bean>
<managed-bean-name>applicationBean</managed-bean-name>
<managed-bean-class>beans.Application</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<!-- il bean del modulo -->
<managed-bean>
<managed-bean-name>form</managed-bean-name>
<managed-bean-class>beans.Form</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>application</property-name>
<value>#{applicationBean}</value>
</managed-property>
</managed-bean>
</faces-config>
- le righe 10-19 configurano il file dei messaggi. Era l'unica configurazione presente nel progetto JSF / EJB,
- le righe 21-35 dichiarano i bean dell'applicazione JSF. Questo era il metodo standard con JSF 1.x. JSF 2 ha introdotto le annotazioni, ma il metodo di JSF e 1.x è ancora supportato,
- righe 21-25: dichiarano il bean applicationBean,
- riga 22: il nome del bean. Si potrebbe essere tentati di utilizzare il nome «application». Da evitare poiché è il nome di un bean predefinito di JSF,
- riga 23: il nome completo della classe del bean,
- riga 24: il suo ambito,
- righe 27-35: definiscono il bean form,
- riga 28: il nome del bean,
- riga 29: il nome completo della classe del bean,
- riga 30: il suo ambito,
- righe 31-34: definiscono una proprietà della classe [beans.Form],
- riga 32: nome della proprietà. La classe [beans.Form] deve avere un campo con questo nome e il relativo setter,
- riga 33: il valore del campo. In questo caso si tratta del riferimento al bean applicationBean definito alla riga 21. Si sta quindi effettuando l’iniezione del bean con ambito application nel bean con ambito session affinché quest’ultimo abbia accesso ai dati con ambito application.
Abbiamo detto in precedenza che il campo [application] del bean [beans.Form] sarebbe stato inizializzato tramite un setter. È quindi necessario aggiungerlo alla classe [beans.Form] se non esiste già:
public void setApplication(Application application) {
this.application = application;
}
4.3.5. Test dell'applicazione
La nostra applicazione è ora priva di errori e pronta per i test:
![]() |
- in [1], il progetto corretto,
- in [2], lo si compila,
- in [3], lo si esegue. È necessario che SGBD e MySQL siano avviati. Il server Tomcat verrà quindi avviato ([4]) se non lo era già, dopodiché verrà visualizzata la pagina iniziale dell’applicazione ([5]):
![]() |
A questo punto, ritroviamo l’applicazione in esame. Lasciamo che sia il lettore a verificare che funzioni. Ora arrestiamo l’applicazione:
![]() |
- in [1], l'applicazione viene scaricata,
- in [2], non è più presente.
Diamo ora un’occhiata ai log di di Tomcat:
Le righe 2 e 4 segnalano un malfunzionamento all’arresto dell’applicazione. La riga 4 indica che esiste un probabile rischio di perdita di memoria. Effettivamente, ciò si verifica e dopo un po’ NetBeans non è più utilizzabile. Questo problema è particolarmente fastidioso perché è necessario riavviare NetBeans ad ogni nuova esecuzione del progetto. Questo problema è già stato riscontrato nel documento “Introduzione a Struts 2 con esempi” [http://tahe.developpez.com/java/struts2].
Su Internet si trovano molte informazioni su questo errore. Esso si verifica quando si carica e scarica ripetutamente un’applicazione da Tomcat. Dopo un po’ compare l’errore java.lang.OutOfMemoryError: PermGen space. Sembra che non esista una soluzione per evitare questo errore quando è causato da archivi di terze parti (jar), come in questo caso. È quindi necessario riavviare Tomcat per eliminarlo.
È tuttavia possibile ritardare il verificarsi di questo errore. Innanzitutto, si aumenta lo spazio della memoria che ha subito un overflow.
![]() |
- in [1], si accede alle proprietà del server Tomcat,
- in [2], nella scheda [Platform], si imposta il valore della memoria che va in overflow. In questo caso, abbiamo impostato 1 GB perché la memoria totale era di 8 GB. Con una memoria più piccola, si potrà impostare 512M (512 megabyte).
Successivamente, si inserisce il driver JDBC da MySQL in <tomcat>/lib, dove <tomcat> è la directory di installazione di Tomcat.
![]() |
- in [1], nelle proprietà di Tomcat, si annota la sua directory di installazione <tomcat>,
- in <tomcat>/lib [2], si inserisce un driver JDBC aggiornato rispetto a MySQL e [3].
Successivamente, si rimuove la dipendenza che il progetto aveva dal driver JDBC rispetto a MySQL e [4].
![]() |
Fatto ciò, si testa l’applicazione. Si constata che è possibile eseguire ripetuti caricamenti e scaricamenti dell’applicazione. I problemi di perdita di memoria, tuttavia, non sono risolti. Si verificano semplicemente più tardi.
4.4. Conclusion
Abbiamo trasferito l’applicazione JSF / EJB / Glassfish in un ambiente JSF / Spring / Tomcat. Ciò è avvenuto essenzialmente tramite copia e incolla tra i due progetti. Ciò è stato possibile perché le tecnologie Spring e EJB3 presentano forti somiglianze. EJB3 è stato infatti creato dopo che Spring si è dimostrato più performante rispetto a EJB2. EJB3 ha quindi ripreso le idee migliori di Spring.
4.5. I test con Eclipse
![]() |
- in [1], si importano i tre progetti Spring,
- in [2], si seleziona il test JUnit del livello [DAO] e lo si esegue in [3],
![]() |
![]() |
- in [4], il test ha esito positivo,
- in [5], i log della console.
![]() |
- in [6A] [6B], si esegue il client della console del livello [métier],
- in [7], la schermata della console ottenuta,
![]() |
- in [8] [9], si esegue il progetto web su un server Tomcat 7 [10],
![]() |
- e [11], la pagina iniziale dell'applicazione viene visualizzata nel browser interno di Eclipse.





































































