Comprender los servicios web, Parte 2: Web Services Description Language (WSDL)

Compartir servicios web

El auge actual de la arquitectura orientada a servicios (SOA) puso a los servicios web en la mira, pero es muy fácil verse abrumado por toda la información disponible. Esta serie le ofrece una síntesis clara de las principales especificaciones de servicios web, comenzando por el Protocolo simple de acceso a objetos (Simple Object Access Protocol o SOAP) hasta llegar al WS Business Process Execution Language (Lenguaje de ejecución de procesos de negocios para servicios web o BPEL). En esta segunda entrega, aprenderá sobre Web Services Definition Language, la forma en que el Departamento de Avisos Clasificados de Daily Moon suele usar WSDL para describir su propio servicio web de manera tal que otros puedan crear fácilmente clientes para acceder al mismo a través de cualquier lenguaje de programación o plataforma.

Nicholas Chase, desarrollo de sitios Web, Backstop Media

Nicholas Chase participó en el desarrollo de sitios Web para empresas como Lucent Technologies, Sun Microsystems, Oracle y Tampa Bay Buccaneers. Nick fue profesor de física en escuela secundaria, gerente de instalaciones para tratamiento de desechos con bajo nivel de radioactividad, editor de una revista online de ciencia ficción, ingeniero multimedios, instructor de Oracle y Director de Tecnología en una empresa de comunicaciones interactivas. Es autor de varios libros, entre ellos XML Primer Plus (Sams).



08-08-2011

Antes de comenzar

Este tutorial está diseñado para que usted pueda comprender Web Services Description Language (Lenguaje de descripción de servicios web - WSDL). Está pensando para desarrolladores que desean exponer sus propios servicios al uso de otros utilizando WSDL y también para desarrolladores que tengan el archivo WSDL para un servicio al que desean acceder y necesitan crear un cliente.

Para poder interpretar este tutorial usted debería tener conocimientos básicos de SOAP, los que puede obtener leyendo la Parte 1 de esta serie de tutoriales; y además conocimientos básicos de XML. WSDL es un lenguaje de programación agnóstico, pero las muestras que aparecen al final del tutorial usan Java y el proyecto Apache Axis2. Sin embargo, los conceptos se aplican a cualquier lenguaje de programación y entorno. Asimismo, el tutorial se focaliza en WSDL 1.1 que es la versión que más se implementa comúnmente, pero los conceptos son los mismos para la próxima versión WSDL 2.0 y cubren las diferencias básicas.

Acerca de esta serie

Esta serie de tutoriales enseña los conceptos básicos de los servicios web a través de las peripecias de un periódico de ficción, el Daily Moon, ya que los empleados usan los servicios web para crear un sistema de flujo de trabajo para aumentar la productividad en estos tiempos turbulentos.

La Parte 1 comienza simplemente explicando los conceptos básicos de los servicios web y le muestra cómo usar SOAP, la especificación que sustenta gran parte de lo que está por ocurrir, conectando al Departamento de Avisos Clasificados con el Sistema de Gestión de Contenidos.

En la Parte 2, el Departamento de Clasificados avanza un paso más, usando Web Services Description Language para definir los mensajes generados y que se esperan de su propio servicio web, permitiéndole al equipo de trabajo crear fácilmente servicios y clientes que se conectan a ellos.

La Parte 3 encuentra al equipo de trabajo con una serie de servicios implementados y la necesidad de ubicarlos fácilmente. En respuesta a ello Universal Description, Discovery and Integration (UDDI) ofrece un registro de servicios disponibles que permite realizar búsquedas como una forma de publicar sus propios servicios para otros.

Las Partes 4 y 5, WS-Security y WS-Policy plantean el objetivo de asegurar los servicios del periódico y los cambios que deben realizar los equipos de trabajo para acceder a esos servicios recientemente asegurados.

Interoperabilidad es la palabra clave en la Parte 6 ya que se debe acceder a servicios provenientes de diferentes implementaciones a través de un único sistema. La parte seis cubre los requisitos y las pruebas que involucra la certificación WS-I.

Por último, la Parte 7 muestra cómo usar Business Process Execution Language (WS-BPEL) para crear aplicaciones complejas partiendo de servicios individuales.

Ahora analicemos más detalladamente los temas que abarca este tutorial.

Acerca de este tutorial

La Parte 1 de esta serie, Understanding web services: SOAP [Comprender los servicios web: SOAP], presentó al personal del periódico de ficción llamado Daily Moon. Específicamente, se conoció al Departamento de Avisos Clasificados. En ese tutorial, el Departamento de Avisos Clasificados construyó un cliente para enviar y recibir mensajes SOAP al servicio web que representa el Sistema de Gestión de Contenidos. Este tutorial, el Departamento de Avisos Clasificados impactado por lo que vieron, decide construir su propio servicio web para tomar y administrar los avisos en la base de datos de clasificados. Sin embargo, para poder usar el servicio necesitarán crear un archivo Web Services Description Language (WSDL). Este archivo brinda las instrucciones a aquellos que construyen clientes para que puedan saber qué mensajes espera y arroja el sistema.

En este tutorial aprenderá lo siguiente:

  • Por qué los archivos WSDL son importantes

  • Qué puede hacer con los archivos WSDL

  • Los conceptos básicos del esquema XML que se incluye en los archivos WSDL

  • Cómo estructurar un archivo WSDL

  • Las diferencias básicas entre WSDL 1.1 y WSDL 2.0

  • Cómo generar un archivo WSDL automáticamente a partir de una clase Java que representa un servicio

  • Cómo generar una clase Java que represente un servicio a partir de un archivo WSDL

  • Cómo generar un cliente de servicios web a partir de un archivo WSDL

El Departamento de Avisos Clasificados construirá un servicio que tome avisos nuevos, edite y visualice avisos existentes y cierre una edición para que ya no acepte más avisos. Usarán mensajería unidireccional y también mensajes de solicitud y respuesta.

Herramientas y requisitos previos

La carga de este tutorial es conceptual, pero para seguir de manera correcta el código hasta el final de este tutorial, necesitará contar con el siguiente software instalado:

Apache Geronimo u otro servidor de aplicaciones -- El equipo crea un nuevo servicio web en el transcurso de este tutorial y usted necesitará una aplicación en la cual ejecutarlo. Como se supone que generalmente los servicios web son interoperables, no importa cuál de ellos use. Este tutorial demuestra el uso de Apache Geronimo que también es la base para WebSphere Community Edition de IBM. Usted también puede usar otros servidores de aplicaciones como WebSphere application server. Puede descargar Apache Geronimo desde http://geronimo.apache.org/downloads.html. Para obtener más información acerca de la instalación de Geronimo, consulte el primer tutorial de esta serie, "Understanding web services specifications, Part 1: SOAP (Comprender las especificaciones de los servicios web, Parte 1: SOAP)."

Apache Axis2 versión 0.95 o superior -- Puede crear mensajes SOAP manualmente, y puede interpretarlos manualmente, pero es mucho más fácil tener una implementación a mano. Este tutorial demuestra el uso de Apache Axis2, el cual contiene implementaciones de varias API relacionadas con SOAP para facilitarle significativamente la tarea. Puede descargar Apache Axis2 en http://ws.apache.org/axis2/download.cgi. Este tutorial usa la versión 0.95, pero otras versiones posteriores pueden funcionar. (Obsérvese que la Parte 1 de esta serie usó la versión 0.94, pero no se probó con el código en esta parte.)

Java 2 Standard Edition versión 1.4 o superior -- Todas estas herramientas se basan en Java ya que éstos son los servicios y los clientes que usted creará en este tutorial. Puede descargar J2SE SDK desde http://java.sun.com/j2se/1.5.0/download.jsp.

También necesitará un navegador Web y un editor de texto, pero seguramente ya los tenga. Si lo desea, también puede usar un entorno de desarrollo integrado (IDE) como Eclipse, pero como nos estamos concentrando en las tecnologías más que en las herramientas, sólo usaremos un editor de texto y la línea de comandos para editar y compilar nuestros archivos.


Generalidades

Antes de profundizar en los detalles, analicemos el panorama general.

Actualización de servicios web

La ventaja principal de usar servicios web en lugar de métodos de programación tradicionales es la interoperabilidad. Se puede crear un sistema distribuido en el cual las computadoras que están física o geográficamente separadas se pueden comunicar, pero en la mayoría de los casos, involucra middleware propietario que limita su flexibilidad. En otras palabras, tanto el remitente como el destinatario deben usar el mismo software.

Los servicios web ofrecen un medio basado en texto (en realidad, basado en XML) para transferir mensajes enviados y recibidos, esto significa que sus aplicaciones no sólo son independientes de las máquinas, sino también del sistema operativo y del lenguaje de programación. Siempre y cuando ambas partes sigan los estándares de servicios web, no importa qué software se esté ejecutando en ambos extremos.

Existen varios estándares para transferir esta información, pero esta serie de tutoriales se basa en SOAP debido a su flexibilidad y al uso de estándares más avanzados.

Actualización de SOAP

Un mensaje SOAP consta de tres partes principales: un Encabezado, el Cuerpo y la carga útil que está incluida en el cuerpo. Consideremos el siguiente ejemplo:

Listado 1. Un mensaje SOAP de muestra
>SOAPenv:Envelope 
       xmlns:SOAPenv="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<
 >SOAPenv:Body<
  >req:getNumberOfArticles xmlns:req="http://daily-moon.com/CMS/"<
     >req:category<classifieds>/req:category<
  >/req:getNumberOfArticles<

 >/SOAPenv:Body<
>/SOAPenv:Envelope<

El mensaje general se llama Sobre, el contenido del cual consta de Encabezado y Cuerpo. El Encabezado contiene información sobre el mensaje propiamente dicho, como información de ruteo o información destinada al procesamiento por parte de “intermediarios SOAP”, o servicios entre el remitente y el destinatario final, quien puede procesar el mensaje. El Cuerpo del mensaje incluye la “carga útil” que a su vez incluye los datos reales que se transmitirán al servicio web.

En este caso, la carga útil es el elemento getNumberOfArticles y su contenido.

Para qué sirve WSDL

Cuando usted crea un servicio, comúnmente lo hace porque desea que la gente lo use. Para que puedan hacerlo, necesitan saber qué información deben enviar al servicio, qué información arrojará el servicio y dónde encontrar el servicio en primer lugar. Por supuesto que usted puede poner esto en un documento de procesamiento de texto, pero es mucho más útil tener un formato estándar para esta información, preferentemente que pueda ser leído por humanos y por máquinas.

WSDL brinda este formato estándar. La ventaja principal, además de eliminar ambigüedad, es el estándar de hecho y está en XML, puede ser leído por máquinas hasta tal punto que permite crear clientes (e incluso hasta el esqueleto de un servicio) automáticamente. El Departamento de Avisos Clasificados va a crear un servicio que acepte y administre avisos clasificados y para permitir que otros como los Sitios Web de agregación de empleos usen más fácilmente el servicio, también lo describirán usando un archivo WSDL.

Qué logrará este tutorial

Durante el transcurso de este tutorial, aprenderá sobre WSDL siguiendo al personal del Departamento de Clasificados de Daily Moon a medida que creen su propio servicio y lo expongan al uso de otros.

Primero, Christine toma esquemas XML, lo cual define los datos que pueden aparecer en el mensaje SOAP. Luego, Larry arma la estructura de un archivo WSDL real para comprender qué información enviarán y recibirán. Posteriormente, Gene toma el archivo WSDL y lo usa como inspiración para una clase Java que representa al servicio y luego, sólo para estar seguro, usa Axis2 para generar un archivo WSDL a través de la clase Java para observar las diferencias. A partir de aquí, Francis toma el archivo WSDL y lo usa para generar el servicio web básico y un cliente para acceder a ese servicio web.

Comience ya.


Una guía rápida sobre el esquema XML

Daily Moon es un periódico muy pequeño y como tal el Departamento de Avisos Clasificados no puede solventar un Administrador de Bases de Datos (DBA) exclusivo, por lo tanto, Christine siempre consideró a la base de datos como a “su bebé”. Después de mirar las especificaciones para WSDL, ella decide asumir la tarea de definir los datos que pueden ir en los mensajes SOAP.

Validación y opciones

El personal ya está familiarizado con XML después de su incursión inicial en los servicios web, así que el próximo paso lógico es que Christine aprenda sobre validación.

La validación XML es el proceso de verificar que tanto la estructura como el contenido de un documento XML cumplan con cualquier requisito predefinido. Originariamente, esto significaba cumplir con la Definición del tipo de documento (DTD), una estructura segregada de la raíces de XML como un lenguaje SGML. Las definiciones DTD no eran particularmente difíciles, pero sí presentaban desventajas importantes en términos de falta de flexibilidad, falta de soporte para espacios de nombres XML y otros problemas, por lo tanto la comunidad XML pasó a otros tipos de esquemas XML.

Obsérvese que la palabra “esquemas” no está con mayúscula en el párrafo anterior porque en este caso estoy hablando sobre el uso general del término que significa la estructura específica para un documento, a diferencia de cualquier idioma para documentar esa estructura. El esquema que más comúnmente soporta estos esquemas XML es el lenguaje de Esquema W3C XML que habitualmente se conoce simplemente como Esquema XML. (Obsérvese el uso de mayúscula.)

Christine ve que usarán Esquema XML para construir su documento WSDL 1.1, pero nota que WSDL 2.0 específicamente soporta otros esquemas, como RELAX NG y Schematron que se pueden usar en su lugar.

El documento de prueba

Christine comienza con un documento de muestra que se aproxima a los datos que estima que entrarán y saldrán del sistema. Toma un par de avisos existentes y crea el XML:

Listado 2. El documento de prueba
Listing 2: the instance document
<?xml version="1.0"?>
<ClassifiedList>

   <ClassifiedAd adId="1138">
      <content>Vintage 1963 T-Bird.  Less than 300 miles.  
Driven by my daughter until I took it away.  Serious inquires
only. 555-3264 after 7 PM.</content>
      <endDate>4/15/2007</endDate>

      <startDate>4/1/2007</startDate>
   </ClassifiedAd>

   <ClassifiedAd adId="2187">
      <content>30 ft ladder, only used once.  Willing to let
go for half its worth. Has slight dent near the middle.  
Harder than a human head. $150 OBO.</content>

      <endDate>4/30/2007</endDate>
      <startDate>4/10/2007</startDate>
   </ClassifiedAd>

</ClassifiedList>

Este documento se conoce como “documento de prueba”, representa los datos que serán definidos por el esquema XML. En este caso, consta de dos elementos ClassifiedAd como elemento secundario de ClassifiedList. Cada ClassifiedAd incluye un atributo adId que corresponde a la clave primaria dentro de la base de datos, el contenido del aviso, y la fecha de inicio y fecha de finalización del aviso.

Ella nombra a este documento classifieds.xml.

El esquema básico

El próximo paso de Christine consiste en crear los documentos de esquema básico para poder probar el sistema. El esquema actual propiamente dicho también es un documento XML, así que ella crea el archivo básico, classifieds.xsd, y un elemento de esquema único, como se observa en el Listado 3:

Listado 3. Elemento de esquema
<xsd:schema
                    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                    </xsd:schema>

Este elemento funcionará como el elemento principal para todas las definiciones que cree Christine.

Validación del documento

El próximo paso, en la prueba del sistema, es armar una aplicación Java simple que validará el documento de prueba comparándolo con el esquema. Todo lo que tiene que hacer la clase es especificar la validación y analizar el documento, como se muestra en el Listado 4.

Listado 4. La clase ValidateWithSchema
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
 
public class ValidateWithSchema {
 
    public static void main(String args[]) {

        DocumentBuilderFactory dbf = 
                        DocumentBuilderFactory.newInstance();
        dbf.setValidating(true);

        dbf.setAttribute(
          "http://java.sun.com/xml/jaxp/properties/schemaLanguage", 
          "http://www.w3.org/2001/XMLSchema");
        dbf.setAttribute(
          "http://java.sun.com/xml/jaxp/properties/schemaSource",
          "classifieds.xsd");

        Document doc = null;
        try{        
           DocumentBuilder parser = dbf.newDocumentBuilder();
           doc = parser.parse("classifieds.xml");
        } catch (Exception e){
           e.printStackTrace();
        }
    }
}

El método real de análisis no es demasiado importante; en este caso, Christine construye la aplicación usando un analizador DOM. Primero, construye una fábrica y luego especifica que cualquier analizador que se cree deberá ser un “analizador de validación”. (No todos los analizadores son para validación. Algunos simplemente verifican que el documento esté “bien formado", sin verificar su estructura real.)

Luego, especifica el lenguaje de esquema que se usará, y el documento de esquema propiamente dicho. En este caso, Christine está usando un archivo local, pero usted puede usar cualquier archivo al que pueda referirse mediante una URL.

Christine guarda el archivo y lo compila, y después de ejecutarlo, obtiene los siguientes datos de salida, como se muestra en el Listado 5.

Listado 5. Ejecución del esquema vacío
Warning: validation was turned on but an org.xml.sax.ErrorHandler 
was not set, which is probably not what is desired.  Parser will 
use a default ErrorHandler to print the first 10 errors.  Please call
the 'setErrorHandler' method to fix this.

Error: URI=file:///E:/WSDLFiles/classifieds.xml Line=2: cvc-elt.1: 
Cannot find the declaration of element 'ClassifiedList'.

La Advertencia no es importante; Christine sólo quiere saber si el archivo es válido o no, así que no necesita ninguna función especial para administración de errores. El Error es importante porque significa que el analizador en realidad está mirando el esquema y validando el documento de prueba.

Ahora ella necesita agregar las definiciones reales.

Creación de un elemento simple

Es fácil definir los primeros elementos. Los elementos content, endDate y startDate constan de cadenas simples, como se muestra en el Listado 6.

Listado 6. Agregar elementos simples
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

   <xsd:element type="xsd:string" name="content" />
   <xsd:element type="xsd:string" name="endDate" />
   <xsd:element type="xsd:string" name="startDate" />

</xsd:schema>

En este caso, Christine definió tres elementos que sólo tienen texto (a diferencia de otros elementos) como contenido. Además, ninguno de ellos tiene atributos. La Recomendación de Esquema XML define una serie de tipos diferentes que usted puede usar para definir su contenido. Por ejemplo, puede especificar que los valores endDate y startDate deben ser valores datetime.

Creación de un elemento más complejo

Resulta obvio que si todos los elementos fueron tan simples, probablemente no habrá necesitado un esquema en primer lugar. Christine avanza para definir el elemento ClassifiedList (ver Listado 7).

Listado 7. Definición del elemento ClassifiedList
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="ClassifiedList">
   <xsd:complexType>

      <xsd:sequence>
         <xsd:element name="ClassifiedAd" maxOccurs="unbounded"
                                       type="ClassifiedAdType"/>

      </xsd:sequence>

   </xsd:complexType>
</xsd:element>

<xsd:complexType name="ClassifiedAdType">
   <xsd:sequence>
       <xsd:element type="xsd:string" name="content" />

       <xsd:element type="xsd:string" name="endDate" />
       <xsd:element type="xsd:string" name="startDate" />
   </xsd:sequence>
</xsd:complexType>

</xsd:schema>

Comenzando desde abajo, Christine crea ClassifiedAdType complexType. Es un tipo de elemento que contiene, en orden, un elemento de contenido, un elemento endDate, y un elemento startDate. Ascendiendo hasta la parte superior, ella define el elemento ClassifiedList que tiene un elemento que a su vez contiene una secuencia de elementos. En este caso, sin embargo, contiene cero o más elementos de tipo ClassifiedAdType, todos llamados ClassifiedAd.

Hasta el momento, con tan solo estas dos definiciones, ella cubrió la mayor parte de la estructura del documento. Ahora todo lo que necesita es agregar una definición para el atributo adId.

Agregar atributos

Un atributo se define en un elemento complexType, como se muestra en el Listado 8.

Listado 8. Agregar atributos
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="ClassifiedList">
   <xsd:complexType>
      <xsd:sequence>
         <xsd:element name="ClassifiedAd" maxOccurs="unbounded"
 type="ClassifiedAdType"/>
      </xsd:sequence>

   </xsd:complexType>
</xsd:element>

<xsd:complexType name="ClassifiedAdType">
   <xsd:sequence>
       <xsd:element type="xsd:string" name="content" />
       <xsd:element type="xsd:string" name="endDate" />

       <xsd:element type="xsd:string" name="startDate" />
   </xsd:sequence>
   <xsd:attribute name="adId" type="xsd:integer" />
</xsd:complexType>

</xsd:schema>

En este caso, Christine limitó el contenido del atributo adId a números enteros. Sin embargo, podría crear un tipo más restrictivo.

Uso de simpleTypes

El Esquema XML ofrece una gran potencia cuando se trata de definir el contenido real que puede tener su documento. Por ejemplo, Christine puede especificar que el atributo adId debe contener sólo números enteros superiores o iguales a 1000 (ver Listado 9).

Listado 9. Restricción de valores
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

...

<xsd:complexType name="ClassifiedAdType">
   <xsd:sequence>
       <xsd:element type="xsd:string" name="content" />
       <xsd:element type="xsd:string" name="endDate" />

       <xsd:element type="xsd:string" name="startDate" />
   </xsd:sequence>
   <xsd:attribute name="adId" type="thousandOrGreater" />
</xsd:complexType>

<xsd:simpleType name="thousandOrGreater">
  <xsd:restriction base="xsd:integer">

      <xsd:minInclusive value="1000"/>
  </xsd:restriction>
</xsd:simpleType>

</xsd:schema>

Esta definición no involucra a ningún elemento, así que Christine usa simpleType en lugar de usar complexType. Ella está creando una restriction -- un límite en el tipo de datos -- con el tipo de base de números enteros. Luego agrega una “faceta” para esa restricción, especificando que el valor mínimo es 1000. El Esquema XML permite definir una gran cantidad de facetas de este tipo para usar. Una vez que ella creó el tipo, puede hacer referencia al mismo en la definición de atributo. Ahora que ya definió los datos, Christine transfiere el esquema a Larry para que él pueda construir el WSDL real.


Creación de un documento WSDL

Mientas que Christine se encarga de los datos reales, Larry es responsable por el mensaje real que va y viene entre el servicio y sus clientes. A través de estos mensajes, él crea el documento WSDL.

Los mensajes

Los primeros pasos de Larry consisten en decidir qué funciones cumplirá realmente el servicio, y luego definir los mensajes para los servicios. Después de consultar al resto del departamento, él obtiene una lista de funciones y sus correspondientes mensajes, y en el resto de esta sección analizaremos estas funciones.

createNewAd

Esta función toma el contenido del aviso y su fecha de finalización y obtiene la idea para el aviso recientemente creado (ver Listado 10 y Listado 11).

Listado 10.createNewAdRequest(Input)
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
  <req:createNewAdRequest 
              xmlns:req="http://daily-moon.com/classifieds/">
     <req:content>Vintage 1963 T-Bird...</req:content>
     <req:endDate>4/30/07</req:endDate>

  </req:createNewAdRequest>
 </env:Body>
Listado 11.createNewAdResponse(Output)

<env:Envelope xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <env:Body> <res:createNewAdResponse xmlns:res="http://daily-moon.com/classifieds/"> <res:newAdId>1138</res:newAdId> </res:createNewAdResponse> </env:Body>

editExistingAd

Esta función toma un aviso existente y reemplaza su contenido en la base de datos. Devuelve un valor booleano indicando si la operación se realizó correctamente (ver Listado 12 y Listado 13).

Listado 12.editExistingAdRequest(Input)
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
  <req:editExistingAdRequest
              xmlns:req="http://daily-moon.com/classifieds/">
      <req:ClassifiedAd>
         <req:id>1138</req:id>

         <req:content>Vintage 1963 T-Bird...</req:content>
         <req:startDate>4/1/2007</req:startDate>
         <req:endDate>4/30/2007</req:endDate>
      <req:ClassifiedAd>

  </req:editExistingAdRequest >
 </env:Body>
Listado 13.editExistingAdResponse(Output)

<env:Envelope xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <env:Body> <res:editExistingAdResponse xmlns:res="http://daily-moon.com/classifieds/"> <res:isOK>true</res:isOK> </res:editExistingAdResponse> </env:Body>

getExistingAds

Esta función devuelve una lista de ClassifiedAds (avisos clasificados) existentes (ver Listado 14 y Listado 15).

Listado 14. getExistingAdsRequest (Input)
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
  <req:getExistingAdsRequest 
                  xmlns:req="http://daily-moon.com/classifieds/">
  </req:getExistingAdsRequest>
 </env:Body>
Listado 15. getExistingAdsResponse (Output)
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
  <res:getExistingAdsResponse
              xmlns:res="http://daily-moon.com/classifieds/">
    <res:ClassifiedList>
      <res:ClassifiedAd>
         <res:id>1138</res:id>

         <res:content>Vintage 1963 T-Bird...</res:content>
         <res:startDate>4/1/2007</res:startDate>
         <res:endDate>4/30/2007</res:endDate>
      </res:ClassifiedAd>

      <res:ClassifiedAd>
         <res:id>2883</res:id>
         <res:content>Championship playoff
 tickets...</res:content>
         <res:startDate>4/1/2007</res:startDate>

         <res:endDate>4/30/2007</res:endDate>
      </res:ClassifiedAd>
    </res:ClassifiedList>
  </res:getExistingAdsResponse >
</env:Body>

finalizeIssue

Esta función es una función "in only" (sólo de entrada) que toma una fecha de emisión y no arroja ningún dato (ver Listado 16).

Listado 16.finalizeIssueRequest(Input)
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
  <req:finalizeIssueRequest 
                  xmlns:req="http://daily-moon.com/classifieds/">
     <req:issueDate>4/10/06</req:issueDate>
  </req:FinalizeIssueRequest >

 </env:Body>

Munido de estos mensajes, Larry se aboca a crear el documento WSDL real.

Creación del documento básico

Larry comienza con el marco de un documento WSDL (ver Listado 17).

Listado 17. El documento WSDL básico
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
</wsdl:types>

<wsdl:message name="createNewAdRequestMessage">
</wsdl:message>

<wsdl:portType name="ClassifiedServicePortType">

</wsdl:portType>

<wsdl:binding name="ClassifiedServiceBinding">
</wsdl:binding>

<wsdl:service name="ClassifiedService">
</wsdl:service>

</wsdl:definitions>

Como los mensajes SOAP que define, WSDL está compuesto por XML. Las definiciones residen dentro del elemento definitions, como podrá ver aquí. Comenzando desde abajo, defina el atributo service. El atributo service usa un binding (enlace) particular que es una implementación de un atributo portType. El atributo portType define operaciones que están compuestas por messages (mensajes). Los mensajes están formados por XML definido en la sección types.

El elemento definitions define dos espacios de nombres. El primero, que usa el prefijo wsdl:, brinda un espacio de nombres para los elementos reales que integran al WSDL. El segundo, targetNamespace, define el espacio de nombres al que pertenecen los elementos definidos por WSDL.

Larry comienza por definir los tipos.

Definición de los tipos

Larry toma las definiciones que le dio Christine y las coloca en el elemento types, creando un esquema nuevo dentro del documento, como se muestra en el Listado 18.

Listado 18. Definición de los tipos
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:ns1="http://org.apache.axis2/xsd" 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://org.apache.axis2/xsd" 
         elementFormDefault="unqualified" 
         attributeFormDefault="unqualified">

    <xs:element type="ns1:ClassifiedAd" name="ClassifiedAd" />

    <xs:complexType name="ClassifiedAd">
      <xs:sequence>
        <xs:element type="xs:int" name="id" />
        <xs:element type="xs:string" name="content" />
        <xs:element type="xs:string" name="endDate" />
        <xs:element type="xs:string" name="startDate" />

      </xs:sequence>
    </xs:complexType>

    <xs:element type="ns1:ClassifiedList" name="ClassifiedList" />
    <xs:complexType name="ClassifiedList">
      <xs:sequence>
        <xs:element minOccurs="0" type="ns1:ClassifiedAd" 
                        name="ClassifiedAd" maxOccurs="unbounded" />

      </xs:sequence>
    </xs:complexType>

    <xs:element name="createNewAdRequest">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:string" name="content" />

          <xs:element type="xs:string" name="endDate" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="createNewAdResponse">
      <xs:complexType>

        <xs:sequence>
          <xs:element type="xs:int" name="newAdId" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="editExistingAdRequest">

      <xs:complexType>
        <xs:sequence>
          <xs:element type="ns1:ClassifiedAd" 
name="ClassifiedAd" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="editExistingAdResponse">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:boolean" name="isOK" />
        </xs:sequence>
      </xs:complexType>

    </xs:element>

    <xs:element name="getExistingAdsRequest">
      <xs:complexType />
    </xs:element>

    <xs:element name="getExistingAdsResponse">

      <xs:complexType>
        <xs:sequence>
          <xs:element type="ns1:ClassifiedList" 
                                   name="ClassifiedList" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="finalizeIssueRequest">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:string" name="issueDate" />
        </xs:sequence>
      </xs:complexType>

    </xs:element>

  </xs:schema>

</wsdl:types>

</wsdl:definitions>

Comenzando desde arriba, observe que tenemos dos secciones de espacios de nombres. La primera está dentro del elemento schema propiamente dicho. Aquí Larry define dos espacios de nombres. El primero con el prefijo xs:, es el espacio de nombres del Esquema XML. El segundo, targetNamespace, define los espacios de nombres a los que pertenecen las definiciones creadas por el esquema. En otras palabras, cuando la segunda definición crea un atributo complexTypellamado ClassifiedAd, esa definición pertenece al espacio de nombres http://org.apache.axis2/xsd. No obstante, para referirse a ese espacio de nombres, Larry necesita crear otro alias. Puede ver ese alias en el elemento de definiciones, con el prefijo ns1:. (Larry podría haber puesto fácilmente esa definición en el elemento de esquema, pero posteriormente la necesitará fuera del elemento de esquema.) Los dos últimos atributos, elementFormDefault y attributeFormDefault, indican si los elementos y los atributos deben tener o no prefijos para espacios de nombres.

Las primeras cuatro definiciones son del esquema que Christine le dio a Larry, pero el resto define los mensajes que Larry creó anteriormente.

Nota sobre espacios de nombres

En XML, como en muchos lenguajes de programación, frecuentemente se debe especificar un "espacio de nombres" para varios elementos y atributos. Esto permite distinguir entre elementos que tienen el mismo nombre, pero diferentes propósitos y tal vez diferentes orígenes. XML hace referencia a un espacio de nombres mediante un identificador URI. Por ejemplo, el espacio de nombres del esquema XML es http://www.w3.org/2001/XMLSchema. Para facilitar la tarea, sin embargo, también se asigna un alias o un prefijo. Por ejemplo, aquí el espacio de nombres de esquema tiene un prefijo xs:. Recuerde que el alias es tan solo eso: un alias. El identificador URI es que realmente importa. Por ello, los elementos o los atributos que forman parte del nombre de espacios ns1: también forman parte del nombre de espacios targetNamespace del esquema.

Creación de los mensajes

Con los tipos ya establecidos, Larry puede definir los mensajes que se enviarán y recibirán en el sobre SOAP (ver Listado 19).

Listado 19. Creación de los mensajes
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:ns1="http://org.apache.axis2/xsd" 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://org.apache.axis2/xsd" 
         elementFormDefault="unqualified" 
         attributeFormDefault="unqualified">
...
    <xs:element name="createNewAdRequest">
      <xs:complexType>
        <xs:sequence>

          <xs:element type="xs:string" name="content" />
          <xs:element type="xs:string" name="endDate" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="createNewAdResponse">

      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:int" name="newAdId" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

...
  </xs:schema>

</wsdl:types>

<wsdl:message name="createNewAdRequestMessage">
  <wsdl:part name="part1" element="ns1:createNewAdRequest"
 />
</wsdl:message>

<wsdl:message name="createNewAdResponseMessage">

  <wsdl:part name="part1" element="ns1:createNewAdResponse" />
</wsdl:message>

<wsdl:message name="getExistingAdsResponseMessage">
  <wsdl:part name="part1" element="ns1:getExistingAdsResponse" />
</wsdl:message>

<wsdl:message name="editExistingAdRequestMessage">
  <wsdl:part name="part1" element="ns1:editExistingAdRequest" />

</wsdl:message>

<wsdl:message name="getExistingAdsRequestMessage">
  <wsdl:part name="part1" element="ns1:getExistingAdsRequest" />
</wsdl:message>

<wsdl:message name="editExistingAdResponseMessage">
  <wsdl:part name="part1" element="ns1:editExistingAdResponse" />
</wsdl:message>

<wsdl:message name="finalizeIssueRequestMessage">
  <wsdl:part name="part1" element="ns1:finalizeIssueRequest" />
</wsdl:message>

<wsdl:portType name="ClassifiedServicePortType">
</wsdl:portType>

<wsdl:binding name="ClassifiedServiceBinding">
</wsdl:binding>

<wsdl:service name="ClassifiedService">
</wsdl:service>

</wsdl:definitions>

Cada mensaje tiene un name (nombre) para referirse al mismo posteriormente. Dentro de cada mensaje, usted define una o más parts (partes). Obsérvese que WSDL 2.0 sólo permite unapart(parte) por message (mensaje), así que Larry cumple aquí con esta pauta.

Cada part(parte) tiene un name (nombre) y el nombre del element (elemento) que compone esa part (parte). El nombre del elemento se relaciona con los tipos definidos en el elemento types(tipos). Obsérvese que el nombre del elemento tiene el prefijo ns1:, el prefijo para los espacios de nombres que coincide con el atributo targetNamespace para el esquema. En otras palabras, cuando Larry creó la definición para createNewAdResponse, esa definición se registró en http://org.apache.axis2/xsd namespace, y como el prefijo ns1: también se refiere también a ese espacio de nombres, usted puede referirse al mismo usando ese prefijo.

Definición de la interfaz (portType)

Los mensajes no funcionan por sí mismos, así que Larry debe asociarlos con operaciones específicas. Estas operation s (operaciones) forman parte del atributo portType. El atributo portType sólo contiene definiciones y no implementaciones. En ese sentido, se parece mucho a una interfaz. Efectivamente, en WSDL 2.0, el nombre de portType se cambió por interface. Larry crea una operación para cada una de las funciones (ver Listado 20).

Listado 20. Agregar operaciones
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:tns=http://ws.apache.org/axis2
       xmlns:ns1="http://org.apache.axis2/xsd" 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
...
</wsdl:types>

<wsdl:message name="createNewAdRequestMessage">

  <wsdl:part name="part1" element="ns1:createNewAdRequest" />
</wsdl:message>

<wsdl:message name="createNewAdResponseMessage">
  <wsdl:part name="part1" element="ns1:createNewAdResponse" />
</wsdl:message>
...
<wsdl:message name="finalizeIssueRequestMessage">
  <wsdl:part name="part1" element="ns1:finalizeIssueRequest" />

</wsdl:message>

<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:operation name="finalizeIssue">
    <wsdl:input message="tns:finalizeIssueRequestMessage" />
  </wsdl:operation>

  <wsdl:operation name="createNewAd">

    <wsdl:input message="tns:createNewAdRequestMessage" />
    <wsdl:output message="tns:createNewAdResponseMessage" />
  </wsdl:operation>

  <wsdl:operation name="editExistingAd">
    <wsdl:input message="tns:editExistingAdRequestMessage" />
    <wsdl:output message="tns:editExistingAdResponseMessage" />

  </wsdl:operation>

  <wsdl:operation name="getExistingAds">
    <wsdl:input message="tns:getExistingAdsRequestMessage" />
    <wsdl:output message="tns:getExistingAdsResponseMessage" />
  </wsdl:operation>

</wsdl:portType>

<wsdl:binding name="ClassifiedServiceBinding">
</wsdl:binding>

<wsdl:service name="ClassifiedService">
</wsdl:service>

</wsdl:definitions>

Larry crea dos tipos de mensajes. Uno, comofinalizeIssue, es una operación "in-only" (sólo de entrada) ya que tiene sólo un mensaje de entrada. Ese mensaje, finalizeIssueRequest, se definió previamente, y como antes, creamos un prefijo nuevo, tns:, para referirnos al espacio de nombres al que pertenece.

El segundo tipo de operación es una operación de solicitud/respuesta, como createNewAd. En este caso, Larry define ambos, los mensajes de entrada y los de salida.

Definición de enlaces

Si el atributo portType es como una interfaz, el binding (enlace) es la implementación de esa interfaz (ver Listado 21).

Listado 21. Creación del enlace
<wsdl:definitions 
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
       xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:tns="http://ws.apache.org/axis2"
       xmlns:ns1="http://org.apache.axis2/xsd" 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
</wsdl:types>

<wsdl:message name="createNewAdRequestMessage">
  <wsdl:part name="part1" element="ns1:createNewAdRequest" />

</wsdl:message>
...
<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:operation name="finalizeIssue">
    <wsdl:input message="tns:finalizeIssueRequestMessage" />
  </wsdl:operation>

  <wsdl:operation name="createNewAd">

    <wsdl:input message="tns:createNewAdRequestMessage" />
    <wsdl:output message="tns:createNewAdResponseMessage" />
  </wsdl:operation>

  <wsdl:operation name="editExistingAd">
    <wsdl:input message="tns:editExistingAdRequestMessage" />
    <wsdl:output message="tns:editExistingAdResponseMessage" />

  </wsdl:operation>

  <wsdl:operation name="getExistingAds">
    <wsdl:input message="tns:getExistingAdsRequestMessage" />
    <wsdl:output message="tns:getExistingAdsResponseMessage" />
  </wsdl:operation>

</wsdl:portType>

<wsdl:binding name="ClassifiedServiceBinding" 
                         type="tns:ClassifiedServicePortType">

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

  <wsdl:operation name="createNewAd">
    <soap:operation soapAction="createNewAd" style="document" />

    <wsdl:input>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
    <wsdl:output>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:output>

  </wsdl:operation>

  <wsdl:operation name="finalizeIssue">
    <soap:operation soapAction="finalizeIssue" style="document" />
    <wsdl:input>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>

  </wsdl:operation>

  <wsdl:operation name="editExistingAd">
    <soap:operation soapAction="editExistingAd" style="document" />
    <wsdl:input>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>

    <wsdl:output>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:output>
  </wsdl:operation>

  <wsdl:operation name="getExistingAds">
    <soap:operation soapAction="getExistingAds" style="document" />

    <wsdl:input>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
    <wsdl:output>
      <soap:body use="literal" 
                      namespace="http://daily-moon.com/classifieds" />
    </wsdl:output>

  </wsdl:operation>

</wsdl:binding>

<wsdl:service name="ClassifiedService">
</wsdl:service>

</wsdl:definitions>

Primero, obsérvese que el enlace type (tipo) se refiere al atributo ClassifiedServicePortType que Larry ya creó. Segundo, obsérvese que se agregó el espacio de nombres soap:. Este binding (enlace) es para mensajes SOAP en HTTP, como puede observar en el elemento soap:binding. Obsérvese el atributo transport. (No se preocupe todavía por style (estilo); analizaremos eso en la sección siguiente).

Para cada operación, que comparta el nombre que tenía en el atributo portType, Larry ahora agrega un elemento soap:operation. Este elemento cumple dos funciones: una, indica que esto es en realidad una operación SOAP; la otra, es para especificar un atributo soapAction, un encabezado de HTTP que se envíe al comienzo del mensaje SOAP real. El servidor puede usar este encabezado para rutear la solicitud a la operación apropiada.

Obsérvese que el atributo soapAction siempre ha sido bastante problemático, y aunque opcional en WSDL 1.1, se lo eliminó completamente de WSDL 2.0.

Cada operación también define un mensaje input (de entrada) y uno output (de salida), en el caso de mensajes que sean exclusivamente de entrada, sólo definirán un mensaje input (de entrada), pero ahora Larry también agrega información SOAP específica. Nuevamente, no se preocupe por el atributo use; también lo analizaremos en un momento. Este elemento define además el espacio de nombres para el contenido de la carga útil.

Definición del servicio

Habiendo definido tanto la interfaz como el enlace, es hora de definir el servicio (ver Listado 22).

Listado 22. Definición del servicio
...
<wsdl:binding name="ClassifiedServiceBinding" 
                     type="tns:ClassifiedServicePortType">

...
</wsdl:binding>

<wsdl:service name="ClassifiedService">
  <wsdl:port name="ClassifiedServicePort" 
                    binding="tns:ClassifiedServiceBinding">
    <soap:address location=
 "http://127.0.0.1:8080/axis2/services/ClassifiedService" />

  </wsdl:port>
</wsdl:service>

</wsdl:definitions>

Un service(servicio) puede tener más de un punto final, cada uno definido por su propio elemento port (puerto). El elemento port corresponde a un enlace en particular e incluye información sobre la forma de acceder al mismo. En este caso, Larry especifica que se puede acceder al puerto a través de SOAP en http://127.0.0.1:8080/axis2/services/ClassifiedService.

Documentación del servicio

Con el servicio ya definido, resta un paso más. Es optativo, pero debería hacerlo. WSDL le permite agregar un elemento de documentación a cualquier elemento en el documento WSDL. El Listado 23 ofrece un ejemplo.

Listado 23. Documentación del servicio
...
<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:document>The finalizeIssue operation is an "in-only"
         operation that tells the system not to accept any 
         more ads for a particular date.</wsdl:document>

  <wsdl:operation name="finalizeIssue">
    <wsdl:input message="tns:finalizeIssueRequestMessage" />

  </wsdl:operation>
...

Hay mucha información sobre WSDL, pero éstos son los conceptos básicos. Antes de avanzar, sin embargo, demos un vistazo rápido a un par de situaciones más avanzadas que podría llegar a enfrentar.

Otros enlaces que no sean SOAP en HTTP

Pese a que el uso más común de WSDL es definir SOAP en HTTP, de ninguna manera es la única forma de especificar un servicio web. Por ejemplo, Larry puede crear un enlace que use SOAP en correo electrónico y lo habilite sólo para la operación finalizeIssue (ver Listado 24).

Listado 24. Agregar un enlace adicional
...
<wsdl:binding name="ClassifiedServiceEmailBinding" 
                          type="tns:ClassifiedServicePortType">

   <soap:binding style="document" transport="http://example.com/smtp"/>

   <wsdl:operation name="finalizeIssue">
     <wsdl:input message="tns:finalizeIssueRequestMessage">

         <soap:body parts="body" use="literal"/>
      </wsdl:input>
   </wsdl:operation>

</wsdl:binding>

<wsdl:service name="ClassifiedService">
  <wsdl:port name="ClassifiedServicePort" 
                    binding="tns:ClassifiedServiceBinding">

    <soap:address location=
       "http://127.0.0.1:8080/axis2/services/ClassifiedService" />
  </wsdl:port>

  <wsdl:port name="ClassifiedServiceEmailPort" 
                       binding="tns:ClassifiedServiceEmailBinding">
    <soap:address location="mailto:finalizeIssue@daily-moon.com" />
  </wsdl:port>
</wsdl:service>

</wsdl:definitions>

Aquí Larry agregó un segundo enlace,ClassifiedServiceEmailBinding. Como el enlace ClassifiedServiceBinding, se base en el atributo ClassifiedServicePortType portType. En este caso, sin embargo, él usa SMTP (el protocolo utilizado para enviar correo electrónico) en lugar de HTTP. Entonces puede agregar un segundo puerto al servicio, basándolo en el nuevo enlace y usando una URL de correo electrónico como la ubicación en lugar de una URL de HTTP.

SOAP y Mime

Otro tópico para el cual la solución no es tan obvia, es el uso de SOAP con adjuntos. Como el mensaje es más que un mero documento XML, ¿cómo lo especificará usando WSDL? La respuesta implica el hecho de que el mensaje se envía usando "multipart MIME". Afortunadamente, usted puede especificar esto en el documento WSDL. Por ejemplo, Larry quiere especificar una operación en la cual los usuarios que editen un aviso existente, obtengan un PDF como constancia del aviso (ver Listado 25).

Listado 25. Especificación de un mensaje multiparte
...
  <wsdl:operation name="editExistingAd">
    <soap:operation soapAction="editExistingAd" 
style="document" />
    <wsdl:input>
      <soap:body use="literal" 
                namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
    <wsdl:output>

      <soap:body use="literal" 
                namespace="http://daily-moon.com/classifieds" />
      <mime:multipartRelated>
          <mime:part>
             <soap:body parts="body" use="literal"/>
          </mime:part>
          <mime:part>

              <mime:content part="docs" type="text/html"/>
          </mime:part>
          <mime:part>
              <mime:content part="proof" type="image/gif"/>
              <mime:content part="proof" type="image/jpeg"/>
           </mime:part>

       </mime:multipartRelated>
    </wsdl:output>
...

En este caso, el mensaje que arroja incluye SOAP, un documento HTML y dos archivos de imágenes.

Análisis de WSDL 2.0

En todas esta sección, intenté señalar algunos de los aspectos en los cuales WSDL 2.0 diferirá de WSDL 1.1, por ejemplo el cambio de portTypes (tipos de puerto) potencialmente múltiples a una única interface (interfaz), y el hecho de que los message s (mensajes) ya no pueden tener más de una parte. También se vislumbran otros cambios.

Varios factores motivan estos cambios, pero la mayoría de ellos se deben a motivos de interoperabilidad --construcciones que no son lícitas en WS-I's Basic Profile y por lo general están prohibidas-- o para facilitar el uso de WSDL con especificaciones SOAP ampliadas. Por ejemplo, WSDL 2.0 usa más de un modelo de "componente", lo cual se adapta mejor a la integración con las demandas de especificaciones tales como WS-Choreography.

Otro cambio involucra la especificación formal de "patrones de intercambio de mensajes". En lugar de esperar simplemente que los usuarios se fijen si hay o no mensajes entrantes o salientes o sólo salientes, WSDL 2.0 le permite indicar específicamente qué patrón está usando (ver el Litado 26).

Listado 26. Especificación de los patrones de intercambio de mensajes
...
  <wsdl:operation name="finalizeIssue"
            pattern=http://www.w3.org/2006/01/wsdl/in-only">
    <wsdl:input message="tns:finalizeIssueRequestMessage" />
  </wsdl:operation>

  <wsdl:operation name="createNewAd"
              pattern="http://www.w3.org/2006/01/wsdl/in-out">

    <wsdl:input message="tns:createNewAdRequestMessage" />
    <wsdl:output message="tns:createNewAdResponseMessage" />
  </wsdl:operation>
...

Ahora pasemos a la parte inferior de los estilos WSDL.


Estilos WSDL

Una de las tareas más confusas para los desarrolladores que están aprendiendo a usar WSDL es elegir un estilo para un documento. Analicemos las diferentes opciones y cómo afectan tanto a WSDL, como al documento SOAP.

Programación de estilos y codificación

En el ámbito de XML, hay generalmente dos tipos de personas: aquellas que consideran a XML como un formato de datos y aquellas que piensan en XML como marcado de documentos. En general, estos dos extremos nunca se juntan. Esta separación se traslada, en cierta forma, a los servicios web. Hay algunos que lo consideran una forma de Llamada a procedimiento remoto (RPC) basada en XML y otros que lo consideran un medio de trasladar información XML de un lugar a otro.

En términos de WSDL, esto se refleja en la elección del "estilo" del mensaje. Cuando cree el enlace, podrá elegir el estilo del documento, como eligió Larry para el servicio de Avisos Clasificados, o el estilo RPC. Ningún estilo es de por sí "correcto" o "incorrecto". Pero ambos tienen ventajas y desventajas.

Cuando se use el estilo RPC, el nombre del método que se ejecutará es también el nombre del elemento raíz de la carga útil. Pero usted dirá, un momento, ¿no es así como está estructurado el WSDL de los Avisos Clasificados? Bueno, sí y no. Sí, el nombre del elemento raíz es el mismo al nombre del método que queremos que aplique el servicio. Sin embargo, esto es, en cierta forma, una coincidencia; Larry estructuró específicamente el servicio de esa manera.

Analicemos las diferentes opciones y cómo funcionan en términos de WSDL y SOAP.

Document/Literal

El estilo document/literal significa que la carga útil contiene sólo los datos reales que se transmitirán al servicio. Cualquier ruteo necesario para que el mensaje llegue a su destino se debe realizar de alguna otra forma, por ejemplo a través del encabezado soapAction o de una URL específica para el servicio.

El mensaje es simple y directo (ver Listado 27).

Listado 27. Un mensaje con estilo document/literal
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
     <req:content>Vintage 1963 T-Bird...</req:content>
     <req:endDate>4/30/07</req:endDate>
</env:Body>

</env:Envelope>

Obsérvese que no hay elemento raíz para la carga útil. En el archivo WSDL, usted define los elementos directamente y los agrega al mensaje (ver Listado 28).

Listado 28. WSDL con estilo document/literal

... <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://org.apache.axis2/xsd" elementFormDefault="unqualified" attributeFormDefault="unqualified"> ... <xs:element type="xs:string" name="content" /> <xs:element type="xs:string" name="endDate" /> ... </xs:schema> </wsdl:types> <wsdl:message name="createNewAdRequestMessage"> <wsdl:part name="part1" element="ns1:content" /> <wsdl:part name="part2" element="ns1:endDate" /> </wsdl:message> ... <wsdl:portType name="ClassifiedServicePortType"> ... <wsdl:operation name="createNewAd"> <wsdl:input message="tns:createNewAdRequestMessage" /> <wsdl:output message="tns:createNewAdResponseMessage" /> </wsdl:operation> ... </wsdl:portType> ... <wsdl:binding name="ClassifiedServiceBinding" type="tns:ClassifiedServicePortType"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <wsdl:operation name="createNewAd"> <soap:operation soapAction="createNewAd" style="document" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> ... </wsdl:binding> ...

Obsérvese que en el estilo de documento, todos los elementos que forman parte de la carga útil también tienen definiciones en el esquema. También, obsérvese que el mensaje tiene dos partes distintas, cada una de las cuales se refiere a un elemento específico.

Document/Literal/wrapped

Este tutorial usa el estilo document/literal/wrapped que es similar al estilo document/literal, excepto que la carga útil tiene un elemento raíz. Esto ofrece la ventaja de incluir el nombre del método que se ejecutará (aunque esto no es un requisito), así como el cumplimiento con los requisitos de WS-I Basic Profile. Como recordatorio, aquí se incluye el mensaje simple (ver Listado 29).

Listado 29. Un mensaje con estilo document/literal/wrapped
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
  <req:createNewAdRequest 
              xmlns:req="http://daily-moon.com/classifieds/">
     <req:content>Vintage 1963 T-Bird...</req:content>
     <req:endDate>4/30/07</req:endDate>

  </req:createNewAdRequest>
 </env:Body>
</env:Envelope>

Para completar el tema, aquí se incluye el respectivo WSDL (ver Listado 30).

Listado 30. WSDL con diseño Document/literal/wrapped
...
<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://org.apache.axis2/xsd" 
         elementFormDefault="unqualified" 
         attributeFormDefault="unqualified">
...
    <xs:element name="createNewAdRequest">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:string" name="content" />

          <xs:element type="xs:string" name="endDate" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>
...
  </xs:schema>

</wsdl:types>

<wsdl:message name="createNewAdRequestMessage">
  <wsdl:part name="part1" element="ns1:createNewAdRequest"
 />
</wsdl:message>
...
<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:operation name="createNewAd">

    <wsdl:input message="tns:createNewAdRequestMessage" />
    <wsdl:output message="tns:createNewAdResponseMessage" />
  </wsdl:operation>
...
</wsdl:portType>
...
<wsdl:binding name="ClassifiedServiceBinding"
 type="tns:ClassifiedServicePortType">

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

  <wsdl:operation name="createNewAd">
    <soap:operation soapAction="createNewAd" style="document"
 />
    <wsdl:input>
      <soap:body use="literal" 
                        namespace="http://ws.apache.org/axis2" />

    </wsdl:input>
    <wsdl:output>
      <soap:body use="literal" 
                        namespace="http://ws.apache.org/axis2" />
    </wsdl:output>
  </wsdl:operation>
...
</wsdl:binding>

...

Nuevamente, todos los elementos en la carga útil se definen en el esquema.

RPC/Literal

El estilo RPC maneja las cosas de manera levemente diferente. Es el nombre propio de WSDL que define qué se incluye en el mensaje, y no el esquema. Por ejemplo, analice este mensaje SOAP (ver Listado 31).

Listado 31. Un mensaje con estilo RPC/literal
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>

  <req:createNewAdRequest 
              xmlns:req="http://daily-moon.com/classifieds/">
     <req:content>Vintage 1963 T-Bird...</req:content>
     <req:endDate>4/30/07</req:endDate>

  </req:createNewAdRequest>
 </env:Body>
</env:Envelope>

El mensaje en sí es idéntico al estilo document/literal/wrapped, pero el WSDL es muy diferente (ver Listado 32).

Listado 32. El WSDL para un mensaje con estilo RPC/literal
...
<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://org.apache.axis2/xsd" 
         elementFormDefault="unqualified" 
         attributeFormDefault="unqualified">
...
  </xs:schema>

</wsdl:types>

<wsdl:message name="createNewAdRequest">

  <wsdl:part name="content" element="xsd:string" />
  <wsdl:part name="endDate" element="xsd:string" />
</wsdl:message>

...
<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:operation name="createNewAd">
    <wsdl:input message="tns:createNewAdRequest" />
    <wsdl:output message="tns:createNewAdResponseMessage" />
  </wsdl:operation>

...
</wsdl:portType>
...
<wsdl:binding name="ClassifiedServiceBinding"
 type="tns:ClassifiedServicePortType">

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

  <wsdl:operation name="createNewAd">
    <soap:operation soapAction="createNewAd" style="rpc" />

    <wsdl:input>
      <soap:body use="literal" 
                        namespace="http://ws.apache.org/axis2" />
    </wsdl:input>
    <wsdl:output>
      <soap:body use="literal" 
                        namespace="http://ws.apache.org/axis2" />

    </wsdl:output>
  </wsdl:operation>
...
</wsdl:binding>
...

Primero, obsérvese que en el esquema no se definió nada realmente. En cambio, el message (mensaje) lleva el nombre del método que se ejecutará, y las message part s (partes del mensaje) directamente definen cada elemento. Obsérvese también que en el estilo RPC, el nombre de la parte del mensaje es importante; es el nombre del elemento dentro de la carga útil. Los tipos de mensaje se definen directamente. (Esto, por supuesto, significa que usted no puede tener elementos complejos como parte de su carga útil, pero como este estilo está diseñado para emular una llamada a procedimiento remoto, esto no representa un problema).

En el atributo portType, cuando usted especifica el mensaje, directamente identifica al mensaje como creado con estos elementos. Entonces, en el enlace, mediante la especificación del estilo RPC, queda claro cómo se traduce todo esto al mensaje SOAP.

RPC/Encoded

El último estilo para analizar es RPC/encoded (RPC/codificado). Este estilo es similar a RPC/literal, excepto que el mensaje SOAP define la información real del tipo (ver Listado 33).

Listado 33. Un mensaje SOAP con estilo RPC/encoded (RPC/codificado)
<env:Envelope 
       xmlns:env="http://schemas.xmlSOAP.org/SOAP/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <env:Body>
   <req:createNewAdRequest 
              xmlns:req="http://daily-moon.com/classifieds/">
     <req:content xsi:type="xs:string">Vintage 1963 
                                          T-Bird...</req:content>

     <req:endDate
 xsi:type="xs:string">4/30/07</req:endDate>
    </req:createNewAdRequest>
   </env:Body>
</env:Envelope>

El WSDL para definir el mensaje es el mismo que RPC/literal, pero se agrega información de codificación adicional al enlace (ver Listado 34).

Listado 34. El WSDL con estilo RPC/encoded (RPC/codificado)
<wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
       xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:tns="http://ws.apache.org/axis2"

       xmlns:axis2="http://ws.apache.org/axis2"
       xmlns:ns1="http://org.apache.axis2/xsd" 
 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://org.apache.axis2/xsd" 
         elementFormDefault="unqualified" 
         attributeFormDefault="unqualified">
...
  </xs:schema>

</wsdl:types>

<wsdl:message name="createNewAdRequest">
  <wsdl:part name="content" element="xsd:string" />
  <wsdl:part name="endDate" element="xsd:string" />
</wsdl:message>
...

<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:operation name="createNewAd">
    <wsdl:input message="tns:createNewAdRequest" />
    <wsdl:output message="tns:createNewAdResponse" />
  </wsdl:operation>

...
</wsdl:portType>

<wsdl:binding name="ClassifiedServiceBinding"
 type="tns:ClassifiedServicePortType">

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

  <wsdl:operation name="createNewAd">
    <soap:operation soapAction="createNewAd" style="document" />

    <wsdl:input>
      <soap:body use="encoded" 
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
  namespace="http://ws.apache.org/axis2" />
    </wsdl:input>
    <wsdl:output>

      <soap:body use="literal" namespace=
"http://ws.apache.org/axis2" />
    </wsdl:output>
  </wsdl:operation>

...
</wsdl:binding>
...

Cuando cree un archivo WSDL manualmente, deberá manejar todo esto usted mismo. Afortunadamente, no siempre tiene que crearlo manualmente.


Generación de código usando WSDL

Gene y Francis son los programadores del grupo en el periódico, convocados por el departamento de TI cuando el departamento de Avisos Clasificados los puede sacar de sus propios proyectos. Ellos van a trabajar en la generación de código WSDL abordándolo de dos maneras; Gene se concentrará en generar código de Java a WSDL y Francis generará el código de WSDL a Java.

Cómo funciona la generación de código

En los comienzos de WSDL, las dos primeras aplicaciones que aparecieron fueron Java2WSDL y WSDL2Java. Después de todo, de qué sirve un formato automatizado si no se puede usar para automatizar algo. Por supuesto que en esos momentos las opciones eran bastante limitadas. El código se orientaba al estilo RPC y era difícil generar automáticamente un sistema con cargas útiles complejas.

Rápidamente hasta llegar al día de hoy, estos problemas se solucionaron de manera contundente. Axis2 puede generar código Java desde virtualmente cualquier documento WSDL y WSDL desde cualquier clase Java. Esto se logra usando el enlace de datos, en el cual una estructura XML se convierte en un objeto Java y viceversa. El proceso de generación crea el código que después se puede alterar, ajustar, compilar y ejecutar.

Primero, Gene comienza con una clase Java y la usa para generar un documento WSDL. Francis luego toma ese documento y lo usa para generar el servicio y el cliente. Para el servicio, el proceso de generación crea un esqueleto en el cual usted puede agregar su propio código para efectuar las acciones que desee que realice el servicio. Para el cliente, crea un stub que usted puede usar para invocar un método de servicio web como si fuera un método Java.

Preparación

El primer paso es asegurarse que su entorno esté listo. Descargue la versión 0.95 de Apache Axis2, desempaquételo y verifique que todos los archivos *.jar en el directorio de la biblioteca estén en CLASSPATH.

Para ejecutar el servicio web, instale Apache Geronimo (si aún no lo hizo) e inícielo. (Ver la Parte 1 para conocer las instrucciones). Descargue la distribución de Axis2 v0.95 War y cópiela al directorio <GERONIMO_HOME>/deploy. Geronimo implantará a Axis2 automáticamente.

Las clases Java

Gene comienza con la clase ClassifiedService, la cual intenta usar como el servicio de asistencia doméstica y también como una forma de probar que todo funcione como se espera (ver Listado 35).

Listado 35. ClassifiedService.java
package org.dailymoon.classifieds;

public class ClassifiedService {
   
   public static int createNewAd(String content, String endDate){

      ClassifiedAd newAd = new ClassifiedAd();
      newAd.setEnd(endDate);
      newAd.setContent(content);
      newAd.save();

      return 1;

   }

   public static boolean editExistingAd(ClassifiedAd adToEdit){

      //Do stuff with the ad here
      return true;

   }


   public static ClassifiedList getExistingAds(){

       ClassifiedAd[] listOfAds = {new ClassifiedAd(), new
 ClassifiedAd(), new ClassifiedAd()};
       ClassifiedList listToReturn = new ClassifiedList(listOfAds);
       return listToReturn;
   }

   public static void finalizeIssue(String dateToFinalize){
       //Don't return anything.
       System.out.println(dateToFinalize + " finalized.");
   }

   public static void main (String args[]){

         ClassifiedService.createNewAd(
           "Eclipse experts needed.  Contact Nick for details.",
                                                   "4/21/2006");

         ClassifiedAd adToEdit = new ClassifiedAd();
         adToEdit.setId(1);
         adToEdit.setStart("4/8/2006");
         adToEdit.setEnd("4/30/2006");
         adToEdit.setContent(
             "Geronimo experts needed.  Contact Nick for details.");

         ClassifiedService.editExistingAd(adToEdit);

         ClassifiedList adList = ClassifiedService.getExistingAds();
         System.out.println(adList.toString());

   }


}

La aplicación propiamente dicha es bastante sencilla, aporta un ejemplo de creación de aviso nuevo, edición de un aviso existente y listado de los avisos existentes, brinda limitaciones básicas para cuatro métodos que Gene desea exponer, createNewAd, editExistingAd, getExistingAds y finalizeIssue.

(Asegúrese de convertir en comentario el método principal antes de generar el WSDL. No dañará nada, pero generará código extra innecesario.)

La case a su vez se refiere a otras dos clases, ClassifiedAd y ClassifiedList. Para que el proceso de generación comprenda cómo estructurar estos objetos como XML, Gene los crea como clases separadas (ver Listado 36).

Listado 36. ClassifiedAd.java
package org.dailymoon.classifieds;

public class ClassifiedAd {

   private int id;
   private String startDate;
   private String endDate;
   private String content;

   public void setId(int newId){
      id = newId;
   }

   public void setStartDate(String newStart){
      startDate = newStart;
   }

   public void setEndDate(String newEnd){
      endDate = newEnd;
   }

   public void setContent(String newContent){
      content = newContent;
   }

   public void save(){
       //Save data here
       System.out.println("Ad saved.");
   }

}

Nuevamente, la clase propiamente dicha no está completa, pero la estructura está implementada (ver Listado 37).

Listado 37. ClassifiedList.java
package org.dailymoon.classifieds;

public class ClassifiedList {

    public ClassifiedAd[] listOfAds;

    public ClassifiedList(ClassifiedAd[] newListOfAds){
        listOfAds = newListOfAds;
    }

    public ClassifiedAd[] getRawAds(){
         return listOfAds;
    }

    public String toString(){
         return "This is a string of results.";
    }

}

Aquí Gene especifica que ClassifiedList está compuesto por una variedad de objetos ClassifiedAd.

Con todas estas clases implementadas, él puede generar el WSDL.

Generación y preparación de WSDL

Generar el WSDL es un proceso sencillo. Desde la línea de comandos, Gene emite el comando, como se muestra en el Listado 38:

Listado 38. Comando para generar el WSDL
 java org.apache.axis2.wsdl.Java2WSDL -cn
                    org.dailymoon.classifieds.ClassifiedService -o

(Obsérvese que esto deberá aparecer en una línea.)

El switch -cn especifica la clase que forma la base para el servicio. El switch -o especifica el directorio de los datos de salida. Suponiendo que no hubiera problemas, la clase se ejecuta tranquilamente, dejando al archivo ClassifiedService.wsdl en el directorio de los datos de salida. Este archivo es muy parecido al que generó Larry anteriormente —por diseño, ya que todos funcionan en el mismo servicio— pero se debe realizar algunos pequeños cambios para acomodar los elementos que fueron nombrados genéricamente por el proceso de generación. Específicamente, los parámetros no se traducen bien y probablemente deban renombrarse.

Aquí está el archivo WSDL generado, con los ajustes en negrita (ver Listado 39).

Listado 39. El archivo WSDL
<wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
       xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:tns="http://ws.apache.org/axis2"
       xmlns:ns1="http://org.apache.axis2/xsd" 
       targetNamespace="http://ws.apache.org/axis2">

<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://org.apache.axis2/xsd" 
         elementFormDefault="unqualified" 
         attributeFormDefault="unqualified">

    <xs:element type="ns1:ClassifiedAd" name="ClassifiedAd" />

    <xs:complexType name="ClassifiedAd">
      <xs:sequence>
        <xs:element type="xs:int" name="id" />
        <xs:element type="xs:string" name="content" />
        <xs:element type="xs:string" name="endDate" />
        <xs:element type="xs:string" name="startDate" />

      </xs:sequence>
    </xs:complexType>

    <xs:element type="ns1:ClassifiedList" name="ClassifiedList" />
    <xs:complexType name="ClassifiedList">
      <xs:sequence>
        <xs:element minOccurs="0" type="ns1:ClassifiedAd" 
                        name="ClassifiedAd" 
maxOccurs="unbounded" />

      </xs:sequence>
    </xs:complexType>

    <xs:element name="createNewAdRequest">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:string" name="content" />

          <xs:element type="xs:string" name="endDate" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="createNewAdResponse">

      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:int" name="newAdId" />
        </xs:sequence>
      </xs:complexType>

    </xs:element>

    <xs:element name="editExistingAdRequest">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="ns1:ClassifiedAd" 
name="existingAd" />

        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="editExistingAdResponse">
      <xs:complexType>
        <xs:sequence>

          <xs:element type="xs:boolean" name="
wasSuccessful"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="getExistingAdsRequest">

      <xs:complexType />
    </xs:element>

    <xs:element name="getExistingAdsResponse">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="ns1:ClassifiedList" 
name="ClassifiedList" />

        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="finalizeIssueRequest">
      <xs:complexType>
        <xs:sequence>

          <xs:element type="xs:string" name="issueToFinalize"
 />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

  </xs:schema>

</wsdl:types>

<wsdl:message name="createNewAdRequestMessage">
  <wsdl:part name="part1" element="ns1:createNewAdRequest" />
</wsdl:message>

<wsdl:message name="createNewAdResponseMessage">
  <wsdl:part name="part1" element="ns1:createNewAdResponse" />

</wsdl:message>

<wsdl:message name="getExistingAdsResponseMessage">
  <wsdl:part name="part1" element="ns1:getExistingAdsResponse" />
</wsdl:message>

<wsdl:message name="editExistingAdRequestMessage">
  <wsdl:part name="part1" element="ns1:editExistingAdRequest" />
</wsdl:message>

<wsdl:message name="getExistingAdsRequestMessage">
  <wsdl:part name="part1" element="ns1:getExistingAdsRequest" />
</wsdl:message>

<wsdl:message name="editExistingAdResponseMessage">
  <wsdl:part name="part1" element="ns1:editExistingAdResponse" />
</wsdl:message>

<wsdl:message name="finalizeIssueRequestMessage">
  <wsdl:part name="part1" element="ns1:finalizeIssueRequest" />
</wsdl:message>

<wsdl:portType name="ClassifiedServicePortType">

  <wsdl:operation name="finalizeIssue">
    <wsdl:input message="tns:finalizeIssueRequestMessage" />

  </wsdl:operation>

  <wsdl:operation name="createNewAd">
    <wsdl:input message="tns:createNewAdRequestMessage" />
    <wsdl:output message="tns:createNewAdResponseMessage" />
  </wsdl:operation>

  <wsdl:operation name="editExistingAd">
    <wsdl:input message="tns:editExistingAdRequestMessage" />
    <wsdl:output message="tns:editExistingAdResponseMessage" />
  </wsdl:operation>

  <wsdl:operation name="getExistingAds">
    <wsdl:input message="tns:getExistingAdsRequestMessage" />

    <wsdl:output message="tns:getExistingAdsResponseMessage" />
  </wsdl:operation>

</wsdl:portType>

<wsdl:binding name="ClassifiedServiceBinding"
 type="tns:ClassifiedServicePortType">

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

  <wsdl:operation name="createNewAd">
    <soap:operation soapAction="createNewAd" style="document" />
    <wsdl:input>
      <soap:body use="literal" 
                 namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
    <wsdl:output>

      <soap:body use="literal" 
                 namespace="http://daily-moon.com/classifieds" />
    </wsdl:output>
  </wsdl:operation>

  <wsdl:operation name="finalizeIssue">
    <soap:operation soapAction="finalizeIssue" style="document" />
    <wsdl:input>

      <soap:body use="literal" 
                  namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
  </wsdl:operation>

  <wsdl:operation name="editExistingAd">
    <soap:operation soapAction="editExistingAd" style="document" />
    <wsdl:input>

      <soap:body use="literal" 
                   namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
    <wsdl:output>
      <soap:body use="literal" 
                   namespace="http://daily-moon.com/classifieds" />
    </wsdl:output>
  </wsdl:operation>

  <wsdl:operation name="getExistingAds">
    <soap:operation soapAction="getExistingAds" style="document" />
    <wsdl:input>
      <soap:body use="literal" 
                   namespace="http://daily-moon.com/classifieds" />
    </wsdl:input>
    <wsdl:output>

      <soap:body use="literal" 
                   namespace="http://daily-moon.com/classifieds" />
    </wsdl:output>
  </wsdl:operation>

</wsdl:binding>

<wsdl:service name="ClassifiedService">
  <wsdl:port name="ClassifiedServicePort" 
                           
 binding="tns:ClassifiedServiceBinding">

    <soap:address location=
         "http://127.0.0.1:8080/axis2/services/ClassifiedService" />
  </wsdl:port>
</wsdl:service>

</wsdl:definitions>

Muchos de estos cambios se realizan por una cuestión de conveniencia y también de utilidad, es mucho más fácil recordar "content" que "param0". Dos de estos cambios --el espacio de nombres que aparece en la parte superior y el prefijo de espacio de nombres en la parte inferior-- se deben a pequeños errores en el proceso de generación que existen en Axis2 versión 0.95 y tal vez al momento de leer esto ya no sean necesarios.

Generación del servicio a desde el WSDL

Una vez que ya existe el archivo WSDL, Francis puede usarlo para generar el servicio y el cliente. (En realidad, Francis podría haber usado fácilmente la versión generada por Larry.)

Francis comienza por generar el código del lado del servidor, como se muestra en el Listado 40:

Listado 40. El código del lado del servidor
java org.apache.axis2.wsdl.WSDL2Java -uri ClassifiedService.wsdl 
-ss -sd -p org.dailymoon.classifieds -d xmlbeans -o service

(Nuevamente, ingrese este comando en una sola línea.)

El primer parámetro es la URL para el archivo WSDL. Sí, usted puede acceder a un archivo remoto usando esta aplicación. El segundo switch, -ss, le pide a la aplicación que genere el servicio y no el cliente. El switch -sd le indica a la aplicación que genere el descriptor de servicio XML, facilitando la implantación del servicio una vez que ha sido generado. El próximo parámetro es, obviamente, el paquete, seguido por el método de enlace de datos. Los métodos disponibles son adb, xmlbeans y jaxme. Por último, para dejar las cosas ordenadas, Francis genera el servicio en un nuevo directorio llamado fuente.

Esto implica literalmente cientos de archivos. Afortunadamente, usted sólo necesita manejarse con uno de ellos.

Implementación del servicio

Aunque en este caso el servicio fue generado por un archivo WSDL que a su vez fue generado a partir de una clase Java, no hay una lógica real en el código generado, sólo aparece la estructura. Para lograr que el servicio haga realmente algo, usted debe editar el archivo del esqueleto.

En esta estructura, el archivo en cuestión se muestra en el Listado 41:

Listado 41. Archivo generado a partir de una clase Java
service\src\org\dailymoon\classifieds\ClassifiedServicePortTypeSkeleton.
java

El código tiene un comentario extenso que puede ser útil cuando trata de descifrarlo para usted mismo, pero puede distraer mucho al intentar explicarlo. Aquí se transcribe la versión limpia junto con el código agregado para implementar parte del servicio (ver Listado 42).

Listado 42. ClassifiedServicePortTypeSkeleton.java
package org.dailymoon.classifieds;

public class ClassifiedServicePortTypeSkeleton {

   public  axis2.apache.org.xsd.CreateNewAdResponseDocument 
                                                      createNewAd   
      (axis2.apache.org.xsd.CreateNewAdRequestDocument param0 ) 
                                                 throws Exception {

      //Todo fill this with the necessary business logic
      //throw new  java.lang.UnsupportedOperationException();

      System.out.println("New ad requested, to end on " +         
                  param0.getCreateNewAdRequest().getEndDate());
      System.out.println(
                  param0.getCreateNewAdRequest().getContent());

      axis2.apache.org.xsd.CreateNewAdResponseDocument 
                                               responseDoc =
          axis2.apache.org.xsd.CreateNewAdResponseDocument
                                        .Factory.newInstance();

      axis2.apache.org.xsd.CreateNewAdResponseDocument
                               .CreateNewAdResponse response =
                        responseDoc.addNewCreateNewAdResponse();

      response.setNewAdId(1138);
      return responseDoc;

   }
     
   public  void finalizeIssue
           (axis2.apache.org.xsd.FinalizeIssueRequestDocument param2)
                                                    throws Exception {

        //Todo fill this with the necessary business logic
                
   }
     
   public  axis2.apache.org.xsd.EditExistingAdResponseDocument 
                                                       editExistingAd
        (axis2.apache.org.xsd.EditExistingAdRequestDocument param3) 
                                                    throws Exception {

      //Todo fill this with the necessary business logic
      throw new  java.lang.UnsupportedOperationException();

   }
     
   public  axis2.apache.org.xsd.GetExistingAdsResponseDocument 
                                                      getExistingAds
        (axis2.apache.org.xsd.GetExistingAdsRequestDocument param5) 
                                                     throws Exception {

      //Todo fill this with the necessary business logic
      throw new  java.lang.UnsupportedOperationException();

   }
     
}

Cada método se inicia arrojando un parámetro UnsupportedOperationException, hasta que usted realmente implementa el método. Para acceder a los datos enviados al servicio, comience con el parámetro y obtenga la solicitud propiamente dicha. A partir de allí, podrá extraer miembros individuales usando métodos captadores.

Obviamente, en un servicio real, usted querrá hacer algo más que simplemente texto de datos de salida, pero Francis sólo está interesada en comprobar que esté funcionando. Para crear la respuesta, comience con el documento de respuesta apropiado, adquiriendo una instancia a través del parámetro Factory de la clase. (Las clases propiamente dichas son bastante complejas, contienen una cantidad de clases internas, pero conviene ver cómo están estructuradas). Una vez que tenga el documento, cree la respuesta real propiamente dicha y agréguela a ese documento.

Usando métodos establecedores, usted puede configurar directamente valores en la respuesta. Simplemente la devolución del documento de respuesta y las clases de soporte se ocuparán de volverlo a enviar al solicitante.

Implantación del servicio

Para implantar el servicio necesitará compilarlo y convertirlo en un archivo Axis2. Comience por compilar y empaquetar el servicio, como se muestra en el Listado 43.

Listado 43. Empaquetado del servicio
set ANT_HOME=e:\apache-ant-1.6.5
PATH=%PATH%;%ANT_HOME%\bin;
set AXIS2_HOME=e:\axis2
cd service
ant jar.service

Ajuste adecuadamente su sintaxis para instalaciones que no sean con Windows y asegúrese de usar sus ubicaciones de archivos reales.

Este trabajo de Ant compila todos los archivos apropiados y crea dos archivos, ClassifiedService.aar y XBeans-packaged.jar, ambos en el directorio build/lib.

Para implantar el servicio, asegúrese que Geronimo esté ejecutándose y señale su navegador como se muestra en el Listado 44:

Listado 44. Implantación del servicio
java org.apache.axis2.wsdl.WSDL2Java -uri ClassifiedService.wsdl -p
 org.dailymoon.classifieds -d xmlbeans -o client

Inicie sesión con las credenciales admin/axis2 y haga clic en Upload Service (Cargar servicio)>Browse (Navegar). Navegue hasta el archivo ClassifiedService.aar y haga clic en OK (Aceptar). Haga clic en Upload (Cargar) para completar el proceso.

Si hace clic en View Services (Ver servicios), debería ver el nuevo servicio en la lista.

Generación del stub de cliente desde WSDL

Ahora sólo falta generar el cliente para acceder al nuevo servicio. Para hacer esto, ejecute el siguiente comando desde la línea de comandos:

Listado 45. Comando para generar el cliente
java org.apache.axis2.wsdl.WSDL2Java -uri ClassifiedService.wsdl -p
 org.dailymoon.classifieds -d xmlbeans -o client

Como siempre, éste es un comando único, destinado a una sola línea. Los parámetros son virtualmente idénticos a aquellos usados para la generación del lado del servidor, excepto que usted no necesita un descriptor de servicio. Además, para mantener las cosas en orden, Francis pone los archivos nuevos en un directorio, cliente, separado.

Esta clase debería ejecutarse tranquilamente, dejando cientos de archivos a su paso, pero usted no necesita ocuparse de ninguno de ellos directamente.

Creación del cliente

El proceso de generación de código en realidad no crea un cliente, sino una clase que usted puede usar fácilmente para crear un cliente. Para simplificar la compilación, Francies crea un archivo de clase nueva llamado Client.java en el directorio client\src\org\dailymoon\classifieds. De esta manera, Ant tomará el archivo .java y lo compilará con el resto de la fuente.

Francis agrega el código en el Listado 46.

Listado 46. El cliente
package org.dailymoon.classifieds;

import axis2.apache.org.xsd.*;

public class Client{

   public static void main(java.lang.String args[]){

      try{
         ClassifiedServicePortTypeStub stub = 
              new ClassifiedServicePortTypeStub(null,
     "http://localhost:8080/axis2/services/ClassifiedService");
            
         CreateNewAdRequestDocument cnaDoc = 
               CreateNewAdRequestDocument.Factory.newInstance();

         CreateNewAdRequestDocument.CreateNewAdRequest cnaReq = 
                              cnaDoc.addNewCreateNewAdRequest();
         cnaReq.setContent("Vintage 1963 T-Bird...");
         cnaReq.setEndDate("7/4/06");

         CreateNewAdResponseDocument cnaResDoc = 
                                       stub.createNewAd(cnaDoc);
         System.out.println("New ad ID number:  "+
                cnaResDoc.getCreateNewAdResponse().getNewAdId());

      } catch(Exception e){
         e.printStackTrace();
      }
   }
}

La clase ClassifiedServicePortTypeStub representa el servicio real y usted lo inicia con el atributo AXIS_HOME (aquí a la izquierda del valor predeterminado) y con la ubicación del servicio real. Luego, cree el documento de solicitud, una vez más haciendo referencia a su Factory, y úselo para crear una nueva solicitud CreateNewAdRequest, agregándola al documento de solicitudes en el proceso. Así como en el servicio propiamente dicho, luego puede establecer atributos directamente usando métodos establecedores.

Para obtener la respuesta, use el stub para ejecutar el método createNewAd(), pasándolo al documento de solicitudes como un parámetro. Una vez que tenga el documento de respuestas, o mejor dicho el parámetro CreateNewAtResponseDocument, puede usarlo para extraer la respuesta propiamente dicha y el atributo de esa respuesta.

Ahora ejecutémoslo.

Ejecución del cliente

Para ejecutar un cliente, Francis primero necesita compilarlo. Ejecute los siguientes pasos (ver Listado 47).

Listado 47. Compilación de cliente
>>set ANT_HOME=e:\apache-ant-1.6.5
>>PATH=%PATH%;%ANT_HOME%\bin;
>>set AXIS2_HOME=e:\axis2
>>cd client
>>ant jar.client
Buildfile: build.xml

init:

pre.compile.test:
     [echo] Stax Availability= true
     [echo] Axis2 Availability= true

compile.src:

compile.test:

jar.client:

BUILD SUCCESSFUL
Total time: 2 seconds

Primero, asegúrese que el entorno tenga las variables apropiadas. (Esto supone que usted ya agregó todos los archivos JAR de AXIS2_HOME\lib). Luego, cambie al directorio de cliente (o cualquier directorio que use los datos de salida para el proceso de generación) y ejecute Ant contra el jar.client de destino. Debería ver resultados similares a los que se muestran en cursiva (ver Listado 47). Para ejecutar el cliente, primero corrija CLASSPATH para que incluya el directorio de los recursos y el directorio que alberga todas las clases creadas por el proceso de enlace de datos (ver Listado 48).

Listado 48. Ejecución del cliente
>>set CLASSPATH=E:\WSDLFiles\client\resources\;E:\WSDLFiles\client\bui
ld\classes\axis2\apache\org\xsd\;%CLASSPATH%
>>cd build\classes
>>java org.dailymoon.classifieds.Client

Debería ver resultados similares a los que se muestran en el Listado 49:

Listado 49. Número de ID del aviso nuevo
Nuevo de ID del aviso nuevo: 1138

Bueno, esto es todo.


Resumen

En la Parte 1 de esta serie, el Departamento de Avisos Clasificados del periódico Daily Moon aprendió cómo usar los servicios web SOAP para conectarse con el Sistema de Gestión de Contenidos del periódico. En este tutorial, el personal aprendió a usar Web Services Description Language (WSDL) para describir sus propios servicios de manera tal que otros pudieran usarlo. El tutorial cubrió los conceptos básicos del esquema XML, sumado a la estructura de un archivo WSDL para que se lo pueda construir manualmente. También describió las diferencias entre los distintos estilos y codificaciones disponibles. El tutorial también explicó cómo usar las herramientas Java2WSDL y WSDL2Java que vienen con Apache Axis2 para generar automáticamente WSDL a partir de archivos Java files y viceversa.

La Parte 3 de esta serie se abocará a analizar la construcción de registros de servicios web usando UDDI.


Descargar

DescripciónNombretamaño
Sample Perl scriptsws-understand-web-services2-WSDL.zip300KB

Recursos

Aprender

Obtener los productos y tecnologías

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=SOA y servicios web
ArticleID=660818
ArticleTitle=Comprender los servicios web, Parte 2: Web Services Description Language (WSDL)
publish-date=08082011