Servicios Web SOAP con JAX-WS, Spring y CXF (II): Clientes

logo java

Tras la primera parte del artículo ya tenemos una aplicación web que publica mediante SOAP un bean de Spring de forma muy sencilla gracias a JAX-WS y Apache CXF. Ahora toca volver a utilizar estas tecnologías pero con el objetivo contrario, esto es, consumir un servicio web que en nuestro caso será el del primer artículo. Lo haremos con un proyecto Maven y una clase Main y, opcionalmente, con Spring.

  1. Servidor
  2. Clientes
  3. Securización TLS + BASIC
  4. Handlers

Sin Spring (cxf-client)

Usaremos un proyecto Maven estándar no web con el siguiente pom.xml donde sólo se tomarán como dependencias las de CXF y log4j


<?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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.danielme.demo</groupId>
	<artifactId>cxf-client</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>
	<description>Maven + JAX-WS + CXF client project just for playing
		around with SOAP web services
	</description>
	<inceptionYear>2013</inceptionYear>

	<licenses>
		<license>
			<name>GNU GENERAL PUBLIC LICENSE, Version 3.0</name>
			<url>http://www.gnu.org/licenses/gpl.html</url>
		</license>
	</licenses>

	<developers>
		<developer>
			<id>danielme.com</id>
			<name>Daniel Medina</name>
		</developer>
	</developers>


	<properties>
		<cxf.version>2.7.3</cxf.version>
		<java.version>1.6</java.version>
		<project.build.mainClass>com.danielme.demo.jaxws.cxf.client.Main</project.build.mainClass>
	</properties>

	<build>

		<plugins>
		
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.0</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<mainClass>${project.build.mainClass}</mainClass>
						</manifest>
					</archive>
				</configuration>
			</plugin>

			<!-- mvn clean package exec:java -->
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<executions>
					<execution>
						<goals>
							<goal>java</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<mainClass>${project.build.mainClass}</mainClass>
				</configuration>
			</plugin>

		</plugins>
	</build>

	<dependencies>

		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-api</artifactId>
			<version>${cxf.version}</version>
		</dependency>

		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>${cxf.version}</version>
		</dependency>

		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>${cxf.version}</version>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>


	</dependencies>

</project>

El cliente que queremos será una clase proxy que se generará en tiempo de ejecución con la factoría JaxWsProxyFactoryBean. Para ello, la clave es tener una interfaz que replique los métodos del servicio web con las anotaciones correspondientes, así como las distintas clases que se utilicen en los mismos. En nuestro caso, basta con incluir en nuestro proyecto tanto la interfaz ITeamService como la clase Player de la primera parte del artículo respetando exactamente la misma ruta de paquetes ya que es la que hemos usado (configuración por defecto) para establecer los namespace de nuestro servicio. Es más, de la interfaz es suficiente con incluir sólo aquellos métodos que vayamos a utilizar, por lo que obviaremos el método foo. La interfaz queda prácticamente igual:

package com.danielme.demo.jaxws.cxf.ws;

import java.util.List;

import javax.jws.WebService;

import com.danielme.demo.jaxws.cxf.model.Player;

/**
 * 
 * @author danielme.com
 *
 */
@WebService
public interface ITeamService 
{	
	List<Player> getTeam();
	
	List<Player> getPlayers(int... numbers);
	
	boolean updatePlayerByNumber(int number, Player player);
	
	boolean deletePlayer(int number);

}

El otro elemento que vamos a necesitar es la ruta en la que se encuentra el descriptor del servicio web:

http://localhost:8080/spring-cxf-ws/ws/v/1/teamService?wsdl

(en este ejemplo se ha sacado del código a un .properties). La estructura final del proyecto es la siguiente:

cxf-client

En la imagen vemos un Main en el que se va a consumir nuestro servicio web y que probará todos los métodos disponibles. El código resaltado es aquel que crea el cliente. Obviamente la aplicación servidor debe estar en ejecución para que el ejemplo funcione.

package com.danielme.demo.jaxws.cxf.client;

import java.util.List;
import java.util.Properties;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.log4j.Logger;

import com.danielme.demo.jaxws.cxf.model.Player;
import com.danielme.demo.jaxws.cxf.ws.ITeamService;

/**
 * 
 * @author danielme.com
 *
 */
public class Main 
{

	public static final Logger logger = Logger.getLogger(Main.class);

	public static void main(String[] args) throws Exception
	{
		Properties properties = new Properties();
		properties.load(Main.class.getClassLoader().getResourceAsStream("config.properties"));
		
		//client
		JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
		jaxWsProxyFactoryBean.setServiceClass(ITeamService.class);
		jaxWsProxyFactoryBean.setAddress(properties.getProperty("endpoint"));
		ITeamService teamServiceClient = (ITeamService) jaxWsProxyFactoryBean.create();
		
		//test
		logger.info("getTeam");		
		List<Player> team = teamServiceClient.getTeam();
		for (Player player : team)
		{
			logger.info("       " + player.getNumber() + " : " + player.getName() + " (" + player.getAge() +")");
		}		
		
		logger.info("\n updatePlayerByNumber");
		teamServiceClient.updatePlayerByNumber(1, new Player(1,"Anders Lindegaard", 28));
		
		logger.info("\n deletePlayer");
		teamServiceClient.deletePlayer(6); 
		
		logger.info("\n getPlayers");
		team = teamServiceClient.getPlayers(1,3,6);		
		for (Player player : team)
		{
			logger.info("       " + player.getNumber() + " : " + player.getName() + " (" + player.getAge() +")");
		}
	}

}

El resultado de la ejecución nos permitirá validar el correcto funcionamiento del ejemplo completo (servicio y cliente):

resultado

Por último, comentar que la creación del cliente lleva algo de tiempo (aunque estemos hablando de milisegundos) por lo que por motivos de eficiencia conviene crearlo una única vez y como un singleton para que sea reutilizado. Este trabajo ya nos lo da hecho el siguiente ejemplo con Spring.

Con Spring (spring-cxf-client)

Vamos a rehacer el ejemplo anterior para que utilice el contenedor de Spring si es que lo estamos usando en nuestra aplicación (tal y como acabmos de ver para cliente sólo necesitamos Apache CXF). La tarea es muy sencilla, puesto que lo único que habrá que hacer es añadir las dependencias correspondientes al pom.xml y llevarnos la creación del cliente del código al applicationContext.xml

Sólo tenemos que añadir una dependencia:

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>3.2.13.RELEASE</version>
		</dependency>

Usaremos el siguiente applicationContext.xml que como es habitual se ubicará en /src/main/resources

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

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="http://www.springframework.org/schema/beans  
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
		http://www.springframework.org/schema/context  
		http://www.springframework.org/schema/context/spring-context-3.2.xsd  
		http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<context:property-placeholder location="classpath:config.properties" />

<jaxws:client id="teamServiceClient"
              serviceClass="com.danielme.demo.jaxws.cxf.ws.ITeamService"
              address="${endpoint}" />
	
</beans>

Nuestro cliente ya es un bean de Spring (y un singleton por defecto), por lo que en la clase Main los únicos cambios a realizar serán iniciar programáticamente el contenedor de Spring y recuperar el bean deseado. La clase completa quedará tal que así, y el resultado final de su ejecución deberá ser el mismo que en el ejemplo anterior. Nota: puesto que el cliente modifica los datos que proporciona el servidor, si se quiere que el resultado sea siempre el mismo se deberá reiniciar el servidor cada vez que se ejecute el cliente.

package com.danielme.demo.jaxws.cxf.client.spring;

import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;


import com.danielme.demo.jaxws.cxf.model.Player;
import com.danielme.demo.jaxws.cxf.ws.ITeamService;

public class Main 
{
	public static final Logger logger = Logger.getLogger(Main.class);


	public static void main(String[] args) throws Exception
	{		
		//Initializes Spring Container 
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");		
		ITeamService teamServiceClient = (ITeamService) applicationContext.getBean("teamServiceClient");		
		
		//test
		logger.info("getTeam");		
		List<Player> team = teamServiceClient.getTeam();
		for (Player player : team)
		{
			logger.info("       " + player.getNumber() + " : " + player.getName() + " (" + player.getAge() +")");
		}		
				
		logger.info("\n updatePlayerByNumber");
		teamServiceClient.updatePlayerByNumber(1, new Player(1,"Anders Lindegaard", 28));
				
				logger.info("\n deletePlayer");
				teamServiceClient.deletePlayer(6); 
				
				logger.info("\n getPlayers");
				team = teamServiceClient.getPlayers(1,3,6);		
				for (Player player : team)
				{
					logger.info("       " + player.getNumber() + " : " + player.getName() + " (" + player.getAge() +")");
				}
	}

}



Código de ejemplo
El código de ejemplo de las dos partes del artículo se encuentra en GitHub Para más información sobre cómo utilizar GitHub, consultar este artículo..

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: