Hace un rato que no echamos algo de código, así que hoy vamos a ver como construir un cliente SOAP sin usar las populares librerías externas de AXIS, CXF o Metro, entre otras que se podrían usar.
Esto no es sólo académico. En ocasiones queremos proveer comportamientos específicos, o “tunear” la conexión de modo particular, ganar
en flexibilidad y control o buscar mejoras en el rendimiento, y por
esta razón nos vemos en la necesidad de efectuar la conexión “a
mano” de un cliente SOAP.
Asumimos en este
artículo que se tienen conocimientos básicos de Java, SOAP, XML,
Xpath, Maven, SoapUI y la librería Freemarker.
Pare este ejemplo
vamos a usar el webservice para CloblaWeater que se ubica en la
siguiente url “http://www.webservicex.net/globalweather.asmx?WSDL”.
Lo primero que
haremos es probar nuestro servicio con el SoapUI para obtener además
el cuerpo del mensaje SOAP.
Marcar la casilla de
“Create Requests” la cual permite obtener las llamadas de ejemplo
para los servicios disponibles en el WSDL.
Al pulsar Ok, se nos
crea un proyecto con una estructura como la que se muestra a
continuación
Vamos a usar la
versión 1.2 de Soap. Vemos que tenemos los métodos
GetCitiesByCountry y, el que nos interesa, GetWeather.
Pulsamos en el
Request1 de GetWeather y podemos ver el cuerpo de la llamada SOAP
Hagamos una prueba
colocando los siguientes valores CityName : Santiago, CountryName :
Chile.
Un ejemplo de la
llamada y su resultado:
Listo, ya sabemos
que funciona nuestro servicio y tenemos incluso el cuerpo de la
llamada SOAP.
Vamos ahora a crear
nuestro programa Java para acceder a ese servicio.
Voy a usar eclipse
neon para crear un nuevo proyecto Maven y dar la configuración
adecuada.
Agrego las
dependencias y mi archivo POM.xml queda 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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.pigbar.hal9k.soap</groupId> <artifactId>ClienteSoapWeather</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ClienteSoapWeather</name> <description>Cliente soap sin librerias</description> <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.2.5</version> </dependency> <dependency> <groupId>freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.9</version> </dependency> </dependencies> </project>
Ahora ejecutamos en
elcipse el comando Run as…. Maven Build… con los Goals “clean
package”, para que Maven descargue las dependencias de los
repositorios indicados. Debe dar una salida como:
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 12.511 s [INFO] Finished at: 2016-07-23T17:16:39-03:00 [INFO] Final Memory: 12M/242M [INFO] ------------------------------------------------------------------------
Procedemos a crear
la plantilla o template que vamos a usar para el Freemarker.
Como es un proyecto
Maven lo hacemos en la carpeta de recursos (src/main/resources).
Estos archivos deben llevar por omisión la extensión .ftl. Creamos
el archivo “getWeatherByCityTemplate.ftl”. En eclipse es posible
que nos solicite instalar un plugin asociado a esa extensión de
archivo o que la asociemos al editor por defecto. Ambas acciones son
válidas y queda a preferencia del lector. En mi caso yo ya tengo
instalado un editor adecuado.
Abrimos el archivo
.ftl y pegamos la Request1 de la función GetWeather de la sección
Soap 1.2 de nuestro proyecto en SoapUI. Nos debe quedar algo como:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://www.webserviceX.NET"> <soap:Header/> <soap:Body> <web:GetWeather> <web:CityName>${CityName}</web:CityName> <web:CountryName>${CountryName}</web:CountryName> </web:GetWeather> </soap:Body> </soap:Envelope>
El punto a destacar
aquí son los parámetros ${CityName} y ${CountryName} que se
declaran en el template.
Ahora procedemos a
crear nuestra clase de prueba para acceder al template, configurar
los parámetros, llamar al servicio SOAP y procesar el resultado.
En eclipse, en
nuestro proyecto Maven, en la sección src/main/java creamos un
paquete adecuado y la clase para procesar la llamada:
package org.pigbar.hal9k.soap; import java.io.IOException; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.w3c.dom.Document; import org.xml.sax.SAXException; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; public class WeatherByCitySoapClient { public static void main(String[] args) { // Configuración de Freemarker Configuration freeMarCfg = new Configuration(); // cliente http HttpClient httpClient = null; try { // Cargar template .ftl Template weatherByCityTmplate = freeMarCfg.getTemplate("src/main/resources/getWeatherByCityTemplate.ftl"); // Cargamos los parámetros Map<String Object=""> params = new HashMap<String Object="">(); params.put("CityName", "Santiago"); params.put("CountryName", "Chile"); // Crear mensaje y Llamar al servicio SOAP // mensaje StringWriter strOut = new StringWriter(); weatherByCityTmplate.process(params, strOut); String strEnvelope = strOut.getBuffer().toString(); System.out.println("La llamada queda así: \n" + strEnvelope); // llamada a servicio httpClient = new DefaultHttpClient(); HttpPost postRequest = new HttpPost("http://www.webservicex.net/globalweather.asmx"); StringEntity input = new StringEntity(strEnvelope); input.setContentType("application/soap+xml"); postRequest.setEntity(input); // procesar respuesta HttpResponse response = httpClient.execute(postRequest); if (response.getStatusLine().getStatusCode() != 200) { throw new RuntimeException("Error en la llamada : " + response.getStatusLine().getStatusCode()); } // Obtener información de la respuesta DocumentBuilderFactory factoryDoc = DocumentBuilderFactory.newInstance(); Document XMLDocument = factoryDoc.newDocumentBuilder().parse(response.getEntity().getContent()); XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr = xpath.compile("//GetWeatherResult"); String respuesta = String.class.cast(expr.evaluate(XMLDocument, XPathConstants.STRING)); System.out.println("\n\nLa Respuesta es:\n" + respuesta); } catch (TemplateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (XPathExpressionException e) { e.printStackTrace(); } } }
Acá los puntos mas
relevantes son:
Cargar la
configuración de Freemarker, cargar plantilla y pasar parámetros.
// Configuración de Freemarker Configuration freeMarCfg = new Configuration(); // Cargar template .ftl Template weatherByCityTmplate = freeMarCfg.getTemplate("src/main/resources//getWeatherByCityTemplate.ftl"); // Cargamos los parámetros Map<String Object=""> params = new HashMap<String Object="">(); params.put("CityName", "Santiago"); params.put("CountryName", "Chile");
Generar cuerpo de
llamada y hacer llamada por método Post de Http.
StringWriter strOut = new StringWriter(); weatherByCityTmplate.process(params, strOut); String strEnvelope = strOut.getBuffer().toString(); System.out.println("La llamada queda así: \n" + strEnvelope); // llamada a servicio httpClient = new DefaultHttpClient(); HttpPost postRequest = new HttpPost("http://www.webservicex.net/globalweather.asmx"); StringEntity input = new StringEntity(strEnvelope); input.setContentType("application/soap+xml"); postRequest.setEntity(input);
Los
datos para el nput.setContentType("application/soap+xml");
Los
podemos ver en el SoapUI, en la respuesta del servicio, en la pestaña
RAW, allí sale el Content-Type adecuado.
Y
obtenemos la información de la respuesta usando Xpath.
// Obtener información de la respuesta DocumentBuilderFactory factoryDoc = DocumentBuilderFactory.newInstance(); Document XMLDocument = factoryDoc.newDocumentBuilder().parse(response.getEntity().getContent()); XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr = xpath.compile("//GetWeatherResult"); String respuesta = String.class.cast(expr.evaluate(XMLDocument, XPathConstants.STRING)); System.out.println("\n\nLa Respuesta es:\n" + respuesta);
Al
ejecutar este programa en eclipse obtenemos una salida similar a la
siguiente:
La llamada queda así: <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://www.webserviceX.NET"> <soap:Header/> <soap:Body> <web:GetWeather> <!--Optional:--> <web:CityName>Santiago</web:CityName> <!--Optional:--> <web:CountryName>Chile</web:CountryName> </web:GetWeather> </soap:Body> </soap:Envelope> La Respuesta es: <?xml version="1.0" encoding="utf-16"?> <CurrentWeather> <Location>Quintero Santiago, Chile (SCER) 32-47S 071-31W 8M</Location> <Time>Mar 30, 2011 - 11:00 AM EDT / 2011.03.30 1500 UTC</Time> <Wind> from the SW (220 degrees) at 10 MPH (9 KT) (direction variable):0</Wind> <Visibility> greater than 7 mile(s):0</Visibility> <Temperature> 75 F (24 C)</Temperature> <DewPoint> 57 F (14 C)</DewPoint> <RelativeHumidity> 53%</RelativeHumidity> <Pressure> 29.91 in. Hg (1013 hPa)</Pressure> <Status>Success</Status> </CurrentWeather>
Como
vemos esta forma de llamada nos brinda un gran control sobre la
ejecución y los recursos asociados. Ideal para esos entornos donde
no podemos usar otras librerías ya mencionadas.
Comentarios
Publicar un comentario