Axis2: Invocación de Servicios Web usando distintos MEP
Introducción.
En este tutorial vamos a llevar a la práctica la invocación de servicios web a través de cada uno de los patrones de intercambios de mensajes (MEP) definidos en WSDL 2.0.
Para ello, vamos a realizar una aplicación de escritorio en donde utilizaremos el API de cliente de Axis2 para invocar la única operación que expone el servicio Web construido en un tutorial anterior » Spring WS: Creación de Servicios Web con Spring con cada uno de los MEP.
Manos a la obra con la aplicación cliente:
El siguiente ejemplo está construido bajo el siguiente entorno:
|
|
Si después de leer este tutorial quieres probarlo tu mismo, en los siguientes enlaces te dejo lo necesario:
- El servicio web en formato WAR, por lo que simplemente deberás desplegarlo en tu servidor o contenedor favorito.
- La parte cliente (proyecto Maven2 y Eclipse) que vemos en este tutorial.
Para poder utilizar los servicios que ofrece un web service necesitamos conocer su descripción o WSDL.
Esta casi siempre es publicada por el servicio web a través de una URL, por ejemplo: http://localhost:8080/bibliotecaWS/bibliotecaWS.wsdl)
WSDL del servicio web a invocar con distintos MEP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
<?xml version="1.0" encoding="utf-8"?> <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://www.adictosaltrabajo.com/spring/ws/schemas" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:tns="http://www.adictosaltrabajo.com/spring/ws/schemas" targetNamespace="http://www.adictosaltrabajo.com/spring/ws/schemas"> <wsdl:types> <xsd:schema xmlns:schemas="http://www.adictosaltrabajo.com/spring/ws/schemas" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.adictosaltrabajo.com/spring/ws/schemas"> <xsd:element name="BooksInfoRequest"> <xsd:complexType> <xsd:sequence> <xsd:element name="categoria" type="xsd:string" /> <xsd:element name="nivel" type="schemas:nivelType" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:simpleType name="nivelType"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="basico" /> <xsd:enumeration value="medio" /> <xsd:enumeration value="avanzado" /> </xsd:restriction> </xsd:simpleType> <xsd:element name="BooksInfoResponse"> <xsd:complexType> <xsd:sequence> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="schemas:libro" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="libro"> <xsd:complexType> <xsd:sequence> <xsd:element name="editorial" type="xsd:string" /> <xsd:element name="titulo" type="xsd:string" /> <xsd:element name="paginas" type="xsd:positiveInteger" /> <xsd:element name="precio" type="xsd:positiveInteger" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types> <wsdl:message name="BooksInfoResponse"> <wsdl:part element="tns:BooksInfoResponse" name="BooksInfoResponse"></wsdl:part> </wsdl:message> <wsdl:message name="BooksInfoRequest"> <wsdl:part element="tns:BooksInfoRequest" name="BooksInfoRequest"></wsdl:part> </wsdl:message> <wsdl:portType name="bibliotecaWS"> <wsdl:operation name="BooksInfo"> <wsdl:input message="tns:BooksInfoRequest" name="BooksInfoRequest"></wsdl:input> <wsdl:output message="tns:BooksInfoResponse" name="BooksInfoResponse"></wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="bibliotecaWSSoap11" type="tns:bibliotecaWS"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="BooksInfo"> <soap:operation soapAction="" /> <wsdl:input name="BooksInfoRequest"> <soap:body use="literal" /> </wsdl:input> <wsdl:output name="BooksInfoResponse"> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:binding name="bibliotecaWSSoap12" type="tns:bibliotecaWS"> <soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="BooksInfo"> <soap12:operation soapAction="" /> <wsdl:input name="BooksInfoRequest"> <soap12:body use="literal" /> </wsdl:input> <wsdl:output name="BooksInfoResponse"> <soap12:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="bibliotecaWSService"> <wsdl:port binding="tns:bibliotecaWSSoap11" name="bibliotecaWSSoap11"> <soap:address location="http://localhost:8080/bibliotecaWS/services" /> </wsdl:port> <wsdl:port binding="tns:bibliotecaWSSoap12" name="bibliotecaWSSoap12"> <soap12:address location="http://localhost:8080/bibliotecaWS/services" /> </wsdl:port> </wsdl:service> </wsdl:definitions> |
Si nos fijamos en el WSDL observamos que (de cara al código fuente que veremos a continuación):
- El servicio Web tiene dos
wsdl:port
de comunicación (líneas 83 a 88) - Sólo hay definido un Servicio Web (líneas 82-89)
- Sólo hay definida una operación
BooksInfo
(líneas 72 a 80) - El formato de la petición a la única operación es el siguiente (líneas 12-26):
1 2 3 4 |
<BooksInfoRequest xmlns="http://www.adictosaltrabajo.com/spring/ws/schemas"> <categoria>Servicios Web</categoria> <nivel>avanzado</nivel> </BooksInfoRequest> |
Axis2MEPApp
: Ejemplo del uso el API de cliente de Axis2 para conseguir invocar un método con distintos patrones de intercambio de mensajes (MEP).
A continuación contruiremos una aplicación de escritorio en donde al ejecutarla podremos elegir el método de invocación al servicio Web: In-Out sincrono, In-Out asíncrono, etc.
El código fuente está autocomentado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
package com.autentia.tutoriales.axis2.cliente; import java.net.URL; import javax.xml.namespace.QName; import org.apache.axiom.om.*; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.client.async.AxisCallback; import org.apache.axis2.context.ConfigurationContext; /** * Ejemplo del uso el API de cliente de Axis2 para conseguir invocar un método * con distintos patrones de intercambio de mensajes (MEP). * @author Carlos García. Autentia */ public class Axis2MEPApp { public static void main(String[] args) { char userOption = '0'; try { while ((userOption < '1') || (userOption > '4')){ System.out.println("Ejemplo de invocación a servicios web con distintos Message Exchange Patterns:"); System.out.println("(1) MEP In-Out Síncrono"); System.out.println("(2) MEP In-Out Asíncrono"); System.out.println("(3) MEP In-Only. Recepción y procesamiento del mensaje asegurada."); System.out.println("(4) MEP In-Only. Recepción y procesamiento del mensaje no asegurada."); System.out.println("Seleccione una opción (1, 2, 3, 4):"); userOption = (char) System.in.read(); } // Usamos la configuración por defecto de Axis2 ConfigurationContext context = null; // URL del WSDL URL wsdlURL = new URL("http://localhost:8080/bibliotecaWS/bibliotecaWS.wsdl"); // Podría ser Null pues sólo hay un servicio en el WSDL QName serviceName = new QName("http://www.adictosaltrabajo.com/spring/ws/schemas", "bibliotecaWSService"); // El WSDL tiene 2 Port elegimos uno. String portName = "bibliotecaWSSoap12"; // Clase cliente básica (No tiene acceso a operaciones más avanzadas como OperationClient) ServiceClient wsClient = new ServiceClient(context, wsdlURL, serviceName, portName); // Operación a invocar QName operation = new QName("http://www.adictosaltrabajo.com/spring/ws/schemas", "BooksInfo"); // Obtenemos la petición a enviar al servicio web OMElement request = Axis2MEPApp.getRequest("Servicios Web", "avanzado"); switch (userOption){ case '1': OMElement response = wsClient.sendReceive(operation, request); // Imprimimos la respuesta System.out.println(response); break; case '2': // CallBack que recibirá la respuesta AxisCallback callback = new MyAxisCallback(); wsClient.sendReceiveNonBlocking(operation, request, callback); for (int i = 0; i < 5; i++){ System.out.println("Continuamos trabajando en otras cosas mientras el servidor procesa la petición" + "(tiempos de red + proceso + configuraciones poco optimas de servidores + ...) y nos responde"); Thread.sleep(100); } break; case '3': wsClient.sendRobust(operation, request); break; case '4': wsClient.fireAndForget(operation, request); // Esperamos un poco para que le de tiempo a enviar la petición (se envia en background) Thread.sleep(200); break; } } catch (Exception ex){ System.out.println(ex); // Es sólo un tutorial, no me preocupo de gestionar bien las excepciones. } System.out.println("FIN DE LA APLICACION"); } /** * @param categoria Categoría del libro a consultar * @param nivel Nivel del libro a consultar, valores permitidos: basico, medio, avanzado * @return Devuelve la petición a enviar al servicio web. */ private static OMElement getRequest(String categoria, String nivel){ /* <BooksInfoRequest xmlns="http://www.adictosaltrabajo.com/spring/ws/schemas"> <categoria>Biología</categoria> <nivel>basico</nivel> </BooksInfoRequest> Nota, también se podría hacer desde un String StAXOMBuilder builder = new StAXOMBuilder(new ByteArrayInputStream("El xml...".getBytes())); OMElement request = builder.getDocumentElement(); */ OMFactory factory = OMAbstractFactory.getOMFactory(); OMNamespace namespace = factory.createOMNamespace("http://www.adictosaltrabajo.com/spring/ws/schemas", ""); OMElement request = factory.createOMElement("BooksInfoRequest", namespace); OMElement categoryNode = factory.createOMElement("categoria", namespace); OMElement nivelNode = factory.createOMElement("nivel", namespace); categoryNode.addChild( factory.createOMText(categoria) ); nivelNode.addChild( factory.createOMText(nivel) ); request.addChild(categoryNode); request.addChild(nivelNode); return request; } } |
MyAxisCallback
: Implementación de un AxisCallback para las operaciones asíncronas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.autentia.tutoriales.axis2.cliente; import org.apache.axiom.om.OMElement; import org.apache.axis2.context.MessageContext; import org.apache.axis2.client.async.AxisCallback; /** * Implementación de un AxisCallback para las operaciones asíncronas. * @author Carlos García. Autentia. */ public class MyAxisCallback implements AxisCallback { /* * @see org.apache.axis2.client.async.AxisCallback#onComplete() */ public void onComplete() { System.out.println("onComplete"); } /* * @see org.apache.axis2.client.async.AxisCallback#onFault(org.apache.axis2.context.MessageContext) */ public void onFault(MessageContext context) { System.out.println("onFault"); OMElement payload = context.getEnvelope().getBody(); System.out.println(payload); } /* * @see org.apache.axis2.client.async.AxisCallback#onError(java.lang.Exception) */ public void onError(Exception ex) { System.out.println("onError " + ex.toString()); } /* * @see org.apache.axis2.client.async.AxisCallback#onMessage(org.apache.axis2.context.MessageContext) */ public void onMessage(MessageContext context) { System.out.println("onMessage"); OMElement payload = context.getEnvelope().getBody(); System.out.println(payload); } } |
Archivo de configuración de Maven 2: pom.xml
:
Por si te sirve de plantilla para otros proyectos, te dejo el pom.xml
del proyecto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.autentia.tutoriales</groupId> <artifactId>bibliotecaWSClienteAxis2</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>bibliotecaWSClienteAxis2</name> <url>http://www.adictosaltrabajo.com</url> <!-- Sintáxis Java 6 --> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> <dependencies> <!-- Todas las dependencias de Axis2 en una --> <dependency> <groupId>org.apache.axis2</groupId> <artifactId>axis2-kernel</artifactId> <version>1.4.1</version> </dependency> </dependencies> </project> |
Referencias
- Reference Guide to Apache Axis2 Client API Parameters.
- Web Services Description Language (WSDL) Version 2.0 Part 1: Core Language.
- Web Services Description Language (WSDL) Version 2.0 Part 2: Adjuncts
- Writing Your Own services.xml for Axis2 Web Services.
Conclusiones
Como veis el API de cliente Axis2 es bastante facil de usar y comprender, siendo además bastante ligero en cuanto número de clases e interfaces.
Aunque con la clase ServiceClient
podemos satisfacer la mayoría de nuestras necesidades de cliente, tenemos disponible la clase OperationClient
para operaciones más avanzadas, como por ejemplo manipulación de cabeceras.
Existen otros patrones MEP en los que es el servidor el que inicia la comunicación Out-Only, etc. ver WSDL 2.0, pero estos se consigen de la misma forma
que la que ya hemos visto, siempre y cuando el servidor conozca las direcciones de los clientes. Si estais interesados podeis mirar los ejemplos de la distribución para profundizar más….
Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.
cgpcosmad@gmail.com