12 octubre, 2013

Contract-First en Spring-WS, Definiendo el contrato

Problema

De acuerdo al enfoque de WS basado en Contract-First, el primer paso de desarrollar un WS es definer el contrato del servicio. ¿Cómo debes hacer eso?

Solucion

Un contrato de WS consiste en dos partes: El contrato de datos y el contrato de servicios. Ambos son definidos con tecnologías XML en una plataforma y lenguaje de forma independiente.

Contrato de datos: Describe la complejidad de los tipos de datos y mensajes de solicitudes y respuestasde del WS. Un contrato de datos es típicamente definido con XSD, pero también puedes usar DTD, RELAX NG, o Schematron.
Contrato de servicios: Describe las operaciones del WS. Un WS puede tener multiples operaciones. Un contrato de servicio es definido con WSDL

Cuando usamos un framework de desarrollo de WS como Spring-WS, el contrato de servicio puede usualmente ser generado automáticamente, pero tú debes crear el contrato de datos por ti mismo.
Para crear el contrato de datos para tu WS, debes comenzar creando el archivo XSD. Dado que hay muchas herramientas XML poderosas disponibles en la comunidad, esto no debería ser muy pesado. Como siempre, muchos desarrolladores prefieren comenzar creando algún mensaje ejemploXML y luego generar el XSD desde este xml. Por supuesto, tu necesitas optimizar manualmente el XSD generado, como también puede que no requiera enteramente ajustarlo, y a veces, tu puedes desear agregar mas restricciones a este.

¿Como funciona?

Creando mensajes XML de ejemplo

Para el servicio del clima, puedes representar la temperatura de una ciudad y fecha particular como en el siguiente mensaje XML:
<TemperaturaInfo ciudad="Santiago" fecha="2013-10-12">
    <min>5</min>
    <max>26</max>
    <promedio>15.5</promedio>
</TemperaturaInfo>
Entonces puedes definir el contrato de datos para el servicio del clima. Supongamos que quieres definir una operación que permita a los clientes consultar las temperaturas de una ciudad en particular, para multiples fechas. Cada solicitud consiste de un elemento de ciudad y multiples elementos de fecha. Debes especificar también el nombre de espacio para esta solicitud para evitar conflictos de nombre con otros documentos XML. Guardemos este mensaje XML como solicitud.xml
<GetTemperaturasRequest 
xmlns="http://spring.codelious.com/clima/schemas"> 
    <ciudad>Santiago</ciudad> 
    <fecha>2013-10-01</fecha> 
    <fecha>2013-10-08</fecha> 
    <fecha>2013-10-15</fecha> 
</GetTemperaturasRequest> 
La respuesta consiste de multiples elementos TemperaturaInfo, donde cada uno representa la temperatura de una ciudad y fecha particular, de acuerdo con las fechas solicitadas. Guardemos el mensaje XML como respuesta.xml
<GetTemperaturasResponse 
xmlns="http://spring.codelious.com/clima/schemas"> 
    <TemperaturaInfo ciudad="Santiago" fecha="2013-10-01"> 
        <min>5.0</min> 
        <max>10.0</max> 
        <promedio>8.0</promedio> 
    </TemperaturaInfo> 
    <TemperaturaInfo ciudad="Santiago" fecha="2013-10-08"> 
        <min>4.0</min> 
        <max>13.0</max> 
        <promedio>7.0</promedio> 
    </TemperaturaInfo> 
    <TemperaturaInfo ciudad="Santiago" fecha="2013-10-15"> 
        <min>10.0</min> 
        <max>18.0</max> 
        <promedio>15.0</promedio> 
    </TemperaturaInfo> 
</GetTemperaturasResponse>

Generando un archivo XSD desde los mensajes XML de ejemplo

Ahora, tu puedes generar el archivo XSD desde los mensajes XML de ejemplos anteriores. Muchas herramientas XML populares y tambien IDEs de Java EE pueden generar un XSD desde un par de archivos XML. Aqui usaremos Apache XMLBeans (http://xmlbeans.apache.org) para generar nuestro archivo XSD.
Apache XMLBeans provee una herramienta llamada inst2xsd que genera archivos XSD desde archivos XML. Este soporta varios tipos de diseños para generar archivos XSD. El mas simple se llama Russian doll design, el cual genera elementos locales y tipos locales para el archivo XSD objetivo. Debido a que no hay un tipo de enumeracion usado en tus mensajes XML, debes tambien deshabilitar la caracteristica de generacion de enumeracion. Puedes ejecutar el siguiente comando para generar el XSD para tu contrato de datos:
inst2xsd -design rd -enumerations never solicitud.xml respuesta.xml
El archivo XSD generado tendra el nombre por defecto schema0.xsd, localizado en el mismo directorio. Renombremoslo a temperatura.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" 
           elementFormDefault="qualified" 
           targetNamespace="http://spring.codelious.com/clima/schemas" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="GetTemperaturasRequest">
    <xs:complexType>
      <xs:sequence>
        <xs:element type="xs:string" name="ciudad"/>
        <xs:element type="xs:date" name="fecha" 
           maxOccurs="unbounded" minOccurs="0"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="GetTemperaturasResponse">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="TemperaturaInfo" 
           maxOccurs="unbounded" minOccurs="0">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:float" name="min"/>
              <xs:element type="xs:float" name="max"/>
              <xs:element type="xs:float" name="promedio"/>
            </xs:sequence>
            <xs:attribute type="xs:string" name="ciudad" use="optional"/>
            <xs:attribute type="xs:date" name="fecha" use="optional"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Optimizando el XSD generado

Como puedes ver, el archivo XSD generado permite a los clientes consultar temperaturas de fechas ilimitadas. Si tu quieres agregar una restriccion sobre el maximo y minimo de consultas de fechas, puedes modificar los atributos maxOccurs y minOccurs.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" 
           elementFormDefault="qualified" 
           targetNamespace="http://spring.codelious.com/clima/schemas" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="GetTemperaturasRequest">
    <xs:complexType>
      <xs:sequence>
        <xs:element type="xs:string" name="ciudad"/>
        <xs:element type="xs:date" name="fecha" 
            maxOccurs="5" minOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="GetTemperaturasResponse">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="TemperaturaInfo" 
            maxOccurs="5" minOccurs="1">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:float" name="min"/>
              <xs:element type="xs:float" name="max"/>
              <xs:element type="xs:float" name="promedio"/>
            </xs:sequence>
            <xs:attribute type="xs:string" name="ciudad" use="optional"/>
            <xs:attribute type="xs:date" name="fecha" use="optional"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Previsualizando el archivo WSDL generado

Como aprenderemos despues, Spring-WS puede automaticamente generar el contrato de servicio para ti, basado en el contrato de datos y algunas convensiones que puedes sobreescribir. Aqui, puedes previsualizar el archivo WSDL generado para entender mejor el contrato de servicio. Para simplificar, las partes menos importantes son omitidas.
<?xml version="1.0" encoding="UTF-8" ?> 
<wsdl:definitions ... 
    targetNamespace="http://spring.codelious.com/clima/schemas"> 
    <wsdl:types> 
        <!-- Copiado desde el archivo XSD --> 
        ... 
    </wsdl:types> 
    <wsdl:message name="GetTemperaturasResponse"> 
        <wsdl:part element="schema:GetTemperaturasResponse" 
            name="GetTemperaturasResponse"> 
        </wsdl:part> 
    </wsdl:message> 
    <wsdl:message name="GetTemperaturasRequest"> 
        <wsdl:part element="schema:GetTemperaturasRequest" 
            name="GetTemperaturasRequest"> 
        </wsdl:part> 
    </wsdl:message> 
    <wsdl:portType name="Clima"> 
        <wsdl:operation name="GetTemperaturas"> 
            <wsdl:input message="schema:GetTemperaturasRequest" 
                name="GetTemperaturasRequest"> 
            </wsdl:input> 
            <wsdl:output message="schema:GetTemperaturasResponse" 
                name="GetTemperaturasResponse"> 
            </wsdl:output> 
        </wsdl:operation> 
    </wsdl:portType> 
    ... 
    <wsdl:service name="ClimaService"> 
        <wsdl:port binding="schema:ClimaBinding" name="ClimaPort"> 
            <soap:address 
                location="http://localhost:8080/clima/services" /> 
        </wsdl:port> 
    </wsdl:service> 
</wsdl:definitions> 
En el portType Clima, una operacion GetTemperaturas es definida cuyo nombre es derivado desde el prefijo de la entrada y salida de mensajes (ejemplo., <GetTemperaturasRequest> y <GetTemperaturasResponse>). Las definiciones de estos dos elementos son incluidas en la parte <wsdl:types>, asi como definidos en el contrato de datos.
Para saber como implementar un Web Services usando Spring-WS y un Contrato generado de la forma explicada aqui, puedes ver el post Spring Web Services Contract First.

No hay comentarios.:

Publicar un comentario