8. Versione 4 – Client/Server in un'architettura di servizi web
In questa nuova versione, l'applicazione [Pam] funzionerà in modalità client/server all'interno di un'architettura di servizi web. Rivediamo l'architettura dell'applicazione precedente:
![]() |
In precedenza, un livello di comunicazione [C, RMI, S] consentiva una comunicazione trasparente tra il client [ui] e il livello remoto [business]. Utilizzeremo un'architettura simile, in cui il livello di comunicazione [C, RMI, S] sarà sostituito da un livello [C, HTTP / SOAP, S]:
![]() |
Il protocollo HTTP/SOAP presenta il vantaggio, rispetto al precedente protocollo RMI/EJB, di essere multipiattaforma. Pertanto, il servizio web può essere scritto in Java e distribuito sul server Glassfish, mentre il client potrebbe essere un client .NET o PHP.
Svilupperemo questa architettura in tre diverse modalità:
- il servizio web sarà fornito dall'EJB [Business]
- il servizio web sarà fornito da un'applicazione web che utilizza l'EJB [Business]
- il servizio web sarà fornito da un'applicazione web che utilizza Spring
Un servizio web può essere implementato in vari modi all'interno di un server Java EE:
- tramite una classe annotata con @WebService che viene eseguita in un contenitore web
![]() |
- da un EJB annotato con @WebService che viene eseguito in un contenitore EJB
![]() |
Inizieremo con quest'ultima architettura.
8.1. Servizio web implementato da un EJB
8.1.1. Il lato server
8.1.1.1. Il progetto NetBeans
Iniziamo creando un nuovo progetto Maven che sia una copia del progetto EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]:
![]() |
Nella seguente architettura:
![]() |
il livello [business] sarà il servizio web a cui si rivolgerà il livello [ui]. Questa classe non deve implementare un'interfaccia. Sono le annotazioni a trasformare un POJO (Plain Old Java Object) in un servizio web. La classe [Business], che implementa il livello [business] sopra indicato, viene trasformata come segue:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
- Riga 4: L'annotazione @WebService trasforma la classe [Business] in un servizio web. Un servizio web espone dei metodi ai propri clienti. Questi metodi devono essere annotati con l'attributo @WebMethod.
- Righe 19 e 25: i due metodi della classe [Metier] diventano metodi del servizio web.
- Riga 29: è importante che i getter e i setter vengano rimossi; altrimenti, saranno esposti nel servizio web, causando errori di sicurezza.
L'aggiunta di queste annotazioni viene rilevata da NetBeans, che quindi modifica la natura del progetto:
![]() |
In [1] è comparsa nel progetto una struttura ad albero [Servizi Web]. Essa contiene il servizio Web Metier e i suoi due metodi. L'applicazione server può essere distribuita [2]. Il server MySQL deve essere in esecuzione e il suo database [dbpam_eclipselink] deve esistere ed essere popolato. Potrebbe essere necessario rimuovere [3] in anticipo gli EJB dal progetto EJB client/server studiato in precedenza per evitare conflitti di nomi. Infatti, il nostro nuovo progetto include gli stessi EJB presenti nel progetto precedente.
![]() |
In [1], vediamo la nostra applicazione server distribuita sul server GlassFish. Una volta distribuito il servizio web, è possibile testarlo:
![]() |
- in [1], nel progetto corrente, testiamo il servizio web [Metier]
- Il servizio web è accessibile tramite diversi URL. L'URL [2] consente di testare il servizio web
- in [3], un link al file XML che definisce il servizio web. I client del servizio web devono conoscere l'URL di questo file. Il livello client (stub) del servizio web viene generato da questo file.
- In [4,5], un modulo per testare i metodi esposti dal servizio web. Questi sono presentati insieme ai loro parametri, che l'utente può definire.
Ad esempio, proviamo il metodo [findAllEmployees], che non richiede parametri:
![]() |
Sopra, testiamo il metodo. Riceviamo quindi la risposta riportata di seguito (vista parziale). Possiamo effettivamente vedere i due dipendenti con i relativi benefici. Il lettore è invitato a testare il metodo [4] allo stesso modo, passando i tre parametri previsti.

8.1.2. Il lato client
![]() |
8.1.2.1. Il progetto NetBeans del cliente -console
Ora creeremo un progetto Java di tipo [Java Application] per il lato client dell'applicazione. A giugno 2012 non era possibile creare un progetto Maven per questo client. Si verifica un errore, che sembra essere noto online ma rimane irrisolto.
![]() | ![]() |
Una volta creato il progetto, specifichiamo che sarà un client del servizio web che abbiamo appena distribuito sul server GlassFish:
![]() |
- in [2], selezioniamo il nuovo progetto e facciamo clic sul pulsante [Nuovo file]
- in [3], specifichiamo che vogliamo creare un client di servizio web
![]() |
- in [4], selezioniamo il progetto NetBeans per il servizio web
- nella finestra [5] sono elencati tutti i progetti con un ramo [Web Services]; qui, solo il progetto [mv-pam-ws-metier-dao-eclipselink].
- Un progetto può implementare più servizi web. In [6], specifichiamo il servizio web a cui vogliamo collegarci.
![]() |
- In [7] viene visualizzato l'URL che definisce il servizio web. Questo URL viene utilizzato dagli strumenti software che generano il livello client che si interfaccerà con il servizio web.
![]() |
- Il livello client [C] [1] che verrà generato è costituito da un insieme di classi Java che saranno collocate nello stesso pacchetto. Il nome di questo pacchetto viene impostato in [8].
- Una volta completata la procedura guidata di creazione del client del servizio web facendo clic sul pulsante [Fine], viene creato il livello [C] sopra descritto.
Ciò si riflette in una serie di modifiche al progetto:
- In [10] sopra, appare un albero [Generated Sources], contenente le classi del livello [C] che consentono al client [3] di comunicare con il servizio web. Questo livello permette al client [3] di comunicare con il livello [business] [4] come se fosse locale anziché remoto.
- In [11] appare un albero [Riferimenti ai servizi web], che elenca i servizi web per i quali è stato generato un livello client.
Si noti che nel livello [C] generato [10] troviamo le classi che sono state implementate sul lato server: Indemnite, Cotisation, Employe, FeuilleSalaire, ElementsSalaire, Metier. Metier è il servizio web, mentre le altre classi sono quelle richieste da tale servizio. Si potrebbe essere curiosi di esaminarne il codice. Vedremo che la definizione delle classi — che, una volta istanziate, rappresentano oggetti manipolati dal servizio — consiste nel definire i campi della classe e i relativi accessori, oltre che nell'aggiungere annotazioni che consentono la serializzazione della classe in un flusso XML. La classe Metier è diventata un'interfaccia contenente i due metodi che sono stati annotati con @WebMethod. Ciascuno di questi dà origine a due classi, ad esempio [CalculatePayroll.java] e [CalculatePayrollResponse.java], dove una incapsula la chiamata al metodo e l'altra il suo risultato. Infine, la classe MetierService è la classe che permette al client di avere un riferimento al servizio web Metier remoto:
package metier;
...
@WebService
@Stateless()
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class Metier implements IMetierLocal,IMetierRemote {
// references on layers [DAO]
@EJB
private ICotisationDaoLocal cotisationDao = null;
@EJB
private IEmployeDaoLocal employeDao=null;
@EJB
private IIndemniteDaoLocal indemniteDao=null;
// get your payslip
@WebMethod
public FeuilleSalaire calculerFeuilleSalaire(String SS,
...
}
// list of employees
@WebMethod
public List<Employe> findAllEmployes() {
...
}
// important - no getters and setters for EJB
}
Il metodo getMetierPort alla riga 2 recupera un riferimento al servizio web Metier remoto.
8.1.2.2. Il client console per il servizio web Metier
Non resta che scrivere il client per il servizio web Metier. Copiamo la classe [MainRemote] dal progetto [mv-pam-client-metier-dao-jpa-eclipselink] — che era un client di un server EJB — nel nuovo progetto.
![]() |
- In [1], la classe client del servizio web. La classe [MainRemote] contiene degli errori. Per correggerli, inizieremo rimuovendo tutte le istruzioni [import] esistenti nella classe e rigenerandole utilizzando l'opzione [Fix Imports]. Questo perché alcune delle classi utilizzate dalla classe [MainRemote] fanno ora parte del pacchetto [client] generato.
- In [3], il frammento di codice in cui viene istanziato il livello [business] [3]. Viene istanziato utilizzando il codice JNDI per ottenere un riferimento a un EJB remoto.
Aggiorniamo il codice come segue:
- il codice JNDI è stato rimosso
- Poiché la classe [PamException] non esiste sul lato client, rimuoviamo il blocco catch associato per mantenere solo il blocco catch della classe padre [Exception].
![]() |
- In [4], dobbiamo ancora ottenere un riferimento al servizio web remoto [Metier] per poter chiamare il suo metodo [calculatePayroll].
- In [5], utilizzando il mouse, trasciniamo il metodo [calculerFeuilleSalaire] dal servizio web [Metier] e lo rilasciamo in [4]. Viene generato il codice [6]. Questo codice generico può quindi essere adattato dallo sviluppatore.
![]() |
- Alla riga 112, vediamo che [calculatePayroll] è un metodo della classe [client.Metier] (riga 111). Ora che sappiamo come ottenere il livello [metier], il codice precedente può essere riscritto come segue:
La riga 7 recupera un riferimento al servizio web Metier. Una volta fatto ciò, il codice della classe rimane invariato, tranne che alla riga 10, dove non viene gestito il tipo [Exception], ma il tipo più generico Throwable, la classe padre della classe Exception. Se si verifica un'eccezione, visualizziamo tutte le sue cause annidate fino alla causa principale.
Siamo pronti per il test:
- Assicurarsi che il DBMS MySQL5 sia in esecuzione e che il database dbpam_eclipselink sia stato creato e inizializzato
- Assicurarsi che il servizio web sia distribuito sul server GlassFish
- Compilare il client (Clean e Build)
- Configurare il client per l'esecuzione
![]() |
- Esegui il client
I risultati nella console sono i seguenti:
Con la seguente configurazione:

otteniamo i seguenti risultati:
Si noti che, sebbene il servizio web [Metier] invii un'eccezione di tipo [PamException], l'eccezione ricevuta dal client è di tipo [SOAPFaultException]. Anche nella catena delle eccezioni, il tipo [PamException] non compare.
8.1.3. Il client Swing per il servizio web Metier
Compito: trasferire il client Swing dal progetto [mv-pam-client-ejb-metier-dao-jpa-eclipselink] al nuovo progetto in modo che diventi anch'esso un client del servizio web distribuito sul server GlassFish.
8.2. Servizio web implementato da un'applicazione web
Stiamo ora lavorando all'interno del seguente quadro architettonico:
![]() |
Il servizio web è fornito da un'applicazione web in esecuzione all'interno del contenitore web del server GlassFish. Questo servizio web si baserà sull'EJB [Metier], che è distribuito nel contenitore EJB3.
8.2.1. Il lato server
Creiamo un'applicazione web:
![]() |
- In [1], creiamo un nuovo progetto
- in [2], questo progetto è di tipo [Web Application]
- in [3], lo chiamiamo [mv-pam-ws-ejb-metier-dao-eclipselink]
![]() |
- in [4], selezioniamo Java EE 6
- in [6], il progetto viene creato
Nel diagramma sottostante, l'applicazione web creata verrà eseguita nel contenitore web. Utilizzerà l'EJB [Metier], che verrà distribuito nel contenitore EJB del server.
![]() |
Per garantire che l'applicazione web creata abbia accesso alle classi associate all'EJB [Metier], aggiungiamo la dipendenza per il server EJB [mv-pam-ejb-metier-dao-eclipselink] — di cui abbiamo già parlato — alle librerie dell'applicazione web [mv-pam-ws-ejb-metier-dao-eclipselink].
![]() |
- In [1], aggiungiamo un progetto alle dipendenze del progetto web;
- in [2], selezioniamo il progetto [mv-pam-ejb-metier-dao-eclipselink],
- in [3], il tipo di dipendenza è ejb,
- in [4], viene specificato l'ambito della dipendenza, il che significa che sarà fornita dall'ambiente di runtime,
- in [5], la dipendenza è stata aggiunta.
Per creare lo stesso servizio web di prima, dobbiamo:
- creare una classe annotata con @WebService
- con due metodi, calculerFeuilleSalaire e findAllEmployes, annotati con @WebMethod
Creiamo una classe [PamWsEjbMetier] nel pacchetto [pam.ws]:
![]() |
![]() |
La classe [PamWsEjbMetier] è la seguente:
- Righe 7–10: La classe importa classi dal modulo EJB [pam-serveurws-metier-dao-jpa-eclipselink], il cui progetto Maven è stato aggiunto alle dipendenze del progetto.
- riga 12: la classe è un servizio web
- riga 13: implementa l'interfaccia IMetier definita nel modulo EJB
- righe 18-19: il metodo `calculerFeuilleSalaire` viene esposto come metodo di servizio web
- Righe 23–24: il metodo `findAllEmployees` viene esposto come metodo di servizio web
- Righe 15–16: l'interfaccia locale dell'EJB [Metier] viene iniettata nel campo alla riga 16. Utilizziamo l'interfaccia locale perché l'applicazione web e il modulo EJB girano nella stessa JVM.
- Righe 20 e 25: I metodi `calculerFeuilleSalaire` e `findAllEmployees` delegano la loro elaborazione ai metodi omonimi nell'EJB [Metier]. La classe serve quindi solo a esporre i metodi dell'EJB [Metier] ai client remoti come metodi di servizio web.
In NetBeans, l'applicazione web viene riconosciuta come esposizione di un servizio web:
![]() |
Per distribuire il servizio web sul server GlassFish, è necessario distribuire entrambi:
- il modulo web nel contenitore web del server
- il modulo EJB nel contenitore EJB del server
Per farlo, dobbiamo creare un'[Applicazione Enterprise] che distribuisca entrambi i moduli contemporaneamente. A tal fine, entrambi i progetti devono essere caricati in NetBeans [2].
Una volta fatto ciò, creiamo un nuovo progetto [3].
![]() |
- In [4], selezioniamo il tipo di progetto [Applicazione Enterprise].
- In [5], assegniamo un nome al progetto
![]() |
- In [6], configuriamo il progetto. La versione Java EE sarà Java EE 6. Un progetto enterprise può essere creato con due moduli: un modulo EJB e un modulo Web. In questo caso, il progetto enterprise incapsulerà il modulo Web e il modulo EJB che sono già stati creati e caricati in NetBeans. Pertanto, non richiediamo la creazione di nuovi moduli.
- In [7], viene creato il progetto enterprise [mv-pam-webapp-ear]. Contemporaneamente è stato creato un altro progetto Maven [mv-pam-webapp]. Non ci occuperemo di esso.
- In [8], aggiungiamo le dipendenze al progetto enterprise
![]() |
- In [9], aggiungiamo il progetto web di tipo WAR,
- In [10], aggiungiamo il progetto EJB di tipo ejb,
![]() |
- In [11], il progetto enterprise con le sue due dipendenze.
Compiliamo il progetto enterprise utilizzando Clean e Build. Siamo quasi pronti per distribuirlo sul server GlassFish. Prima di farlo, potrebbe essere necessario scaricare eventuali applicazioni già in esecuzione sul server per evitare potenziali conflitti di nomi EJB [11]:
![]() |
Il server MySQL deve essere in esecuzione e il database [dbpam_eclipselink] deve essere disponibile e popolato. Una volta fatto ciò, l'applicazione aziendale può essere distribuita [12]. In [13], possiamo vedere che è stata distribuita con successo sul server GlassFish.
Possiamo testare il servizio web appena distribuito:
![]() |
- In [1], richiediamo di testare il servizio web [PamWsEjbMetier]
- in [2], la pagina di test. Lasciamo al lettore il compito di eseguire i test.
8.2.2. Il lato client
Compito: seguendo la procedura descritta nella Sezione 8.1.2.1, realizzare un client console per il servizio web precedente.
8.3. Servizio web implementato con Spring e Tomcat
Stiamo ora lavorando all'interno del seguente framework architettonico:
![]() |
Il servizio web è fornito da un'applicazione web in esecuzione all'interno del contenitore web del server Tomcat. L'architettura dell'applicazione sarà la seguente:
![]() |
Partiremo dal progetto [mv-pam-spring-hibernate] creato nella Sezione 5.11:
![]() |
8.3.1. Il lato server
Creiamo un'applicazione Maven di tipo web denominata [mv-pam-ws-spring-tomcat] [1]:
![]() |
Modifichiamo il file [pom.xml] per includere le seguenti dipendenze [2]:
- righe 3–7: la dipendenza dal progetto [spring-pam-jpa-hibernate],
- righe 8–17: dipendenze dal framework Apache CXF [http://cxf.apache.org/]. Questo framework facilita la creazione di servizi web.
Questo file [pom.xml] introduce numerose dipendenze [2].
Torniamo all'architettura dell'applicazione:
![]() |
Le chiamate al servizio web che stiamo per realizzare sono gestite da un servlet del framework CXF. Ciò si riflette nel file [WEB-INF/web.xml] come segue:
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-spring-hibernate</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Apache CXF dependencies -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.2.12</version>
</dependency>
</dependencies>
- Il framework CXF dipende da Spring. Righe 4–6: viene dichiarato un listener. La classe corrispondente verrà caricata contemporaneamente all'applicazione web. Utilizzerà il file di configurazione Spring [WEB-INF/applicationContext.xml]:
![]() |
- Righe 8–12: il servlet CXF che gestirà le chiamate al servizio web che stiamo per creare,
- Righe 13–16: Gli URL gestiti dal servlet CXF avranno il formato /ws/*. Gli altri non saranno gestiti da CXF.
Per definire il servizio web, definiamo un'interfaccia e la sua implementazione:
![]() |
L'interfaccia [IWsMetier] sarà la seguente:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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-app_2_5.xsd">
<display-name>mv-pam-ws-spring-tomcat</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- CXF configuration -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
- riga 7: l'interfaccia [IWsMetier] deriva dall'interfaccia [IMetier] nel livello [business] del progetto [mv-pam-spring-hibernate],
- riga 6: l'interfaccia [IWsMetier] è quella di un servizio web.
La classe di implementazione di questa interfaccia è la seguente:
package pam.ws;
import javax.jws.WebService;
import metier.IMetier;
@WebService
public interface IWsMetier extends IMetier{
}
- riga 11: la classe [PamWsMetier] implementa l'interfaccia definita in precedenza,
- riga 10: definisce la classe come servizio web,
- riga 14: il livello [business] verrà iniettato da Spring,
- righe 21, 26: l'annotazione @WebMethod trasforma un metodo in un metodo esposto dal servizio web,
- righe 23, 28: i metodi sono implementati utilizzando il livello [business].
Dobbiamo ancora definire il contenuto del file di configurazione di Spring [applicationContext.xml]:
![]() |
Il suo contenuto è il seguente:
package pam.ws;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebService;
import jpa.Employe;
import metier.FeuilleSalaire;
import metier.IMetier;
@WebService
public class PamWsMetier implements IWsMetier {
// business layer
private IMetier metier;
// manufacturer
public PamWsMetier(){
}
@WebMethod
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillees, int nbJoursTravailles) {
return metier.calculerFeuilleSalaire(SS, nbHeuresTravaillees, nbJoursTravailles);
}
@WebMethod
public List<Employe> findAllEmployes() {
return metier.findAllEmployes();
}
// getters and setters
public void setMetier(IMetier metier) {
this.metier = metier;
}
}
- Righe 13–15: vengono importati i file di configurazione di Apache CXF. Questi vengono cercati nel classpath del progetto (attributo classpath),
- Righe 4, 9, 10: vengono dichiarati i namespace specifici di Apache CXF,
- riga 18: viene importato il file di configurazione Spring per il progetto [mv-pam-spring-hibernate],
- Righe 21–23: definiscono il bean del servizio web con la sua dipendenza dal livello [business] (riga 22),
- Righe 24–27: definiscono il servizio web stesso,
- riga 25: il bean Spring che implementa il servizio web è quello definito alla riga 21;
- riga 26: definisce l'URL a cui sarà disponibile il servizio web, in questo caso /business. In combinazione con il formato richiesto per gli URL elaborati da Apache CXF (vedere il file web.xml), questo URL diventa /ws/business.
Il nostro progetto è pronto per essere eseguito. Lo avviamo (Run) e richiediamo l'URL [http://localhost:8080/mv-pam-ws-spring-tomcat/ws] in un browser:

La pagina elenca tutti i servizi web distribuiti. Qui ce n'è solo uno. Seguiamo il link WSDL:
![]() |
Il testo visualizzato [1] proviene da un file XML che definisce le funzionalità del servizio web, come richiamarlo e quali risposte invia. Prendere nota dell'URL [2] di questo file WSDL. Tutti i client del servizio web devono conoscerlo.
8.3.2. Il lato client
Compito: seguendo la procedura descritta nella Sezione 8.1.2.1, creare un client da console per il servizio web precedente.
Nota: per specificare l'URL del file WSDL del servizio web, procedere come segue:
![]() |
Inserisci l'URL annotato in precedenza al punto [2] nel campo [3].












































