Ir al contenido principal

Cómo crear un cliente SOAP en Java sin usar AXIS, CXF o Metro


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 urlhttp://www.webservicex.net/globalweather.asmx?WSDL.

Así que manos a las sobras… digo, a las obras jajajaja


Lo primero que haremos es probar nuestro servicio con el SoapUI para obtener además el cuerpo del mensaje SOAP.

Abrinos el SoapUI y creamos un proyecto SOAP con la URL antes suministrada.




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



Podemos notar los parámetros CityName y CountryName que se le deben pasar a la función.
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

Entradas populares de este blog

El Melange todavía corre

Ese era el estribillo de un capítulo de unas de mis series favoritas de la infancia, Meteoro o Speed Racer. En ese capítulo un auto “fantasma” el X-3, aparecía de imprevisto y dejaba a todos asombrados con su rendimiento y prestaciones y volvía a desaparecer. Traigo ese episodio a colación puesto que recientemente sostuve una amena charla con un querido amigo, en la que el me manifestaba como los Mainframes habían muerto, o mejor dicho, el concepto de la computación distribuida basada en Mainframes había desaparecido. Para variar, yo no estuve de acuerdo, y le dije que por el contrario, el modelo de computación basado en Mainframes está mas vigente que nunca. Estos fueron mis argumentos:

Primeros pasos con Camunda BPM – Modelando un Proceso BPMN 2.0

Tenemos entre manos la tercera publicación de nuestra serie sobre la Plataforma de BPM de Camunda .  El día de hoy vamos, por fin, a empezar a modelar o construir nuestro primer proceso sencillo en notación BPMN 2.0. Para ello vamos a usar el modelador o editor que ya hemos instalado en nuestra primera publicación , y vamos a guardarlo en la sección de recursos del proyecto Maven Java que configuramos en la segunda publicación . Así que, como ya es costumbre, manos a las sobras…

Como configurar jBPM para usar nuestra propia Base de Datos en un sólo paso

Llevo un buen rato trabajando con jBPM en su serie 6.x, y mi opinión sobre este producto en la versión mecionada no ha mejorado para nada. Es una herramienta plena de funciones y caracteristicas avanzadas, pero tambien está llena de Bugs y es realmente inestable, sobre todo en el ambiente de modelamiento.  Así mismo, debo decir que tiene una muy aceptable API REST y que el motor de procesos y la consecuente ejecución de los procesos es estable y bastante rápida. En esta publicación daré inicio a una serie de artículos que hablan sobre ciertas configuraciones comunes e importantes que se hacen con jBPM. Hoy iniciamos con la configuración de jBPM para que use nuestra base de datos favorita. Esto tiene sentido porque el producto viene con la base de datos H2 por omisión, la cual es excelente para pruebas y evaluaciones rápidas de la herramienta, pero es completamente inaceptable en un ambiente de desarrollo, QA o producción cualquiera. Así que manos a l