Un web service puede procesar mensajes de protocolo (por lo general SOAP) o payloads de un mensaje (para SOAP, los contenidos del elemento body de un mensaje SOAP). Un Web Service de esta naturaleza debe implementar la interfaz javax.xml.ws.Provider ser anotada con @WebServiceProvider. Veamos como se ve uno de estos Web Services:
Antes de empezar esto, es necesario decir que contábamos con el Schema Definition (StringProcessorService_payloads.xsd) y el documento WSDL (StringProcessorService.wsdl). Con estos archivos y la herramienta XJC hemos creado las siguientes clases Java, necesarias para el trabajo con JAXB:
ObjectFactory.java
package-info.java
ReverseStringReq.java
ReverseStringResp.java
Habiendo dicho esto, proseguimos.
La anotación @WebServiceProvider se utiliza cuando la clase service endpoint implementa la interfaz Provider. Como se ve, la clase de arriba no contiene métodos a ser expuestos como operaciones de Web Service, sino sólamente el método invoke. El método invoke será "invocado" cada vez que el web service reciba un request y el mensaje de protocolo - o el payload del mensaje- serán enviados como parámetros al método invoke.
La anotación @ServiceMode nos indica si la clase provider recibirá y producirá mensajes de protocolo completos o sólamente los payloads. En el ejemplo, se opta por lo segundo.
Los valores de arriba son los valores por defecto, así que podríamos haber omitido la anotación.
Al declarar la clase provider:
public class StringProcessor implements Provider
Las clases Service Endpoint deben implementar la interfaz javax.xml.ws.Provider, para permitirles el recibir y procesar mensajes de protocolo o payloads de mensaje. La especificación JAX-WS da soporte a los siguientes providers:
Provider En el modo Payload de mensaje. La interfaz Source es implementada por las siguientes clases: DOMSource, JAXBSource, SAXSource, StaxSource, StreamSource
Provider En modo mensaje
Provider
En lo referente a la clase Provider, se establecen también las siguientes restricciones:
Las clases Provider deben implementar el constructor por defecto, o no implementar ninguno.
Una clase provider debe estar ligada a un tipo específico. No se puede implementar Provider
Una clase provider debe estar anotada con @WebServiceProvider
En este ejemplito vamos a utilizar la herramienta wsgen para generar los artefectos del web service antes del despliegue. Este servicio tendrá una Service Endpoint Interface (SEI) que contendrá los métodos que la Service Implementation Bean expondrá como operaciones de Web Service. Si es que no generamos la SEI explícitamente, el runtime de JAX-WS se encargará de hacerlo.
Service Implementation Bean
Esta clase contendrá sólamente dos métodos: un método de inicialización y un método que será expuesto como Web Service. Dado que vamos a generar el documento WSDL y las clases wrapper de request y response con la herramienta wsgen, hemos hecho uso extensivo de anotaciones. Así:
public int add (final int intNumber1, final int intNumber2){
System.out.println("Adding "+ intNumber1 + " and " + intNumber2);
return intNumber1 + intNumber2;
}
}
La anotación @WebService le indica a JAX-WS que esta clase contiene métodos que van a ser expuestos como Web Services. Además, contiene los siguientes elementos opcionales:
name="Calculator"
Nombre del Web Service. En WSDL 1.1, esto se traduciría en el nombre del elemento <wsdl:portType> : <portType name= "Calculator">
serviceName="CalculatorService"
Nombre del Servicio para el Web Service. En WSDL 1.1, esto se traduciría en el elemento <wsdl:service>: <service name= "CalculatorService">
Los valores de arriba son los valores por defecto para la anotación SOAPBinding. Nótese que el parameter Style Wrapped es necesario dado que hay más de un parámetro en el método a exponer como Web Service.
También, tenemos una variable de instancia anotada con @Resource:
@Resource private WebServiceContext mWSContext;
Que le permite a la clase del Web Service acceder al contexto del Web Service.
El primer métodos de la clase es init():
@PostConstruct
@WebMethod(exclude=true)
public void init()
La anotación @PostConstruct marca al método para que sea invocado después de realizarse la inyección de dependencia en la clase de WS, pero antes que WS entre en uso.
La anotación @WebMethod se utiliza para excluir al método init() de ser expuesto como operación de Web Service.
Finalmente, tenemos al método add(), que queremos exponer como Web Service:
Han muerto 199 niños en el Perú. Eso implica que 598 padres peruanos los lloraron intensamente, y compraron esos ataúdes pequeños que no bastan para enterrar ese dolor tan grande. En 199 salones de clases, una carpeta vacía les recuerda a otros niños que uno de sus amigos se fue, y que es muy probable que ellos también se vayan de la misma manera. Casi 200 pequeños que pudieron llegar a ser hombres, y que ahora sólo son una gran ausencia.
Y parece que estas 200 muertes no les importa a nadie, o quizás les importa a pocos; porque prácticamente ningún medio peruano le da cobertura a esta tragedia. Son 200 niños peruanos, pero son 200 niños andinos. ¿Será por eso que nadie dice nada? ¿Es que nacer en la sierra te hace anónimo, invisible e intrascendente?
A mi entender, no. Y el ministerio de Salud usa millones de soles del tesoro en comprar antivirales contra una pandemia que , afortunadamente, no ha matado aún a ningún compatriota; mientras que 200 niños ya han muerto... de frío. Y nadie dice nada.
“Que decenas de seres humanos se mueran de frío por ser pobres, anónimos, desprotegidos y vivir en las punas de nuestro país”, dice Elmer Huerta, en una columna bastante recomendable de Guillermo Giacosa. No parece justo, ni digno. Es necesario actuar, y es necesario que el Perú se entere.
¿Tienes un blog? Únete a la causa. Todos deben saberlo, y todos tenemos el deber de hacer algo.
El estilo document es valor defecto según WS-Basic Profile. Este estilo nos permite, a través de XSD en la sección types del WSDL, definir los tipos de datos para los mensajes SOAP.
Al usar la convención wrapped, se le da a los servicios con estilo document el look and feel de un servicio de estilo rpc. Primero, veamos un mensaje SOAP unwrapped:
El body del SOAP envelope de request unwrapped tiene dos elementos: num1 y num2. Estos números deben sumarse. El cuerpo del SOAP no contiene el nombre de la operación del servicio que va a realizar la suma y enviar el resultado como respuesta. Por otro lado, el body del SOAP Envelope de request wrapped tiene un sólo elemento: addNums (que se corresponde con la operación a invocar) y dos subelementos, cada uno conteniendo el número a sumar. La versión wrapped hace la operación de servicio explícita. Los argumentos de la operación estñan anidados, como sub-elementos dentro del elemento de la operación addNums.
Para implementar la convención wrapped, para el estilo document, hacer lo siguiente:
El cuerpo del SOAP Envelope debe tener sólamente una parte, o sea, un sólo elemento XML con la cantidad de sub-elementos requerida
La relación entre el XSD del WSDL y el elemento XML del cuerpo SOAP está definida
Los elementos del XSD nos sirven como wrappers del cuerpo del mensaje SOAP. Así:
En la sección bindings del WSDL, el atributo style puede tomar los valores de rpc y document, donde document es el valor por defecto. El atributo use puede tomar los valores de literal y encoded, donde literal es el valor por defecto. Entonces, las combinaciones posibles de style y use serían:
De todas las combinaciones listadas, las más frecuentes de Web Services basados en SOAP son document/literal y rpc/literal. El valor encoded del atributo use es válido en un documento WSDL, pero no se ajusta a las directivas de WS-I (Web Services- Interoperability).
Servicios document-style
El estilo document indica que los mensajes de nuestro Web Service basado en SOAP contienen documentos XML completos. Por otro lado, el estilo rpc indica que los mensajes SOAP contienen parametros en los mensajes request y valores de retorno en mensajes response. Veamos un WSDL con estilo rpc:
La sección types está vacía porque el servicio utiliza, como valores retornados, sólamente los tipos simples xsd:string y xsd:long. Los tipos simples no requieren una definición en la sección types del WSDL. También, los atributos name de los mensajes muestran la relación con los métodos Java (@WebMethods) correspondientes (getTimeAsString y getTimeAsElapsed). Los mensajes response tienen un sub-elemento part, que contiene el tipo de dato del valor devuelto. Como los mensajes request no requieren argumentos (en este caso), los mensajes request no necesitan sub-elementos part.
Ahora, el WSDL de TimeServer, pero usando como estilo document:
El documento XSD define cuatro tipos complejos cuyos nombres son los nombres de los mensajes correspondientes en la seccción messages. En el estilo rpc, los mensajes llevan los nombresde los @WebMethods; mientras que con el estilo document los mensajes llevan los nombres de los tipos XSD definidos en la sección types del WSDL.
El estilo document soporta servicios con tipos de datos definidos explícitamente, dado que el WSDL del servicio puede hacerlo mediante un documento XSD. Desde una perspectiva de arquitectura, el estilo document es más simple dado que el cuerpo del mensaje SOAP es un documento autocontenido y preciso. El estilo rpc requiere mensajes con los nombres de las operaciones asociadas y con parámetros como sub-elementos.
Ahora, el atributo use determina como los tipos de datos del servicio van a ser codificados y decodificados. En el servicio, el documento WSDL tiene que especificar como los tipos de datos usados en el lenguaje de implementación (Java) son serializados a tipos soportados por WSDL (tipos de esquemas XML). Por el lado del cliente, los tipos soportados por WSDL deben ser deserializados en tipos del lenguaje del cliente (Ruby). La configuración use='literal'implica que los tipos del servicio siguen el esquema XML del documento WSDL "literalmente". Por otro lado, la configuración use='encoded' significa que los definiciones de tipos del servicio provienen de reglas de codificación, por lo general reglas de codificación de la especificación SOAP 1.1.
Un documento WSDL es un contrato entre un servicio y sus consumidores. Este contrato establece els ervice endpoint, las operaciones del servicio, y los tipos de datos requeridos para estas operaciones. El elemento más externo de un WSDL se llama definitions, dado que nos provee definiciones agrupadas en las siguientes secciones:
La sección types (opcional), nos provee definiciones de tipos de datos sobre un sistema de tipos de datos, como un esquema XML. un documento que define tipos de datos es un XSD (XML Schema Definition). La sección de tipos mantiene referencias a XSD's. Si esta sección está vacía, sólamente se utilizan tipos de datos simples, como xsd:string y xsd:long.
La sección message define los mensajes que implementan el servicio. Los mensajes son construidos en base a los tipos de datos definidos en la sección anterior. El orden de los mensajes indica el patrón de servicio.Los mensajes in son los que llegan al servicios, mientras que los out son enviados desde el servicio. Si el orden de los mensajes es in/out, entonces el patrón es request/response, mientras que si el orden es out/in el patrón es solicit/response.
La sección portType nos presenta el servicio como un grupo de operaciones, donde cada operación tiene uno o más mensajes. Las operaciones son nombradas de acuerdo a los métodos anotados con @WebMethod.
La sección binding es donde las definiciones WSDL se concretizan. Un binding WSDL es una especie de "implementación" de un portType WSDL, y provee detalles concretos del servicio como:
El protocolo de transportea usar al enviar y recibir los mensajes SOAP. Se puede usar HTTP o SMTP como protocolo de capa de aplicación. Por ejemplo:
El valor del atributo transporte señala que los mensajes SOAP del servicio van a ser enviados y recibidos sobre HTTP.
El estilo del servicio, puede tomar los valores de rpc o document. El estilo document es el valor por defecto. La anotación:
@SOAPBinding(style= Style.RPC)
Configura el atributo style para que tenga el valor de rpc, el documento WSDL generado.
La sección service especifica uno o más endpoints donde la funcionalidad del servicio -el total de sus operaciones- estará disponible. En términos técnicos, la sección service lista uno o más elementos port, donde un port consiste en un portType jutno a su binding correspondiente
@WebService public class Teams{ private TeamsUtility utils;
public Teams(){ utils = new TeamsUtility(); utils.make_test_teams(); }
@WebMethod public Team getTeam(String name){ return utils.getTeam(name); }
@WebMethod public List getTeams(){ return utils.getTeams(); } }
El servicio Teams ha sido implementado en una sóla clase Java, en vez de usar una SEI y un SIB. La operación getTeam recibe un parámetro y retorna una instancia de Team, que es un tipo definido por el programador, que ha su vez es una lista de instancias de Player (otro tipo definido por el programador). Por otro lado, la operación getTeams nos devuelve List, o sea una colección Java.
En un post anterior, se vio que la SEI para el servicio TimeServer contenía la siguiente anotación:
@SOAPBinding(style = Style.RPC)
Esta anotación requiere que el servicio utilice sólamente tipos simples, como String e Integer. Sin embargo, el servicio Teams utiliza tipos más complejos, por lo que el estilo a utilizar es Style.DOCUMENT (el valor por defecto). Este estilo requiere más trabajo de configuración.
Para desplegar el Web Service es necesario compilar a todas las clases involucradas: TeamService, Team, Player, TeamsUtility y TeamsPublisher, cuyo código es:
package ch01.team;
import javax.xml.ws.Endpoint;
class TeamPublisher{ public static void main(String[] args){ int port= 8888; String url = "http://localhost:"+ port+ "/teams"; System.out.println("Publicando teams en el puerto: " + port); Endpoint.publish(url, new Teams()); } }
Ahora necesitaremos la ayudita de wsgen, que está incluido en core Java 6. wsgen generará varios artefactos, entre los que están las clases Java que necesita Endpoint.publish para generar el WSDL.
Ahora si podemos ejecutar TeamPublisher y tener nuestro WS operativo. Para generar el cliente podemos recurrir a wsimport (también en core Java 6), con lo que tendríamos algo como esto:
class TeamClient{ public static void main(String[] args){ TeamsService service = new TeamsService(); Teams port = service.getTeamsPort(); List teams = port.getTeams(); for (Team team: teams){ //... } } }
Puede sernos útil en algún momento el generar y procesar mensajes SOAP. Veamos un poco del SOAP API, mediante una aplicación que simula enviar un mensaje SOAP como request y recibir otro como response:
public class DemoSoap{ private static final String LocalName = "TimeRequest"; private static final String Namespace = "http://ch01/mysoap/"; private static final String NamespacePrefix = "ms";
private void process_incoming_soap(){ try{ coordinate_streams(); SOAPMessage msg = create_soap_message(in); Name lookup_name = create_qname(msg); SOAPHeader = msg.getSOAPHeader(); Iterator it = header.getChlidElements(lookup_name); Node next = (Node)it.next(); String value = (next==null)?"Error!": next.getValue(); if (value.toLowerCase().contains("time_request")){ String now = new Date().toString(); SOAPBody body = msg.getSOAPBody(); body.addBodyElement(lookup_name).addTextNode(now); msg.saveChanges(); msg.writeTo(out); trace("The received/processed SOAP message: ", msg); } } catch (SOAPException e){System.err.println(e);} catch (IOException e){System.err.println(e);}
}
private void coordinate_streams(){ in = new ByteArrayInputStream(out.toByteArray()); out.reset(); }
private void extract_contents_and_print(SOAPMessage msg){ try{ SOAPBody body = msg.getSOAPBody(); Name lookup_name = create_qname(name); Iterator it = body.getChildElements (lookup_name); Node next = (Node) it.next(); String value = (next==null)? "Error!": next.getValue(); System.out.println("\n\nReturned from server: " + value); } catch (SOAPException e){System.err.println(e);} } }
Entonces, esta sencillísima y breve aplicación genera un mensaje SOAP y añade la cadena time_request al header del SOAP Envelope. Eso lo hace en las líneas:
En la que el primer argumento es una colección de headers de la capa de transporte (como los headers HTTP) y el segundo es un inputStream con los bytes para crear el mensaje. Una vez que el mensaje es creado, se extraen las cabeceras del SOAP Envelope y se inserta un nodo de texto XML con el valor de time_request. El mensaje SOAP resultante sería:
El cuerpo del mensaje (SOAP body) siempre es obligatorio, pero puede estar vacío. La SOAP header es opcional, pero en este caso contiene el texto time_request.
EL método request envía el mensaje SOAP a través de un ByteArrayOutputStream, para simular el enviarlo a través de la red a otro host. El método request invoca al método process_request, que a su vez delega el procesamiento a otros métodos. En fin, el mensaje SOAP es creado de un ByteArrayInputStream,dado que este stream contiene el mensaje SOAP. Así:
Y luego procesamos este mensaje SOAP para extraer la cadena time_request. La extracción se hace así: se extrae la SOAP Header del mensaje SOAP y se itera sobre los elementos con el nombre de tag:
Una vez encontrado, se busca que contenga la cadena time_request con la siguiente lógica:
SOAPHeader header = msg.getSOAPHeader(); Iterator it = header.getChildElements(lookup_name); Node next = (Node) it.next(); String value = (next == null) ? "Error!" : next.getValue();
Si la SOAP header contiene la cadena solicitada, se extrae el SOAP Body del mensaje SOAP recibido, y se le añade un elemento conteniendo la hora actual. El mensaje SOAP modificado es enviado como respuesta. Así:
if (value.toLowerCase().contains("time_request")) { String now = new Date().toString(); SOAPBody body = msg.getSOAPBody(); body.addBodyElement(lookup_name).addTextNode(now); msg.saveChanges();
class TimeClient{ URL url = new URL("http://localhost:9876/ts?wsdl"); QName qname = new QName ("http://ts.ch01", "TimeServerImplService"); Service service = Service.create(url, qname); TimeServer eif = service.getPort(TimeServer.class); System.out.println(eif.getTimeAsString()); System.out.println(eif.getTimeAsElapsed()); }
A ver. Se observa que hemos instanciado la clase QName como nombre calificado XML del servicio. El primer argumento es el URI del servicio, y el segundo es el nombre del servicio publicado en el WSDL. Una vez creado el URL (apuntando a la ruta del WSDL) y el QName, se invoca al método Service.create, para crear una factory para el servicio.
Luego se invoca al método getPort del service creado, que extrae una endpoint interface ("port" del servicio). En el documento WSDL, la sección portType describe las operaciones a incluir en el Web Service. El método getPort devuelve una referencia a un objeto Java que pueda invocar a las operaciones de este porType. La referencia a port es del tipo TimerService, que es la SEI del Web Service.
En los Web Services basados en SOAP, un cliente realiza una llamada remota a un procedimiento del servicio invocando a una de las operaciones del Web Service. Para realizar esto se realiza un intercambio de mensajes request y response, que en este caso son mensajes SOAP. Imaginémonos un cliente PERL, que genera un request HTTP para invocar a un Web Service. El cuerpo de este request es un mensaje SOAP. Veámoslo:
En start line HTTP se observa el método request (POST). Se utiliza POST en lugar de GET, dado que los request POST poseen un cuerpo, que encapsula nuestro mensaje SOAP. Después de esto viene el URL del request, seguido de la versión de HTTP (1.1)
A continuación, vienen los headers HTTP, que son pares código/valor, separados por dos puntos. El código Accept se ve tres veces, especificando que el cliente puede aceptar una respuesta XML, una respuesta con adjuntos de cualquier tipo y un documento SOAP. El código SOAPAction siempre esta presente en los headers HTTP de request a Web Services.
Tenemos dos saltos de línea, que separan a los headers HTTP del cuerpo del HTTP. Aquí, el cuerpo HTTP contiene el documento SOAP (o SOAP envelope). El elemento más externo del documento se denomina Envelope, y dentro del Envelope se encuentra el SOAP body que contiene un sólo elemento, cuyo nombre local es getTimeAsString, que se corresponde con el nombre de la operación a invocar en el Web Service.
Por el lado del Web Service, las librerías Java procesan el request HTTP, entraen el SOAP Envelope y determinan que operación del servicio invocar. Se llama al método Java correspondiente (getTimeAsString) y se genera un mensaje SOAP para llevar el resultado del método al cliente. El response HTTP del WS sería así:
Una vez más, el SOAP Envelope es el cuerpo del mensaje HTTP. El start line contiene el código de estatus (200) y el texto correspondiente (OK), indicándonos que se procesó bien la solicitud del cliente. El SOAP Envelope contiene la hora actual entre las tags XML return. La librería SOAP del Perl extrae el SOAP Envelope del response HTTP y, según lo indicado en el documento WSDL, extrae el resultado de la operación del contenido del elemento return.
Para compilar y desplegar Web Services con Java, lo único que necesitamos es descargar la Java Stantard Edition 6 (o superior). Java 6 soporta JAX-WS (Java API for XML Web Services), la cúal a su vez soporta servicios basados en SOAP y de estilo REST.
Un Web Service basado en SOAP puede implementarse en una clase Java, pero debería existir una interfaz que declare los métodos (operaciones del Web Service) y una implementación que defina estos métodos. A la interfaz la llamaremos SEI (Service Endpoint Interf ace), y a la implementación la llamaremos SIB (Service Implementation Bean). El SIB puede ser un POJO, o un EJB.
@WebService @SOAPBinding (style= Style.RPC) public interface TimeServer{ @WebMethod String getTimeAsString(); @WebMethod long getTimeAsElapsed(); }
Aquí, la anotación @WebService nos indica que se trata de una SEI. La anotación @WebMethod nos señala que cada método es una operación del Web Service. La anotación @SOAPBinding tiene impacto sobre la construcción del contrato del servicio: el documento WSDL (Web Services Definition Language). Style.RPC simplifica este contrato, y con ello el despliegue. Ahora, conozcamos al SIB:
@WebService(endpointInterface= "ch01.ts.TimeServer") public class TimeServerImpl implements TimeServer{ public String getTimeAsString{ return new Date().toString();} public long getTimeAsElapsed() {return new Date().getTime();} }
La propiedad endpointInterface de la anotación @WebService enlaza el SIB (TimeServerImpl ) junto a la SEI correspondiente (TimeServer). Nótese que las implementaciones de los métodos no están anotadas como @WebMethods.
Para desplegar la aplicación, compilamos los archivos Java; y como se trata de un ejemplito inofensivo usamos una clase Java para su publicación (en la vida real se utilizaría un servidor de aplicaciones):
package ch01.ts; import javax.xml.ws.Endpoint; public class TimeServerPublisher{ public static voiud main(String [] args){ Endpoint.publish("http://127.0.0.1:9876/ts", new TimeServerImpl()); } }
Con esta clasecita, publicamos el Web Service cuyo SIB es TimeServerImpl en la dirección 127.0.0.1 (localhost) y el puerto 9876, con ruta de publicación /ts. Utilizamos a la clase Endpoint y a su método publish, donde el primer argumento es el URL de publicación y el segundo una instancia del SIB del servicio. Para el ver el contrato del servicio (documento WSDL), bastaría compilar y ejecutar esta clase, y luego acceder al este URL : http://127.0.0.1:9876/ts?wsdl mediante un browser. Si todo salió bien, esto sería una parte de lo obtenido:
La sección portType de este documento WSDL (en negrita) agrupa las operaciones ofrecidas por el Web Service (getTimeAsString y getTimeAsElapsed), que se corresponden con los métodos declarados en el SEI e implementados en el SIB. El portType de un WSDL es como una interfaz Java, ya que nos muestra las operaciones del servicio pero no da detalles de la implementación. Cada operación del Web Service consiste en un mensaje input y un mensaje output. En tiempo de ejecución, cada mensaje es un documento SOAP. La otra sección en negrita es service, donde se observa que el atributo location es la URL de publicación. Este URL se denomina service endpoint, e informa a los clientes sobre donde pueden acceder al servicio.
El documento WSDL es útil para crear y ejecutar clientes para un web service. Para generar un cliente en base al WSDL , se puede usar la herramienta wsimport.
Creo que en los anteriores posts me fui de boca y le puse mucho énfasis a los estándares y a los acrónimos (bueno, en parte era culpa de lo que estaba leyendo xD). En fin, pienso subsanar el error, y cambiar de libro xD. Así que comencemos de nuevo con lo de los Web Services.
Un Web Service es una especie particular de aplicación Web, o sea , que se ejecuta sobre HTTP. Es una aplicación distribuida cuyos componentes pueden ser desplegados y ejecutados en muchos dispositivos.
Existen dos tipos de Web Services: Basados en SOAP y de estilo REST. SOAP significa Simple Object Access Protocol, y es un dialecto XML donde los documentos son los mensajes a transmitir. En un escenario típico, la librería SOAP del cliente envía un mensaje SOAP solicitando un servicio, y la librería SOAP del Web Service envía otro mensaje SOAP como respuesta del servicio. Así:
Por otro lado, REST significa Representational State Transfer. SOAP posee estándares, herramientas y librerías. REST no tiene estándares, pocas herramientas y aún menos librerías. Por eso, se considera a REST como una alternativa simple a la complejidad de SOAP.
Por lo general, un cliente de un Web Service (basado en SOAP o de estilo REST) no es un browser, sino una aplicación sin interfaz de usuario. Este cliente puede estar escrito en cualquier lenguaje, y el Web Service y el cliente no deben estar escritos en el mismo lenguaje necesariamente.
Las tecnologías XML son las que dan soporte a esta interoperabilidad, y permiten el intercambio y procesamiento de documentos estructurados. Así, para un Web Service basado en SOAP, el cliente envía un documento SOAP como request al Web Service, el cual devuelve otro documento SOAP como response. Para servicios de estilo REST, el cliente envía un request HTTP estándar al web Service, y recibe un documento XML como respuesta.
Algunas características de los Web Services:
Infraestructura abierta: Los WS se despliegan usando estándares de la industria, y protocolos independientes del vendedor como HTTP y XML.
Transparencia de lenguajes: Los Web Services y sus clientes puede interactuar aún si han sido escritos en lenguajes diferentes.
Diseño modular: Nuevos servicios pueden ser generados a través de la integración e interacción de servicios existentes.
WSEE 1.2 define una arquitectura y empaquetamiento que asegure la portabilidad de los Web Services a través de servidores de aplicaciones Java EE.
Port Component Un port component es lo que se empaqueta y despliega en el contenedor para implementar un Web Service.
Un port component define los artefactos que constituyen una aplicación Web Service portable, incluyendo a los Service Implementation Beans (SIB). El SIB es el único artefacto obligatorio . Opcionalmente, se puede incluir dentro del port component: documento WSDL, SEI (Service Endpoint Interface) y el descriptor webservices.xml.
Servlet Endpoints Según WSEE 1.2, un POJo, siempre y cuando cumpla con los requisitos de WS-Metadata para un SIB, puede ser usado para implementar un Web Service a ser desplegado en un contenedor Web. A este POJO, se le conoce también como servlet endpoint.
EJB Endpoint Según WSEE 1.2, un stateless session bean puede ser usado para implementar un Web Service a ser desplegado en un contenedor EJB. A este EJB lo llamamos EJB endpoint.
Empaquetamiento simplificado Para muchos escenarios, no es necesario el uso de descriptores de despliegue.
Modelo de Programación para Handlers Las anotaciones para Handlers son definidas en WS-Metadata 2.0; pero el modelo de programación y de ejecución es descrito en WSEE 1.2. La anotación @HandlerChain asocia una handler chain con un port component.
WS-Metadata 2.0 define las anotaciones estándar utilizadas para desarrollar y desplegar Web Services mediante Java SE 6 o dentro de un contenedor Java EE 5. Para mayor detalle, veamos un ejemplito:
@WebService marca esta clase Java como un Web Service, de modo que la implementación de JWS sepa que debe ser desplegada
@SOAPBinding indica que este Web Service (WS) utiliza el protocolo SOAP
El elemento @SOAPBinding.style indica que el WS debe ser desplegado usando el estilo document. Esta anotación configura el atributo style del elemento soap:binding del WSDL como se observa en el gráfico
El elemento @SOAPBinding.use indica que los mensajes para este WS deben ser enviados usando el formato literal (o sea , no encoded). Esta anotación afecta el atributo use de los elementos soap:body
El elemento @SOAPBinding.parameterStyle indica que los mensajes para este WS deben ser parámetros wrapped. Así, el atributo name del parámetro wrapper se denomina "SubmitPO"
El elemento @WebMethod.operationName especifica que el atributo name de la operación del WSDL debe ser "SubmitPO"
El elemento @WebResult.name especifica que el mensaje de respuesta debe ser un elemento llamado "PurchaseOrderAck"
El elemento @WebParam.name especifica que el nombre del parámetro de request correspondiente al parámetro Java purchaseOrder debe llamarse "PurchaseOrder"
Anotaciones para mapping WSDL Permiten darle forma al mapping WSDL/Java especificando información como el nombre de operación para un método Java en particular. En el gráfico anterior tenemos : @WebService, @WebResult y @webParam.
Anotaciones para binding SOAP Nos permiten personalizar el estilo de binding SOAP, así como el uso y el estilo de parámetros. Los valores por defecto de JAX-WS para estilo de binding, uso y estilo de parámetros son document, literal y wrapped, respectivamente. Sin embargo, mediante la anotación @SOAPBinding se pueden especificar otras posibilidades, como rpc/literal o document/literal.
Anotaciones para Handlers El despliegue de handlers se especifica mediante anotaciones de WS-Metadata. La anotación javax.jws.HandlerChain se utiliza para asociar un Web Service con una handler chain definido en un archivo externo referenciado mediante @HandlerChain.file.
Service Implementation Bean WS-Metadata define los requisitos para desplegar una clase Java como web Service. Las clases que cumplen estos requerimientos se denomina Service Implementation Beans (SIB's). Tanto los POJOs como los EJB's que cumplan estos requisitos pueden desplegarse como Web Services
Comenzar desde WSDL y Java El modelo de desarrollo "Comenzar desde WSDL y Java" es soportado mediante anotaciones que mapean componentes de clase Java existente con componentes de un documento WSDL. Por ejemplo la anotación @WebMethod.operation-name se utiliza para asociar un método a una wsdl:operation pre-existente.
Despliegue automático El despliegue automático es un modelo de despliegue "copiar y pegar", similar al utilizado para páginas JSP. Es parte de la especificación de WS-metadata, pero no es obligatorio. En este modelo, el despliegue en tiempo de ejecución del WS depende totalmente de anotaciones, y no se requieren herramientas del vendedor. Glassfish posee esta funcionalidad.
Binding run-time framework Es este framework el que implementa las operaciones de marshal y unmarshal.
Marshaling es el proceso de convertir instancias de clases anotadas en una representación XML (como un documento XML en un archivo o un DOM). Por otro lado, unmarshalling es el proceso de convertir una representación XML en un árbol de objetos.
Veamos como funciona este framework al invocar un Web Service:
La aplicación recibe un mensaje SOAP en el endpoint desplegado
JAX-WS prepara una instancia del contexto del mensaje (javax.xml.ws.handler.soap.SOAPMessageContext), que incluye la representación SAAJ (SOAP with Attachments API for Java) del mensaje.
JAX-WS invoca a los handlers.
Después que los handlers terminan, el runtime de JAXB ejecuta el unmarshal de los contenidos del mensaje SOAP dentro de un bean request.
Este bean request, construido por el runtime de JAXB, contiene los objetos Java que son pasados como parámetros al método Java que implementa el Web Service. JAX-WS invoca al método usando estos parámetros.
JAX-WS obtiene un objeto Java como resultado de la invocación del método, y lo utiliza para crear una instancia de un bean response.
JAXB ejecuta el marshalling del bean response.
Como en el paso 2, JAX-WS actualiza el contexto del mensaje para incluir la representación SAAJ del response.
JAX-WS invoca a los handlers de response.
JAX-WS envía el mensaje response a través de un protocolo de transporte.
Entonces, la deseralización ocurre en dos etapas. Un request SOAP comienza con su formato original (en el gráfico, un paquete MIME). La primera etapa convierte este formato a una representación XML y provee acceso SAAJ. Es en esta forma que llega al handler. La segunda etapa aplica unmarshalling a la representación XML y la convierte en objetos Java. Estos objetos se envían Web Service como parámetros para la invocación de métodos.
Validación: Para implementar un Web Service, es indispensable controlar como se va a tratar XML inválido, respecto al esquema WSDL/XML que define al Web Service.
A diferencia de JAXB 1.0, el unmarshalling de XML inválido es permitido por JAXB 2.0 (por defecto, pero puede deshabilitarse). Si la validación está activada, el comportamiento por defecto es lanzar una excepción al primer error.
Portabilidad La portabilidad es lograda mediante las anotaciones para mapping. El marshaller de cualquier implementación JAXB debe ser capaz de serializar una clase anotada de JAXB a una instancia de su esquema XML objetivo. Así, el unmarshaller debe ser capaz de deserializar una inastancia de un esquema a una instancia de una clase anotada JAXB.
Esto implica que uno puede desplegar un Web Service construido de clases anotadas JAXB cualquier plataforma JAXB 2.0 (como SAP Netweaver, JBoss, Glassfish) sin tener que recompilar el esquema y/o reestructurar el Web Service para usar clases de implementación propias del entorno.
Marshal event callback Los callbacks permiten procesamiento específico de nuestra aplicación al momento de serializar. Estos procesos pueden ocurrir antes de serializar o después de la serialización. Esto es bastante útil cuando queremos asignar valores a propiedades fuera del proceso de serialización.
Binding parcial La clase javax.xml.bind.Binder soporta el binding parcial de un documento XML. Esto nos permite crear un binding JAXB de una cabecera SOAP sin procesar el cuerpo.
Binary Data Encoding El modelo de programación JWS soporta la optimización de la transmisión de data binaria dentro de mensajes SOAP. Esto implica codificar la data binaria (como un archivo de imagen como xsd:base64Binary), sacarla del sobre SOAP, adjuntar una versión comprimida al paquete MIME y colocar referencias a las partes codificadas en el sobre SOAP. JAXB nos brinda servicios que desempacan la data bianria antes del unmarshalling, y la empaquetan después del marshalling como parte de la serialización del mensaje SOAP. JAXB 2.0 soporta dos tipos de codificación de data binaria: MTOM/XOP y WSIAP.
MTOM es un estándar de la W3C y significa "SOAP Message Transmission Optimization Mechanism". Describe un proceso estándar para sacar el contenido de la representación XML, comprimirlo, empaquetarlo como un adjunto MIME y reemplazarlo por una referencia en la representación XML. La codificación en paquetes utilizada como MTOM es XOP (XML-binary Optimized Packaging).
WSIAP por otro lado significa WS-I Attachments Profile Version 1.0. Sin embargo, aparentemente MTOM/XOP se está convirtiendo en un estándar de la industria.
JAXB utiliza anotaciones para espedificar que propiedades Java de la clase deber ser serializadas usando MTOM o WSIAP. Para MTOM, la anotación @XmlMimeType nos permite especificar como una propiedad Java binaria (como java.awt.Image) está ligada a un elemento de esquema decorado como un atributo xmime:content-Type.
(Imagen: Rembrandt - Cristo en la tormenta en el lago de Galilea)
JAXB 2.0 define un binding para JAVA/XML estándar, para obtener representaciones Java de componentes de esquemas XML y viceversa. Este binding asocia un conjunto de clases Java con un esquema XML, de modo que las instancias de este esquema puedan ser manipuladas mediante métodos Java.
Tenemos dos escenarios para empezar con el binding:
Comenzar por Java: Las clases Java ya existen y se utilizan para generar un esquema XML con el generador de esquemas de JAXB 2.0.
Comenzar de un esquema XML: El esquema ya existe y las clases Java y el binding son creados mediante el compilador de esquemas de JAXB 2.0
Algunas características interesantes de JAXB son:
Binding de esquemas XML a representaciones Java Las implementaciones de JAXB 2.0 nos proveen de un compilador de esquemas, que nos genera clases Java en función a un esquema XML. Cada clase generada nos permite acceder al contenido del componente del esquema correspondiente mediante getters y setters. Así, los elementos y atributos de un tipo son mapeados a propiedades de una clase Java.
Mapping de tipos Java a esquemas XML JAX-WS 2.0 hace uso de esta característica de JAXB para generar el WSDL de una clase Java desplegada como Web Service. Para esto, los parámetros y tipos devueltos por el método Java desplegado como una wsdl:operation determinan los componentes del esquema en la sección wsdl:types (esta sección contiene los elementos del esquema XML y las definiciones de tipos usados en el WSDL).
Las implementaciones de JAXB nos proveen de un generados de esquemas, que crea esquemas en base a clases existentes. Este generador examina las propiedades JavaBean y las mapea a atributos y elementos. Se utilizan anotaciones para personalizar este mapeo.
Anotaciones para Mapping Las anotaciones para mapping son el mecanismo de JAXB 2.0 para personalizar el binding Java/XML. El generador de esquemas descrito en la sección anterior, requiere como inputs un conjunto de clases, y un conjunto de anotaciones para mapping (aunque de no existir, se toman valores por defecto).
Aquí un ejemplito, donde se mapea la propiedad PurchaseOrderNumber al elemento de esquema orderNum:
public class PurchaseOrder{ //... @XmlElement(name="orderNum") String getPurchaseOrderNumber(); void setPurchaseOrderNumber(String po);
}
También, al generar clases Java de un esquema XML (la forma inversa), el código Java que genera JAXB contiene anotaciones que documentan los componentes XML de los cuáles fueron mapeados.
Lenguaje de Binding El lenguaje de binding de JAXB 2.0 nos permite anotaciones en XML que nos permiten personalizar la representación Java de un esquema XML. Se utiliza para darle forma a los tipos Java utilizados como parámetros y tipos de retorno de una SEI (Service Endpoint Interface).
A diferencia de las anotaciones, que siempre van entre líneas dentro del código Java, las personalizaciones de lenguaje de binding pueden estar dentro del esquema XML o en un archivo de configuración separado.