Skip to main content

Consumir Web Services SOAP con WebServiceTemplate de Spring

· 8 min read

En este post se va a explicar cómo consumir un web service SOAP utilizando WebServiceTemplate de Spring. Concretamente, se va a consumir los servicios proporcionados por http://www.webservicex.net/globalweather.asmx, el cual tiene dos métodos, uno GetWeather, que permite obtener la temperatura en una ciudad de un país dado, y otro, GetCitiesByCountry, que permite obtener la relación de ciudades de un país de las que se tiene información meteorológica. El WSDL del web service se encuentra en http://www.webservicex.net/globalweather.asmx?WSDL.

Básicamente, la idea central de WebServiceTemplate es enviar un objeto Java al web service y recibir los datos que devuelve dicho web service en otro objeto Java. Si bien la comunicación en servicios SOAP se realiza enviando y recibiendo datos en formato XML, a través de conversores, WebServiceTemplate utiliza en todo momento para el intercambio de información objetos java, haciendo transparente todo el proceso que requiere SOAP. Esta conversion de datos, marshalling/unmarhsalling, se realiza utilizando JAXB2.

Los pasos que se van a seguir son:

  • Crear un proyecto básico de Spring
  • Adecuar el archivo pom.xml con las dependencias de web services y de JAXB2.
  • Crear las clases de los objetos request y response del web service
  • Configurar el WebServiceTemplate en el application context.
  • Clases test de JUnit
  • Utilizar el monitor TCP/IP para ver los mensajes SOAP

Crear un proyecto básico de Spring

En primer lugar crearemos un proyecto Spring básico en el que al final definiremos una clase de testeo JUnit en donde haremos las llamadas al web service meteorológico. Para crear el proyecto, en nuestro STS pincharemos en File > New > Spring Project. El nombre del proyecto será webservice-template-demo y elegiremos como plantilla Simple Spring Maven, tal y como se muestra en la siguiente imagen:

Adecuar el archivo pom.xml con las dependencias de web services y de JAXB2.

En el pom.xml que se genera automáticamente eliminaremos lo relativo a Hibernate, ya que no lo vamos a utilizar. Añadiremos lo necesario para las dependencias de web services de spring y de oxm, éste último relacionado con JAXB2. Las dependencias mencionadas se incluyen del siguiente modo:

<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>${spring.ws.version}</version>
</dependency>

La versión que vamos a utilizar de spring framework es la 3.2.3.RELEASE y la de webservice la 2.1.4.RELEASE. Al indicar la dependecia de spring-ws-core, automáticamente se incluirá lo neceario de oxm, en concreto, el jar spring-oxm-3.2.4.RELEASE.jar.

Crear las clases de los objetos request y response del web service

La comunicación SOAP se realiza mediante información xml, cuyo xsd podemos obtener el WSDL proporcionado por el web service, concretamente, en la sección wsdl:types. Copiamos esta sección en un archivo, que llamaremos, global-weather.xsd y que situaremos bajo src/main/resources/schemas. El contenido de este archivo será:

<?xml version="1.0" encoding="UTF-8"?>
<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" targetNamespace="http://www.webserviceX.NET">
<s:element name="GetWeather">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="CityName"
type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="CountryName"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetWeatherResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="GetWeatherResult"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetCitiesByCountry">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="CountryName"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetCitiesByCountryResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1"
name="GetCitiesByCountryResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="string" nillable="true" type="s:string" />
</s:schema>

Dado que el archivo xsd es en sí mismo un archivo xml, hemos añadido en la primera línea la información referente a xml.

A partir del xsd generaremos las clases java que representan a los objetos que se utilizarán en SOAP. La generación de estas clases la vamos a llevar a cabo con la utilidad xjc, que normlamente ya encontraremos en las instalaciones linux y mac. Para ejecutar esta utilidad nos apoyaremos en un pequeño job de ant. Crearemos un archivo llamado create-classes.xml y lo situaremos en el raíz del proyecto. El contenido de este archivo es:

<?xml version="1.0"?>
<project name="global-weather" default="generate-sources">
<property name="src.dir" value="src/main/java"/>
<property name="repository.dir" value="${basedir}/../../repository"/>

<target name="generate-sources">
<exec executable="xjc">
<arg line=" -d ${src.dir} -p com.lostinsoftware.ws.types src/main/resources/schemas/global-weather.xsd"/>
</exec>
</target>

</project>

Ejecutaremos este archivo situando encima de él, pulsando seguidamente click-derecho y eligiendo Run As ... > Ant Build. Tras la ejecución, al refrescar el directorio src/main/java, veremos que se ha creado un paquete con una serie de clases en su interior. Son clases con notación JAXB que se utilizarán en la comunicación con el web service.

Configurar el WebServiceTemplate en el application context

Bajo sc/main/resource creamos la carpeta META-INF y bajo ésta la carpta spring, donde ubicaremos el archivo app-context.xml de definición del application context de Spring. El contenido de este archivo será:

<?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:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">

<description>Example WebServiceTemplate</description>

<context:component-scan base-package="com.lostsoftware.wsclient" />

<oxm:jaxb2-marshaller id="marshaller" contextPath="com.lostinsoftware.ws.types"/>

<bean class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
<property name="defaultUri" value="http://www.webserviceX.NET/globalweather.asmx" />
</bean>

</beans>

En este archivo podemos comprobar que se marshaller de jaxb2 hace referencia a las clases que hemos creado anteriormente y que este objeto se asigna al WebServiceTemplate, que lo utilizará cuando tenga que hacer las correspondientes conversiones en los mensajes intercambiados en el web service. También indicamos la uri del web service.

Clases de test JUnit

Para las pruebas del web service, crearemos la clase WebServiceTest en el paquete com.lostinsoftware.ws.client bajo la carpeta src/test/java. También crearemos el archivo de configuración del contexto Spring para el testeo, el cual se llamará WebServiceTest-context.xml, y lo situaremos bajo la carpeta src/test/resources en una estructura de directorios que respete el nombre de paquete antes mencionado. En el contenido de este archivo de configuración únicamente llamaremos de forma directa al archivo de configuración de contexto general de la aplicación. El contenido es:

<?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.xsd">

<import resource="classpath:/META-INF/spring/app-context.xml"/>

</beans>

El método que utilizaremos para, por ejemplo, validar el método GetCitiesByCountry utilizando objetos JAXB, sería similar a lo siguiente:

  @Test
public void testWebServiceGetCitiesByCountryWithJAXB() throws Exception {
GetCitiesByCountry request = new GetCitiesByCountry();
request.setCountryName("Spain");
GetCitiesByCountryResponse response =
(GetCitiesByCountryResponse) webServiceTemplate.marshalSendAndReceive(request,
new SoapActionCallback("http://www.webserviceX.NET/GetCitiesByCountry"));

assertNotNull(response);
System.out.println(response.getGetCitiesByCountryResult());
}

En el código mostrado hay que destacar el párametro new SoapActionCallback("http://www.webserviceX.NET/GetCitiesByCountry"), que indica al web service qué operación queremos que se ejecute. La url que se debe indicar, según la operación que queramos ejecutar, la podemos encontrar en el WSDL, dentro de la sección wsdl:binding, en wsdl:operation en el parámetro de soapAction del tag soap:operation o soap12:operation.

Para hacer el mismo test, pero sin usar objetos JAXB, sino utilizando sólo datos con formato XML, el código que debemos utilizar sería similar a:

  @Test
public void testWebServiceGetCitiesByCountryWithXml() throws Exception {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element requestElement = document.createElementNS(NAMESPACE\_URI, "GetCitiesByCountry");
Element countryName = document.createElement("CountryName");
countryName.setTextContent("Spain");
requestElement.appendChild(countryName);
DOMSource source = new DOMSource(requestElement);
DOMResult result = new DOMResult();
webServiceTemplate.sendSourceAndReceiveToResult(source,
new SoapActionCallback("http://www.webserviceX.NET/GetCitiesByCountry"),
result);
Element responseElement = (Element) result.getNode().getFirstChild();

assertNotNull(responseElement);

}

La estructura final de paquetes, clases, directorios y archivos del proyecto queda como se muestra en la siguiente imagen:

Utilizar el monitor TCP/IP para ver los mensajes SOAP

Para ver el contenido que se transmite XML en las peticiones al web service, podemos utilizar el monitor TCP/IP que el Web Tools Plugin de Eclipse provee. Es una vista que se puede añadir a la perspectiva. Para añadir esta vista debemos ir a Window > Show View > Other... y en la ventana que nos aparece escribir tcp

En la vista que aparece haremos click en la flecha pequeña que apunta hacia abajo y pincharemos en propiedades.

En la ventana que aparece, pincharemos en el botón Add.. y cumplimentaremos los datos tal y como se muestran en la siguiente imagen:

En local monitor indicaremos un puerto que esté libre en nuestra máquina, en nuestro caso, el 8081. Las peticiones enviadas a este puerto serán luego derivadas al servidor que indiquemos en Host name y por el puerto elegido en Port. Esto permite a Eclipse filtrar todas las peticiones y mostrar el contenido de los mensaje transmitidos. Tras cumplimentar los datos pulsaremos en OK y luego en Start.

A continuación, modificaremos el parámetro defaultUri del WebServiceTemplate en el app-context.xml, porque ahora las peticiones tienen que hacerse a http://localhost:8081. La configuración del WebServiceTemplate es:

<bean class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
<property name="defaultUri" value="http://localhost:8081/globalweather.asmx" />
</bean>

Si ahora ejecutamos de nuevo el test del método testWebServiceGetCitiesByCountryWithXml en JUnit podemos ver la información xml, eligiendo en los desplegable del viewer type el valor xml, que se transmite en los mensajes, tal y como se muestra en la siguiente imagen:

Si ahora ejecutamos el método testWebServiceGetCitiesByCountryWithJAXB, la información que se transmite es la siguiente:

Los fuentes del proyecto se encuentran en el archivo webservice-template-demo.zip

Post relacionados: