6. Clientes Flex del servicio JEE de citas
A continuación, presentamos dos clientes Flex del servicio web JEE de citas. El IDE utilizado es Flex Builder 3. Se puede descargar una versión de demostración de este producto en la URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 es un IDE Eclipse. Por otra parte, para ejecutar el cliente Flex, utilizamos un servidor web Apache de la herramienta Wamp [http://www.wampserver.com/]. Cualquier servidor Apache es válido. El navegador que muestra el cliente Flex debe disponer del plugin Flash Player, versión 9 como mínimo.
Las aplicaciones Flex tienen la particularidad de ejecutarse dentro del complemento Flash Player del navegador. En este sentido, se asemejan a las aplicaciones Ajax, que incorporan en las páginas enviadas al navegador scripts JavaScript que luego se ejecutan dentro del navegador. Una aplicación Flex no es una aplicación web en el sentido habitual del término: es una aplicación cliente de servicios prestados por servidores web. En este sentido, es análoga a una aplicación de escritorio que fuera cliente de esos mismos servicios. Sin embargo, difiere en un punto: se descarga inicialmente desde un servidor web en un navegador que dispone del complemento Flash Player capaz de ejecutarla.
Al igual que una aplicación de escritorio, una aplicación Flex se compone principalmente de dos elementos:
- una parte de presentación: las vistas que se muestran en el navegador. Estas vistas tienen la riqueza de las ventanas de las aplicaciones de escritorio. Una vista se describe mediante un lenguaje de etiquetas llamado MXML.
- una parte de código que gestiona principalmente los eventos provocados por las acciones del usuario en la vista. Este código también puede escribirse en MXML o con un lenguaje orientado a objetos llamado ActionScript. Hay que distinguir dos tipos de eventos:
- el evento que requiere un intercambio con el servidor web: rellenar una lista con datos proporcionados por una aplicación web, enviar los datos de un formulario al servidor, etc. Flex ofrece una serie de métodos para comunicarse con el servidor de forma transparente para el desarrollador. Estos métodos son asíncronos por defecto: el usuario puede seguir interactuando con la vista mientras se realiza la solicitud al servidor.
- El evento que modifica la vista mostrada sin intercambio de datos con el servidor, por ejemplo, arrastrar un elemento de un árbol para soltarlo en una lista. Este tipo de evento se gestiona íntegramente de forma local dentro del navegador.
Una aplicación Flex suele ejecutarse de la siguiente manera:
![]() |
- en [1], se solicita una página HTML
- en [2], se envía. Incluye un archivo binario SWF (ShockWave Flash) que contiene la totalidad de la aplicación Flex: todas las vistas y el código de gestión de eventos de las mismas. Este archivo será ejecutado por el complemento FlashPlayer del navegador.
![]() |
- La ejecución del cliente Flex se realiza de forma local en el navegador, salvo cuando necesita datos externos. En ese caso, los solicita al servidor [3]. Los recibe en [4] en diversos formatos: XML o binario. La aplicación consultada en el servidor web puede estar escrita en cualquier lenguaje. Lo único que importa es el formato de la respuesta.
Hemos descrito la arquitectura de ejecución de una aplicación Flex para que el lector perciba claramente la diferencia entre esta y la de una aplicación web clásica, sin Ajax, como la aplicación Asp.Net descrita anteriormente. En esta última, el navegador es pasivo: simplemente muestra páginas HTML generadas en el servidor web que se las envía.
A continuación, ofrecemos dos ejemplos de clientes Flex con el único objetivo de mostrar la diversidad de clientes de un servicio web. Dado que el autor es él mismo un principiante en Flex, es posible que algunos puntos no se detallen como deberían.
6.1. Un primer cliente Flex
Ahora escribimos un primer cliente Flex para mostrar la lista de clientes. La arquitectura cliente/servidor que se implementará será la siguiente:
![]() |
En esta arquitectura, hay dos servidores web:
- el servidor Glassfish, que ejecuta el servicio web remoto
- el servidor Apache que ejecuta el cliente Flex del servicio web remoto
Creamos el cliente Flex con IDE Flex Builder 3:
![]() |
- en Flex Builder 3, creamos un nuevo proyecto en [1]
- le damos un nombre en [2] y especificamos en [3] en qué carpeta generarlo
![]() |
- en [4], se le da un nombre a la aplicación principal (la que se va a ejecutar)
- en [5], el proyecto una vez generado
- en [6], el archivo principal de la aplicación MXML
- un archivo MXML contiene una vista y el código de gestión de eventos de dicha vista. La pestaña [Source] [7] da acceso al archivo MXML. En él se encuentran las etiquetas <mx> que describen la vista, así como el código ActionScript.
- La vista se puede construir gráficamente utilizando la pestaña [Design] [8]. Las etiquetas MXML que describen la vista se generan entonces automáticamente en la pestaña [Source]. Lo contrario también es cierto: las etiquetas MXML añadidas directamente en la pestaña [Source] se reflejan gráficamente en la pestaña [Design].
Al igual que se hizo con los clientes C# y Asp.Net anteriores, vamos a generar el proxy local C [B] del servicio web remoto S [A]:
![]() |
Para que se pueda generar el proxy C, es necesario que el servicio web JEE esté activo.
![]() |
- en [1], seleccione la opción Datos / Importar servicio web
- en [2], seleccione la carpeta de generación de clases e interfaces del proxy C.
![]() |
- en [3], introduzca la URI del archivo WSDL del servicio web remoto S (véase el apartado 4.10.2) y, a continuación, pase al siguiente paso
- en [4] y [5], el servicio web descrito por el archivo WSDL indicado en [3]
- en [6]: la lista de métodos que se generarán para el proxy C. Cabe señalar que estos no son los métodos reales del servicio S. No tienen la firma correcta. Aquí, cada método presentado tiene un único parámetro, independientemente del número de parámetros del método real del servicio web. Este único parámetro es una instancia de clase que encapsula en sus campos los parámetros esperados por el método remoto.
- en [7]: el paquete en el que se generarán las clases e interfaces del proxy C
- en [8]: el nombre de la clase local que actuará como proxy hacia el servicio web remoto
- en [9]: finalizar el asistente.
- en [10]: la lista de clases e interfaces del proxy C generado.
![]() |
- en [11]: la clase [WsDaoJpaService] que implementa los métodos del proxy C.
La clase [WsDaoJpaService] generada implementa la siguiente interfaz [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
{
//Funciones stub para la operación 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;
...
}
}
- línea 11: la interfaz [IWsDaoJpaService] implementada por la clase [WsDaoJpaService]
- líneas 19-31: los diferentes métodos generados para el método getAllClients() del servicio web remoto. El único que se aproxima al realmente expuesto por el servicio web es el de la línea 19. Tiene el nombre correcto, pero no la firma correcta: el método getAllClients() del servicio web remoto no tiene parámetros.
El único parámetro del método getAllClients del proxy C generado es del tipo GetAllClients, como se muestra a continuación:
/**
* 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() {}
}
}
Se trata de una clase vacía. Esto podría deberse a que el método de destino getAllClients no admite parámetros.
Ahora analicemos las clases generadas para las entidades Medecin, Client, Rv y Creneau. Veamos, por ejemplo, la clase 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() {}
}
}
La clase Client también está vacía. Deriva (línea 7) de la siguiente clase 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;
}
}
- líneas 11-15: encontramos los atributos de la clase Personne definida dentro del servicio web JEE.
Tenemos los elementos principales del proxy C. Ahora podemos utilizarlo.
El archivo principal [rdvmedecins01.xml] del cliente es el siguiente:
<?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;
...
// datos
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>
En este código, hay que distinguir varios aspectos:
- la definición de la aplicación (línea 2)
- la descripción de la vista de la misma (líneas 27-30)
- los controladores de eventos en lenguaje ActionScript dentro de la etiqueta <mx:Script> (líneas 3-26).
Comencemos por comentar la definición de la propia aplicación y la descripción de su vista:
- línea 2: define
- el modo de disposición de los componentes en el contenedor de la vista. El atributo layout="vertical" indica que los componentes se colocarán uno debajo de otro.
- el método que se ejecutará cuando se haya instanciado la vista, c.a.d. el momento en que se hayan instanciado todos sus componentes. El atributo creationComplete="init();" indica que se debe ejecutar el método init de la línea 13. creationComplete es uno de los eventos que puede emitir la clase Application.
- Las líneas 27-30 definen los componentes de la vista
- línea 27: define un texto
- línea 28: una lista en la que se incluirá la lista de clientes. La etiqueta dataProvider="{clients}" indica la fuente de los datos que deben rellenar la lista. En este caso, la lista se rellenará con el objeto clients definido en la línea 11. Para poder escribir dataProvider="{clients}", es necesario que el campo clients tenga el atributo [Bindable] (línea 10). Este atributo permite que se haga referencia a una variable ActionScript fuera de la etiqueta <mx:Script>. El campo clients es de tipo ArrayCollection, un tipo ActionScript que permite almacenar listas de objetos, en este caso una lista de objetos de tipo Client.
- Línea 29: un botón. Se gestiona su evento click. El atributo click="loadClients()" indica que el método loadClients de la línea 17 debe ejecutarse al hacer clic en el botón. Será este botón el que active la solicitud al servicio web de la lista de clientes.
- Línea 30: un cuadro de texto destinado a mostrar cualquier mensaje de error que el servidor devuelva en respuesta a la solicitud anterior.
Las líneas 27-30 generan la siguiente vista en la pestaña [Design]:
![]() |
- [1]: ha sido generado por el componente Label de la línea 27
- [2]: generado por el componente List de la línea 28
- [3]: generado por el componente Button de la línea 29
- [4]: generado por el componente Text de la línea 30
- [5]: un ejemplo de ejecución
Veamos ahora el código ActionScript de la página. Este código gestiona los eventos de la vista.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
import generated.webservices.Client;
...
// datos
private var ws:WsDaoJpaService;
[Bindable]
private var clients:ArrayCollection;
private function init():void{
// se instancia el proxy del servicio web
ws=new WsDaoJpaService();
// se configuran los gestores de eventos
ws.addgetAllClientsEventListener(loadClientsCompleted);
ws.addEventListener(FaultEvent.FAULT,loadClientsFault);
}
private function loadClients():void{
// se solicita la lista de clientes
ws.getAllClients(new GetAllClients());
}
private function loadClientsCompleted(event:GetAllClientsResultEvent):void{
// se recuperan los clientes en el resultado enviado
clients=event.result as ArrayCollection;
}
private function loadClientsFault(event:FaultEvent):void{
// se muestra el mensaje de error
txtMsgErreur.text=event.fault.message;
}
private function displayClient(client:Client):String{
// se muestra 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"/>
- línea 9: ws designará el proxy C de tipo WsDaoJpaService, la clase generada anteriormente que implementa los métodos de acceso al servicio web remoto.
- línea 13: el método init se ejecuta cuando se ha instanciado la vista (línea 1)
- línea 15: se crea una instancia del proxy C
- línea 17: se asocia un controlador de eventos al evento «el método asíncrono GetAllClients ha finalizado correctamente». Para cualquier método m del servicio web remoto, el proxy C implementa un método addmEventListener que permite asociar un controlador al evento «el método asíncrono m ha finalizado con éxito». Aquí, la línea 17 indica que el método loadClientsCompleted de la línea 26 debe ejecutarse cuando el cliente Flex haya recibido la lista de clientes.
- Línea 18: se asocia un controlador de eventos al evento «un método asíncrono del proxy C ha finalizado con un error». Aquí, la línea 18 indica que el método loadClientsFault de la línea 31 debe ejecutarse cada vez que falle una solicitud asíncrona del proxy C al servicio web S. En este caso, la única solicitud que se realizará es la que pide la lista de clientes.
- Finalmente, el método init de la línea 13 ha instanciado el proxy C y ha definido los controladores de eventos para la solicitud asíncrona que se realizará posteriormente.
- Línea 21: el método que se ejecuta al hacer clic en el botón [Afficher les clients] (línea 44)
- Línea 23: se ejecuta el método asíncrono getAllClients del proxy C. Se le pasa una instancia GetAllClients encargada de encapsular los parámetros del método remoto llamado. Aquí no hay ningún parámetro. Se crea una instancia vacía. El método getAllClients es asíncrono. La ejecución continúa sin esperar a los datos devueltos por el servidor. El usuario puede, en particular, seguir interactuando con la vista. Los eventos que provoque seguirán siendo gestionados. Gracias al método init, sabemos que:
- el método loadClientsCompleted (línea 26) se ejecutará cuando el cliente Flex haya recibido la lista de clientes
- el método loadClientsFault (línea 31) se ejecutará si la solicitud termina en un error.
- línea 28: la lista de clientes se recupera en el evento. Sabemos que el método getAllClients del servicio web remoto devuelve una lista. La colocamos en el campo clients de la línea 11. Es necesario un transtypage. Dado que la lista de la línea 43 está vinculada (Bindable) al campo clientes, se le notifica que sus datos han cambiado. A continuación, muestra la lista de clientes. Mostrará cada elemento de la lista clients con el método displayClient (línea 43).
- línea 36: el método displayClient recibe un tipo Client. Debe devolver la cadena de caracteres que debe mostrar la lista para este cliente. En este caso, el nombre y los apellidos (línea 38).
- línea 31: el método que se ejecuta cuando falla una solicitud al servicio web. Recibe un parámetro de tipo FaultEvent. Esta clase tiene un campo fault que encapsula el error devuelto por el servidor. fault.message es el mensaje que acompaña al error.
- Línea 33: el mensaje de error se muestra en el cuadro de texto previsto para ello.
Una vez compilada la aplicación, su código ejecutable se encuentra en la carpeta [bin-debug] del proyecto Flex:
![]() |
Arriba,
- el archivo [rdvmedecins01.html] representa el archivo HTML que el navegador solicitará al servidor web para obtener el cliente Flex
- el archivo [rdvmedecins01.swf] es el binario del cliente Flex que se encapsulará en la página HTML enviada al navegador y que luego ejecutará el plugin Flash Player de este.
Ya estamos listos para ejecutar el cliente Flex. Antes, debemos configurar el entorno de ejecución que necesita. Volvamos a la arquitectura cliente/servidor probada:
![]() |
Lado del servidor:
- ejecutar SGBD MySQL
- iniciar el servidor Glassfish
- implementar el servicio web JEE de citas si aún no está implementado
- si es necesario, probar uno de los clientes anteriores para comprobar que todo está en su sitio en el lado del servidor.
En el lado del cliente:
Iniciar el servidor Apache al que se solicitará la aplicación Flex. Aquí utilizamos la herramienta Wamp. Con esta herramienta, podemos asociar un alias a la carpeta [bin-debug] del proyecto Flex.
![]() |
- El icono de Wamp se encuentra en la parte inferior de la pantalla [1]
- Haciendo clic con el botón izquierdo del ratón en el icono Wamp, selecciona la opción Apache [2] / Alias Directories [3, 4]
- seleccionar la opción [5]: Añadir un alias
![]() |
- en [6], asigne un alias (cualquier nombre) a la aplicación web que se va a ejecutar
- en [7] indicar la raíz de la aplicación web que llevará este alias: es la carpeta [bin-debug] del proyecto Flex que acabamos de crear.
Recordemos la estructura de la carpeta [bin-debug] del proyecto Flex:
![]() |
El archivo [rdvmedecins01.html] es el archivo HTML de la aplicación Flex. Gracias al alias que acabamos de crear en la carpeta [bin-debug], se podrá acceder a este archivo a través de la URL [http://localhost/rdvmedecins/rdvmedecins01.html]. Solicitamos esta URL en un navegador que disponga del complemento Flash Player versión 9 o superior:
![]() |
- en [1], la URL de la aplicación Flex
- en [2], solicitamos la lista de clientes
- en [3], el resultado obtenido cuando todo va bien
- en [4], el resultado obtenido cuando solicitamos la visualización de los clientes aunque el servicio web se haya detenido.
Quizás le interese ver el código fuente de la página HTML recibida
El cuerpo de la página comienza en la línea 39. No contiene HTML clásico, sino un objeto (línea 47) de tipo «application/x-shockwave-flash» (línea 60). Se trata del archivo [rdvmedecins01.swf] (línea 54) que se puede ver en la carpeta [bin-debug] del proyecto Flex. Es un archivo de gran tamaño: unos 600 K para este sencillo ejemplo.
6.2. Un segundo cliente Flex
El segundo cliente Flex no va a utilizar el proxy C generado para el primero. Queremos demostrar que este paso no es imprescindible, aunque presenta ventajas con respecto al que se va a presentar aquí.
El proyecto evoluciona de la siguiente manera:
![]() |
- en [1] la nueva aplicación Flex
- en [2] los ejecutables asociados a ella
- en [3] la nueva vista: vamos a mostrar la lista de médicos.
El código MXML de la aplicación [rdvmedecins02.mxml] es el siguiente:
<?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;
// Datos
[Bindable]
private var medecins:ArrayCollection;
private function loadMedecins():void{
// Se solicita la lista de médicos
wsrdvmedecins.getAllMedecins.send();
}
private function loadMedecinsCompleted(event:ResultEvent):void{
// se recuperan los médicos
medecins=event.result as ArrayCollection;
}
private function loadMedecinsFault(event:FaultEvent):void{
// se muestra el mensaje de error
txtMsgErreur.text=event.fault.message;
}
// visualización de un médico
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>
Solo comentaremos las novedades:
- líneas 42-45: la nueva vista. Es idéntica a la anterior, salvo que se ha adaptado para mostrar los médicos en lugar de los clientes.
- líneas 35-41: el servicio web se describe aquí mediante una etiqueta <mx:WebService> (línea 35). El proxy C utilizado en la versión anterior ya no se utiliza aquí.
- línea 35: el atributo id da un nombre al servicio web.
- línea 36: el atributo wsdl proporciona la URI del archivo WSDL del servicio web. Es la misma URI que la utilizada por el cliente anterior y definida en el apartado 4.10.2.
- líneas 37-40: definen un método del servicio web remoto mediante la etiqueta <mx:operation>
- línea 37: el método al que se hace referencia se define mediante el atributo name. Aquí hacemos referencia al método remoto getAllMedecins.
- línea 38: se definen los métodos que se ejecutarán en caso de éxito de la operación (atributo result) y en caso de fallo (atributo fault).
- línea 39: la etiqueta <mx:request> sirve para definir los parámetros de la operación. En este caso, el método remoto getAllMedecins no tiene parámetros, por lo que no introducimos nada. Para un método que admita los parámetros param1 y param2, se escribiría:
donde param1 y param2 serían variables declaradas e inicializadas en la etiqueta <mx:Script>
En la etiqueta <mx:Script> encontramos código ActionScript análogo al estudiado en el cliente anterior. Solo difiere el método loadMedecins de las líneas 13-16. Es el modo de llamada del método remoto [getAllMedecins] lo que difiere:
- línea 15: se utiliza el servicio web [wsrdvmedecins] definido en la línea 35 y su operación [getAllMedecins] definida en la línea 37. Para ejecutar esta operación, se utiliza el método send. Es este método el que inicia la llamada asíncrona al método getAllMedecins del servicio web definido en la línea 35. El método send realizará la llamada con los parámetros definidos por la etiqueta <mx:request> de la línea 39. Aquí no hay ningún parámetro. Si el método hubiera tenido los parámetros param1 y param2, el script loadMedecins habría asignado valores a estos parámetros antes de llamar al método send.
Ahora solo nos queda probar esta nueva aplicación:
![]() |

















