Interoperabilidad con Patrones de Intercambio de Mensajes creados con BEA WebLogic 8.1.3

Publicado: 7 de Abril de 2006 | Actualizado: 2 de Julio de 2007

Jesús Rodríguez
Enero 2005

Se aplica a:
 Microsoft .NET Framework 1.1
 Microsoft Visual Studio .NET 2003
 BEA WebLogic 8.1.3

Resumen: Este es el primer artículo de la serie WSIG (Web Services Interoperability Guidance) que analiza la interoperabilidad mediante patrones de intercambio de mensajes (message exchange patterns). Este documento analiza la definición de un patrón para un servicio Web conversacional, muestra cómo se pueden implementar con BEA WebLogic 8.1.3, e investiga el desarrollo de un cliente con .NET Framework que pueda llamar a estos servicios (13 páginas impresas).

En esta página
Interoperabilidad de servicios WebInteroperabilidad de servicios Web
Patrones de Intercambio de MensajesPatrones de Intercambio de Mensajes
BEA WebLogic 8.1.3 y los Patrones de Intercambio de Mensajes (conversaciones)BEA WebLogic 8.1.3 y los Patrones de Intercambio de Mensajes (conversaciones)
Uso de CallbacksUso de Callbacks
Tiempo de vida de la conversaciónTiempo de vida de la conversación
Implementación en BEA WebLogic 8.1.3Implementación en BEA WebLogic 8.1.3
Interoperabilidad con clientes .NETInteroperabilidad con clientes .NET
Implementación de las CallbacksImplementación de las Callbacks
ConclusiónConclusión

Interoperabilidad de servicios Web

La interoperabilidad entre Microsoft .NET Framework y BEA WebLogic 8.1.3 suele ser uno de los asuntos más tenidos en cuenta por los desarrolladores que escriben aplicaciones que se ejecutan en ambas plataformas.

El uso de servicios Web se está convirtiendo en una de las formas más populares de conseguir la interoperabilidad entre ambas plataformas. EN algunos escenarios, sin embarco, incluso con servicios Web basados en estándares como SOAP, WSDL, UDDI, y WS-*, el conseguir una interoperabilidad real puede seguir siendo un reto. Entre los problemas a resolver aparecen escenarios complejos, como el mapeo de tipos de datos y compatibilidad a nivel de protocolo.

Además, los fabricantes (como Microsoft, BEA, IBM y Sun) pueden tener implementaciones que difieren ligeramente entre sí, basadas en esos estándares. Estas implementaciones hacen un uso pleno de la plataforma, pero habrá que tenerlas en cuenta en las situaciones donde se necesita interoperabilidad.

Este artículo analiza uno de los aspectos más avanzados de la interoperabilidad entre Microsoft .NET y BEA WebLogic 8.1.3, usando un patrón de intercambio de mensajes que BEA denomina "Conversational Web services". No trato de analizar la interoperabilidad del Perfil Básico ni establecer recomendaciones entre ambos. Al contrario, le recomiendo la lectura del artículo Guía de Interoperabilidad de Servicios Web (WSIG): BEA WebLogic 8.1 SP3 (8.1.3) de Simon Guest.

Principio de la páginaPrincipio de la página

Patrones de Intercambio de Mensajes

Los servicios Web pueden implementar diversos patrones de intercambio de mensajes. Algunos de los más frecuentes son Petición-Respuesta (Request/Response) y Unidireccíonal (One-Way). En este artículo se considera que una conversación es un patrón de intercambio de mensajes equivalente a una conversación humana: por ejemplo, una serie de operaciones que identifican semánticamente un orden bien definido puede agruparse dentro de una conversación.

El diseño de servicios Web es tal que múltiples clientes pueden llamar a un único punto final. Por medio de este patrón, el desarrollador puede garantizar la conservación del estado entre llamadas, ofrecer un medio para hacer seguimiento de los datos entre llamadas y garantizar que el servicio Web responde al cliente correcto.

Principio de la páginaPrincipio de la página

BEA WebLogic 8.1.3 y los Patrones de Intercambio de Mensajes (conversaciones)

BEA WebLogic 8.1.3 identifica una conversación utilizando conversationID, que normalmente es una cadena de caracteres. WebLogic utiliza este identificador para correlacionar los siguientes intercambios de mensajes en el tiempo de vida de un servicio Web concreto. Cuando un cliente inicia una conversación con un servicio Web específico, WebLogic genera automáticamente un contexto para mantener el estado de la conversación.

Los servicios Web que implementan conversaciones mantienen por definición el estado en el contexto de las conversaciones. Por ello, las conversaciones equivalen a servicios Web con mantenimiento de estado. Dicho esto, no todas las operaciones de un servicio Web que mantienen el estado representan una conversación, pero esta capacidad es un requisito previo para un servicio Web que implemente conversaciones.

Aparte, una conversación debe exigir un orden bien definido en la operación de un servicio Web. Las operaciones que integran una conversación también definen su estado. Una operación puede iniciar, continuar o finalizar una conversación. Cuando se inicia una conversación se genera un contexto para mantener el estado. Cuando finaliza, el contexto se destruye y todo el estado relativo a la misma se elimina.

Principio de la páginaPrincipio de la página

Uso de Callbacks

Las Callbacks son el principal mecanismo utilizado por BEA WebLogic 8.1.3 para enviar notificaciones desde un servicio Web a otro punto final. La implementación de callback de WebLogic exige que los receptores potenciales de las notificaciones puedan entender mensajes SOAP.

El uso de callbacks, no obstante, tiene ciertas consecuencias sobre el rendimiento. Cuando un servicio Web necesita enviar múltiples callbacks, tiene que esperar a que se procese el mensaje en el lado del cliente antes de enviar la siguiente callback. En muchas situaciones esa solución es poco práctica debido a los problemas de rendimiento que supone. Para evitarlo, WebLogic ofrece una solución interesante a este problema: los buffers de mensajes.

Los buffers de mensajes se pueden aplicar a métodos y callbacks de servicios Web, de forma que se puede añadir un buffer de mensaje a cualquier método o llamada, partiendo de que el método o los parámetros de callback son serializables y el tipo de dato retornado es void. Cuando un servicio Web envía una callback, el mensaje se guarda en un buffer y el servicio ya no tiene que esperar a que el cliente procese la callback.

Si se añade un buffer de mensaje a una callback de un servicio Web, los mensajes salientes (invocaciones de la callback) se encolan en la máquina local y la invocación a callback retorna de inmediato. Con esto evitamos que el servicio deba esperar mientras el mensaje se envía al servidor remoto y se recibe la respuesta (void). En resumen, el servicio Web no tiene que esperar a que se produzcan todos los eventos de red. Si se añade un buffer de mensaje a una callback de un control Java, el buffer pasa a ser implementado por el servicio Web. Se puede indicar si se reintentará la invocación del buffer en caso de error, y en qué momento.

La infraestructura provista por WebLogic para soportar callbacks es sencilla, pero muy útil. De cara al futuro creo que esta solución puede sustituirse con implementaciones de algunas de las especificaciones venideras de gestión de eventos y notificación (WS-Eventing, WS-BaseNotification, WS-BrokeredNotification, y WS-Topics).

Principio de la páginaPrincipio de la página

Tiempo de vida de la conversación

El tiempo de vida es un aspecto muy importante en las conversaciones. Como parte de su definición, una conversación puede incluir una serie de elementos de control que gestionen sus estados. WebLogic ofrece una serie de atributos que se pueden utilizar para controlar el tiempo de vida de la conversación:

Maximum Idle Time—Indica el tiempo que puede transcurrir entre los mensajes entrantes antes de que la conversación del servicio Web termine automáticamente.

Maximum Age—Indica el tiempo máximo de vida de una instancia de conversación antes de su finalización.

El patrón de conversación es, sin duda, un medio extremadamente potente para aumentar la capacidad de las aplicaciones que utilizan servicios Web. En las siguientes secciones analizaremos cómo desarrollar servicios Web que implementan este patrón en BEA WebLogic 8.1.3, y cómo desarrollar clientes en Microsoft .NET que pueden interactuar con estos servicios Web.

Principio de la páginaPrincipio de la página

Implementación en BEA WebLogic 8.1.3

En este artículo utilizaremos un ejemplo que muestra un servicio Web que realiza operaciones matemáticas sobre un número dado. Este servicio Web implementa las siguientes operaciones:

SetSeed—Acepta un entero como parámetro, para iniciar la conversación. Se asigna al número una propiedad llamada Seed.

Add—Aumenta el valor de Seed con un número recibido como parámetro. Este método sigue la conversación identificada por correlationID.

Sub—Resta de Seed un número, recibido como parámetro. Este método continúa la conversación identificada por correlationID.

ExceedValue—Es una callback que aparece cuando el valor de seed es mayor que 10. Este método continúa la conversación identificada por correlationID.

GetResult—Retorna el valor de Seed y finaliza la conversación.

El valor de Seed representa semánticamente el estado del servicio Web. Este estado debe mantenerse entre llamadas sucesivas Add y Sub, demostrando así que se trata de una verdadera conversación.

La conversación implementada por este servicios Web se puede representar como una serie de pasos. Se crea cuando un cliente invoca la operación SetSeed en el servicio Web. En ese momento, el cliente tiene que pasar una cadena que identifica de forma unívoca la conversación. Para clientes nativos, WebLogic genera de forma automática ese identificador.

Después el cliente deberá realizar llamadas sucesivas a las operaciones Add y Sub del servicio Web usando el identificador correlationID para hacer referencia a la conversación establecida. Si el cliente intenta invocar una operación Add con un correlationID no válido, el servicio Web devuelve SoapFault.

Finalmente, el cliente invoca la operación GetResult para obtener el valor final del número calculado y terminar así la conversación. Para iniciar otra conversación, el cliente debe volver a invocar la operación SetSeed. En la Figura 1 se muestra el diseño del servicio Web en BEA WebLogic WorkShop IDE.

Figura 1. Vista de diseño del servicio Web de cálculo matemático

Figura 1. Vista de diseño del servicio Web de cálculo matemático

Figura 1. Vista de diseño del servicio Web de cálculo matemático

WebLogic utiliza una serie de anotaciones para identificar operaciones de servicio Web y su papel en la conversación. Por ejemplo, la operación Add podría tener este aspecto:

/**
     * @common:operation
     * @jws:conversation phase="continue"
     * @common:message-buffer enable="true"
     */
    public void Add(int value)
    {
        FSeed= FSeed + value;
        if(FSeed > 10)
          callback.ExceedValue(FSeed);
    }

Como puede verse, la anotación @jws:conversation phase identifica el papel de la operación en el estado de la conversación. Los posibles valores de este atributo son: start, continue, finish, y callback.

Cuando se compila este método y se pone en explotación en el servidor, el WSDL producido por WebLogic es muy grande e incluye tres enlaces (Soap, HttpGet, HttpPost) y tres servicios. Dentro de este WSDL las declaraciones de la cabecera son parecidas a esta:

<s:schema elementFormDefault="qualified" 
targetNamespace="http://www.openuri.org/2002/04/soap/conversation/">
  <s:element name="StartHeader" type="conv:StartHeader" /> 
  <s:element name="ContinueHeader" type="conv:ContinueHeader" /> 
  <s:element name="CallbackHeader" type="conv:CallbackHeader" /> 
 <s:complexType name="StartHeader">
 <s:sequence>
  <s:element minOccurs="0" maxOccurs="1" name="conversationID" type="s:string" /> 
  <s:element minOccurs="0" maxOccurs="1" name="callbackLocation" type="s:string" /> 
  </s:sequence>
  </s:complexType>
 <s:complexType name="ContinueHeader">
 <s:sequence>
  <s:element minOccurs="1" maxOccurs="1" name="conversationID" type="s:string" /> 
  </s:sequence>
  </s:complexType>
 <s:complexType name="CallbackHeader">
 <s:sequence>
  <s:element minOccurs="1" maxOccurs="1" name="conversationID" type="s:string" /> 
  </s:sequence>
  </s:complexType>
  </s:schema>

Esta sección declara cabeceras que se pasarán en los mensajes SOAP para mantener la conversación con el servicio Web. El conversationID identifica de forma unívoca una conversación. Estas cabecera debe aparecer siempre que el cliente invoque cualquier operación incluida en la conversación. CallbackLocation identifica el URI al cual el servicio Web envía sus mensajes de callback. Esta cabecera debe aparecer siempre que el cliente invoque una operación que de comienzo a una conversación.

La sección de enlace del servicio basado en SOAP contendrá un código similar a este:

<binding name="MathServiceSoap" type="s0:MathServiceSoap">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> 
 <operation name="SetSeed">
  <soap:operation soapAction="http://www.openuri.org/SetSeed" 
style="document" /> 
  <cw:transition phase="start" /> 
 <input>
  <soap:body use="literal" /> 
  <soap:header xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
wsdl:required="true" message="s0:StartHeader_literal" part="StartHeader"
use="literal" /> 
  </input>
 <output>
  <soap:body use="literal" /> 
  </output>
  </operation>
 <operation name="GetResult">
  <soap:operation soapAction="http://www.openuri.org/GetResult" 
style="document" /> 
  <cw:transition phase="finish" /> 
 <input>
  <soap:body use="literal" /> 
  <soap:header xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
wsdl:required="true" message="s0:ContinueHeader_literal" 
part="ContinueHeader" use="literal" /> 
  </input>
 <output>
  <soap:body use="literal" /> 
  </output>
  </operation>
 <operation name="Add">
  <soap:operation soapAction="http://www.openuri.org/Add" style="document" /> 
  <cw:transition phase="continue" /> 
 <input>
  <soap:body use="literal" /> 
  <soap:header xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
wsdl:required="true" message="s0:ContinueHeader_literal" 
part="ContinueHeader" use="literal" /> 
  </input>
 <output>
  <soap:body use="literal" /> 
  </output>
  </operation>
 <operation name="Sub">
  <soap:operation soapAction="http://www.openuri.org/Sub" style="document" /> 
  <cw:transition phase="continue" /> 
 <input>
  <soap:body use="literal" /> 
  <soap:header xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
wsdl:required="true" message="s0:ContinueHeader_literal" 
part="ContinueHeader" use="literal" /> 
  </input>
 <output>
  <soap:body use="literal" /> 
  </output>
  </operation>
 <operation name="ExceedValue">
  <soap:operation soapAction="http://www.openuri.org/ExceedValue" style="document" /> 
 <input>
  <soap:body use="literal" /> 
  </input>
 <output>
  <soap:body use="literal" /> 
  </output>
  </operation>
  </binding>

Esta sección muestra la cabecera ya declarada con las operaciones del servicio.

El asunto más importante de cara a mantener una conversación con servicios Web basados en WebLogic es la manipulación de las cabeceras que se pasan como parte de los mensajes SOAP. Cuando se desarrollan clientes nativos, el soporte de la conversación lo gestionan automáticamente las librerías de cliente de WebLogic: las cabeceras necesarias para la conversación se generan de forma automática y son gestionadas por el propio entorno.

No obstante, esto no es igual con clientes no nativos. En la siguiente sección investigaremos cómo desarrollar clientes basados en .NET Framework capaces de mantener una conversación con un servicio Web basado en BEA WebLogic.

Principio de la páginaPrincipio de la página

Interoperabilidad con clientes .NET

Para participar en una conversación con un servicio Web basado en WebLogic, un cliente no WebLogic tiene que disponer de un conjunto de cabeceras que identifiquen cada estado posible de la conversación.

Siguiendo con nuestro ejemplo matemático, para iniciar una conversación con el servicio Web el cliente necesita enviar la siguiente petición SOAP:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <StartHeader xmlns="http://www.openuri.org/2002/04/soap/conversation/">
      <conversationID>123456</conversationID>
      <callbackLocation>http://localhost/MyServices/Service1.asmx</callbackLocation>
      <callbackLocation />
    </StartHeader>
  </soap:Header>
  <soap:Body>
    <SetSeed xmlns="http://www.openuri.org/">
      <seed>12</seed>
    </SetSeed>
  </soap:Body>
</soap:Envelope>

La sección StartHeader indica que la operación invocada debe comenzar una conversación identificada con los elementos conversationID. El elemento callbackLocation representa el URI hacia el cual los servicios Web enviarán los mensajes callback generados en esta conversación.

Cuando utilizamos wsdl.exe para generar un proxy de servicio Web, la cabecera se transforma de esta manera:

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/", IsNullable=false)]
public class ContinueHeader : System.Web.Services.Protocols.SoapHeader {
    
    /// <remarks/>
    public string conversationID;
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/", IsNullable=false)]
public class StartHeader : System.Web.Services.Protocols.SoapHeader {
    
    /// <remarks/>
    public string conversationID;
    
    /// <remarks/>
    public string callbackLocation;
}

Ahora tendremos que realizar algunas modificaciones sobre el código de cliente tradicional para poder participar en una conversación con un servicio Web WebLogic. Antes de invocar las operaciones de inicio, tenemos que inicializar el campo StartHeader en el proxy generado por el servicio Web. Para el conversationID, generaremos un nuevo GUID.

MathService proxy= new MathService();
proxy.StartHeaderValue= new StartHeader();
proxy.StartHeaderValue.conversationID= Guid.NewGuid().ToString();
proxy.SetSeed(6);

El paso siguiente consiste en incluir toda la lógica necesaria para invocar las operaciones continue en el proxy del servicio Web. En el ejemplo de código siguiente, nos creamos una cabecera ContinueHeader con el mismo conversationID de StartHeader.

[System.Web.Services.Protocols.SoapHeaderAttribute("ContinueHeaderValue")]    
[System.Web.Services.Protocols.SoapDocumentMethodAttribute
("http://www.openuri.org/Add", RequestNamespace="http://www.openuri.org/", 
ResponseNamespace="http://www.openuri.org/", 
Use=System.Web.Services.Description.SoapBindingUse.Literal, 
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public void Add(int value) 
   {
       if(ContinueHeaderValue == null)
   {
     ContinueHeaderValue= new ContinueHeader();
     ContinueHeaderValue.conversationID= StartHeaderValue.conversationID;
   }
        this.Invoke("Add", new object[] {value});
    }

Poniendo estos dos elementos juntos ya podemos desarrollar una conversación como esta:

MathService proxy= new MathService();
//start the conversation...
proxy.StartHeaderValue= new StartHeader();
proxy.StartHeaderValue.conversationID= Guid.NewGuid().ToString();
//continue with the conversation...
proxy.SetSeed(6);
proxy.Add(2);
proxy.Sub(1);
//finish the conversation...
int Result= proxy.GetResult();
Principio de la páginaPrincipio de la página

Implementación de las Callbacks

En la sección anterior hemos visto cómo desarrollar un cliente .NET que interactúa con un servicio Web en una comunicación unidireccional (el cliente invoca operaciones dentro de la conversación y el servicio Web realiza dichas operaciones).

En la primera parte del artículo explicaba que las callbacks son una de las funciones más importantes de las conversaciones. ¿Cómo podemos desarrollar un servicio Web basado en .NET que reciba los eventos que surgen en un servicio Web de WebLogic?. Es muy sencillo: una callback es precisamente una llamada a una operación en el servicio Web de destino. El servicio Web de WebLogic envía una petición SOAP a una dirección bien definida e invoca una operación identificada con un nombre y une espacio de nombres.

En el siguiente ejemplo se muestra el código de un servicio Web que puede funcionar como receptor de callbacks para nuestro caso:

[WebService(Namespace="http://www.openuri.org/")]
   public class Service1 : System.Web.Services.WebService
   {
      public Service1()
      {
         InitializeComponent();
      }

      [WebMethod]
      public void ExceedValue(int CurrentValue)
      {
         TextWriter strm= File.AppendText("d:\\temp\\wlout.txt");
         strm.WriteLine(CurrentValue.ToString());
         strm.Close();         
      }      
   }

El atributo de espacio de nombres coincide con la petición SOAP entrante y con el nombre de la operación. Por supuesto, podemos utilizar también el atributo WebMethod para personalizar el nombre de la operación:

[WebMethod(MessageName= "ExceedValue")]
public void ReceiveCallback(int CurrentValue)
{
  TextWriter strm= File.AppendText("d:\\temp\\wlout.txt");
  strm.WriteLine(CurrentValue.ToString());
  strm.Close();         
}

Finalmente, dentro del código del cliente, ponemos callbacklocation como parte de StartHeader:

proxy.StartHeaderValue.callbackLocation=    
"http://localhost/TargetService/Service1.asmx";

Con estas funcionalidades podemos ver lo fácil que resulta crear clientes .NET que pueden interactuar con servicios Web conversacionales ejecutándose sobre BEA WebLogic 8.1.3.

Principio de la páginaPrincipio de la página

Conclusión

Este artículo ha demostrado que trabajando con cabeceras SOAP y atributos de serialización es posible desarrollar un cliente .NET que consume servicios Web conversacionales de forma sencilla.

Quisiera agradecer a Simon Guest por su ayuda durante la redacción de este artículo. El ha contribuido en una gran cantidad de ediciones y aportado valiosos comentarios y respuestas a mis preguntas.

Acerca del autor

Jesús Rodriguez es un participante activo en las comunidades J2EE y .NET. Participa de forma regular en los grupos de noticias de Microsoft y escribe artículos analizando interesantes tecnologías .NET y J2EE. Se puede contactar con Jesús a través de su blog, en Jesus Rodriguez's WebLog.


Principio de la páginaPrincipio de la página