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,…

Hola Julio.
He probado el cliente con AXIS2 1.3 tanto del motor como del plugin para Eclipse. La parte servidora parece que va bien (porque no veo nada que me diga lo contrario, el servicio esta desplegado) pero cuando ejecuto el Test visualiza lo siguiente:
log4j:WARN No appenders could be found for logger (org.apache.axis2.description.AxisService).
log4j:WARN Please initialize the log4j system properly.
*** Stub creado para endpoint = http://localhost:8080/axis2/services/myVirtualShop
*** Invocando operacion GetCatalog…
Exception in thread “main” org.apache.axis2.AxisFault: Please implement org.example.www.myvirtualshop.wsdl.MyVirtualShopSkeleton#getCatalog
at org.apache.axis2.util.Utils.getInboundFaultFromMessageContext(Utils.java:486)
at org.apache.axis2.description.OutInAxisOperationClient.handleResponse(OutInAxisOperation.java:343)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:389)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:211)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:163)
at org.example.myvirtualshop.wsclient.MyVirtualShopStub.getCatalog(MyVirtualShopStub.java:205)
at org.example.myvirtualshop.wsclient.TestClient.main(TestClient.java:29)
Saludos.
Comment by Andres Ortega — October 2, 2007 @ 11:49 am
Hola Andres.
Tienes que implementar el metodo getCatalog en org.example.www.myvirtualshop.wsdl.MyVirtualShopSkeleton. Revisa el tutorial del webservice.
Un saludo.
Comment by Administrator — October 5, 2007 @ 6:18 pm
Cuando hago clic en Finish del wizard me tira este error el eclipse:
“An error ocurred while completing process -java.lang.InterruptedException: Encoded use is not suported”
¿Como puedo solucionarlo?
Comment by Leandro — October 17, 2007 @ 2:04 pm
Hola Leando.
Nunca me dio ese error. ¿Qué versiones de Axis2, Axis2 Codegen plugin, Eclipse y Java estas usando?
Comment by Administrator — October 17, 2007 @ 8:27 pm
Hice un web service con el plugin de Eclipse Europa para Axis2, y del lado del cliente puse una consulta a una aplicación que tengo corriendo, y cuando trata de conectarse a la base de mi aplicación a través de la API me da este error del lado del lado del cliente:
org.apache.axis2.AxisFault: org/pimslims/hibernate/ModelImpl
at org.apache.axis2.util.Utils.getInboundFaultFromMessageContext(Utils.java:486)
at org.apache.axis2.description.OutInAxisOperationClient.handleResponse(OutInAxisOperation.java:343)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:389)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:211)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:163)
at experiments.GetExperimentsStub.getListElementForIntegration(GetExperimentsStub.java:156)
at experiments.ClientExperiments.main(ClientExperiments.java:32)
Comment by Ramiro — October 18, 2007 @ 4:01 am
Hola Ramiro.
Tiene toda la pinta de que la excepcion ocurre en el servidor y es un error de aplicacion, no de axis2.
Comment by Administrator — October 19, 2007 @ 12:35 pm
Hola, he estado revisando por toda la red temas de web service, y sigo sin encontrar una solución, si pueden ayudarme seria de lujo, el problema es el siguiente, tengo un web service con SOAP que tiene que ser invocado desde un CGI, pero no he encontrado como invocarlo.
Podrian ayudarme?
Comment by Humberto Balleza — January 9, 2008 @ 5:52 pm
Hola Julio,
Yo he seguido tus instrucciones al pie de la letra para construir un cliente y cuando ejecuto el código me dice:
Exception in thread “main” org.apache.axis2.AxisFault: The service cannot be found for the endpoint reference (EPR) 127.0.0.1/axis2/services/myVirtualShop
Si testeo esta URL en el navegador obtengo respuesta, pues he arrancado el servidor de axis para probar los servicios
Sera una tonteria pero no lo veo
un saludo y gracias por el articulo
Comment by Gmanao — March 18, 2008 @ 6:40 pm
Estas fijando el endpoint correctamente?
String sEndPoint = “http://localhost:8080/axis2/services/myVirtualShop”;
MyVirtualShopStub stub = new MyVirtualShopStub(sEndPoint);
Me mosquea que en la URL que pones en tu msg no sale el puerto 8080
Comment by Julio — March 18, 2008 @ 6:57 pm