Desarrollar un cliente webservice desde WSDL en Axis2
August 26, 2007En esta ocasión, y como paso lógico después de ver cómo desarrollar un webservice, voy a contar cómo construir un cliente para invocar un webservice utilizando WSDL2Java en Axis2.
El webservice al que invocará el cliente es el desarrollado en el artículo anterior, myVirtualShop. Puedes descargarlo aquí listo para desplegar en el repositorio de Axis2.
Preliminares
Si deseas seguir este tutorial paso a paso debes tener instalado:
Por mi parte he aprovechado para probar la nueva versión 1.3 de Axis2. En teoría sólo mejora el rendimiento y corrije algunos bugs. Con la 1.2 funciona igual.
En qué consiste
Crear un cliente webservice utilizando la herramienta WSDL2Java es realmente sencillo, sólo necesitas tener acceso al WSDL que describe el webservice y ejecutar el WSDL2Java para generar las clases Java correspondientes al cliente.
Antes de
Antes de nada vamos a dejar el webservice desplegado y funcionando. Copia el webservice myVirtualShop.aar al repositorio de Axis2, subcarpeta services, y arranca el servidor con el script axis2server localizado en bin. Comprueba que todo está correcto accediendo al endpoint del webservice en http://localhost:8080/axis2/services/myVirtualShop.
Ahora con el webservice funcionando podemos centrarnos exclusivamente en el cliente. Lo primero es crear un nuevo proyecto Java, myVirtualShopClient, y configurar su Build Path para añadir los jars de Axis2, localizados en lib. Para independizar al proyecto cliente de la instalación de Axis2 crea una carpeta lib y copia dentro los jars de Axis2. Seleccionalos todos y añadelos al Build Path. Sé que son muchos y que algunos no hacen falta, pero otros dependen de determinadas decisiones que se tomen durante el desarrollo, como la elección del Databinding a utilizar, de si queremos hacer llamadas asíncronas, enviar attachments, etc. Mi recomendación es tenerlos todos disponibles durante el desarrollo y eliminar los jars innecesarios al crear la distribución.
Lo siguiente es añadir al proyecto el WSDL del webservice y sus schemas asociados. Crea una carpeta resources y copia dentro el WSDL y su schema. Recuerda borrar las extensiones jpg. A continuación un screenshot de como queda el proyecto.
Momento WSDL2Java
En este punto ya podemos utilizar WSDL2Java para generar de forma automática el código Java para el cliente webservice a partir del fichero WSDL. El código generado incluye un stub cliente para invocar al webservice y las clases Java necesarias para el Databinding de los mensajes XML. Como motor de Databinding vamos a usar adb.
Para ejecutar WSDL2Java se puede optar por hacerlo mediante una consola, ANT, Maven o un plugin de Eclipse o IDEA. Nosotros vamos a ver cómo usar el plugin de Eclipse Code Generator Wizard. Es más fácil y descriptivo:
-
Pulsa File->New->Other->Axis2 Wizards->Axis2 Code Generator.
-
Elije la opción de generar código Java desde un fichero WSDL. Pulsa Next.
-
Selecciona el fichero myVirtualShop.wsdl localizado en resources. Pulsa Next.
-
En la pantalla de opciones, selecciona custom como Codegen option e indica org.example.myvirtualshop.wsclient como Custom package name. Fijate que dejamos adb como Databinding y que se va a generar código cliente, tanto para llamadas síncronas como asíncronas. Pulsa Next.
-
Finalmente selecciona el proyecto myVirtualShopClient como destino del código a generar. Para ello elije la primera opción y elije el proyecto al pulsar el botón Browse. Pulsa Finish.
En las siguientes imágenes se ilustran los pasos anteriores.
Si refrescas el proyecto podrás ver las clases creadas en el nuevo package org.example.myvirtualshop.wsclient:
-
MyVirtualShopStub: Esta es la clase stub cliente con la que se pueden invocar las operaciones del webservice. Contiene un método por cada operación del webservice y clases internas que representan los elementos de los mensajes XML (databinding). Además, y como hemos seleccionado el modo asíncrono durante el wsdl2java, existe un método, que empieza por start, para realizar cada operación del webservice de forma asíncrona.
-
MyVirtualShopCallbackHandler: Esta es una clase abstracta que representa el manejador de respuestas asíncronas. Su funcionamiento lo veremos más adelante.
Despues de
Con el cliente ya creado sólo falta incorporar el código Java necesario a nuestra aplicación para crear el mensaje de petición deseado, usar el stub para invocar la operación correspondiente y procesar la respuesta.
Lo primero es crear una instancia del stub e indicar la URL del endpoint del webservice al que queremos invocar. Ejemplo:
String sEndPoint = "http://localhost:8080/axis2/services/myVirtualShop";
MyVirtualShopStub stub = new MyVirtualShopStub(sEndPoint);
Construir un mensaje es muy fácil. Existe una clase interna (JavaBean) por cada elemento complejo del mensaje XML que se utiliza para realizar el databinding o mapeo entre objetos Java y XML (y viceversa). Por tanto no es más que ir creando objetos y usar metodos set. Para ver las clases internas puedes ayudarte de la vista Outline del propio Eclipse. Sorpresa! Los incomprensibles sufijos _typeX siguen sin desaparecer en la versión 1.3 de Axis2.
El siguiente código es un ejemplo de cómo se construiría una petición para la operación Order:
//Elemento raiz del mensaje
OrderRequest orderRequest = new OrderRequest();
//Elemento Cliente
Cliente_type0 cliente = new Cliente_type0();
cliente.setNombre("John Waterfall");
cliente.setDireccion("NA");
orderRequest.setCliente(cliente);
//Elemento items
Items_type0 items = new Items_type0();
Item_type0[] aItem = new Item_type0[1];
//Elemento item
aItem[0] = new Item_type0();
aItem[0].setRef("R0001");
aItem[0].setCantidad(100);
items.setItem(aItem);
orderRequest.setItems(items);
Para invocar al webservice el stub cuenta con un método por cada operación. Los métodos son getCatalog, search, order y getStateOrder. Estos métodos tienen como parámetro la clase que representa el mensaje petición de la operación y devuelven la correspondiente respuesta. Siguiendo con el ejemplo, lo siguiente sería el código para invocar la operación Order y procesar la respuesta:
OrderResponse orderResponse = stub.order(orderRequest);
System.out.println("** OrderResponse.getIdPedido() = " + orderResponse.getIdPedido());
System.out.println("** OrderResponse.getFechaPrevistaEntrega() = " + orderResponse.getFechaPrevistaEntrega());
System.out.println("** OrderResponse.getgetTotal() = " + orderResponse.getTotal());
Si deseas ver un ejemplo un poco más completo dónde se invocan las cuatro operaciones del webservice, puedes descargarte la siguiente clase TestClient, copiarla al package org.example.myvirtualshop.wsclient y ejecutar su método main. Asegurate antes de que el webservice se encuentra desplegado y ten cuidado con los nombres de las clases internas del databinding, los números de los sufijos podrían variar.
Modo asíncrono
Una de las novedades de Axis2 es que permite realizar llamadas asíncronas sobre las operaciones de un webservice.
¿Qué diferencia hay entre una llamada síncrona y asíncrona? Cuando se invoca a un método de forma síncrona o bloqueante, el flujo de control del programa no sigue con la siguiente instrucción hasta que no termina la ejecución del método invocado (el 99.9% de las llamadas en Java). Mientras que cuando se invoca a un método de forma asíncrona o no bloqueante, el flujo de control del programa continua automáticamente con la siguiente instrucción y en otro thread se ejecuta el método invocado (concurrencia).
Estas llamadas asíncronas forman el patrón de intercambio de mensajes (MEP) conocido por In-Only y es muy útil para invocar operaciones de las que no necesitamos una respuesta o no podemos disponer de la respuesta en ese mismo instante.
Para invocar las operaciones del webservice de forma asíncrona el stub contiene un método por cada operación que se llama igual pero empezando por el prefijo start. Los métodos son startGetCatalog, startSearch, startOrder y startGetStateOrder. Estos métodos tienen dos parametros, el mensaje de petición y el manejador de la respuesta asíncrona (CallbackHandler) que será el encargado de recibir la respuesta de la operación invocada.
Para crear un CallbackHandler hay que usar, más bien heredar, la clase abstracta CallbackHandler generada por el wsdl2java. Esta clase tiene dos métodos por cada operación del webservice, uno para procesar la respuesta y otro para procesar un posible error en la comunicación. Por tanto habrá que sobreescribir el par de métodos correspondientes a la operación que se vaya a invocar de forma asíncrona con el código para procesar la respuesta de la operación o tratar un posible error.
Veámoslo en detalle y siguiendo nuestro ejemplo para el webservice MyVirtualShop. Los pasos para poder invocar la operación Order de forma asíncrona serían los siguientes:
- Crea una clase que herede de la clase abstracta MyVirtualShopCallbackHandler, llámala OrderCallbackHandler. Aquí tienes su código.
- Sobreescribe el método receiveResultorder con el siguiente código de ejemplo para procesar la respuesta:
public void receiveResultorder(OrderResponse result) {
System.out.println("** Procesando respuesta asíncrona de operación Order…");
System.out.println("** OrderResponse.getIdPedido() = " + result.getIdPedido());
System.out.println("** OrderResponse.getFechaPrevistaEntrega() = " + result.getFechaPrevistaEntrega());
System.out.println("** OrderResponse.getgetTotal() = " + result.getTotal());
System.out.println("** Procesando respuesta asíncrona de operación Order… OK");
} - Sobreescribe el método receiveErrororder con el siguiente código de ejemplo para procesar un posible error:
public void receiveErrororder(Exception e) {
System.out.println("** Error en invocación asíncrona a operación Order…");
e.printStackTrace();
System.out.println("** Error en invocación asíncrona a operación Order… OK");
} - Crea una nueva clase, TestAsynClient, con un método main para probar la llamada asíncrona a la operación Order. Aquí tienes su código. El código para crear el stub y la petición es exactamente el mismo que en el ejemplo de más arriba. Sólo cambia el código para hacer la llamada, que sería el siguiente:
System.out.println("\n*** Invocando operacion Order de forma ASÍNCRONA…");
OneCallbackHandler callback = new OneCallbackHandler();
stub.startorder(orderRequest, callback);
System.out.println("*** El flujo de control del programa continua con la siguiente instruccion a la llamada asíncrona\n");
Thread.sleep(10000); //Espera 10s para que no termine el thread main y por tanto el programa sin recibir antes la respuesta - Para probarlo ejecuta la clase recién creada. Si quieres probar el caso de receiveError sólo tienes que parar el Axis2 y volver a ejecutar la clase.
Conclusiones
Hasta aquí esto es todo. Como habeis visto usar un webservice con Axis2 es rápido y sencillo, pero con otras tecnologías el proceso es muy similar. Que el webservice haya sido creado también con Axis2 es irrelevante. Para crear un cliente sólo necesitamos el wsdl del servicio, es el único punto en común que tienen el cliente y el servicio. Pero, para evitar sorpresas en cuanto a la interoperabilidad se debe desarrollar el wsdl de forma independiente a una tecnología en concreto, sobretodo si es un wsdl complejo. Y a pesar de todo, la interoperabilidad 100% no se puede garantizar. Las sorpresas siempre están ahí, llámense bugs, attachments, estándares,…

