sábado, 18 de mayo de 2013

Introducción a los servicios web con Spring y Apache CXF.

En esta primera entrega de la serie de servicios web con Spring veremos un ejemplo bastante sencillo de cómo usar Maven para crear y gestionar un proyecto de una aplicación web que expone un servicio y lo prueba internamente. La prueba externa la realizaremos usando la herramienta SOAPUI.

Para esta entrada hice uso de un tutorial que pueden encontrar en este blog: http://pfelitti87.blogspot.com/ realmente me pareció bastante sencillo y que funciona sin mucha modificación. Luego lo iremos incrementando con más elementos en otras entradas hasta tener un servicio web completamente funciona. En esta entrada solo quiero mostrarles cómo funciona lo básico.

Lo primero es tener un IDE de desarrollo, el eclipse STS de Spring en mí caso que ya viene con un plugin para Maven instalado, y crear un proyecto de Maven con el arquetipo para aplicaciones web.

Luego modificamos el pom del proyecto para que quede así:

<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.maven.test</groupId>

<artifactId>Spring-WS-CXF</artifactId>

<packaging>war</packaging>

<version>0.0.1-SNAPSHOT</version>

<name>Spring-WS-CXF Maven Webapp</name>

<url>http://maven.apache.org</url>

<dependencies>

<!-- Dependencias de Spring -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>3.2.2.RELEASE</version>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>3.2.2.RELEASE</version>

<scope>compile</scope>

</dependency>

<!-- Dependencias de Apache CXF para servicios web -->

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-api</artifactId>

<version>2.6.0</version>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-rt-frontend-jaxws</artifactId>

<version>2.6.0</version>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-rt-transports-http</artifactId>

<version>2.6.0</version>

<scope>compile</scope>

</dependency>

<!-- For testing purposes -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>3.2.2.RELEASE</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.11</version>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<finalName>Spring-WS-CXF</finalName>

<plugins>

<!-- Con este plugin se puede iniciar jetty ejecutando mvn jetty:run -->

<plugin>

<groupId>org.mortbay.jetty</groupId>

<artifactId>maven-jetty-plugin</artifactId>

<version>6.1.26</version>

<configuration>

<scanIntervalSeconds>3</scanIntervalSeconds>

</configuration>

</plugin>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.0</version>

<configuration>

<source>1.7</source>

<target>1.7</target>

</configuration>

</plugin>

</plugins>

</build>

</project>


De esta manera ya tendremos todas las librerías descargadas y listas para ser usadas en el proyecto.

Luego crearemos los paquetes tal y como se muestran en la siguiente imagen, claro ustedes lo pueden modificar a su gusto:

image

Como pueden apreciar tengo una clase interface HelloWordService que tiene lo siguiente:

package org.blog.samples.webservices.Interface;

import javax.jws.WebService;

@SuppressWarnings("restriction")

@WebService

public interface HelloWorldService {

public void sayHello();

}

Vean la anotación @WebService que la uso para indicar que esta clase interface es para un servicio web.

Luego viene su implementación:

package org.blog.samples.webservices.Impl;

import org.blog.samples.webservices.Interface.HelloWorldService;

import javax.jws.WebService;

@SuppressWarnings("restriction")

@WebService(endpointInterface = "org.blog.samples.webservices.Interface.HelloWorldService")

public class HelloWorldServiceBean implements HelloWorldService {

public void sayHello() {

System.out.println("Hello World!!!");

}

}

En este caso la anotación tiene un parámetro que establece la interface del endpoint apuntando a la interfaz anterior.

Como pueden ver la implementación es bastante sencilla, pues no es mi intención en esta entrada llegar a una implementación real del servicio, solo los componentes necesarios que lo conforman.

Ahora creamos el fichero service-definition-beans.xml que lo pondremos en src/main/resources y que contendrá lo siguiente:

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

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<bean id="helloWorldService" class="org.blog.samples.webservices.Impl.HelloWorldServiceBean" />

</beans>

Como pueden ver solo se está creando un bean normal en este fichero.

Ahora vamos a especificar el endpoint del servicio a través del siguiente fichero de configuración webservice-definition-beans.xml que se ubica en la carpeta WEB-INF de la aplicación web:

<?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:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<import resource="classpath:service-definition-beans.xml" />

<jaxws:endpoint id="helloWorld" implementor="#helloWorldService"

address="/HelloWorld" />

</beans>

Aquí estamos creando un endpoint que tiene como implementador el bean que creamos en el fichero de configuracion anterior y estamos diciendo que su dirección relativa a la aplicación será /HelloWorld. Luego veremos qué es esto.

Ahora nos encargamos de como probar el servicio para que en caso de que haya un fallo la aplicación no sea creada, o pensando en positivo para crear la aplicación siempre que el servicio pase la prueba.

En este caso la prueba es un poco falsa pues poco hay que probar. Esta es la clase correspondiente:

package org.blog.samples.webservices.test;

import org.blog.samples.webservices.Interface.HelloWorldService;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = { "/service-definition-beans-test.xml" })

public class HelloWorldServiceTest {

@Autowired

@Qualifier("helloWorldClient")

private HelloWorldService helloWorldClient;

@Test

public void helloWorldClientTest() {

helloWorldClient.sayHello();

}

}

Y el fichero service-definition-beans-test.xml es este:

<?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:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<jaxws:client id="helloWorldClient"

serviceClass="org.blog.samples.webservices.Interface.HelloWorldService"

address="http://localhost:8080/Spring-WS-CXF/HelloWorld" />

</beans>

Deben ubicarlo en el directorio resources de la aplicación web.

Como ven solo corremos una prueba que utiliza el bean del servicio anteriormente creado, mediante la anotación @Autowired y llama a la operación sayHello.

Si queremos ya podemos construir el war de nuestra aplicación y para eso lanzamos el comando Maven: mvn clean install aunque si ya estamos en el STS damos clic derecho encima del proyecto/Run AS/Maven build y ponemos algo como lo que se muestra en la siguiente imagen. Fíjense que hemos marcado que no se ejecuten las pruebas porque se intentará conectarse a la aplicación web que aún no está corriendo. Si lo quieren hacer desde la línea de comando pueden tirar lo siguiente: mvn clean install -Dmaven.test.skip=true

image

Ahora viene lo interesante porque podemos desde Maven ejecutar un servidor ligero para aplicaciones web y levantar automáticamente nuestra aplicación. Esto es gracias al plugin de Maven “maven-jetty-plugin”

Para eso hacemos lo siguiente:

image

O mvn jetty:run desde la línea de comando.

Esto nos levantará el servidor de jetty y desplegará nuestra aplicación así que ya podemos ejecutar nuestras pruebas a través del siguiente comando: “mvn test” o desde el eclipse, como gusten. Podrán ver la siguiente salida:

-------------------------------------------------------

T E S T S

-------------------------------------------------------

Running org.blog.samples.webservices.test.HelloWorldServiceTest

may 13, 2013 10:41:07 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions

Información: Loading XML bean definitions from class path resource [service-definition-beans-test.xml]

may 13, 2013 10:41:08 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh

Información: Refreshing org.springframework.context.support.GenericApplicationContext@134fd233: startup date [Mon May 13 22:41:08 EDT 2013]; root of context hierarchy

may 13, 2013 10:41:08 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons

Información: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@10554f8b: defining beans [helloWorldClient.proxyFactory,helloWorldClient,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy

may 13, 2013 10:41:08 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass

Información: Creating Service {http://Interface.webservices.samples.blog.org/}HelloWorldServiceService from class org.blog.samples.webservices.Interface.HelloWorldService

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.243 sec

Results :

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

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 6.438s

[INFO] Finished at: Mon May 13 22:41:10 EDT 2013

[INFO] Final Memory: 12M/169M

[INFO] ------------------------------------------------------------------------

Si quieren probar la aplicación pueden ir a: http://localhost:8080/Spring-WS-CXF/

Y verán esto:

image

Si pinchan en el enlace podrán ver el WSDL del servicio que tiene el siguiente aspecto:

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

<wsdl:definitions name="HelloWorldServiceBeanService" targetNamespace="http://Impl.webservices.samples.blog.org/" xmlns:ns1="http://Interface.webservices.samples.blog.org/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Impl.webservices.samples.blog.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<wsdl:import location="http://localhost:8080/Spring-WS-CXF/HelloWorld?wsdl=HelloWorldService.wsdl" namespace="http://Interface.webservices.samples.blog.org/">

</wsdl:import>

<wsdl:binding name="HelloWorldServiceBeanServiceSoapBinding" type="ns1:HelloWorldService">

<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

<wsdl:operation name="sayHello">

<soap:operation soapAction="" style="document"/>

<wsdl:input name="sayHello">

<soap:body use="literal"/>

</wsdl:input>

<wsdl:output name="sayHelloResponse">

<soap:body use="literal"/>

</wsdl:output>

</wsdl:operation>

</wsdl:binding>

<wsdl:service name="HelloWorldServiceBeanService">

<wsdl:port binding="tns:HelloWorldServiceBeanServiceSoapBinding" name="HelloWorldServiceBeanPort">

<soap:address location="http://localhost:8080/Spring-WS-CXF/HelloWorld"/>

</wsdl:port>

</wsdl:service>

</wsdl:definitions>

De esta manera sabemos que el servicio está corriendo dentro de la aplicación desplegada con jetty.

Por último algo que siempre hago es probar el servicio, para eso uso el SOAPUI, pero esto lo dejaré para otra entrada para no extenderme más.

Quedo al tanto de sus comentarios.


¿Te ha gustado este Post? Compártelo con tus amigos.

1 comentario:

  1. Excelente articulo, de casualidad tendras ese codigo en algun repo para poder clonarlo ? Me justaria poder seguir el tuto de manera practica. Saludos.

    ResponderEliminar

IconIconIcon