Spring WS: Construcción de Clientes de Servicios Web con Spring

1
24990

Spring WS: Construcción de Clientes de Servicios Web con Spring

En el siguiente tutorial vamos a ver algunas de las aportaciones que nos ofrece Spring en relación a la construcción de clientes
de servicios web.

Se presupone que el lector ya posee conocimientos de Servicios Web, Maven y Spring.

Si estás interesado en la construcción de servicios web con Spring, puedes ver el siguiente tutorial

Índice de contenido:

  1. Introducción.
  2. Ejemplo de construcción de un cliente de servicio web con Spring.
    1. Entorno.
    2. Describiendo la petición y la respuesta de comunicación con el servicio web.
    3. Código fuente del cliente.
    4. Archivo de configuración de Spring 2 (/main/resources/applicationContext.xml).
    5. Archivo de configuración de Maven 2: pom.xml.
    6. Configuración de logs para monitorizar el tráfico SOAP: /main/resources/log4j.xml.
    7. Invocación del servicio Web.
  3. Referencias
  4. Conclusiones

Introducción

Al igual que en otros temas en Spring como JMS o DAO, Spring apuesta por el uso de plantillas que nos ahorren tener que
realizar las tareas comunes evitándonos además cometer ciertos errores ligados a estas partes.

Por ejemplo, en el caso de DAO, las plantillas nos evitan tener que abrir conexiones, liberar recursos, etc.

En el caso de los servicios web, Spring nos proporciona la plantilla org.springframework.ws.client.core.WebServiceTemplate a tráves de la cual podremos enviar
mensajes (payload) a un servicio Web, y será la plantilla la que se encarge de envolverla en sobres SOAP, de establecer conexiones, liberar recursos, controlar errores de obligada captura, etc.

Además esta plantilla nos proporciona imnumerables métodos para enviar mensajes con cualquier tipo de mensajería y médio transporte.

Cabe destacar que para construir un cliente de un servicio web usando Spring necesitas entender el WSDL que define el servicio web, es decir:

  • Protocolo de comunicación.
  • Dónde escucha la peticiones.
  • Formatos peticiones, respuestas y fallos.

Entender un WSDL no es tan dificil como a veces se puede pensar, en un par de tardes con ganas los lees sin problemas 🙂

Nuestro cliente usará por dentro una instancia de WebServiceTemplate (normalmente inyectada por Spring) y está plantilla se deberá de configurar dos parámetros (en la configuración de Spring):

  1. La fábrica de mensajes SOAP: Envuelve el Payload que envias a través de la plantilla en sobres SOAP Envelope.
  2. La clase de envio de menssajes SOAP: Se encarga del envio en si del mensaje construido anteriormente.

Tendrás información más detallada en los comentarios del archivo de configuración de Spring que se verá más adelante en este mismo tutorial.

Ejemplo de construcción de un cliente de servicio web con Spring:

A continuación vamos a ver un completo ejemplo en donde construiremos un cliente para el Servicio Web de búsqueda de libros que definimos en un tutorial anterior. Ver tutorial.

El código fuente de este tutorial puede ser descargado desde aquí (proyecto Eclipse con Maven 2).

Entorno

El siguiente ejemplo está construido en el siguiente entorno:

  • HP Pavilion.
  • Windows Vista Home Premium.
  • Eclipse Ganymede.
  • Java 6.
  • Maven 2.
  • Plugin Maven 4QE para Eclipse.

Describiendo la petición y la respuesta de comunicación con el servicio web:

Spring SÓLO APUESTA por el modelo contrato-primero (Contract-First), por lo que debemos de enviar y recibir la información con el formato que se especifique en el contrato del servicio Web (WSDL).

A continuación vemos el formato de datos de la comunicación con el servicio web y al cual debemos ceñirnos estrictamente.

Código fuente del cliente:

com.autentia.tutoriales.spring.ws.biblioteca.cliente.BibliotecaCliente:

package com.autentia.tutoriales.spring.ws.biblioteca.cliente;

import java.util.ArrayList;
import java.util.List;
import java.io.StringReader;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.w3c.dom.*;

/**
 * Spring cliente WS del servicio de consulta de libros 
 * @author Carlos García. Autentia.
 * @see http://www.mobiletest.es
 */
public class BibliotecaCliente  {
	private final String	   URI = "http://localhost:8080/bibliotecaWS/services";
	private WebServiceTemplate webServiceTemplate;
	
	/**
	 * Será inyectada por Spring
	 */
	public void setWebServiceTemplate(WebServiceTemplate webServiceTemplate) {
		this.webServiceTemplate = webServiceTemplate;
	}

	/**
	 * @param categoria Categoría del libro a consultar
	 * @param nivel Nivel (avanzado, medio o básico)
	 * @return Devuelve la lista de libros que cumplen los criterios especificados
	 */
	public java.util.List getLibros(String categoria, String nivel) {
		String		 xmlRequest = this.getPeticion(categoria, nivel);
		StreamSource peticion   = new StreamSource(new StringReader(xmlRequest));
		DOMResult	 respuesta	= new DOMResult();
		List  libros = null;
		
		boolean hayRespuesta = webServiceTemplate.sendSourceAndReceiveToResult(URI, peticion, respuesta);
		if (hayRespuesta){
			libros = this.resultToBooks((Document) respuesta.getNode());
		}
		return libros;
	}

	/**
	 * @return Genera la petición (payload) que se enviará al servicio Web
	 */
	private String getPeticion(String categoria, String nivel){
		StringBuffer buffer = new StringBuffer(1024);
		buffer.append("");
		buffer.append("");
		buffer.append(categoria);
		buffer.append("");
		buffer.append("");
		buffer.append(nivel);
		buffer.append("");
		buffer.append("");
		return buffer.toString();
	}
	
	/**
	 * @return Devuelve una lista de Libro a partir del DOM
	 */
	private List resultToBooks(Document doc){
		NodeList nodos 	 = doc.getFirstChild().getChildNodes();
		Node	 current = null;
		Libro	 libro	 = null;	
		
		ArrayList libros = new ArrayList();
		for (int i = 0, num = nodos.getLength(); i < num; i++){
			current = nodos.item(i);
			
			libro	= new Libro();
			libro.setEditorial(this.getProperty(current, "editorial"));
			libro.setTitulo(this.getProperty(current, "titulo"));
			libro.setPrecio(Integer.parseInt(this.getProperty(current, "precio")));
			libro.setPaginas(Integer.parseInt(this.getProperty(current, "paginas")));
			
			libros.add(libro);
		}
		
		return libros;
	}
	
	/**
	 * 
	 * @param node Nodo padre en el cual se busca la propiedad
	 * @param tag Nombre del elemento buscado
	 * @return Devuelve el valor de un elemento xxxxxx
	 */
	private String getProperty(Node nodo, String tag){
		NodeList props  = nodo.getChildNodes();
		Node	 prop   = null;
		String	 value	= null;
		
		for (int j = 0, num = props.getLength(); j < num; j++){
			prop = props.item(j);
			if (tag.equals(prop.getNodeName())){
				value = prop.getFirstChild().getNodeValue();
				break;
			}
		}
		return value;
	}	
}

com.autentia.tutoriales.spring.ws.biblioteca.cliente.Libro:

package com.autentia.tutoriales.spring.ws.biblioteca.cliente;

/**
 * Representación de un libro
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class Libro {
	private String editorial;
	private String titulo;
	private int	   paginas;
	private int	   precio;
		
	public String getEditorial() {
		return editorial;
	}
	public void setEditorial(String editorial) {
		this.editorial = editorial;
	}
	public String getTitulo() {
		return titulo;
	}
	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}
	public int getPaginas() {
		return paginas;
	}
	public void setPaginas(int paginas) {
		this.paginas = paginas;
	}
	public int getPrecio() {
		return precio;
	}
	public void setPrecio(int precio) {
		this.precio = precio;
	}
}

Archivo de configuración de Spring 2 (/main/resources/applicationContext.xml):

El archivo está autocomentado.

<?xml version="1.0" encoding="UTF-8"?>

	
	
	
		
	
	
	
	
		
		
		
		
			  
			
			
        		
            		
        		
        				
		
		
				
		
	          
		
	

Archivo de configuración de Maven 2: pom.xml:

A continuación exponemos el archivo de configuración de Maven, se presupone que el lector ya tiene nociones de Maven.


	4.0.0
	com.autentia.tutoriales
	bibliotecaWSClienteSpring
	jar
    1.0-SNAPSHOT
	bibliotecaWSClienteSpring
	https://adictosaltrabajo.com
	
		
			
			
				maven-compiler-plugin
				
					1.5
					1.5
					UTF-8
				
			
		
	
	
		
		
			org.springframework.ws
			spring-ws-core
			1.5.6
		
		
		
		
		    log4j
		    log4j
		    1.2.14
		
		
		
		
			junit
			junit
			4.3.1
			test
			
	

Configuración de log para monitorizar el tráfico SOAP: /main/resources/log4j.xml:

<?xml version="1.0" encoding="UTF-8"?>


	
	
	
		
		
		

		
			
		
	


	
	
		
		
	

Invocación del servicio Web:

A continuación nos creamos un test funcional con JUnit para probar la invocación y respuesta del servicio Web.

com.autentia.tutoriales.spring.biblioteca.cliente.test.BibliotecaTest:

package com.autentia.tutoriales.spring.biblioteca.cliente.test;

import org.junit.Assert;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.autentia.tutoriales.spring.ws.biblioteca.cliente.BibliotecaCliente;
import com.autentia.tutoriales.spring.ws.biblioteca.cliente.Libro;

/**
 * Tests de verificación del servicio web de consulta de libros
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class BibliotecaTest  {
	private ApplicationContext	factory;
	
	/**
	 * Inicializamos el contexto de Spring
	 */
	@org.junit.Before
	public void initTests(){
		this.factory  = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
	}
	
	/**
	 * En verdad esto no es un test, simplemente lo pongo aqui por comodidad y no 
	 * tener que crearme un proyecto independenciente con una aplicación que realize lo mismo 
	 * que puedo hacer con este "test".
	 */
    @org.junit.Test
    public void test1(){
    	BibliotecaCliente		stub	= (BibliotecaCliente) factory.getBean("biblioStub");
		java.util.List	libros	=  stub.getLibros(".net", "avanzado");
		if (libros != null){
			 for (int i = 0, lcount = libros.size(); i < lcount; i++){
				 Libro libro = libros.get(i);
				 System.out.println(libro.getEditorial() + " " + libro.getTitulo() + " "  +  libro.getPaginas() + " " +  libro.getPrecio());		 
			 }
		} else {
			System.out.println("No hay libros");
		}
		
    	Assert.assertTrue(true);
    }
}

Y para terminar, ejecutamos los tests y vemos la salida generada mvn test:

........
........

Editorial libro 0 Titulo libro 0 100 50
Editorial libro 1 Titulo libro 1 101 51
Editorial libro 2 Titulo libro 2 102 52
Editorial libro 3 Titulo libro 3 103 53
Editorial libro 4 Titulo libro 4 104 54
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.681 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

........
........

Vemos los logs que nos ha dejado la aplicación (bibliotecaWSClienteSOAP.log):

2009-03-29 23:05:39 [TRACE] [org.springframework.ws.client.core.WebServiceTemplate.sendRequest(WebServiceTemplate.java:581)] 
Sent request [<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Header/><env:Body><BooksInfoRequest xmlns="https://adictosaltrabajo.com/spring/ws/schemas"><categoria>.net</categoria><nivel>avanzado</nivel></BooksInfoRequest></env:Body></env:Envelope>]

2009-03-29 23:05:39 [TRACE] [org.springframework.ws.client.core.WebServiceTemplate.logResponse(WebServiceTemplate.java:637)] 
Received response [<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Header/><env:Body><BooksInfoResponse xmlns="https://adictosaltrabajo.com/spring/ws/schemas"><libro><editorial>Editorial libro 0</editorial><titulo>Titulo libro 0</titulo><paginas>100</paginas><precio>50</precio></libro><libro><editorial>Editorial libro 1</editorial><titulo>Titulo libro 1</titulo><paginas>101</paginas><precio>51</precio></libro><libro><editorial>Editorial libro 2</editorial><titulo>Titulo libro 2</titulo><paginas>102</paginas><precio>52</precio></libro><libro><editorial>Editorial libro 3</editorial><titulo>Titulo libro 3</titulo><paginas>103</paginas><precio>53</precio></libro><libro><editorial>Editorial libro 4</editorial><titulo>Titulo libro 4</titulo><paginas>104</paginas><precio>54</precio></libro></BooksInfoResponse></env:Body></env:Envelope>] for request [<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Header/><env:Body><BooksInfoRequest xmlns="https://adictosaltrabajo.com/spring/ws/schemas"><categoria>.net</categoria><nivel>avanzado</nivel></BooksInfoRequest></env:Body></env:Envelope>]

Referencias

Conclusiones

Si lo piensas bien, este forma de comunicación con servicios Web puede parecer que requiere un gran esfuerzo en comparación con otras técnicas, pero no es para tanto, usando técnicas
OXM (Object XML Mapping) y el uso de plantillas (WebServiceTemplate) el esfuerzo se reduce enormemente y lo que es mejor, para las personas que nos gusta tener el control sobre lo que está pasando, esta solución es ideal,
pues todo son mensajes en XML fácilmente depurables.

Otra cosa, recuerda que esto no es más que un tutorial, hay más puntos al respecto sobre los que profundizar.

En Autentia impartimos constantemente cursos a medida sobre estas y otras tecnologías, espero nos tengais en cuenta en caso de que necesiteis formación.

Un saludo, espero que os haya parecido útil este tutorial.

Carlos García. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.

1 COMENTARIO

  1. Hola, lo primero de todo, gracias por el tutorial. Es muy bueno.

    Aplicando dicho tutorial a mi aplicación, resulta que cuando ejecuta la línea:

    boolean hayRespuesta = webServiceTemplate.sendSourceAndReceiveToResult(URI, peticion, respuesta);

    realiza la petición al WS, y devuelve la respuesta (lo veo a través del log de SOAP), pero resulta que \\\»respuesta\\\» está a null e incluso, la variable \\\»peticion\\\», que antes contenía el string de la peticón xml, ahora también está a null. ¿Alguna sugerencia?

    Gracias.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad