Creación de un nodo de proceso de mensajes o un nodo de salida en Java

Antes de empezar

zWebSphere Business Integration Message Broker proporciona el origen para dos nodos de ejemplo definidos por el usuario, llamados SwitchNode y TransformNode. Puede utilizar estos nodos en su estado actual, o puede modificarlos.

Conceptualmente, un nodo de proceso de mensajes se utiliza para procesar un mensaje de una forma concreta, y un nodo de salida se utiliza para producir un mensaje como corriente de bits. No obstante, al codificar un nodo de proceso de mensajes o un nodo de salida, ambos son, esencialmente, el mismo nodo. Puede procesar mensajes en un nodo de salida y, del mismo modo, puede producir un mensaje en una corriente de bits utilizando un nodo de proceso de mensajes. Para que resulte más fácil, en este tema se identifica al nodo, principalmente, con el nodo de proceso de mensajes; no obstante, se describe la funcionalidad de ambos tipos de nodo.

No desarrolle nodos Java en z/OS que tenga previsto difundir en un intermediario de una plataforma distribuida, ya que el nivel de Java en z/OS puede no generar código que sea compatible con el nivel de Java de la plataforma distribuida.

Acceso a los datos de mensaje

En muchos casos, el nodo definido por el usuario necesita acceder al contenido del mensaje recibido en su terminal de entrada. El mensaje se representa como un árbol de elementos de sintaxis. Se proporcionan grupos de funciones de programas de utilidad para la gestión de mensajes, el acceso al almacenamiento intermedio de mensajes, la navegación por los elementos de sintaxis y el acceso a los elementos de sintaxis.

La clase MbElement proporciona la interfaz de los elementos de sintaxis. Si desea más información sobre la API Java, consulte el documento Java.

Por ejemplo:

  1. Para ir al elemento de sintaxis relevante en el mensaje XML:
        MbElement rootElement = assembly.getMessage().getRootElement();
        MbElement switchElement = 
    			rootElement.getLastChild().getFirstChild().getFirstChild();
  2. Para seleccionar el terminal indicado por el valor de este elemento:
        String terminalName;
        String elementValue = (String)switchElement.getValue();
        if(elementValue.equals("add"))
          terminalName = "add";
        else if(elementValue.equals("change"))
          terminalName = "change";
        else if(elementValue.equals("delete"))
          terminalName = "delete";
        else if(elementValue.equals("hold"))
          terminalName = "hold";
        else
          terminalName = "failure";
        
        MbOutputTerminal out = getOutputTerminal(terminalName);

Transformación de un objeto de mensaje

El mensaje de entrada recibido es de sólo lectura, por lo tanto, antes de transformar un mensaje debe escribirlo en un nuevo mensaje de salida. Puede copiar elementos desde el mensaje de entrada, o puede crear nuevos elementos y adjuntarlos al mensaje. Generalmente, los nuevos elementos se encuentran en un dominio de analizador.

La clase MbMessage proporciona los constructores de copia y los métodos para obtener el elemento raíz del mensaje. La clase MbElement proporciona la interfaz de los elementos de sintaxis.

Por ejemplo, si tiene un conjunto de mensajes entrantes con mensajes intercalados:
  1. Cree una nueva copia del conjunto de mensajes y de los mensajes intercalados:
        MbMessage newMsg = new MbMessage(assembly.getMessage());
        MbMessageAssembly newAssembly = new MbMessageAssembly(assembly, newMsg);
  2. Vaya al elemento de sintaxis relevante en el mensaje XML:
        MbElement rootElement = newAssembly.getMessage().getRootElement();
        MbElement switchElement = 
    			rootElement.getFirstElementByPath("/XML/data/action");
  3. Cambie el valor de un elemento existente:
      String elementValue = (String)switchElement.getValue();
        if(elementValue.equals("add"))
          switchElement.setValue("change");
        else if(elementValue.equals("change"))
          switchElement.setValue("delete");
        else if(elementValue.equals("delete"))
          switchElement.setValue("hold");
        else
          switchElement.setValue("failure");
  4. Añada un nuevo código como elemento dependiente del código de conmutación:
        MbElement tag = switchElement.createElementAsLastChild(MbElement.TYPE_NAME,
                                                               "PreviousValue",
                                                               elementValue);
  5. Añada un atributo a este nuevo código:
        tag.createElementAsFirstChild(MbElement.TYPE_NAME_VALUE,
                                      "NewValue",
                                      switchElement.getValue());
    
        MbOutputTerminal out = getOutputTerminal("out");

Acceso a ESQL

Los nodos pueden invocar expresiones ESQL utilizando la sintaxis ESQL del nodo Compute. Puede crear y modificar los componentes del mensaje utilizando expresiones ESQL, y puede hacer referencia a elementos del mensaje de entrada y de datos de una base de datos externa.

El procedimiento siguiente demuestra cómo controlar transacciones en el nodo definido por el usuario utilizando ESQL:
  1. Establezca el nombre del origen de datos ODBC que va a utilizar. Por ejemplo:
    String dataSourceName = "myDataSource";
  2. Establezca la sentencia ESQL que va a ejecutar:
    String statement = 
       "SET OutputRoot.XML.data = 
              (SELECT Field2 FROM Database.Table1 WHERE Field1 = 1);";
    O, si desea ejecutar una sentencia que no devuelva ningún resultado:
    String statement = "PASSTHRU(
                            'INSERT INTO Database.Table1 VALUES(
                                 InputRoot.XML.DataField1,
                                 InputRoot.XML.DataField2)');";
  3. Seleccione el tipo de transacción que desee de entre las siguientes:
    MbSQLStatement.SQL_TRANSACTION_COMMIT
    Difundir la transacción inmediatamente después de ejecutar la sentencia ESQL.
    MbSQLStatement.SQL_TRANSACTION_AUTO
    Difundir la transacción cuando el flujo de mensajes se haya completado (se realizan restituciones si es necesario).
    Por ejemplo:
    int transactionType = MbSQLStatement.SQL_TRANSACTION_AUTO;
  4. Obtenga la sentencia ESQL. Por ejemplo:
    MbSQLStatement sql = 
           createSQLStatement(dataSourceName, statement, transactionType);
    Puede utilizar el método createSQLStatement(dataSource, statement) para que el valor por omisión del tipo de transacción sea MbSQLStatement.SQL_TRANSACTION_AUTO).
  5. Cree el nuevo conjunto de mensajes que vaya a propagar:
    MbMessageAssembly newAssembly = 
           new MbMessageAssembly(assembly, assembly.getMessage());
  6. Ejecute la sentencia ESQL:
    sql.select(assembly, newAssembly);
    O, si desea ejecutar una sentencia ESQL que no devuelva ningún resultado:
    sql.execute(assembly);

Para obtener más información sobre ESQL, consulte el apartado ESQL.

Manejo de excepciones

La clase mbException se utiliza para recibir excepciones y acceder a ellas. Esta clase devuelve una matriz de objetos de excepción que representan los elementos dependientes de una excepción en la lista de excepciones del intermediario. En cada elemento devuelto se especifica el tipo de excepción al que pertenece. Si una excepción no tiene elementos dependientes, se devuelve una matriz vacía. El ejemplo de código siguiente muestra un ejemplo de la utilización de la clase MbException.

public void evaluate(MbMessageAssembly assembly, MbInputTerminal inTerm) throws MbException
  {
    try
      {

        // funcionalidad de plug-in

      }
    catch(MbException ex)
      {
        traverse(ex, 0);

        throw ex; // si vuelve a emitirse, debe ser la excepción original recibida
      }
  }

  void traverse(MbException ex, int level)
  {
    if(ex != null)
      {
        // Realizar aquí la acción necesaria
        System.out.println("Level: " + level);
        System.out.println(ex.toString());
        System.out.println("traceText:  " + ex.getTraceText());

        // atravesar la jerarquía
        MbException e[] = ex.getNestedExceptions();
        int size = e.length;
        for(int i = 0; i < size; i++)
          {
            traverse(e[i], level + 1);
          }
      }
  }

Consulte el documento Java si desea ver más información detallada sobre cómo utilizar la clase mbException.

Puede desarrollar un nodo de proceso de mensajes o un nodo de salida definido por el usuario de manera que ambos puedan acceder a todas las excepciones actuales. Por ejemplo, para recibir excepciones de base de datos puede utilizar la clase MbSQLStatement. Esta clase establece el valor del atributo 'throwExceptionOnDatabaseError', que determina el comportamiento del intermediario cuando encuentra un error de base de datos. Si el atributo se establece en true, la extensión definida por el usuario podrá recibir y manejar una excepción que se haya emitido.

El ejemplo de código siguiente muestra un ejemplo de cómo utilizar la clase MbSQLStatement.

public void evaluate(MbMessageAssembly assembly, MbInputTerminal inTerm) throws MbException
  {
    MbMessage newMsg = new MbMessage(assembly.getMessage());
    MbMessageAssembly newAssembly = new MbMessageAssembly(assembly, newMsg);

    String table = 
       assembly.getMessage().getRootElement().getLastChild().getFirstChild().getName();

    MbSQLStatement state = createSQLStatement( "dbName", 
       "SET OutputRoot.XML.integer[] = PASSTHRU('SELECT * FROM " + table + "');" );

    state.setThrowExceptionOnDatabaseError(false);
    state.setTreatWarningsAsErrors(true);

    state.select( assembly, newAssembly );

    int sqlCode = state.getSQLCode();
    if(sqlCode != 0)
      {
        // Realizar aquí el manejo de errores

        System.out.println("sqlCode = " + sqlCode);
        System.out.println("sqlNativeError = " + state.getSQLNativeError());
        System.out.println("sqlState = " + state.getSQLState());
        System.out.println("sqlErrorText = " + state.getSQLErrorText());
      }

    getOutputTerminal("out").propagate(assembly);
  }

Propagación del mensaje

Antes de propagar un mensaje, debe decidir qué datos de flujo de mensajes desea propagar, y cuál de los terminales del nodo va a recibir los datos. Debe finalizar el mensaje antes de propagarlo. Después de propagar un mensaje, debe suprimir el mensaje de salida.

Por ejemplo:
  1. Para propagar el mensaje al terminal de salida "out":
    MbOutputTerminal out = getOutputTerminal("out");
            out.propagate(newAssembly);
  2. Para suprimir el mensaje de salida:
      newMsg.clearMessage();	

Escritura en un dispositivo de salida

Para escribir en un dispositivo de salida, el mensaje lógico (jerárquico) debe volver a convertirse en una corriente de bits. Para hacerlo, debe utilizar el método getBuffer en la clase MbMessage, como se indica a continuación:

public void evaluate( MbMessageAssembly assembly, MbInputTerminal in)
                                                     throws MbException
{
  MbMessage msg = assembly.getMessage();
  byte[] bitstream = msg.getBuffer();

  // escribir la corriente de bits en algún lugar
  writeBitstream( bitstream );   // método de usuario

 }

Normalmente, para un nodo de salida el mensaje no se propaga a ningún terminal de salida, de modo que puede regresar simplemente a este punto.

Nota: al escribir en colas de WebSphere MQ, debe utilizar el nodo MQOutput suministrado, ya que el intermediario mantiene internamente una conexión WebSphere MQ y manejadores de cola abiertos hebra por hebra, que se ponen en antememoria para optimizar el rendimiento. Además, el intermediario maneja escenarios de recuperación cuando se producen ciertos sucesos de WebSphere MQ, y esto podría tener efectos adversos si se utilizaran llamadas de WebSphere MQ MQI en un nodo de salida definido por el usuario.

Creación de un nuevo proyecto Java

Puede crear nodos Java desde el área de trabajo, utilizando el entorno de desarrollo de plug-ins (PDE) proporcionado. Para hacerlo, debe crear un nuevo proyecto Java como se indica a continuación:
  1. Cambie a la Perspectiva Desarrollo de plug-in.
  2. Pulse Archivo > Nuevo > Proyecto. Seleccione Java en el menú de la izquierda y, a continuación, seleccione Proyecto Java en el menú de la derecha.
  3. Elija un nombre para el proyecto.

    Se muestra el panel de configuración de Java.

  4. Seleccione el separador Bibliotecas, y pulse Añadir JAR externos.
  5. Seleccione dir_instalación\classes\jplugin.jar.
  6. Siga las indicaciones de los demás separadores para definir otros valores de creación.
  7. Pulse Finalizar.
Cuando haya completado todos los pasos, podrá desarrollar el origen para el nodo Java en este proyecto.

Declaración de la clase de nodo de proceso de mensajes

Una clase que implemente la interfaz MbNodeInterface y se encuentre en la vía de acceso de clase del intermediario (o vía de acceso LIL) se registra con el intermediario como nodo de proceso de mensajes. Si implementa la interfaz MbNodeInterface, debe implementar también un método evaluate para esta clase. El intermediario invoca el método evaluate para cada mensaje que pasa a través del flujo.

Por ejemplo, para declarar la clase de nodo de proceso de mensajes:
package com.ibm.jplugins;

import com.ibm.broker.plugin.*;

public class BasicNode extends MbNode implements MbNodeInterface
Puede hacer esto en el área de trabajo como se indica a continuación:
  1. Pulse Archivo > Nuevo > Clase.
  2. Establezca los campos de nombre de clase y de paquete en los valores adecuados.
  3. Suprima el texto que haya en el campo de texto Superclase y pulse el botón Examinar.
  4. Seleccione MbNode.
  5. Pulse el botón Agregar, situado junto al campo de texto Interfaces, y seleccione MbNodeInterface.
  6. Pulse Finalizar.

Definición del constructor de nodos

Al crearse instancias del nodo, se invoca el constructor de la clase de nodo del usuario. Aquí es donde se crean los terminales del nodo, y donde se inicializan los valores por omisión para los atributos.

Un nodo de proceso de mensajes está asociado a varios terminales de entrada y de salida. Los métodos createInputTerminal y createOutputTerminal se utilizan para añadir terminales a un nodo cuando se crean instancias para dicho nodo. Por ejemplo, para crear un nodo con un terminal de entrada y dos terminales de salida:

public MyNode() throws MbException
{
		// crear terminales aquí
		createInputTerminal ("in");
		createOutputTerminal ("out");
		createOutputTerminal ("failure");
}

Declaración del nombre de nodo

Es necesario declarar el nombre del nodo para que el área de trabajo lo identifique. Todos los nombres de nodo deben terminar por la palabra "Node". El nombre se declara utilizando el método siguiente:

public static String getNodeName()
{
   return "BasicNode";
}
Si no se declara este método, la infraestructura API Java crea un nombre de nodo por omisión utilizando las normas siguientes:
  • El nombre de clase se añade al nombre de paquete.
  • Los puntos se eliminan, y la primera letra de cada parte del nombre de clase y de paquete se escribe en mayúscula.
Por ejemplo, se asigna, por omisión, a la clase siguiente el nombre de nodo "ComIbmPluginsamplesBasicNode":
package com.ibm.pluginsamples;
public class BasicNode extends MbNode implements MbNodeInterface
{
   ...

Declaración de atributos

Los atributos de nodo deben declararse del mismo modo que las propiedades de Java Bean. El usuario es responsable de escribir los métodos get y set para los atributos, y la infraestructura de la API infiere los nombres de atributo utilizando las normas de introspección de Java Bean. Por ejemplo, si declara los dos métodos siguientes:

private String attributeVariable;

public String getFirstAttribute()
{
  return attributeVariable;
}

publc void setFirstAttribute(String value)
{
  attributeVariable = value;
}

El intermediario infiere que este nodo tiene un atributo llamado firstAttribute. Este nombre se deriva de los nombres de los métodos get y set, no de los nombres de variable de miembro de clase internos. Los atributos sólo pueden exponerse como series de caracteres, así que debe convertir los tipos numéricos a series de caracteres de los métodos get o set, o convertirlos a partir de dichas series de caracteres. Por ejemplo, el método siguiente define un atributo llamado timeInSeconds:

int seconds;

public String getTimeInSeconds()
{
  return Integer.toString(seconds);
}

public void setTimeInSeconds(String value)
{
  seconds = Integer.parseInt(value);
}

Implementación de la funcionalidad del nodo

Como se ha descrito anteriormente, para los nodos de proceso de mensajes o de salida, debe implementar el método evaluate, definido en la interfaz MbNodeInterface. El intermediario invoca este método para procesar el mensaje. Este método deberá proporcionar todas las funciones de proceso para el nodo.

El método evaluate tiene dos parámetros que pasa el intermediario:
  1. El parámetro MbMessageAssembly, que contiene los objetos siguientes a los que se accede utilizando los métodos apropiados:
    • El mensaje entrante
    • El entorno local
    • El entorno global
    • La lista de excepciones
  2. El terminal de entrada en el que ha llegado el mensaje.

Los datos de flujo de mensajes, es decir, el mensaje, el entorno global, el entorno local y la lista de excepciones se reciben en el terminal de entrada del nodo.

Supresión de una instancia del nodo

Una instancia del nodo se suprime en uno de estos casos:
  • Si cierra el intermediario
  • Si elimina el nodo o el flujo de mensajes que contiene el nodo, y vuelve a difundir la configuración
Es aconsejable informar al nodo acerca de su supresión mientras ésta se lleva a cabo, a fin de que pueda realizar operaciones de borrado, como el cierre de sockets. Si el nodo implementa el método onDelete opcional, el intermediario invocará dicho método justo antes de que se suprima el nodo.

El método onDelete debe implementarse como se indica a continuación:

public void onDelete()
{
  // realizar borrado de nodo si es necesario
}

Conceptos relacionados
Planificación de extensiones definidas por el usuario
Extensiones definidas por el usuario en el entorno de ejecución
Diseño de extensiones definidas por el usuario
Nodos de proceso de mensajes definidos por el usuario
Nodos de salida definidos por el usuario
ESQL

Tareas relacionadas
Desarrollo de extensiones definidas por el usuario
Implementación de los ejemplos proporcionados
Compilación de un nodo definido por el usuario en Java

Referencia relacionada
API de nodo Java definida por el usuario
Estructura de la lista de excepciones