Generando clientes de servicios web JAX-WS desde .NET

Es normal en mi trabajo que los servicios web que hacemos estén basados en tecnología Java y que, sin embargo, sean utilizados desde clientes con tecnología .NET. Si no se observan de forma rigurosa los estándares el utilizar diferentes plataformas tecnológicas puede llegar a dar problemas. Por eso es indispensable probar los servicios web en todas aquellas plataformas susceptibles de utilizarlos.

Esta entrada explica las dos herramientas más comunes para generar clientes .NET, que a la postre servirán para probar la compatibilidad entre plataformas; Wsdl.exe y Svcutil.exe.

Web Services Description Language Tool (Wsdl.exe)

Desde la versión .NET Framework 1.1 hasta la versión 2.x, para crear aplicaciones SOA, Microsoft ofrece el framework ASMX. Este framework simplifica la creación tanto de los servicios web como de sus clientes. Los servicios web creados a través de esta tecnología son invocados exclusivamente a través del protocolo HTTP. Es por eso que son conocidos como Web References.

Al crear un cliente de servicio web utilizando Web Reference, estamos utilizando por debajo la herramienta Wsdl.exe proporcionada por Microsoft.

Si en la plataforma donde vamos a ejecutar las aplicaciones estamos limitados a la versión 2.x, entonces el uso de esta herramienta es obligatoria. Sin embargo, si se dispone de la versión 3.x o superiores de .NET Framework, ASMX está obsoleto y conviene reemplazarlo por WCF.

Wsdl.exe genera los objetos .NET necesarios para crear un cliente que invoque las operaciones que publica el servicio web. Esta invocación se puede hacer, entre otras, a través del wizard que proporciona Visual Studio o bien a través de línea de comando.

Visual Studio 2005, al estar basado en .NET Framework 2.0, sólo genera clientes mediante ASMX (wsdl.exe).  Visual Studio 2008 a la hora de crear el cliente permite crearlo tanto con ASMX (wsdl.exe) como con WCF (svcutil.exe). La versión de 2010 directamente ya no permite crearlo con ASMX y se oculta esta opción en un botón de opciones avanzadas.

También se pueden obtener los objetos ASMX ejecutando la herramienta Wsdl.exe desde la consola. Esta herramienta se encuentra en el directorio <Program Files>\Microsoft SDKs\Windows\<version>\Bin\wsdl.exe.

La sintaxis de ejecución es:

wsdl [options] {URL | path}

ServiceModel Metadata Utility Tool (Svcutil.exe)

Windows Communication Foundation, conocido como WCF, es el nuevo framework que a partir de la versión 3.x proporcionó Microsoft para desarrollar aplicaciones SOA.

WCF viene a sustituir al framework ASMX y aunque está disponible desde la versión 3.x del .NET Framework no es sino hasta la versión 4.x donde se simplifica y generaliza su uso.

Los clientes de servicios web que se crean con ésta tecnología son conocidos como Service References.

La herramienta que Microsoft proporciona para la generación de Service References es ServiceModel Metadata Utility Tool (svcutil.exe). Esta herramienta sirve tanto para generar los objetos .NET a partir de un fichero wsdl, como para hacer el proceso inverso, generar un wsdl a partir de objetos .NET.

Svcutil.exe se encuentra en el directorio <Program Files>\Microsoft SDKs\Windows\<version>\Bin\svcutil.exe.

La sintaxis de ejecución es:

 svcutil.exe [/t:code] <metadataDocumentPath>* | <url>* | <epr>

El servicio web de ejemplo.

Para probar las generación del cliente .NET con las herramientas descritas, el servicio web de ejemplo es un viejo conocido del blog, ya que es el servicio CalculadoraService que creé en la entrada «Creando un servicio web mediante anotaciones JAX-WS utilizando un enfoque ascendente (bottom-up)«. En esta entrada se creaba un servicio web JAXWS que proporcionaba cuatro operaciones aritméticas básicas: suma, resta, multiplicación y división.

Este servicio web está publicado en un servidor de Weblogic y se puede acceder a su descriptor a través de la URL: http://localhost:7001/JaxWSEjemploWAR/CalculadoraService?WSDL.

Generando el cliente .NET desde Visual Studio con svcutil.exe.

La generación del cliente .NET utilizando Visual Studio es la manera más gráfica y sencilla. Para realizar esta operación se utiliza una versión de prueba de Visual Studio 2010 y el framework .NET versión 4.0. El lenguaje elegido para la implementación es C# por la similitud que tiene con Java.

Para no complicar en exceso el ejemplo se elige crear una aplicación de consola, ya que lo que importa es ver que la llamada al servicio web se hace correctamente, dejando de lado las dificultades que podrían suponer añadir elementos gráficos.

El nombre de la aplicación de consola será CalculadoraServiceServiceReference.

Console Application para probar el cliente mediante Service References

El siguiente paso es añadir una referencia de servicio web a la aplicación. Para ello en la ventana Solution Explorer se hace click botón derecho en el apartado References y se selecciona Add Service Reference.

Botón derecho sobre References y seleccionar Add Service Reference...

Se abre el wizard para añadir referencias a servicios. En el campo Address se escribe la URL del WSDL del servicio web que se quiere añadir como referencia; http://localhost:7001/JaxWSEjemploWAR/CalculadoraService?WSDL. El nombre de la referencia es CalculadoraServiceReference.

Se pega el WSDL del servicio al que hay que acceder

Automáticamente se generan los objetos .NET que se necesitan para comunicarse con el servicio web. La clase del cliente propiamente dicha y que se instanciará para obtener la funcionalidad del servicio web es CalculadoraPortTypeClient.cs.

Objetos generados por svcutil.exe

Finalmente, en el método Main de la aplicación de consola se instancia esta clase del cliente y se llama a cualquiera de sus operaciones. En el ejemplo, se llama a la operación suma mostrando su resultado por consola.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CalculadoraServiceServiceReference.CalculadoraServiceReference;
namespace CalculadoraServiceServiceReference
{
  class Program
  {
    static void Main(string[] args)
    {
      int operando1 = 10;
      int operando2 = 13;
      CalculadoraPortTypeClient cliente = new CalculadoraPortTypeClient();
      int resultado = cliente.suma(operando1, operando2);
      Console.WriteLine("CalculadoraServiceSvcutil Prueba Empírica");
      Console.WriteLine(operando1.ToString() + "+" + operando2.ToString() + "=" + resultado.ToString());
      Console.ReadKey(true);
    }
  }
}

El resultado es:

Resultado llamada mediante Service Reference

Generando el cliente .NET desde Visual Studio con wsdl.exe.

Para generar el mismo ejemplo, pero utilizando ASMX con la versión de Visual Studio 2010, se accede a la generación Web Reference de las opciones Avanzadas que presenta el wizard que añade referencias de servicio. Esta opción avanzada no hace otra cosa que lanzar el wizard para añadir referencias web que se basa en la herramienta wsdl.exe.

Para realizar este ejemplo, al igual que el anterior se crea una aplicación de consola.

El nombre de la aplicación de consola será CalculadoraServiceWebReference.

Se selecciona una aplicación de consola para crear el cliente mediante web references.

Se añade una referencia de servicio web a la aplicación. Para ello en la ventana Solution Explorer se hace click botón derecho en el apartado References y se selecciona Add Service Reference.

Botón derecho sobre References y se selecciona Add Service Reference...

En el wizard que se abre se pincha directamente en el botón Advanced….

Se pincha el botón Advanced...

Las opciones avanzadas permiten personalizar la creación del cliente, pero todavía basándose en svcutil.exe, que no es lo que se pretende. Para utilizar wsdl.exe se pincha el botón Add Web Reference.

Click en el botón Add Web Reference...

Se abre el wizard antiguo para la generación de clientes mediante Web Reference. En el campo URL se introduce el WSDL del servicio para el que se genera el cliente. En el campo Web reference name se introduce el nombre del objeto .NET que va a encapsular este cliente; CalculadoraServiceWebReference. Click en el botón Add Reference.

Se rellena la URL con el WSDL y automáticamente detecta las operaciones del Web Service. Click en el botón Add Reference.

Automáticamente se generan los objetos .NET que se necesitan para comunicarse con el servicio web. La clase que se instancia para hacer uso del cliente es CalculadoraService.

Objetos .NET del cliente basado en Web Reference.

Finalmente, en el método Main de la aplicación de consola se instancia la clase del cliente y se llama a cualquiera de sus operaciones. En este ejemplo, y para hacer que se parezca lo mejor posible al anterior, también se elige la operación suma.

El cliente generado con wsdl.exe, sin embargo, difiere del generado con la herramienta svcutil.exe. La llamada a la operación suma necesita dos parámetros booleanos, que antes no aparecían, y que indican si se han informado o no los operandos. Además, el resultado no es un retorno de función, si no que se devuelve en un parámetro output de entrada.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CalculadoraServiceWebReference.CalculadoraServiceWebReference;
namespace CalculadoraServiceWebReference
{
  class Program
  {
    static void Main(string[] args)
    {
      int operando1 = 10;
      bool operando1Informado = true;
      int operando2 = 13;
      bool operando2Informado = true;
      int resultado;
      bool resultadoInformado;
      CalculadoraService cliente = new CalculadoraService();
      cliente.suma(operando1, operando1Informado, operando2, operando2Informado, out resultado, out resultadoInformado);
      Console.WriteLine("CalculadoraServiceWebReference Prueba Empírica");
      Console.WriteLine(operando1.ToString() + "+" + operando2.ToString() + "=" + resultado.ToString());
      Console.ReadKey(true);
    }
  }
}

Pese a la diferencia de generación del cliente, el servicio web se invoca correctamente y el resultado de la suma se puede ver por la consola:

Resultado de la invocación al servicio web vía web references (ASMX)

Conclusión

Microsoft dió un paso adelante en la interoperabilidad entre plataformas y en el framework .NET 3.x incluyó WCF para el manejo de servicios web. Esta nueva tecnología sustituye al framework ASMX que ha quedado relegado a ser utilizado en aquellas arquitecturas donde todavía se emplea el framework .NET 1.1 o el 2.x.

De todas formas, ya hemos visto que Microsoft en su plataforma de desarrollo, permite desarrollar todavía con las dos tecnologías ASMX y WCF. Pero, salvo que estemos limitados por la versión de framework .NET, lo recomendable es utilizar WCF, ya que nos aportará más compatibilidad entre plataformas y una aproximación a los estándares mucho más fiable que ASMX.

Links

Creando un servicio web mediante anotaciones JAX-WS utilizando un enfoque ascendente (bottom-up)

Para la programación de servicios web, independientemente de la tecnología, ya sea Java, ya sea .NET, se pueden utilizar dos enfoques distintos; crear el servicio web a partir de un WSDL ya existente (enfoque top-down o descendente), o generar el correspondiente WSDL a partir de código fuente ya existente (enfoque bottom-up o ascendente).

El objetivo de esta entrada es generar un servicio web mediante el enfoque bottom-up o ascendente y utilizando la tecnología JAXWS. En concreto, en esta entrada se utilizan las anotaciones de la especificación JSR-181.

¿Qué necesitas para seguir esta entrada?

Me gustaría aclarar que no voy a utilizar para la generación de este servicio web ninguna ayuda (wizard) de las que proporcionan las IDEs Java que hay en el mercado, por tanto, cualquiera de éstas herramientas sirve para desarrollar el ejemplo.

En este caso, y porque ya lo tengo instalado, voy a utilizar Eclipse 3.7.1 (Helios) con el plugin de Oracle (OEPE) para realizar un rápido despliegue en un dominio básico de Weblogic que he creado para esta ocasión.

El servidor de aplicaciones sobre el que voy a desplegar y publicar el servicio web va a ser el parche 10.3.6 de Weblogic 11g.

El dominio básico de Weblogic creado para estas pruebas tiene una única instancia que nos servirá tanto para publicar la consola administrativa del servidor, como la aplicación de ejemplo que voy a generar.

La JDK instalada es la versión 1.6.0_29 del Hotspot de Oracle.

Los componentes de la prueba

Los servicios web, en java, se publican en aplicaciones web dinámicas y se empaquetan en ficheros WAR. Teniendo esto en cuenta, para esta prueba voy a crear un proyecto Web Dinámico que a su vez voy a incluir en una aplicación JEE empaquetada en un EAR.

El motivo de incluir el módulo WAR en un EAR es simplemente una decisión personal del grupo «porquemeapetece«. En realidad, simplemente con el módulo WAR ya nos bastaría para desplegar y publicar el servicio web en el servidor de Weblogic.

La aplicación EAR la voy a llamar AppEjemploEAR y el módulo WAR lo voy a llamar JaxWSEjemploWAR.

La clase java de ejemplo.

La lógica de negocio que quiero convertir en servicio web es una calculadora muy básica compuesta por cuatro métodos: suma, resta, multiplicacion y division, que realizan las operaciones aritméticas correspondientes entre dos operandos.

La implementación de este servicio es la clase CalculadoraService.java que se encuentra en el directorio de fuentes de la aplicación WAR.

package ejemplo.jaxws; 
/**
 * @author EGV
 * <p>Clase calculadora que proporciona las cuatro operaciones aritméticas 
 * básicas: suma, resta, multiplicación y división.</p> 
 */
public class CalculadoraService {

  /**
   * Realiza la suma de los operandos.
   * @param operando1 Operando uno.
   * @param operando2 Operando dos.
   * @return Operando1 + Operando2.  
   */
  public Integer suma (Integer operando1, Integer operando2)
  {
    return operando1 + operando2;
  }

  /**
   * Realiza la resta de los dos operandos.
   * @param operando1 Operando uno.
   * @param operando2 Operando dos.
   * @return Operando1 - Operando2
   */
  public Integer resta (Integer operando1, Integer operando2)
  {
    return operando1 - operando2; 
  }

  /**
   * Realiza la multiplicación de los dos operandos.
   * @param operando1 Operando uno.
   * @param operando2 Operando dos.
   * @return Operando1 * Operando2
   */
  public Integer multiplicacion (Integer operando1, Integer operando2)
  {
    return operando1 * operando2;
  }

  /**
   * Realiza la división entre los operandos.
   * @param operando1 Operando1
   * @param operando2 Operando2
   * @return Operando1 / Operando2   
   */
  public Integer division (Integer operando1, Integer operando2)  
  {
    return operando1 / operando2;
  }

  /**
  * @param args
  */
  public static void main(String[] args) {
    Integer operando1 = new Integer(10);
    Integer operando2 = new Integer(3);
    Integer resultado;
    CalculadoraService calculadora = new CalculadoraService();
    StringBuffer bsTraza;

    resultado = calculadora.suma(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("+").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

    resultado = calculadora.resta(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("-").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

    resultado = calculadora.multiplicacion(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("*").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

    resultado = calculadora.division(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("/").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

  }

}

Las anotaciones básicas para definir Servicios Web.

La especificación JSR-181 define una serie de anotaciones que permiten generar, a partir de una clase java, un servicio web. Gracias a estas anotaciones el desarrollo del servicio web se simplifica enormemente, además, las anotaciones de esta especificación están incluidas en la JDK a partir de la versión 1.5 y sucesoras, por tanto, incluidas en mi versión 1.6.0_29.

Las anotaciones que voy a utilizar son las siguientes:

  • @WebService –> javax.jws.WebService
  • @WebMethod –> javax.jws.WebMethod
  • @WebParam –> javax.jws.WebParam
  • @WebResult –> javax.jws.WebResult
  • @SOAPBinding –> javax.jws.soap.SOAPBinding

Las anotaciones @WebParam, @WebResult y @SOAPBinding las incluyo para dejar constancia de su funcionamiento, en verdad para este ejemplo tan básico no serían necesarias ya que la configuración por defecto, es la más adecuada.

La clase java de ejemplo anotada.

La clase CalculadoraService.java con las anotaciones quedaría de la siguiente forma:

package ejemplo.jaxws;
import javax.jws.WebMethod;
 import javax.jws.WebParam;
 import javax.jws.WebResult;
 import javax.jws.WebService;
 import javax.jws.soap.SOAPBinding;
/**
* @author EGV
* <p>Clase calculadora que proporciona las cuatro operaciones aritméticas
* básicas: suma, resta, multiplicación y división.</p>
* <p>Esta clase se anota con la especificación JSR-181 para convertirla
* en un servicio web.</p>
*/
@WebService(serviceName="CalculadoraService",
  name="CalculadoraPortType",
  targetNamespace="http://egv.com/ejemplo-jaxws/calculadora")
@SOAPBinding( style=SOAPBinding.Style.DOCUMENT,
  use=SOAPBinding.Use.LITERAL, 
  parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
public class CalculadoraService {
  /**
  * Realiza la suma de los operandos.
  * @param operando1 Operando uno.
  * @param operando2 Operando dos.
  * @return Operando1 + Operando2.
  */
  @WebMethod( operationName="suma",
    action="sumaAction")
  @WebResult( name="sumaResponse")
  public Integer suma (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 + operando2;
  }

  /**
  * Realiza la resta de los dos operandos.
  * @param operando1 Operando uno.
  * @param operando2 Operando dos.
  * @return Operando1 - Operando2
  */
  @WebMethod( operationName="resta",
    action="restaAction")
  @WebResult( name="restaResponse")
  public Integer resta (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 - operando2;
  }

  /**
  * Realiza la multiplicación de los dos operandos.
  * @param operando1 Operando uno.
  * @param operando2 Operando dos.
  * @return Operando1 * Operando2
  */
  @WebMethod( operationName="multiplicacion",
    action="multiplicacionAction")
  @WebResult( name="multiplicacionResponse")
  public Integer multiplicacion (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 * operando2;
  }

  /**
  * Realiza la división entre los operandos.
  * @param operando1 Operando1
  * @param operando2 Operando2
  * @return Operando1 / Operando2
  */
  @WebMethod( operationName="division",
    action="divisonAction")
  @WebResult( name="divisionResponse")
  public Integer division (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 / operando2;
  }

Las anotaciones @WebService y @SOAPBinding son generales al servicio web generado, por lo que se asocian a la clase. Las anotaciones @WebMethod y @WebResult están relacionadas con el método y por tanto hay una por cada método a publicar. La anotación @WebParam está relacionada a los parámetros, por lo que habrá una por cada parámetro que haya en los métodos.

Desplegando, publicando y probando el servicio web.

Una vez que la clase java que queremos publicar como servicio web está anotada, desplegamos la aplicación EAR en el servidor de Weblogic.

Si hemos hecho correctamente la anotación, el servidor desplegará AppEjemploEAR con el módulo JaxWSEjemploWAR dentro y el servicio web CalculadoraService publicado.

Vista Despliegue AppEjemploEAR

Para probar el servicio web se puede hacer de varias maneras; generando un cliente de web service, utilizando la herramienta de test de servicios web proporcionada por Weblogic, utilizando herramientas externas como SOAPUi, etcétera.

En la imagen se puede ver el cliente que proporciona el servidor de Weblogic. Si en la vista de despliegue se pincha en el servicio web a probar, en este caso CalculadoraService, nos abrirá la pantalla con la configuración propia de este servicio. En la pestaña de Prueba existe un Punto de Prueba que se llama Test 

Client que nos llevará a una página web que nos permitirá probar las diferentes operaciones de CalculadoraService.

Cliente Test de CalculadoraService

El WSDL generado

El WSDL del servicio web es generado a partir de la clase anotada.

Podemos observar como las anotaciones que hemos incluido en el código Java se reflejan en los diferentes apartados del documento WSDL.

<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. -->
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. -->
<definitions 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://egv.com/ejemplo-jaxws/calculadora" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://egv.com/ejemplo-jaxws/calculadora" name="CalculadoraService">
  <types>
    <xsd:schema>
      <xsd:import namespace="http://egv.com/ejemplo-jaxws/calculadora" schemaLocation="http://172.16.204.140:7001/JaxWSEjemploWAR/CalculadoraService?xsd=1"/>
    </xsd:schema>
  </types>
  <message name="suma">
    <part name="parameters" element="tns:suma"/>
  </message>
  <message name="sumaResponse">
    <part name="parameters" element="tns:sumaResponse"/>
  </message>
  <message name="resta">
    <part name="parameters" element="tns:resta"/>
  </message>
  <message name="restaResponse">
    <part name="parameters" element="tns:restaResponse"/>
  </message>
  <message name="multiplicacion">
    <part name="parameters" element="tns:multiplicacion"/>
  </message>
  <message name="multiplicacionResponse">
    <part name="parameters" element="tns:multiplicacionResponse"/>
  </message>
  <message name="division">
    <part name="parameters" element="tns:division"/>
  </message>
  <message name="divisionResponse">
    <part name="parameters" element="tns:divisionResponse"/>
  </message>
  <portType name="CalculadoraPortType">
    <operation name="suma">
      <input message="tns:suma"/>
      <output message="tns:sumaResponse"/>
    </operation>
    <operation name="resta">
      <input message="tns:resta"/>
      <output message="tns:restaResponse"/>
    </operation>
    <operation name="multiplicacion">
      <input message="tns:multiplicacion"/>
      <output message="tns:multiplicacionResponse"/>
    </operation>
    <operation name="division">
      <input message="tns:division"/>
      <output message="tns:divisionResponse"/>
    </operation>
  </portType>
  <binding name="CalculadoraPortTypePortBinding" type="tns:CalculadoraPortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="suma">
      <soap:operation soapAction="sumaAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="resta">
      <soap:operation soapAction="restaAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="multiplicacion">
      <soap:operation soapAction="multiplicacionAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="division">
      <soap:operation soapAction="divisonAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="CalculadoraService">
    <port name="CalculadoraPortTypePort" binding="tns:CalculadoraPortTypePortBinding">
      <soap:address location="http://172.16.204.140:7001/JaxWSEjemploWAR/CalculadoraService"/>
    </port>
  </service>
</definitions>

Los XML Request y Response de la operación Suma.

Para invocar a la operación Suma del servicio web CalculadoraService se envía el XML de petición de servicio. Como respuesta, el servicio web enviará otro XML con la información de la operación. Estos XMLs también están influidos por las anotaciones de la clase Java anotada.

XML Request

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header />
  <env:Body>
    <suma xmlns="http://egv.com/ejemplo-jaxws/calculadora">
      <!--Optional:-->
      <operando1 xmlns="">10</operando1>
      <!--Optional:-->
      <operando2 xmlns="">13</operando2>
    </suma>
  </env:Body>
</env:Envelope>

XML Response

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <S:Body>
    <ns2:sumaResponse xmlns:ns2="http://egv.com/ejemplo-jaxws/calculadora">
      <sumaResponse>23</sumaResponse>
    </ns2:sumaResponse>
  </S:Body>
</S:Envelope>

Conclusión

Si disponemos de una clase java, bien sea un POJO, o bien sea un Entreprise Java Bean, que contiene lógica de negocio que quisiéramos publicar para su utilización por otras aplicaciones en una arquitectura SCA (orientada a componentes), una buena idea es convertir de manera muy sencilla esa clase en un servicio web mediante las anotaciones que proporciona la especificación JSR-181.
Esta especificación se incluyó por primera vez en la JDK 1.5 y permite que una aplicación EAR automáticamente despliegue y publique servicios web.
Estas anotaciones se incluyen en la clase Java y definen el contrato WSDL del servicio web.
En siguientes entradas iré comentando con más en detalle el uso de las anotaciones JAX-WS para manipular el WSDL generado, haciendo un uso avanzado además de las anotaciones relacionadas con JAXB para la manipulación de los XML de las operaciones.

Links

WSDL (VII) – wsdl:service y wsdl:port

Esta entrada es la séptima y última entrega dedicada al interesante mundo del WSDL. En esta entrada encontrarás en detalle los elementos wsdl:service y wsdl:port, que contiene la dirección del endpoint donde se va a publicar el servicio web.

Descripción

Wsdl:service forma parte del objeto wsdl:definitions.

Wsdl:port forma parte del elemento wsdl:service.

El elemento wsdl:port define el endpoint para un wsdl:binding concreto.

Gramática

La gramática del elemento wsdl:types es la siguiente (? –> es opcional, * –> pueden ser de 0 a N elementos):

<wsdl:service .... > *
 <wsdl:port name="nmtoken" binding="qname"> *
 <-- extensibility element (1) -->
 </wsdl:port>
</wsdl:service>

Atributos

El elemento wsdl:service cuenta con los siguientes atributos:

  • name: este atributo proporciona un nombre único para el servicio web. Debe coincidir con el atributo name del elemento wsdl:definitions.
El elemento wsdl:port cuenta con los siguientes atributos:
  • binding: esta atributo proporciona el nombre del wsdl:binding para el que se crea el endpoint.
  • name: este atributo proporciona un nombre único para este endpoint.

SubElementos

El elemento «extensibility element (1)» indica la información para construir la URI del endpoint. Un puerto sólo puede tener un endpoint. El elemento soap:address mediante el atributo location es el encargado de darle una URI donde se publica el servicio WEB.  

Ejemplo.

Este ejemplo muestra la definición de un binding para el portType AsistenteProducto. 

<wsdl:service nameAsistenteProductoSW«>
<wsdl:port bindingtns:AsistenteProductoSWSoapBinding« nameAsistenteProducto«>
</wsdl:port>
</wsdl:service>

Conclusión

El apartado wsdl:services es el qué marca la URI donde se publica el servicio web. Esta URI es la que se debe distribuir a los clientes que deben acceder a las funcionalidades proporcionadas por el servicio. 

Links

WSDL (VI) – wsdl:binding

Esta entrada es la sexta entrega dedicada al interesante mundo del WSDL. En esta entrada encontrarás en detalle el elemento wsdl:binding, que contiene los detalles sobre el formato del mensaje, así como del protocolo utilizado para transportar la información tanto de entrada como de salida.

Descripción

Wsdl:binding forma parte del objeto wsdl:definitions.

El elemento wsdl:binding define el formato del mensaje y los detalles del protocolo que se utiliza en los diferentes procesos de comunicación del servicio web. Es posible definir diferentes bindings asociados al mismo portType, por lo que un mismo servicio web puede implementar la comunicación a través de diferentes protocolos y dentro de cada operación definir diferentes configuraciones para la comunicación de entrada y de salida.

El tipo de binding elegido puede influir en la compatibilidad multiplataforma del servicio web 

Gramática

La gramática del elemento wsdl:types es la siguiente (? –> es opcional, * –> pueden ser de 0 a N elementos):

<wsdl:binding name="nmtoken" type="qname"> *
 <-- extensibility element (1) --> *
 <wsdl:operation name="nmtoken"> *
 <-- extensibility element (2) --> *
 <wsdl:input name="nmtoken"? > ?
 <-- extensibility element (3) --> 
 </wsdl:input>
 <wsdl:output name="nmtoken"? > ?
 <-- extensibility element (4) --> *
 </wsdl:output>
 <wsdl:fault name="nmtoken"> *
 <-- extensibility element (5) --> *
 </wsdl:fault>
 </wsdl:operation>
 </wsdl:binding>

Atributos

El elemento wsdl:binding cuenta con los siguientes atributos:

  • name: este atributo proporciona un nombre único que identifica el binding dentro del documento WSDL
  • type: este atributo enlaza el elemento wsdl:binding con el portType definido, de hecho, deben llevar el mismo nombre.

SubElementos

El elemento «extensibility element (1)» indica el tipo de binding que se elige para las operaciones especificadas. Cada elemento wsdl:binding solo puede hacer referencia a un único protocolo. Esto se hace indicando cualquiera de estos tres elementos:

  • <soap:binding style=»document | rpc» transport=»http://schemas.xmlsoap.org/soap/{http | smtp | ftp}»/>
  • <http:binding verb=»GET | POST»>
  • <mime:multipartRelated>
El primer binding es un binding basado en el protocolo SOAP. El segundo binding está basado en GET y POST de http y el último es el binding MIME que suele ir siempre ligado a mensajes de INPUT/OUTPUT de operaciones SOAP.  
 
El elemento wsdl:operation describe la operación sobre la que se va a aplicar información de binding. Esta información viaja en el elemento «extensibility element (2)» que detallará la información de binding propia de la operación.
 
El elemento wsdl:input describe el objeto de entrada al que se va a aplicar información de binding. Esta información viaja en el elemento «extensibility element (3)» que detallará la información de binding para el objeto de entrada de esta operación. 
 
El elemento wsdl:output describe el objeto de respuesta al que se va a aplicar información de binding. Esta información viaja en el elemento «extensibility element (4)» que detallará la información de binding para el objeto de respuesta de esta operación.
 
El elemento wsdl:fault describe en caso de fallo las características de binding. Esta información viaja en el elemento «extensibility element (5)» que detallará la información de binding para el objeto de error.
 

Ejemplo de SOAP Binding.

Este ejemplo muestra la definición de un binding para el portType AsistenteProducto. 

<wsdl:binding 
   name="AsistenteProductoSWSoapBinding" 
   type="tns:AsistenteProducto">
 <soap:binding 
    styledocument«    
 <wsdl:operation nameconsultaInteraccionesTratamientoCompleto«>     
   <soap:operation 
     soapAction/consultaInteraccionesTratamientoCompletoOP«          
     styledocument«/>
  <wsdl:input nameconsultaInteraccionesTratamientoCompleto«         
     <soap:body useliteral«/>
  </wsdl:input>
  <wsdl:output nameconsultaInteraccionesTratamientoCompletoResponse«>
    <soap:body useliteral«/>
  </wsdl:output>
 </wsdl:operation>
 <wsdl:operation nameconsultaDuplicidadesTratamientoNuevo«>
  <soap:operation 
      soapAction/consultaDuplicidadesTratamientoNuevoOP«      
      styledocument«/>
  <wsdl:input nameconsultaDuplicidadesTratamientoNuevo«>
    <soap:body useliteral«/>
  </wsdl:input>
  <wsdl:output nameconsultaDuplicidadesTratamientoNuevoResponse«>
    <soap:body useliteral«/>
  </wsdl:output>
 </wsdl:operation>
 <wsdl:operation nameconsultaDuplicidadesTratamientoCompleto«>
  <soap:operation 
      soapAction/consultaDuplicidadesTratamientoCompletoOP«       
      styledocument«/>
  <wsdl:input nameconsultaDuplicidadesTratamientoCompleto«>
    <soap:body useliteral«/>
  </wsdl:input>
  <wsdl:output nameconsultaDuplicidadesTratamientoCompletoResponse«>
    <soap:body useliteral«/>
  </wsdl:output>
 </wsdl:operation>
 <wsdl:operation nameconsultaInteraccionesTratamientoNuevo«>
  <soap:operation 
     soapAction/consultaInteraccionesTratamientoNuevoOP«    
     styledocument«/>
  <wsdl:input nameconsultaInteraccionesTratamientoNuevo«>
    <soap:body useliteral«/>
  </wsdl:input>
  <wsdl:output nameconsultaInteraccionesTratamientoNuevoResponse«>
    <soap:body useliteral«/>
  </wsdl:output>
 </wsdl:operation>
</wsdl:binding>

En el ejemplo se puede ver que el binding es de tipo SOAP a través de HTTP. El estilo de SOAP en este caso es document.  Si no se pone nada, el estilo seguirá siendo «document«.  

<wsdl:binding 
   name="AsistenteProductoSWSoapBinding" 
   type="tns:AsistenteProducto">
 <soap:binding 
    style="document"    
    transport="http://schemas.xmlsoap.org/soap/http"/>

Podemos ver que cada operación puede definir un SOAP style propio, teniendo como posibles valores también RPC y DOCUMENT. En el ejemplo no se indica, por defecto el estilo es document. El campo soapAction especifica el valor de cabecera SOAPAction. En el caso de protocolo SOAP sobre HTTP, el valor SOAPAction de la cabecera es obligatorio, por lo que siempre habrá que rellenarlo. 

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

Cada elemento que conforma la operación, wsdl:input, wsdl:output o wsdl:fault definen el elemento soap:body. Este elemento especifica como se incluyen los elementos wsdl:parts en el elemento SOAP Body.  El atributo use marca la codificación que se realiza en la información, y puede ser literal o encoded. En el ejemplo, el objeto de entrada de consultaInteraccionesTratamientoNuevo la codificación del body se realiza de forma literal

<wsdl:input name="consultaInteraccionesTratamientoNuevo">
    <soap:body useliteral«/>
</wsdl:input>

 

DOCUMENT versus RPC

A la hora de especificar dentro de las operaciones SOAP como queremos que se vea el mensaje transportado la definición del atributo style de la etiqueta soap:binding o soap:operation (si queremos aplicar un estilo diferente por cada operación) es la clave. 

El atributo style puede tomar dos posibles valores: document y rpc (remote procedure call). 

Un mensaje SOAP de estilo RPC se construye con la idea de invocar al servicio web como si fuera un método de una aplicación. Por tanto, el cuerpo del mensaje SOAP contiene un elemento XML por cada parámetro que necesita el método y estos elementos de parámetro se incluyen en otro elemento superior que representa el nombre del método al que se llama. El WSDL para una operación codificada en RPC es menos compleja que sus hermanas codificadas en estilo DOCUMENT. 

Un ejemplo de mensaje SOAP RPC es:

<soap:envelope>
  <soap:body>
    <multiply>    <!-- web method name -->
      <a>2.0</a>  <!-- first parameter -->
      <b>7</b>    <!-- second parameter -->
    </multiply>
  </soap:body>
</soap:envelope>

Un mensaje SOAP de estilo DOCUMENT, sin embargo, no contiene restricciones en la construcción del cuerpo del mensaje. Permite la inclusión libre de datos en formato XML representados por un Schema XML. De esta forma, el cliente y el proveedor son los encargados de hacer el trabajo de marshall y unmarshall. Esto contrasta con RPC donde este trabajo de marshal y unmarshall debe ser realizado por la librería SOAP que se esté utilizando. 

Un ejemplo de mensaje SOAP DOCUMENT es:

<soap:envelope>
  <soap:body>
    <!-- arbitrary XML -->
    <movies xmlns="http://www.myfavoritemovies.com">
      <movie>
        <title>2001: A Space Odyssey</title>
        <released>1968</released>
      </movie>
      <movie>
        <title>Donnie Darko</title>
        <released>2001</released>
      </movie>
    </movies>
  </soap:body>
</soap:envelope>

 

La gran ventaja de RPC sobre DOCUMENT es que es fácilmente interpretable por humanos. Sin embargo, esta ventaja no es tal, ya que no tiene gran utilidad que los humanos podamos leer de forma fácil los WSDL.

Por otro lado, esta ventaja, se convierte en desventaja debido al acoplamiento tan fuerte que hay con el código de la aplicaciónlo. Esto quiere decir, por ejemplo,  que si se quiere realizar un cambio en el orden de los parámetros implica irremediablemente un cambio en la definición del servicio web. 

Sin embargo, el estilo DOCUMENT tiene un acoplamiento muy débil con el código de la aplicación. La gran ventaja es que cambios realizados en el orden de los parámetros o incluso en el tipo de los mismos no influyen en la definición del servicio web. La desventaja  es que el SOAP generado es más difícil de interpretar visualmente por una persona (lo que tampoco tiene porque ser una gran desventaja).

Además, el que no haya reglas específicas para la generación de SOAP en el estilo DOCUMENT es una ventaja/desventaja. Es ventaja porque nos permite libertad total a la hora de codificar la información y es una desventaja porque el trabajo de MARSHALL y UNMARSHALL recae en el cliente y en el proveedor.

Encoded versus Literal

La otra gran opción a la hora de determinar el aspecto final del código SOAP generado es el atributo use del elemento soap:body, que puede tomar valores encoded o literal.

El uso Literal implica que el cuerpo SOAP sigue un esquema XML para definir su contenido. Este esquema XML se incluye en el apartado wsdl:types del WSDL. Por tanto, siempre que el cliente tenga acceso al WSDL sabrá como debe hacer el marshall/unmarshall del documento.

El uso Encoded, sin embargo, implica que el cuerpo SOAP no sigue ningún esquema XML, aunque tenga que seguir un formato específico que incluya la información para que el cliente sepa como hacer el marshall y el unmarshall de la información. El modo encoded no es recomendable y no está aceptado por el estándar WS-I, debido a las incompatibilidades que las diferentes implementaciones SOAP hagan en los diferentes frameworks de servicios web y los diferentes lenguajes de programación.

Conclusión

El apartado wsdl:binding es el qué más problemas de incompatibilidad puede dar en el uso multiplataforma de un servicio web. En él se define el protocolo utilizado en la comunicación, así como la codificación de la información que viaja por cada operación. 

Es recomendable que la codificación se realice siempre en estilo DOCUMENT y uso LITERAL. De esta forma nos aseguramos libertad en el envío de la información y garantizamos que dicha información cumple un esquema que facilita el tratamiento de la misma.

Links

WSDL (V) – wsdl:portType

Esta entrada es la quinta dedicada al interesante mundo del WSDL. En esta entrada encontrarás en detalle el elemento wsdl:portType, que contiene la definición de las operaciones que se incluyen en el servicio web.

Descripción

Wsdl:portType forma parte del objeto wsdl:definitions.

El elemento wsdl:portType define un grupo de operaciones que forman parte del servicio web. Este elemento también es conocido como interface, de hecho en la nueva especificación WSDL 1.2 (que en el momento de escribir este artículo se encuentra en estado «draft«) se sustituye el termino «portType» por el de «interface«. 

Gramática

La gramática del elemento wsdl:types es la siguiente (? –> es opcional, * –> pueden ser de 0 a N elementos):

 <wsdl:portType name="nmtoken">
<wsdl:operation name="nmtoken" .... /> *
</wsdl:portType>

Atributos

El principal atributo del elemento wsdl:portType es name. Este nombre servirá para identificar este portType en el apartado wsdl:binding, de forma que cada portType pueda tener sus propias características de envío. 

SubElementos

El elemento wsdl:operation contiene la información de las diferentes operaciones que se incluyen en el portType. Este elemento wsdl:operation puede estar formado por los siguientes elementos:

  • wsdl:input para definir el mensaje de entrada 
  • wsdl:output para definir el mensaje de salida
  • wsdl:fault para definir el mensaje en caso de error. 

 El orden en el que se presentan estos elementos define el patrón de intercambio de mensajes utilizado. 

  • Patrón One-way. El endpoint recibe un mensaje. Se define solamente un elemento de entrada.
  • Patrón Request-response. El endpoint recibe un mensaje y envía la correspondiente respuesta. Se define un elemento de entrada seguido de uno de salida. 
  • Patrón Solicit-Response. El endpoint envía un mensaje y recibe la respuesta correspondiente. Se define un elemento de salida y seguido uno de entrada. 
  • Patrón Notification. El endpoint envia un mensaje. Se define un elemento de salida. 

 

Ejemplo

Este ejemplo muestra el portType AsistenteProducto

<wsdl:portType name="AsistenteProducto">
<wsdl:operation nameconsultaInteraccionesTratamientoCompleto«>
<wsdl:input messagetns:consultaInteraccionesTratamientoCompleto« nameconsultaInteraccionesTratamientoCompleto«></wsdl:input>
<wsdl:output messagetns:consultaInteraccionesTratamientoCompletoResponse« nameconsultaInteraccionesTratamientoCompletoResponse«></wsdl:output>
</wsdl:operation>

<wsdl:operation nameconsultaDuplicidadesTratamientoNuevo«>
<wsdl:input messagetns:consultaDuplicidadesTratamientoNuevo« nameconsultaDuplicidadesTratamientoNuevo«></wsdl:input>
<wsdl:output messagetns:consultaDuplicidadesTratamientoNuevoResponse« nameconsultaDuplicidadesTratamientoNuevoResponse«></wsdl:output>
</wsdl:operation>

<wsdl:operation nameconsultaInteraccionesTratamientoNuevo«>
<wsdl:input messagetns:consultaInteraccionesTratamientoNuevo« nameconsultaInteraccionesTratamientoNuevo«></wsdl:input>
<wsdl:output messagetns:consultaInteraccionesTratamientoNuevoResponse« nameconsultaInteraccionesTratamientoNuevoResponse«></wsdl:output>
</wsdl:operation>

<wsdl:operation nameconsultaDuplicidadesTratamientoCompleto«>
<wsdl:input messagetns:consultaDuplicidadesTratamientoCompleto« nameconsultaDuplicidadesTratamientoCompleto«></wsdl:input>
<wsdl:output messagetns:consultaDuplicidadesTratamientoCompletoResponse« nameconsultaDuplicidadesTratamientoCompletoResponse«></wsdl:output>
</wsdl:operation>
</wsdl:portType>

Como podemos observar  el portType está formado por 4 operaciones, cada una con su entrada y salida propias:

  • consultaInteraccionesTratamientoCompleto
  • consultaDuplicidadesTratamientoNuevo
  • consultaInteraccionesTratamientoNuevo
  • consultaDuplicidadesTratamientoCompleto 
Todas las operaciones definidas siguen el patrón Request – Response, de forma que para que cualquier operación de este servicio web devuelva información debe recibir primero una petición informada.
 
Si generamos un cliente con este portType obtendremos una estructura similar a la que se muestra en la siguiente imagen:
 
Operaciones incluidas en un portType y en un binding

Conclusión

El apartado wsdl:portType indica las operaciones que se van a incluir de forma pública en el servicio web.

WSDL (IV) – wsdl:messages

Esta entrada es la cuarta entrega dedicada al interesante mundo de los WSDL. En esta entrada encontrarás en detalle el elemento wsdl:messages, que define los mensaje abstractos que sirven de entrada y salida de las operaciones.

Descripción

Wsdl:messages forma parte del objeto wsdl:definitions.

El elemento wsdl:messages define los mensaje abstractos que sirven de entrada y salida de las operaciones.

Estos mensajes abstractos agrupan los diferentes tipos de datos definidos en wsdl:types y conforman finalmente la entrada o salida del servicio web.

Para agrupar los tipos de datos el elemento wsdl:messages utiliza el elemento wsdl:part, de forma que si un mensaje incluye varios tipos de datos, cada uno de ellos irá incluido en un elemento wsdl:part.

Gramática

La gramática del elemento wsdl:types es la siguiente (? –> es opcional, * –> pueden ser de 0 a N elementos):

<wsdl:message name="nmtoken"> *
  <wsdl:documentation .... />?
  <wsdl:part name="nmtoken" element="qname"? type="qname"?/> *
</wsdl:message>

Atributos

El principal atributo del elemento wsdl:message es name. Este nombre servirá para identificar el mensaje cuando se definan las operaciones en el elemento wsdl:portType.

SubElementos

El elemento wsdl:documentation es opcional y muestra la información general de los mensajes definidos en este apartado. 

El elemento wsdl:part define el o los tipos de datos que participan en el mensaje.

Ejemplo

Este ejemplo muestra la definición del mensaje consultaDuplicidadesTratamientoNuevo, que será el objeto input de la operación ConsultaDuplicidadesTratamientoNuevo del servicio web AsistenteProductoWS. 

<wsdl:definitions xmlns:asp="http://VDM/esquemas/asistenteproducto" [...]
<wsdl:message name="consultaDuplicidadesTratamientoNuevo">
   <wsdl:part 
      elementasp:ConsultaDuplicidadesTratamientoNuevoPet«   
      nameparameters«></wsdl:part>
</wsdl:message>

Este mensaje está formado por un único tipo de dato que es ConsultaDuplicidadesTratamientoNuevoPet. 

Ejemplo de generación de objeto de entrada a partir del wsdl:messages de ejemplo anteriores.

Si generamos el cliente para cualquiera de las operaciones del servicio web obtendremos un código similar al siguiente: 

<soapenv:Envelope 
     xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
     xmlns:asis="http://VDM/esquemas/asistenteproducto">
     <soapenv:Header/>
<soapenv:Body>
<asis:ConsultaDuplicidadesTratamientoNuevoPet>
<Cabecera>
<FechaHoraPet>?</FechaHoraPet>
<Idioma>?</Idioma>
<IdTransaccion>?</IdTransaccion>
</Cabecera>
<TratamientoNuevo>
<IdPrescripcion>?</IdPrescripcion>
<IdUnicoProducto>?</IdUnicoProducto>
</TratamientoNuevo>
<!--1 or more repetitions:-->
<TratamientosComparacion>
<IdPrescripcion>?</IdPrescripcion>
<IdUnicoProducto>?</IdUnicoProducto>
</TratamientosComparacion>
</asis:ConsultaDuplicidadesTratamientoNuevoPet>
</soapenv:Body>
</soapenv:Envelope>

Conclusión

El apartado wsdl:messages es una abstracción más que agrupa definitivamente los inputs y los outputs de las operaciones que se definen en wsdl:portType.

Si la definición de los tipos de datos es buena, los mensajes deberían quedar correctamente estructurados y hacer entendible de un sólo vistazo cual es la entrada y la salida de una operación en un servicio web. 

WSDL (III) – wsdl:types

Esta entrada es la tercera que estoy dedicando al interesante mundo del WSDL. En esta entrada encontrarás en detalle el elemento wsdl:types, que contiene la definición de los tipos de datos que se utilizan en las operaciones del servicio web. Es muy importante definir estos tipos de datos para garantizar que la comunicación entre el cliente y el proveedor del servicio se entienden sin errores.

Descripción

Wsdl:types forma parte del objeto wsdl:definitions.

El elemento wsdl:types define directamente los tipos de datos que se utilizan en las operaciones del servicio web. Esta definición de tipos se realiza mediante Esquemas XML (XSD) que son los encargados de validar el correcto formato de los mensajes en la comunicación entre cliente y proveedor.

Gramática

La gramática del elemento wsdl:types es la siguiente (? –> es opcional, * –> pueden ser de 0 a N elementos):

<wsdl:types> ?
   <wsdl:documentation .... />?
     <xsd:schema .... />*
     <-- extensibility element --> *
</wsdl:types>

SubElementos

El elemento wsdl:documentation es opcional y muestra la información general de los tipos de datos que se definen en este apartado. 

El elemento xsd:schema define los tipos de datos haciendo uso de esquemas XML. Esta definición puede hacerse de dos maneras: 

  • Directamente en el WSDL utilizando el lenguaje XSD.
  • Importando un XSD ya existente en el WSDL.

Ejemplo de WSDL con tipos de datos incluidos implícitamente.

Este ejemplo muestra la definición del tipo de dato ConsultaDuplicidadesTratamientoNuevoPet que es la entrada (input) de la operación ConsultaDuplicidadesTratamientoNuevo que proporciona el servicio web AsistenteProductoWS. 

<xsd:schema

   xmlns:Q1=»http://VDM/esquemas/comun»

   xmlns:this=»http://VDM/esquemas/asistenteproducto»

   xmlns:xsd=»http://www.w3.org/2001/XMLSchema»

   targetNamespace=»http://VDM/esquemas/asistenteproducto»>

     

  <xsd:complexType nametipoCabecera«>

    <xsd:annotation>
    <xsd:documentation>
   Todas las peticiones que reciba el sistema universal de prescripción tendrán una cabecera    fija. Los datos de esta cabecera están en este tipo.
      </xsd:documentation>
      </xsd:annotation>
      <xsd:sequence>
      <xsd:element nameFechaHoraPet« typexsd:dateTime«>
      <xsd:annotation>
      <xsd:documentation>Fecha y hora de la petición</xsd:documentation>
      </xsd:annotation>
      </xsd:element>
      <xsd:element nameIdioma« typethis:tipoIdioma«>
      <xsd:annotation>
      <xsd:documentation>
     Idioma de la petición. Pudiendo ser los valores indicados en el tipo tipoIdioma: Euskera y Castellano.
      </xsd:documentation>
      </xsd:annotation>
      </xsd:element>
      <xsd:element nameIdTransaccion« typexsd:string«>
      <xsd:annotation>
       <xsd:documentation>
      El id de transacción es único para cada petición y generado por la aplicación cliente de la plataforma SUPRE.
       </xsd:documentation>
      </xsd:annotation>
      </xsd:element>
      </xsd:sequence>
     </xsd:complexType>

     <xsd:complexType nametipoElementoConsultaDuplicidades«>
     <xsd:annotation>
      <xsd:documentation>Información para la consulta de duplicidades</xsd:documentation>
     </xsd:annotation>
     <xsd:sequence>
     <xsd:element nameIdPrescripcion« typexsd:string«>
      <xsd:annotation>
       <xsd:documentation>Identificador de prescripción</xsd:documentation>
      </xsd:annotation>
     </xsd:element>
     <xsd:element nameIdUnicoProducto« typexsd:long«>
      <xsd:annotation>
       <xsd:documentation>Identificador único de producto de la prescripción</xsd:documentation>
      </xsd:annotation>
     </xsd:element>
     </xsd:sequence>
     </xsd:complexType>

     <xsd:complexType name=»tipoConsultaDuplicidadesTratamientoNuevoPet»>

       <xsd:annotation>

          <xsd:documentation>Información de la petición</xsd:documentation>

       </xsd:annotation>

       <xsd:sequence>

          <xsd:element name=»Cabecera» type=»this:tipoCabecera»>

             <xsd:annotation>

               <xsd:documentation>Información de la cabecera</xsd:documentation>

             </xsd:annotation>

          </xsd:element>

          <xsd:element name=»TratamientoNuevo» type=»this:tipoElementoConsultaDuplicidades»>

            <xsd:annotation>

              <xsd:documentation>

              Datos del tratamiento nuevo para comprobar su duplicidad

              </xsd:documentation>

            </xsd:annotation>

          </xsd:element>

          <xsd:element 

              maxOccurs=»unbounded» 

              name=»TratamientosComparacion» 

              type=»this:tipoElementoConsultaDuplicidades»>

             <xsd:annotation>

               <xsd:documentation>

                 Lista de datos de tratamiento que ya tenía el paciente para comprobar su duplicidad

               </xsd:documentation>

             </xsd:annotation>

          </xsd:element>

       </xsd:sequence>

     </xsd:complexType>


     <xsd:element 

         name=»ConsultaDuplicidadesTratamientoNuevoPet»

         type=»this:tipoConsultaDuplicidadesTratamientoNuevoPet»>

        <xsd:annotation>

           <xsd:documentation>Consulta de duplicidades que permite comprobar si un Principio Activo ATC no está duplicado en otros con el resto de prescripciones ya creadas. La respuesta esperada es una lista de pares del Tratamiento Nuevo con alguno de los tratamientos que ya tenía el paciente

           </xsd:documentation>

        </xsd:annotation>

     </xsd:element>

</xsd:schema>

 

El elemento base que define el tipo de dato ConsultaDuplicidadesTratamientoNuevoPet está definido en verde.

<xsd:element 
     name="ConsultaDuplicidadesTratamientoNuevoPet"
     type="this:tipoConsultaDuplicidadesTratamientoNuevoPet"
</xsd:element>

 

Entre sus atributos destacan el nombre del tipo de dato y el tipo, que en este caso es un tipo de dato complejo definido en el elemento tipoConsultaDuplicidadesTratamientoNuevoPet de wsdl:types. Este elemento, tal y como indica el prefijo this, se tiene que encontrar en este mismo fichero. Si nos fijamos en los namespaces incluidos en el elemento xsd:schema veremos que hace referencia a sí mismo http://VDM/esquemas/asistenteproduto

xsd:schema
   targetNamespace="http://VDM/esquemas/asistenteproducto"
   xmlns:Q1="http://VDM/esquemas/comun"
   xmlns:this="http://VDM/esquemas/asistenteproducto"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   

 

El objeto tipoConsultaDuplicidadesTratamientoNuevoPet está definido por una secuencia de tres elementos, una cabecera, un tratamiento nuevo y una lista de tratamientos de comparación. 

<xsd:complexType name="tipoConsultaDuplicidadesTratamientoNuevoPet">
  <xsd:sequence>
    <xsd:element name="Cabecera" type="this:tipoCabecera">
    <xsd:element name="TratamientoNuevo" type="this:tipoElementoConsultaDuplicidades">
    <xsd:element 
       maxOccurs="unbounded" 
       name="TratamientosComparacion
       type="this:tipoElementoConsultaDuplicidades">
  </xsd:sequence>
</xsd:complexType>


El elemento Cabecera es un elemento complejo definido en un objeto this.tipoCabecera que está definido en el propio WSDL. El elemento TratamientoNuevo es un elemento complejo definido en el objeto this.tipoElementoConsultaDuplicidades también definido en el propio WSDL. El elemento TratamientosComparacion es un elemento complejo definido por el mismo objeto que TratamientoNuevo, sólo que esta vez se repite ilimitadamente (maxOccurs=»unbounded»). 

Si un elemento no tiene definido el atributo maxOccurs por defecto tomará el valor «1«. El valor «unbounded» indica que el elemento puede repetirse de forma ilimitada.

La definición de los elementos tipoCabecera y tipoElementoConsultaDuplicidades se encuentran también en el elemento wsdl:types. En el ejemplo se muestran en azul. Estos objetos complejos están definidos por elementos primitivos. Por ejemplo, el tipo complejo tipoElementoConsultaDuplicidades está formado por dos tipos primitivos, IdPrescripcion que es un xsd:string y IdUnicoProducto que es un xsd:long.  

     <xsd:complexType name="tipoElementoConsultaDuplicidades">
     <xsd:sequence>
     <xsd:element nameIdPrescripcion« typexsd:string«>
   <xsd:element nameIdUnicoProducto« typexsd:long«>
  </xsd:sequence>
     </xsd:complexType>

Los tipos primitivos válidos se pueden encontrar en el apartado datatypes de la especificación Schema XML. 

Ejemplo de WSDL con tipos de datos importados.

Importar el tipo de datos aporta sencillez al diseño del WSDL facilitando la identificación a primera vista de los tipos de datos. Si se quiere profundizar en los mismos se podrá acceder a través de la URI de schemaLocation al detalle del tipo de dato que está definido en su correspondiente XSD.

En el siguiente ejemplo se define el tipo de dato ConsultaDuplicidadesTratamientoNuevoPet pero utilizando ésta vez un fichero xsd. 

<wsdl:types>
  <xsd:schema 
     xmlns:asp="http://VDM/esquemas/asistenteproducto"
     xmlns:ns1="http://cxf.apache.org/bindings/xformat"       
     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:tns="http://VDM/wsdls/AsistenteProductoWSDL" 
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"     
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     elementFormDefault="unqualified" 
     targetNamespace="http://VDM/wsdls/AsistenteProductoWSDL" 
     version="1.0">
   
   <xsd:import 
                      xsd=xsd/ConsultaDuplicidadesTratamientoNuevoPet.xsd«/>
 </xs:schema>
</wsdl:types>

Claramente se ve que el diseño del WSDL es mucho más sencillo que en el ejemplo anterior. Sin embargo, si queremos ver el detalle de la información se hace necesario navegar a la URI del atributo schemaLocation y acceder al XSD.

Este XSD contendrá un código similar al del ejemplo anterior, pudiendo tener a su vez importaciones a otros XSDs que especifiquen los objetos complejos tipoCabecera y tipoElementoConsultaDuplicidades que podrían estar definidos en sus propios XSDs.

Ejemplo de generación de objeto de entrada a partir de los wsdl:types de ejemplo anteriores.

Si generamos el cliente para cualquiera de las operaciones del servicio web obtendremos un código similar al siguiente: 

<soapenv:Envelope 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:asis="http://VDM/esquemas/asistenteproducto">
   <soapenv:Header/>
   <soapenv:Body>
      <asis:ConsultaDuplicidadesTratamientoNuevoPet>
         <Cabecera>
             <FechaHoraPet>?</FechaHoraPet>
             <Idioma>?</Idioma>
             <IdTransaccion>?</IdTransaccion>
         </Cabecera>
         <TratamientoNuevo>
             <IdPrescripcion>?</IdPrescripcion>
             <IdUnicoProducto>?</IdUnicoProducto>
         </TratamientoNuevo>
         <!--1 or more repetitions:-->
         <TratamientosComparacion>
             <IdPrescripcion>?</IdPrescripcion>
             <IdUnicoProducto>?</IdUnicoProducto>
         </TratamientosComparacion>
      </asis:ConsultaDuplicidadesTratamientoNuevoPet>
   </soapenv:Body>
</soapenv:Envelope>

Conclusión

El apartado wsdl:types es uno de los más importantes en la descripción del Web Service ya que permite generar una correcta comunicación entre proveedor y cliente. 

Esta claro que hay que encontrar un equilibrio en la definición de los tipos de datos a través de XSDs. Si hay pocos XSDs, nos encontraremos con un fichero inmenso difícil de manejar, y si por el contrario, definimos demasiados XSDs, tendremos que navegar por todos ellos para enterarnos de los datos que participan en el servicio web, y si no disponemos de un buen mapa de objetos podemos perder la visión de lo datos. 

Links

WSDL (II) – wsdl:definitions

Esta entrada forma parte de la incursión en el interesante mundo del WSDL, comenzada en la entrada WSDL(I) – Introducción. El WSDL es conocida como la especificación estándar que permite detallar el contenido de un servicio web mediante XML. En esta entrada detallo el elemento wsdl:definitions, que es el elemento principal en la definición de un servicio web y es el primero que se aborda.

Descripción

Wsdl:definitions es el elemento raíz de todo WSDL.

En él se incluye toda la información necesaria para describir el servicio web; las operaciones que publica el servicio web, los tipos de datos que se utilizan en las operaciones, los tipos de binding dependiendo del protocolo que se utiliza en la llamada al servicio web y la url en la que es publicado y a la que se debe llamar para invocarlo. 

Gramática

La gramática completa del elemento definitions es la siguiente (? –> campo opcional; * –> 0 a N campos): 

<wsdl:definitions name="nmtoken"? targetNamespace="uri"?>

    <import namespace="uri" location="uri"/>*

    <wsdl:documentation .... /> ?

    <wsdl:types> ?
        <wsdl:documentation .... />?
        <xsd:schema .... />*
        <-- extensibility element --> *
    </wsdl:types>

    <wsdl:message name="nmtoken"> *
        <wsdl:documentation .... />?
        <part name="nmtoken" element="qname"? type="qname"?/> *
    </wsdl:message>

    <wsdl:portType name="nmtoken">*
        <wsdl:documentation .... />?
        <wsdl:operation name="nmtoken">*
           <wsdl:documentation .... /> ?
           <wsdl:input name="nmtoken"? message="qname">?
               <wsdl:documentation .... /> ?
           </wsdl:input>
           <wsdl:output name="nmtoken"? message="qname">?
               <wsdl:documentation .... /> ?
           </wsdl:output>
           <wsdl:fault name="nmtoken" message="qname"> *
               <wsdl:documentation .... /> ?
           </wsdl:fault>
        </wsdl:operation>
    </wsdl:portType>

    <wsdl:binding name="nmtoken" type="qname">*
        <wsdl:documentation .... />?
        <-- extensibility element --> *
        <wsdl:operation name="nmtoken">*
           <wsdl:documentation .... /> ?
           <-- extensibility element --> *
           <wsdl:input> ?
               <wsdl:documentation .... /> ?
               <-- extensibility element -->
           </wsdl:input>
           <wsdl:output> ?
               <wsdl:documentation .... /> ?
               <-- extensibility element --> *
           </wsdl:output>
           <wsdl:fault name="nmtoken"> *
               <wsdl:documentation .... /> ?
               <-- extensibility element --> *
           </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="nmtoken"> *
        <wsdl:documentation .... />?
        <wsdl:port name="nmtoken" binding="qname"> *
           <wsdl:documentation .... /> ?
           <-- extensibility element -->
        </wsdl:port>
        <-- extensibility element -->
    </wsdl:service>

    <-- extensibility element --> *

</wsdl:definitions>

 

Atributos

El elemento wsdl:definitions contiene los siguientes atributos:

  • name. Este atributo es el nombre del servicio web. Conviene que este nombre sea el mismo que el nombre utilizado en el apartado wsdl:service.
  • targetNamespace. Este atributo permite al WSDL referirse a si mismo y establecer los namespaces que se usan en la descripción. La URI que se da para definir el namespace propio no tiene porque existir, basta con que sea un valor único y diferente a los otros namespaces definidos. Además, la URI no puede ser una ruta relativa. 

Subelementos.

Los subelementos de wsdl:definitions contienen la descripción completa del servicio web. Dada su importancia y para organizar mejor este conocimiento, estos subelementos los iré comentando en sus propias entradas.
 

Ejemplo básico.

A continuación se muestra un ejemplo básico de un elemento wsdl:definitions que se utiliza en el servicio web AsistenteProductoSW.
<wsdl:definitions 
name="AsistenteProductoSW"
targetNamespace="
http://VDM/wsdls/AsistenteProductoWSDL"
xmlns:asp="http://VDM/esquemas/asistenteproducto"
xmlns:ns1="http://cxf.apache.org/bindings/xformat"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://VDM/wsdls/AsistenteProductoWSDL"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">

Del ejemplo se pueden deducir varias cosas:

WSDL (I) – Introducción.

WSDL es el acrónimo de Web Services Description Language (en castellano Lenguaje para la Definición de Servicios Web) y es una especificación estándar que, basada en XML, sirve para definir todos los elementos que conforman un servicio web. Estos elementos descritos desde el WSDL pueden ser:

  • Información del interface describiendo todas las funciones disponibles.
  • Información de los tipos de datos que se utilizan en los mensajes de petición y de respuesta de las funciones.
  • Información del tipo de binding utilizado sobre el protocolo que usado en la comunicación con el servicio.
  • Información de la ubicación del servicio y la url desde la que se puede acceder a esta información.

Se podría resumir que WSDL proporciona el contrato de un servicio entre la entidad que lo solicita y la que lo proporciona, de forma que usando el WSDL, un cliente puede localizar un servicio web e invocar cualquiera de sus funciones de forma correcta.

El WSDL se divide principalmente en los siguiente grupos de elementos:

wsdl:definitions
  import
  wsdl:types
    xsd:schema
  wsdl:message
    part
  wsdl:portType
    wsdl:operation
      wsdl:input
      wsdl:output
      wsdl:fault
  wsdl:binding
    wsdl:operation
      wsdl:input
      wsdl:output
      wsdl:fault
   wsdl:service 
     wsdl:port

wsdl:definitions.

El elemento definitions es el elemento raíz de todo WSDL y en él se incluye toda la información necesaria para describir el servicio web; las operaciones que publica el servicio web, los tipos de datos que se utilizan en las operaciones, los tipos de binding dependiendo del protocolo que se utiliza en la llamada al servicio web y la url en la que es publicado y a la que se debe llamar para invocarlo. 

wsdl:types.

El elemento wsdl:types define directamente los tipos de datos que se utilizan en las operaciones del servicio web. Esta definición de tipos se realiza mediante Esquemas XML (XSD) que validan el formato de los mensajes en la comunicación. 

wsdl:messages

El elemento wsdl:messages define los mensaje abstractos que sirven de entrada y salida de las operaciones. Estos mensajes están formados por una o más parts. Estas parts están asociada con un elemento o un tipo de dato de los definidos en wsdl:types.

wsdl:portType (wsdl:interfaces)

El elemento wsdl:portType define un grupo de operaciones a incluir en el servicio web. Este elemento también es conocido como interface, de hecho en la nueva especificación WSDL 1.2 (que en el momento de escribir este artículo se encuentra en estado “draft”) se sustituye el termino “portType” por el de “interface”.  

Cada portType debe tener un nombre único para referirse a él desde cualquier parte en el WSDL. El portType puede contener una o más operaciones. Cada operación contiene una combinación de elementos de entrada y salida. También puede definirse un elemento fault para manejar los errores. El orden de estos elementos es importante porque define el patrón de intercambio de mensajes utilizado. 

  • Patrón One-way. El endpoint recibe un mensaje. Se define solamente un elemento de entrada.
  • Patrón Request-response. El endpoint recibe un mensaje y envía la correspondiente respuesta. Se define un elemento de entrada seguido de uno de salida. 
  • Patrón Solicit-Response. El endpoint envía un mensaje y recibe la respuesta correspondiente. Se define un elemento de salida y seguido uno de entrada. 
  • Patrón Notification. El endpoint envia un mensaje. Se define un elemento de salida. 

wsdl:binding

El elemento wsdl:binding define el formato del mensaje y los detalles del protocolo que se utiliza en la comunicación con el servicio web, de hecho, puede haber diferentes bindings asociados al mismo portType, por lo que un mismo servicio web puede implementar la comunicación a través de diferentes protocolos. 

En los elementos wsdl:binding sólo se puede especificar un único protocolo; SOAP, GET/POST o MIME

En los elementos wsdl:binding no se puede aportar ninguna información sobre la dirección de publicación del servicio web ya que esta información se proporciona en el elemento wsdl:port del elemento wsdl:service.

wsdl:service y wsdl:port

El elemento wsdl:service contiene los wsdl:port que se publican en el servicio web.  

El elemento wsdl:port define un endpoint individual especificando para cada binding definido en wsdl:binding una dirección única de acceso. 

Por tanto, el elemento wsdl:service proporciona la información necesaria para poder acceder a las diferentes funcionalidades proporcionadas por el servicio web. 

 

Eclipse Juno y la configuración fallida del proxy

Este post, cuyo título puede sonar a película de ciencia ficción, es un problema que todos aquellos que trabajamos con Eclipse, más tarde o más temprano nos encontramos a la hora de conectarnos a internet en aquellos sitios donde la conexión no se realiza directamente sino que se hace a través de un proxy.

Este es el típico problema de conexión que deriva en una excepción InvocationTargetException al acceder al MarketPlace cuando queremos instalar aquellos plugins necesarios para los desarrollos a llevar a cabo. En ese momento comienzan un sin fin de pruebas de configuración del proxy que puede que consigan, o no, solucionar la conectividad.

La configuración del proxy de Eclipse en el arranque.

Tras mucho renegar con las preferencias de la conexión vía proxy, probar las configuraciones manual, nativa, directa y combinaciones varias de las anteriores; después de modificar la propia configuración del sistema operativo y jugar con las variables de entorno de manera infructuosa; por fin he encontrado un método que hace que Eclipse funcione sí o sí a través del proxy sin quebraderos de cabeza.

La solución se realiza añadiendo la configuración del proxy en el fichero eclipse.ini.

Este fichero se encuentra en el mismo directorio del ejecutable, en la carpeta home de la instalación, y en él se pueden añadir parámetros que modifican en el arranque la funcionalidad de Eclipse. Los parámetros que determinan la configuración del proxy son algunos de los que se pueden establecer en el arranque, las líneas concretas son las siguientes:

-Dhttp.proxyPort=<puerto_del_proxy>
-Dhttp.proxyHost=<nombre_dns_o_dir_ip_del_proxy>
-Dhttp.proxyUser=<nombre_usuario_proxy>
-Dhttp.proxyPassword=<password_usuario_proxy>
-Dhttp.nonProxyHosts=<excepciones_sin_proxy_separadas_por_|_pipe>

La contra de utilizar este solución es que en aquellas instalaciones en las que es necesario acceder al proxy de forma autenticada, al incluir el nombre del usuario y la contraseña como parámetros, si esa instalación se utiliza en una máquina virtual y se distribuye a un montón de desarrolladores, cada uno tiene que cambiar esos dos parámetros para incluir su usuario y contraseña propios.

Si lo anterior no es un problema, ésta solución funciona siempre, termina con los quebraderos de cabeza de las preferencias de Eclipse y sirve para cualquier plataforma, ya sea windows, linux o macintosh.

Links

Documentación Eclipse Juno – Network Connections how to. http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.platform.doc.user%2Freference%2Fref-net-preferences.htm