1. Introducción a los Servicios Web

El diseño del software tiende a ser cada vez más modular. Las aplicaciones se componen de una serie de componentes (servicios) reutilizables, que pueden encontrarse distribuidos a lo largo de una serie de máquinas conectadas en red.

Los Servicios Web nos permitirán distribuir nuestra aplicación a través de Internet, pudiendo una aplicación utilizar los servicios ofrecidos por cualquier servidor conectado a Internet.

1.1 ¿Qué es un Servicio Web?

Un Servicio Web es un componente al que podemos acceder mediante protocolos Web estándar, utilizando XML para el intercambio de información.

Normalmente nos referimos con Servicio Web a una colección de procedimientos (métodos) a los que podemos llamar desde cualquier lugar de Internet o de nuestra intranet, siendo este mecanismo de invocación totalmente independiente de la plataforma que utilicemos y del lenguaje de programación en el que se haya implementado internamente el servicio.

Cuando conectamos a un servidor web desde nuestro navegador, el servidor nos devuelve la página web solicitada, que es un documento que se mostrará en el navegador para que lo visualice el usuario, pero es difícilmente entendible por una máquina. Podemos ver esto como web para humanos. En contraposición, los Servicios Web ofrecen información con un formato estándar que puede ser entendido fácilmente por una aplicación. En este caso estaríamos ante una web para máquinas.

1.2 Características de los Servicios Web

Las características deseables de un Servicio Web son:

1.3 Arquitectura de los Servicios Web

Podemos ver la arquitectura de los Servicios Web desde dos puntos de vista. En el primero de ellos estudiaremos la función de cada agente en el mecanismo de localización e invocación de Servicios Web, mientras que en el segundo abstraeremos una serie de capas en las que se organizan los protocolos que se utilizan para realizar estas funciones

1.3.1 Arquitectura funcional

Podemos distinguir tres agentes con diferentes funciones:

Proveedor de servicio Implementa unas determinadas operaciones (servicio). Un cliente podrá solicitar uno de estos servicios a este proveedor.
Cliente del servicio Invoca a un proveedor de servicio para la realización de alguna de los operaciones que proporciona.
Registro de servicios Mantiene una lista de proveedores de servicios disponibles, junto a sus descripciones.

El mecanismo básico de invocación de servicios consistirá en que un cliente solicitará un determinado servicio a un proveedor, efectuando el proveedor dicho servicio. El servidor devolverá una respuesta al cliente como resultado del servicio invocado.

Esto podremos hacerlo así si el cliente conoce de antemano el proveedor del cual va a obtener el servicio. Pero hemos de pensar que en Internet encontraremos una gran cantidad de Servicios Web dispersos, lo cual hará difícil localizar el que busquemos. Además, si hemos localizado uno que realiza la función que necesitamos, si dicho servicio no está mantenido por nosotros puede ocurrir que en algún momento este servicio cambie de lugar, de interfaz o simplemente desaparezca, por lo que no podremos confiar en que vayamos a poder utilizar siempre este mismo servicio.

Los registros de servicios nos permiten automatizar la localización de Servicios Web. Un proveedor puede anunciarse en un determinado registro, de forma que figurará en dicho registro la localización de este servicio junto a una descripción de su funcionalidad y de su interfaz, que podrá ser entendida por una aplicación.

Cuando un cliente necesite un determinado servicio, puede acudir directamente a un registro y solicitar el tipo de servicio que necesita. Para ello es importante establecer un determinada semántica sobre las posibles descripciones de funcionalidades de servicios, evitando las posibles ambigüedades.

El registro devolverá entonces una lista de servicios que realicen la función deseada, de los cuales el cliente podrá elegir el más apropiado, analizar su interfaz, e invocarlo.

1.3.2 Arquitectura de capas de protocolos

Los protocolos utilizados en los Servicios Web se organizan en una serie de capas:

Capa Descripción
Transporte de servicios Es la capa que se encarga de transportar los mensajes entre aplicaciones. Normalmente se utiliza el protocolo HTTP para este transporte, aunque los servicios web pueden viajar mediante otros protocolos de transferencia de hipertexto como SMTP, FTP o BEEP.
Mensajería XML Es la capa responsable de codificar los mensajes en XML de forma que puedan ser entendidos por cualquier aplicación. Puede implementar los protocolos XML-RPC o SOAP.
Descripción de servicios Se encarga de definir la interfaz pública de un determinado servicio. Está definición se realiza mediante WSDL.
Localización de servicios Se encarga del registro centralizado de servicios, permitiendo que estos sean anunciados y localizados. Para ello se utiliza el protocolo UDDI.

Más adelante describiremos cada una de las tecnologías para Servicios Web vistas en las distintas capas.

1.4 Seguridad

En la utilización de los Servicios Web, encontramos problemas de seguridad en diferentes aspectos. Podemos encontrar problemas de seguridad en cuanto a la confidencialidad, la autentificación y la seguridad de la red.

1.4.1 Confidencialidad

Cuando un cliente utiliza un Servicio Web, deberá enviarle un mensaje a este servicio a través de la red, y el servicio le responderá mediante otro mensaje. Estos mensajes contendrán información que puede ser confidencial.

Dado que estos mensajes se envían mediante protocolo HTTP, podrán ser encriptados mediante SSL evitando de esta forma que puedan ser interceptados por un tercero.

Sin embargo, en aplicaciones en las que el mensaje deba atravesar una cadena de servicios, este mensaje deberá desencriptarse y volverse a encriptar entero en cada uno de estos servicios, por lo que los datos estarán inseguros dentro de cada nodo. Podríamos solucionar este problema permitiendo que sólo se desencripte en cada nodo una parte del mensaje, que será la parte que atañe a dicho nodo, pero que no pueda acceder al resto del mensaje que no le concierne.

1.4.2 Autentificación

Puede que necesitemos identificar a un usuario para prestarle un determinado servicio, o bien para saber si tiene autorización para acceder a dicho servicio.

Podemos utilizar para ello la autentificación que nos proporciona el protocolo HTTP. Encontramos el mismo problema que en el caso anterior, necesitamos invocar un conjunto de servicios, deberemos autentificarnos por separado para cada uno de ellos, ya que pueden estar distribuidos en distintos servidores a través de Internet. Para solucionar este problema, deberíamos contar con un contexto compartido global de donde cualquier servicio pudiese obtener esta información de autentificación.

1.4.3 Seguridad de la red

Hemos de pensar que estamos permitiendo invocar procedimientos remotos mediante protocolo HTTP, que en un principio fue diseñado para la extracción de documentos. Por lo tanto, sus puertos no suelen ser cortados por ningún firewall, de forma cualquiera podrá utilizar estos servicios libremente, sin que los firewalls puedan controlarlo.

1.5 Tecnologías básicas

Tenemos una serie de tecnologías, todas ellas basadas en XML, que son fundamentales para el desarrollo de Servicios Web. Estas tecnologías son independientes tanto del SO como del lenguaje de programación utilizado para implementar dichos servicios. Por lo tanto, serán utilizadas para cualquier Servicio Web, independientemente de la plataforma sobre la que construyamos dichos servicios (como puede ser J2EE o .NET).

1.5.1 SOAP

Se trata de un protocolo derivado de XML que nos sirve para intercambiar información entre aplicaciones.

Normalmente utilizaremos SOAP para conectarnos a un servicio e invocar métodos remotos, aunque puede ser utilizado de forma más genérica para enviar cualquier tipo de contenido. Podemos distinguir dos tipos de mensajes según su contenido:

Cuando hablamos de Servicios Web normalmente nos referimos a RPC, por lo que nos centraremos en este tipo de mensajes.

Puede ser utilizado sobre varios protocolos de transporte, aunque está especialmente diseñado para trabajar sobre HTTP.

Dentro del mensaje SOAP podemos distinguir los siguientes elementos:

Hemos visto como los mensajes SOAP nos sirven para intercambiar cualquier documento XML entre aplicaciones. Pero puede ocurrir que necesitemos enviar en el mensaje datos que no son XML, como puede ser una imagen. En ese caso tendremos que recurrir a la especificación de mensajes SOAP con anexos.

Los mensajes SOAP con anexos añaden un elemento más al mensaje:

Nuestro mensaje podrá contener tantos anexos como queramos.

Un ejemplo de mensaje SOAP es el siguiente:

<SOAP-ENV:Envelope 
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"    
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">    
	<SOAP-ENV:Body> 
		<ns:getTemperatura xmlns:ns="http://rvg.ua.es/ns">    
			<area>Alicante</area> 
		</ns:getTemperatura> 
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

En él estamos llamando a nuestro método getTemperatura para obtener información meteorológica, proporcionando como parámetro el área de la que queremos obtener la temperatura.

Podemos encontrar la especificación de SOAP y SOAP con anexos publicada en la página del W3C, en las direcciones http://www.w3.org/TR/SOAP/ y http://www.w3.org/TR/SOAP-attachments respectivamente.

1.5.2 WSDL

Es otro lenguaje derivado de XML, que se utiliza para describir los Servicios Web, de forma que una aplicación pueda conocer de forma automática la función de un Servicio Web, así como la forma de uso de dicho Servicio Web.

El fichero WSDL describirá la interfaz del Servicio Web, con los métodos a los que podemos invocar, los parámetros que debemos proporcionarles y los tipos de datos de dichos parámetros.

Si desarrollamos un Servicio Web, y queremos que otras personas sean capaces de utilizar nuestro servicio para sus aplicaciones, podremos proporcionar un documento WSDL describiendo nuestro servicio. De esta forma, a partir de este documento otros usuarios podrán generar aplicaciones clientes en cualquier plataforma (ya que WSDL se define como un estándar) que se ajusten a nuestro servicio.

El elemento raíz dentro de este fichero es definitions, donde se especifican los espacios de nombres que utilizamos en nuestro servicio. Dentro de este elemento raíz encontramos los siguientes elementos:

Un documento WSDL de ejemplo es el siguiente:

<?xml version="1.0" encoding="utf-8" ?> 
  <definitions xmlns:s="http://www.w3.org/2001/XMLSchema"
	 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"    
	 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
	 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
     xmlns:tns="http://rvg.ua.es/wsdl"
     xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
	 targetNamespace="http://rvg.ua.es/wsdl"
     xmlns="http://schemas.xmlsoap.org/wsdl/">
   <message name="getTempRequest">
	   <part name="string_1" 
			xmlns:partns="http://www.w3.org/2001/XMLSchema"
		    type="partns:string" /> 
   </message>
   <message name="getTempResponse">
	   <part name="double_1" 
			xmlns:partns="http://www.w3.org/2001/XMLSchema"
		    type="partns:double" /> 
   </message>
   <portType name="TempPortType">
	   <operation name="getTemp">
		   <input message="tns:getTempRequest" /> 
		   <output message="tns:getTempResponse" /> 
	   </operation>
   </portType>
   <binding name="TempPortSoapBinding" type="tns:TempPortType">
	   <soap:binding style="rpc" 
		transport="http://schemas.xmlsoap.org/soap/http" /> 
       <operation name="getTemp">
			<soap:operation soapAction="" style="rpc" /> 
			<input>
				<soap:body use="encoded" 
				namespace="http://rvg.ua.es/wsdl" 
				encodingStyle=
					"http://schemas.xmlsoap.org/soap/encoding/" /> 
			</input>
			<output>
				<soap:body use="encoded" 
				namespace="http://rvg.ua.es/wsdl"    
				encodingStyle=
					"http://schemas.xmlsoap.org/soap/encoding/" /> 
			   </output>
		</operation>
   </binding>
   <service name="Temp">
	   <documentation>Documentacion</documentation>    
	   <port name="TempPort" binding="tns:TempPortSoapBinding">
		   <soap:address 
				location="http://localhost:7001/sw_temp/Temp" /> 
	   </port>
   </service>
</definitions>

En el que se define un servicio que proporciona el método getTemp, que toma como parámetro una cadena con el nombre del área que queremos consultar, y nos devuelve un valor real.

En los elementos message vemos que tenemos dos mensajes: los mensajes de entrada y salida de la operación getTemp de nuestro servicio. El mensaje de entrada contiene un dato de tipo string (el parámetro del método), y el de salida es de tipo double (la temperatura que devuelve el servicio).

El elemento portType define la operación getTemp a partir de los mensajes de entrada y salida que la componen, y en binding se establece esta operación como de tipo RPC y se indica la codificación de estos mensajes.

Por último en el apartado service se especifica el puerto al que podemos conectar para usar el servicio, dando la URL a la que nuestro cliente deberá acceder.

Podemos encontrar la especificación de WSDL publicada en la página del W3C, en la dirección http://www.w3.org/TR/wsdl.

1.5.3 UDDI

UDDI nos permite localizar Servicios Web. UDDI define la especificación para construir un directorio distribuido de Servicios Web, donde los datos se almacenan en XML.

Además, UDDI define una API para trabajar con dicho registro, que nos permitirá buscar datos almacenados en él, y publicar datos nuevos.

De esta forma, una aplicación podrá anunciar sus servicios en un registro UDDI, o bien localizar servicios que necesitemos mediante este registro.

Esta capacidad de localizar servicios en tiempo de ejecución, y de que una aplicación pueda saber cómo utilizarlo inmediatamente gracias a la descripción del servicio, nos permitirá realizar una integración débilmente acoplada de nuestra aplicación.

La interfaz de UDDI está basada en SOAP. Para acceder al registro se utilizarán mensajes SOAP, que son transportados mediante protocolo HTTP.

Podemos encontrar la especificación de UDDI, documentación, y más información en la dirección http://www.uddi.org/.

1.6. Tecnologías J2EE para Servicios Web

Hemos visto las tecnologías en las que se basan los Servicios Web, y que los hacen independientes de la plataforma y del lenguaje de programación utilizado. Sin embargo, escribir manualmente los mensajes SOAP desde nuestras aplicaciones puede ser una tarea tediosa. Por ello, las distintas plataformas existentes incorporan librerías y utilidades que se encargan de realizar esta tarea por nosotros.

En este tema veremos las librerías que incorpora J2EE para la generación y el procesamiento de código XML, que nos servirán para implementar y utilizar Servicios Web.

1.6.1 JAXP

La API JAXP nos permite procesar cualquier documento XML desde lenguaje Java. Tiene en cuenta los espacios de nombres, lo cual nos permite trabajar con DTDs que podrían tener conflictos de nombres si estos no estuviesen soportados. Además, soporta XSLT, lo cual nos permitirá convertir un documento XML a otro formato, como por ejemplo HTML.

Esta es una librería genérica, para procesar cualquier documento XML. A continuación veremos una serie de librerías, para tareas más especificas, que se apoyan en JAXP para realizar el procesado de diferentes lenguajes como SOAP, WSDL y UDDI, todos ellos derivados de XML. Por lo tanto, todas estas librerías dependerán de JAXP para su correcto funcionamiento.

1.6.2 JAXM

La API JAXM implementa la mensajería XML en Java orientada al documento. Nos permitirá de forma sencilla crear mensajes XML, insertando el contenido que queramos en ellos, y enviarlos a cualquier destinatario, así como extraer el contenido de los mensajes que recibamos. Permite enviar y recibir los mensajes de forma síncrona (modelo petición-respuesta) o asíncrona (envío de mensaje sin esperar respuesta).

Los mensajes XML con los que trabaja JAXM siguen la especificación SOAP y SOAP con anexos. Dentro de JAXM encontramos dos APIs:

1.6.3 JAX-RPC

La API JAX-RPC implementa la infraestructura para realizar llamadas a procedimiento remoto (RPC) mediante XML. En este caso se enviará un mensaje SOAP con el método que queremos invocar junto a los parámetros que le pasamos, y nos devolverá de forma síncrona una respuesta SOAP con el valor devuelto por el método tras su ejecución.

Por lo tanto, JAX-RPC dependerá de SAAJ para construir los mensajes SOAP, para enviarlos, y para extraer la información del mensaje SOAP que nos devuelve como resultado.

Esta API nos permitirá, de forma sencilla, invocar Servicios Web de tipo RPC, así como crear nuestros propios Servicios Web RPC a partir de clases Java que tengamos implementadas. Cuando hablamos de Servicios Web, normalmente nos referimos a este tipo de Servicios Web.

1.6.4 JAXR

La API JAXR nos permitirá acceder a registros XML a través de una API estándar Java. Esta API pretende proporcionar una interfaz única para acceder a distintos tipos de registros, cada uno de los cuales tiene un protocolo distinto.

Actualmente JAXR es capaz de trabajar con registros UDDI y ebXML. Podremos realizar dos tipos de tareas distintas cuando accedamos a un registro mediante JAXR:

1.6.5 JAXB

La API de JAXB (Java API for Binding) nos permite asociar esquemas XML y código Java. A partir de un esquema XML, podremos generar una clase Java que represente dicho esquema.

De esta forma podremos convertir un documento XML a una serie de objetos Java que contendrán la información de dicho documento (unmarshalling). Podremos entonces trabajar desde nuestra aplicación con estos objetos, accediendo y modificando sus valores. Finalmente, podremos volver a obtener un documento XML a partir de los objetos Java (marshalling).

Esto nos va a simplificar la tarea de utilizar tipos de datos propios en llamadas a Servicios Web, ya que utilizando JAXB podremos realizar de forma sencilla la conversión entre nuestra clase Java y un documento XML con la información de dicha clase.

1.6.6 Java API for WSDL

La API de Java para WSDL nos permite de forma sencilla analizar documentos WSDL, y de esa forma poder descubrir las características de un servicio en tiempo de ejecución.

Mediante esta API, podremos "interrogar" un servicio a partir de su especificación WSDL, y obtener información como las operaciones que podemos invocar en este servicio, los parámetros que deberemos proporcionar a cada una de ellas, y el tipo de datos resultante que nos devuelven.

De esta forma podremos realizar la integración de la aplicación en tiempo de ejecución, ya que no será necesario indicar al programa cómo debe acceder a un servicio, ni los métodos a los que debe llamar, sino que el programa será capaz de determinar esta información analizando la especificación WSDL del servicio.