6. Client Flex del servizio JEE degli appuntamenti
Presentiamo ora due client Flex del servizio web JEE per gli appuntamenti. Il IDE utilizzato è Flex Builder 3. Una versione dimostrativa di questo prodotto è scaricabile all'URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 è un IDE Eclipse. Inoltre, per eseguire il client Flex, utilizziamo un server web Apache dello strumento Wamp [http://www.wampserver.com/]. È possibile utilizzare qualsiasi server Apache. Il browser che visualizza il client Flex deve disporre del plugin Flash Player, versione 9 o superiore.
Le applicazioni Flex hanno la particolarità di essere eseguite all’interno del plugin Flash Player del browser. In questo senso sono simili alle applicazioni Ajax, che incorporano nelle pagine inviate al browser degli script JavaScript che vengono poi eseguiti all’interno del browser. Un'applicazione Flex non è un'applicazione web nel senso comune del termine: è un'applicazione client che utilizza servizi forniti da server web. In questo senso, è analoga a un'applicazione desktop che fungesse da client per gli stessi servizi. Si differenzia tuttavia per un aspetto: viene inizialmente scaricata da un server web all'interno di un browser dotato del plugin Flash Player in grado di eseguirla.
Come un’applicazione desktop, un’applicazione Flex è composta principalmente da due elementi:
- una parte di presentazione: le viste visualizzate nel browser. Queste viste hanno la ricchezza delle finestre delle applicazioni desktop. Una vista è descritta tramite un linguaggio a tag chiamato MXML.
- una parte di codice che gestisce principalmente gli eventi generati dalle azioni dell’utente sulla vista. Questo codice può essere scritto anche in MXML o con un linguaggio orientato agli oggetti chiamato ActionScript. È necessario distinguere due tipi di eventi:
- l'evento che richiede uno scambio con il server web: il popolamento di un elenco con dati forniti da un'applicazione web, l'invio dei dati di un modulo al server, ... Flex fornisce una serie di metodi per comunicare con il server in modo trasparente per lo sviluppatore. Questi metodi sono di default asincroni: l'utente può continuare a interagire con la vista durante la richiesta al server.
- l'evento che modifica la vista visualizzata senza scambio di dati con il server, ad esempio trascinare un elemento da un albero per rilasciarlo in un elenco. Questo tipo di evento viene gestito interamente a livello locale all'interno del browser.
Un'applicazione Flex viene spesso eseguita nel modo seguente:
![]() |
- in [1], viene richiesta una pagina HTML
- in [2], questa viene inviata. Essa include un file binario SWF (ShockWave Flash) contenente l’intera applicazione Flex: tutte le viste e il codice di gestione degli eventi relativi a queste. Questo file verrà eseguito dal plugin FlashPlayer del browser.
![]() |
- L'esecuzione del client Flex avviene localmente sul browser, tranne quando ha bisogno di dati esterni. In tal caso, li richiede al server [3]. Li riceve in [4] in vari formati: XML o binario. L'applicazione interpellata sul server web può essere scritta in qualsiasi linguaggio. Ciò che conta è solo il formato della risposta.
Abbiamo descritto l’architettura di esecuzione di un’applicazione Flex affinché il lettore comprenda bene la differenza tra questa e quella di un’applicazione web classica, senza Ajax, come l’applicazione Asp.Net descritta in precedenza. In quest’ultima, il browser è passivo: si limita a visualizzare pagine HTML generate sul server web che gliele invia.
Di seguito forniamo due esempi di client Flex con l’unico scopo di illustrare la varietà dei client di un servizio web. Poiché l’autore è egli stesso un principiante in Flex, alcuni punti potrebbero non essere approfonditi come dovrebbero.
6.1. Un primo client Flex
Scriviamo ora un primo client Flex per visualizzare l’elenco dei clienti. L’architettura client/server implementata sarà la seguente:
![]() |
In questa architettura sono presenti due server web:
- il server Glassfish che esegue il servizio web remoto
- il server Apache che esegue il client Flex del servizio web remoto
Realizziamo il client Flex con IDE Flex Builder 3:
![]() |
- in Flex Builder 3, creiamo un nuovo progetto in [1]
- gli si assegna un nome in [2] e si specifica in [3] in quale cartella generarlo
![]() |
- in [4], si assegna un nome all'applicazione principale (quella che verrà eseguita)
- in [5], il progetto una volta generato
- in [6], il file principale dell'applicazione MXML
- un file MXML contiene una vista e il codice di gestione degli eventi di tale vista. La scheda [Source] [7] consente di accedere al file MXML. In esso si trovano i tag <mx> che descrivono la vista, nonché il codice ActionScript.
- La vista può essere creata graficamente utilizzando la scheda [Design] [8]. I tag MXML che descrivono la vista vengono quindi generati automaticamente nella scheda [Source]. Vale anche il contrario: i tag MXML aggiunti direttamente nella scheda [Source] vengono riprodotti graficamente nella scheda [Design].
Come già fatto con i precedenti client C# e Asp.Net, genereremo il proxy locale C [B] del servizio web remoto S [A]:
![]() |
Affinché il proxy C possa essere generato, è necessario che il servizio web JEE sia attivo.
![]() |
- in [1], selezionare l'opzione Dati / Importa servizio web
- in [2], selezionare la cartella di generazione delle classi e delle interfacce del proxy C.
![]() |
- in [3], inserire l'URI del file WSDL del servizio web remoto S (cfr. paragrafo 4.10.2), quindi passare alla fase successiva
- in [4] e [5], il servizio web descritto dal file WSDL indicato in [3]
- in [6]: l'elenco dei metodi che verranno generati per il proxy C. Si noti che non si tratta dei metodi effettivi del servizio S. Non hanno la firma corretta. In questo caso, ogni metodo presentato ha un unico parametro, indipendentemente dal numero di parametri del metodo effettivo del servizio web. Questo unico parametro è un’istanza di classe che incapsula nei propri campi i parametri previsti dal metodo remoto.
- in [7]: il pacchetto in cui verranno generate le classi e le interfacce del proxy C
- in [8]: il nome della classe locale che fungerà da proxy verso il servizio web remoto
- in [9]: completare la procedura guidata.
- in [10]: l'elenco delle classi e delle interfacce del proxy C generato.
![]() |
- in [11]: la classe [WsDaoJpaService] che implementa i metodi del proxy C.
La classe [WsDaoJpaService] generata implementa la seguente interfaccia [IWsDaoJpaService]:
/**
* Service.as
* This file was auto-generated from WSDL by the Apache Axis2 generator modified by Adobe
* Any change made to this file will be overwritten when the code is re-generated.
*/
package generated.webservices{
import mx.rpc.AsyncToken;
import flash.utils.ByteArray;
import mx.rpc.soap.types.*;
public interface IWsDaoJpaService
{
//Funzioni stub per l'operazione getAllClients
/**
* Call the operation on the server passing in the arguments defined in the WSDL file
* @param getAllClients
* @return An AsyncToken
*/
function getAllClients(getAllClients:GetAllClients):AsyncToken;
....
function getAllClients_send():AsyncToken;
...
function get getAllClients_lastResult():GetAllClientsResponse;
...
function set getAllClients_lastResult(lastResult:GetAllClientsResponse):void;
...
function addgetAllClientsEventListener(listener:Function):void;
...
function get getAllClients_request_var():GetAllClients_request;
...
function set getAllClients_request_var(request:GetAllClients_request):void;
...
}
}
- riga 11: l'interfaccia [IWsDaoJpaService] implementata dalla classe [WsDaoJpaService]
- righe 19-31: i vari metodi generati per il metodo getAllClients() del servizio web remoto. L'unico che si avvicina a quello effettivamente esposto dal servizio web è quello della riga 19. Ha il nome corretto ma non la firma corretta: il metodo getAllClients() del servizio web remoto non ha parametri.
L’unico parametro del metodo getAllClients del proxy C generato è di tipo GetAllClients come segue:
/**
* GetAllClients.as
* This file was auto-generated from WSDL by the Apache Axis2 generator modified by Adobe
* Any change made to this file will be overwritten when the code is re-generated.
*/
package generated.webservices
{
import mx.utils.ObjectProxy;
import flash.utils.ByteArray;
import mx.rpc.soap.types.*;
/**
* Wrapper class for a operation required type
*/
public class GetAllClients
{
/**
* Constructor, initializes the type class
*/
public function GetAllClients() {}
}
}
Si tratta di una classe vuota. Ciò potrebbe corrispondere al fatto che il metodo di destinazione getAllClients non accetta parametri.
Ora esaminiamo le classi generate per le entità Medecin, Client, Rv e Creneau. Prendiamo ad esempio la classe Client:
package generated.webservices
{
import mx.utils.ObjectProxy;
import flash.utils.ByteArray;
import mx.rpc.soap.types.*;
public class Client extends generated.webservices.Personne
{
public function Client() {}
}
}
Anche la classe Client è vuota. Deriva (riga 7) dalla seguente classe Personne:
package generated.webservices
{
import mx.utils.ObjectProxy;
import flash.utils.ByteArray;
import mx.rpc.soap.types.*;
public class Personne
{
public function Personne() {}
public var id:Number;
public var nom:String;
public var prenom:String;
public var titre:String;
public var version:Number;
}
}
- righe 11-15: si ritrovano gli attributi della classe Personne definita all'interno del servizio web JEE.
Abbiamo gli elementi principali del proxy C. Ora possiamo utilizzarlo.
Il file principale [rdvmedecins01.xml] del client è il seguente:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
import generated.webservices.Client;
...
// dati
private var ws:WsDaoJpaService;
[Bindable]
private var clients:ArrayCollection;
private function init():void{
...
}
private function loadClients():void{
...
}
...
private function displayClient(client:Client):String{
...
}
]]>
</mx:Script>
<mx:Label text="Liste des clients" fontSize="14"/>
<mx:List dataProvider="{clients}" labelFunction="displayClient"></mx:List>
<mx:Button label="Afficher les clients" click="loadClients()"/>
<mx:Text id="txtMsgErreur" width="454" height="75"/>
</mx:Application>
In questo codice, occorre distinguere diversi elementi:
- la definizione dell’applicazione (riga 2)
- la descrizione della relativa vista (righe 27-30)
- i gestori di eventi in linguaggio ActionScript all'interno del tag <mx:Script> (righe 3-26).
Per cominciare, commentiamo la definizione dell'applicazione stessa e la descrizione della sua vista:
- riga 2: definisce
- la modalità di disposizione dei componenti nel contenitore della vista. L’attributo layout="vertical" indica che i componenti saranno disposti uno sotto l’altro.
- il metodo da eseguire una volta che la vista sarà stata istanziata, c.a.d. il momento in cui tutti i suoi componenti saranno stati istanziati. L’attributo creationComplete="init();" indica che deve essere eseguito il metodo init della riga 13. creationComplete è uno degli eventi che può essere emesso dalla classe Application.
- le righe 27-30 definiscono i componenti della vista
- riga 27: definisce un testo
- riga 28: un elenco in cui verrà inserito l'elenco dei clienti. Il tag dataProvider="{clients}" indica la fonte dei dati che devono popolare l'elenco. In questo caso, l'elenco verrà popolato con l'oggetto clients definito alla riga 11. Per poter scrivere dataProvider="{clients}", è necessario che il campo clients abbia l'attributo [Bindable] (riga 10). Questo attributo consente di fare riferimento a una variabile ActionScript al di fuori del tag <mx:Script>. Il campo clients è di tipo ArrayCollection, un tipo ActionScript che consente di memorizzare elenchi di oggetti, in questo caso un elenco di oggetti di tipo Client.
- riga 29: un pulsante. Il suo evento click è gestito. L'attributo click="loadClients()" indica che il metodo loadClients della riga 17 deve essere eseguito quando si fa clic sul pulsante. Sarà questo pulsante a innescare la richiesta al servizio web per ottenere l'elenco dei clienti.
- riga 30: una casella di testo destinata a visualizzare un eventuale messaggio di errore che potrebbe essere restituito dal server in risposta alla richiesta precedente.
Le righe 27-30 generano la seguente visualizzazione nella scheda [Design]:
![]() |
- [1]: è stato generato dal componente Label della riga 27
- [2]: è stato generato dal componente List della riga 28
- [3]: è stato generato dal componente Button della riga 29
- [4]: è stato generato dal componente Text della riga 30
- [5]: un esempio di esecuzione
Esaminiamo ora il codice ActionScript della pagina. Questo codice gestisce gli eventi della vista.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
import generated.webservices.Client;
...
// dati
private var ws:WsDaoJpaService;
[Bindable]
private var clients:ArrayCollection;
private function init():void{
// si istanzia il proxy del servizio web
ws=new WsDaoJpaService();
// si configurano i gestori di eventi
ws.addgetAllClientsEventListener(loadClientsCompleted);
ws.addEventListener(FaultEvent.FAULT,loadClientsFault);
}
private function loadClients():void{
// si richiede l'elenco dei clienti
ws.getAllClients(new GetAllClients());
}
private function loadClientsCompleted(event:GetAllClientsResultEvent):void{
// si recuperano i clienti dal risultato inviato
clients=event.result as ArrayCollection;
}
private function loadClientsFault(event:FaultEvent):void{
// viene visualizzato il messaggio di errore
txtMsgErreur.text=event.fault.message;
}
private function displayClient(client:Client):String{
// visualizzazione di un cliente
return client.nom + " " + client.prenom;
}
]]>
</mx:Script>
<mx:Label text="Liste des clients" fontSize="14"/>
<mx:List dataProvider="{clients}" labelFunction="displayClient"></mx:List>
<mx:Button label="Afficher les clients" click="loadClients()"/>
<mx:Text id="txtMsgErreur" width="454" height="75"/>
- riga 9: ws indicherà il proxy C di tipo WsDaoJpaService, la classe generata in precedenza che implementa i metodi di accesso al servizio web remoto.
- riga 13: il metodo init viene eseguito quando la vista è stata istanziata (riga 1)
- riga 15: viene creata un'istanza del proxy C
- riga 17: viene associato un gestore di eventi all'evento "il metodo asincrono GetAllClients si è concluso con successo". Per ogni metodo m del servizio web remoto, il proxy C implementa un metodo addmEventListener che consente di associare un gestore all’evento «il metodo asincrono m si è concluso con successo». Qui, la riga 17 indica che il metodo loadClientsCompleted della riga 26 deve essere eseguito quando il client Flex avrà ricevuto l’elenco dei clienti.
- riga 18: un gestore di eventi è associato all’evento “un metodo asincrono del proxy C si è concluso con un errore”. Qui, la riga 18 indica che il metodo loadClientsFault della riga 31 deve essere eseguito ogni volta che una richiesta asincrona dal proxy C al servizio web S fallisce. In questo caso, l’unica richiesta che verrà effettuata è quella che richiede l’elenco dei clienti.
- Infine, il metodo init alla riga 13 ha istanziato il proxy C e definito i gestori di eventi per la richiesta asincrona che verrà effettuata in seguito.
- riga 21: il metodo eseguito quando si fa clic sul pulsante [Afficher les clients] (riga 44)
- riga 23: viene eseguito il metodo asincrono getAllClients del proxy C. Gli viene passata un'istanza GetAllClients incaricata di incapsulare i parametri del metodo remoto chiamato. In questo caso, non vi sono parametri. Viene creata un'istanza vuota. Il metodo getAllClients è asincrono. L'esecuzione prosegue senza attendere i dati restituiti dal server. L'utente può, in particolare, continuare a interagire con la vista. Gli eventi da lui generati continueranno a essere gestiti. Grazie al metodo init, sappiamo che:
- il metodo loadClientsCompleted (riga 26) verrà eseguito quando il client Flex avrà ricevuto l’elenco dei clienti
- il metodo loadClientsFault (riga 31) verrà eseguito se la richiesta termina con un errore.
- riga 28: l’elenco dei clienti viene recuperato nell’evento. Sappiamo che il metodo getAllClients del servizio web remoto restituisce un elenco. Lo inseriamo nel campo clients della riga 11. È necessaria una conversione di tipo. Poiché l’elenco della riga 43 è associato (Bindable) al campo clienti, viene segnalato che i suoi dati sono cambiati. A questo punto visualizza l’elenco dei clienti. Visualizzerà ogni elemento dell’elenco clients utilizzando il metodo displayClient (riga 43).
- riga 36: il metodo displayClient riceve un tipo Client. Deve restituire la stringa di caratteri che l’elenco deve visualizzare per quel cliente. In questo caso, il cognome e il nome (riga 38).
- riga 31: il metodo eseguito quando una richiesta al servizio web fallisce. Riceve un parametro di tipo FaultEvent. Questa classe dispone di un campo fault che incapsula l'errore restituito dal server. fault.message è il messaggio che accompagna l'errore.
- riga 33: il messaggio di errore viene visualizzato nell'area di testo prevista a tale scopo.
Una volta compilata l’applicazione, il suo codice eseguibile si trova nella cartella [bin-debug] del progetto Flex:
![]() |
Sopra,
- il file [rdvmedecins01.html] rappresenta il file HTML che il browser richiederà al server web per ottenere il client Flex
- il file [rdvmedecins01.swf] è il file binario del client Flex che verrà incapsulato nella pagina HTML inviata al browser e successivamente eseguito dal plugin Flash Player di quest’ultimo.
Siamo pronti per eseguire il client Flex. Prima però dobbiamo configurare l’ambiente di esecuzione necessario. Torniamo all’architettura client/server testata:
![]() |
Lato server:
- avviare SGBD MySQL
- avviare il server Glassfish
- distribuire il servizio web JEE degli appuntamenti, se non è già stato distribuito
- eventualmente testare uno dei client precedenti per verificare che tutto sia a posto sul lato server.
Lato client:
Avviare il server Apache a cui verrà richiesta l’applicazione Flex. In questo caso utilizziamo lo strumento Wamp. Con questo strumento, possiamo associare un alias alla cartella [bin-debug] del progetto Flex.
![]() |
- l’icona di Wamp si trova nella parte inferiore dello schermo [1]
- facendo clic con il tasto sinistro sull’icona Wamp, selezionare l’opzione Apache [2] / Alias Directories [3, 4]
- selezionare l’opzione [5]: Aggiungi un alias
![]() |
- in [6] assegnare un alias (un nome qualsiasi) all'applicazione web che verrà eseguita
- in [7] indicare la radice dell'applicazione web a cui verrà assegnato questo alias: si tratta della cartella [bin-debug] del progetto Flex che abbiamo appena creato.
Ricordiamo la struttura della cartella [bin-debug] del progetto Flex:
![]() |
Il file [rdvmedecins01.html] è il file HTML dell'applicazione Flex. Grazie all'alias che abbiamo appena creato sulla cartella [bin-debug], questo file sarà accessibile tramite l'URL [http://localhost/rdvmedecins/rdvmedecins01.html]. Lo apriamo in un browser dotato del plugin Flash Player versione 9 o superiore:
![]() |
- in [1], l’URL dell’applicazione Flex
- in [2], richiediamo l’elenco dei clienti
- in [3], il risultato ottenuto quando tutto funziona correttamente
- in [4], il risultato ottenuto quando richiediamo la visualizzazione dei clienti mentre il servizio web è stato arrestato.
Potreste essere curiosi di visualizzare il codice sorgente della pagina HTML ricevuta
Il corpo della pagina inizia alla riga 39. Non contiene HTML classico, ma un oggetto (riga 47) di tipo "application/x-shockwave-flash" (riga 60). Si tratta del file [rdvmedecins01.swf] (riga 54) che è possibile visualizzare nella cartella [bin-debug] del progetto Flex. È un file di dimensioni considerevoli: circa 600 K per questo semplice esempio.
6.2. Un secondo client Flex
Il secondo client Flex non utilizzerà il proxy C generato per il primo. Vogliamo dimostrare che questa fase non è indispensabile, anche se presenta dei vantaggi rispetto a quella che verrà illustrata qui di seguito.
Il progetto si evolve come segue:
![]() |
- in [1], la nuova applicazione Flex
- in [2] gli eseguibili ad essa associati
- in [3] la nuova vista: verrà visualizzato l’elenco dei medici.
Il codice MXML dell’applicazione [rdvmedecins02.mxml] è il seguente:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
// dati
[Bindable]
private var medecins:ArrayCollection;
private function loadMedecins():void{
// viene richiesta la lista dei medici
wsrdvmedecins.getAllMedecins.send();
}
private function loadMedecinsCompleted(event:ResultEvent):void{
// si recuperano i medici
medecins=event.result as ArrayCollection;
}
private function loadMedecinsFault(event:FaultEvent):void{
// viene visualizzato il messaggio di errore
txtMsgErreur.text=event.fault.message;
}
// visualizzazione di un medico
private function displayMedecin(medecin:Object):String{
return medecin.nom + " " + medecin.prenom;
}
]]>
</mx:Script>
<mx:WebService id="wsrdvmedecins"
wsdl="http://localhost:8080/serveur-webservice-ejb-dao-jpa-hibernate/WsDaoJpaService?wsdl">
<mx:operation name="getAllMedecins"
result="loadMedecinsCompleted(event)" fault="loadMedecinsFault(event);">
<mx:request/>
</mx:operation>
</mx:WebService>
<mx:Label text="Liste des médecins" fontSize="14"/>
<mx:List dataProvider="{medecins}" labelFunction="displayMedecin"></mx:List>
<mx:Button label="Afficher les médecins" click="loadMedecins()"/>
<mx:Text id="txtMsgErreur" width="300" height="113"/>
</mx:Application>
Commenteremo solo le novità:
- righe 42 - 45: la nuova vista. È identica alla precedente, tranne per il fatto che è stata adattata per visualizzare i medici anziché i clienti.
- righe 35-41: il servizio web è qui descritto da un tag <mx:WebService> (riga 35). Il proxy C utilizzato nella versione precedente non viene più utilizzato qui.
- riga 35: l’attributo id assegna un nome al servizio web.
- riga 36: l’attributo wsdl indica l’URI del file WSDL del servizio web. Si tratta dello stesso URI utilizzato dal client precedente e definito nel paragrafo 4.10.2.
- righe 37-40: definiscono un metodo del servizio web remoto tramite il tag <mx:operation>
- riga 37: il metodo a cui si fa riferimento è definito dall’attributo name. In questo caso si fa riferimento al metodo remoto getAllMedecins.
- riga 38: si definiscono i metodi da eseguire in caso di esito positivo dell'operazione (attributo result) e in caso di esito negativo (attributo fault).
- riga 39: il tag <mx:request> serve a definire i parametri dell'operazione. In questo caso il metodo remoto getAllMedecins non ha parametri, quindi non inseriamo nulla. Per un metodo che ammette i parametri param1 e param2, si scriverebbe:
dove param1 e param2 sarebbero variabili dichiarate e inizializzate nel tag <mx:Script>
All'interno del tag <mx:Script> si trova il codice ActionScript, analogo a quello esaminato nel client precedente. L'unica differenza riguarda il metodo loadMedecins nelle righe 13-16. È la modalità di chiamata del metodo remoto [getAllMedecins] a differire:
- riga 15: si utilizza il servizio web [wsrdvmedecins] definito alla riga 35 e la sua operazione [getAllMedecins] definita alla riga 37. Per eseguire questa operazione, viene utilizzato il metodo send. È proprio questa operazione che avvia la chiamata asincrona al metodo getAllMedecins del servizio web definito alla riga 35. Il metodo send effettuerà la chiamata con i parametri definiti dal tag <mx:request> alla riga 39. In questo caso non vi sono parametri. Se il metodo avesse avuto i parametri param1 e param2, lo script loadMedecins avrebbe assegnato dei valori a tali parametri prima di chiamare il metodo send.
Non ci resta che testare questa nuova applicazione:
![]() |

















