Creación de un nodo de entrada en Java

Antes de empezar

WebSphere 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.

Un nodo Java definido por el usuario se distribuye como un archivo .jar. En este tema se describen los pasos necesarios para crear un nodo de entrada utilizando Java. En él se describen los pasos siguientes:
  1. Creación de un nuevo proyecto Java
  2. Declaración de la clase de nodo de entrada
  3. Definición del constructor de nodos
  4. Declaración del nombre de nodo
  5. Declaración de atributos
  6. Implementación de la funcionalidad del nodo
  7. Supresión de una instancia del 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.

Recepción de datos externos en un almacenamiento intermedio

Un nodo de entrada puede recibir datos procedentes de cualquier tipo de origen externo, como un sistema de archivos, una cola o una base de datos, del mismo modo que cualquier otro programa Java, siempre que la salida del nodo tenga el formato correcto.

El usuario proporciona un almacenamiento intermedio de entrada (o corriente de bits) para contener datos de entrada y asociarlos con un objeto de mensaje. Asimismo, crea un mensaje a partir de una matriz de bytes utilizando el método createMessage de la clase MbInputNode y, a continuación, genera un conjunto de mensajes válido a partir de este mensaje (consulte el documento Java para obtener información detallada acerca de estos métodos). Por ejemplo, para leer la entrada desde un archivo:

  1. Cree una corriente de entrada para leer desde el archivo:
    FileInputStream inputStream = new FileInputStream("myfile.msg");
  2. Cree una matriz de bytes que tenga el tamaño del archivo de entrada:
    byte[] buffer = new byte[inputStream.available()];
  3. Lea desde el archivo en la matriz de bytes:
    inputStream.read(buffer);
  4. Cierre la corriente de entrada:
    inputStream.close();
  5. Cree un mensaje para transferirlo a la cola:
    MbMessage msg = createMessage(buffer);
  6. Cree un nuevo conjunto de mensajes para retener este mensaje:
    msg.finalizeMessage(MbMessage.FINALIZE_VALIDATE);
    MbMessageAssembly newAssembly =
         new MbMessageAssembly(assembly, msg);

Propagación del mensaje

Cuando haya creado un conjunto de mensajes, podrá propagarlo a uno de los terminales del nodo.

Por ejemplo, para propagar el conjunto de mensajes a la terminal "out" (de salida):
MbOutputTerminal out = getOutputTerminal("out");
out.propagate(newAssembly);

Control del trabajo con hebras y el comportamiento transaccional

La infraestructura de intermediario maneja cuestiones transaccionales, como el control de la confirmación de cualquier unidad de trabajo de WebSphere MQ o de base de datos cuando el proceso de mensajes se ha completado. No obstante, si se utiliza un nodo definido por el usuario, el intermediario no podrá confirmar automáticamente las actualizaciones de recursos.

Cada hebra de flujo de mensajes se asigna desde una agrupación de hebras mantenida para cada flujo de mensajes, y empieza a ejecutarse en el método run.

El nodo definido por el usuario utiliza valores de retorno para indicar si una transacción se ha procesado correctamente, para controlar si las transacciones se confirman o restituyen, y para verificar el momento en que la hebra se devuelve a la agrupación. Las excepciones que no se hayan manejado las recibe la infraestructura de intermediario, y la transacción se restituye.

El usuario determina el comportamiento de las transacciones y las hebras utilizando un valor de retorno adecuado de entre los siguientes:

MbInputNode.SUCCESS_CONTINUE
La transacción se confirma, y el intermediario vuelve a invocar el método run utilizando la misma hebra.
MbInputNode.SUCCESS_RETURN
La transacción se confirma, y la hebra se devuelve a la agrupación de hebras, suponiendo que no sea la única hebra para este flujo de mensajes.
MbInputNode.FAILURE_CONTINUE
La transacción se restituye, y el intermediario vuelve a invocar el método run utilizando la misma hebra.
MbInputNode.FAILURE_RETURN
La transacción se restituye, y la hebra se devuelve a la agrupación de hebras, suponiendo que no sea la única hebra para este flujo de mensajes.
MbInputNode.TIMEOUT
El método run no debe bloquear el flujo indefinidamente mientras espera que lleguen los datos de entrada. Mientras el flujo está bloqueado por código de usuario, no es posible cerrar o reconfigurar el intermediario. El método run debe ceder el control al intermediario de forma periódica regresando del método run. Si después de transcurrido un período de tiempo (por ejemplo, cinco segundos) no se han recibido datos de entrada, el método deberá mostrar el código de retorno TIMEOUT. Presuponiendo que el intermediario no deba reconfigurarse ni cerrarse, vuelve a invocarse inmediatamente el método run del nodo de entrada.
Para crear flujos de mensajes de múltiples hebras, es necesario invocar el método dispatchThread después de haber creado un mensaje, pero antes de propagar dicho mensaje a un terminal de salida. Esto garantiza que sólo haya una hebra esperando los datos, mientras otras hebras están procesando el mensaje. Pueden obtenerse nuevas hebras de la agrupación de hebras hasta el límite máximo especificado mediante el atributo additionalInstances del flujo de mensajes. Por ejemplo:
public int run( MbMessageAssembly assembly ) throws MbException
{
  byte[] data = getDataWithTimeout();  // método proporcionado por el usuario
                                       // devuelve nulo si se excede el tiempo de espera
  if( data == null )
    return TIMEOUT;

  MbMessage msg = createMessage( data );
  msg.finalizeMessage( MbMessage.FINALIZE_VALIDATE );
  MbMessageAssembly newAssembly =
       new MbMessageAssembly( assembly, msg );

  dispatchThread();

  getOutputTerminal( "out" ).propagate( newAssembly );

  return SUCCESS_RETURN;
}

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);
  }

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 entrada

Una clase que implemente MbInputNodeInterface y esté incluida en la vía de acceso de clase del intermediario (o vía de acceso a la LIL), se registra con el intermediario como nodo de entrada. Al implementar MbInputNodeInterface, debe implementar también un método run para esta clase. El método run representa el inicio del flujo de mensajes, contiene los datos que formulan el mensaje y propaga el mensaje hacia abajo en el flujo de mensajes. El intermediario invoca el método run cuando las hebras se hacen disponibles, en función del modelo de trabajo con hebras especificado.

Por ejemplo, para declarar la clase de nodo de entrada:

package com.ibm.jplugins;

import com.ibm.broker.plugin.*;

public class BasicInputNode extends MbInputNode implements MbInputNodeInterface
{
...
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 MbInputNode.
  5. Pulse el botón Agregar, situado junto al campo de texto Interfaces, y seleccione MbInputNodeInterface.
  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 entrada está asociado a varios terminales de salida, pero normalmente no tiene ningún terminal de entrada. Utilice el método createOutputTerminal para añadir terminales de salida a un nodo cuando se creen instancias para dicho nodo. Por ejemplo, para crear un nodo con tres terminales de salida:

public BasicInputNode() throws MbException
{
	createOutputTerminal ("out");
	createOutputTerminal ("failure");
	createOutputTerminal ("catch");
   setAttribute ("firstParserClassName","myParser");
   attributeVariable  = new String ("none");
}

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 "BasicInputNode";
}
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 "ComIbmPluginsamplesBasicInputNode":
package com.ibm.pluginsamples;
public class BasicInputNode extends MbInputNode implements MbInputNodeInterface
{
   ...

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, el intermediario invoca el método run para crear el mensaje de entrada. Este método deberá proporcionar todas las funciones de proceso para el nodo de entrada.

Alteración temporal de los atributos de analizador de mensajes por omisión (opcional)

Normalmente, la implementación de un nodo de entrada determina qué analizador de mensajes analiza inicialmente un mensaje de entrada. Por ejemplo, el nodo MQInput primitivo dicta que es necesario un analizador MQMD para analizar la cabecera MQMD. Un nodo de entrada definido por el usuario puede seleccionar una cabecera o un analizador de mensajes adecuado, y la modalidad en que se controlará el análisis, utilizando los atributos siguientes que se incluyen por omisión, pero que pueden alterarse temporalmente:

rootParserClassName
Define el nombre del analizador de raíz que analiza los formatos de mensaje a los que da soporte el nodo de entrada definido por el usuario. Este atributo toma por omisión GenericRoot, un analizador de raíz suministrado que hace que el intermediario asigne analizadores y los encadene juntos. Es improbable que un nodo necesite modificar este valor de atributo.
firstParserClassName
Define el nombre del primer analizador, en lo que puede ser una cadena de analizadores que son responsables de analizar la corriente de bits. Este atributo toma por omisión XML.
messageDomainProperty
Atributo opcional que define el nombre del analizador de mensajes necesario para analizar el mensaje de entrada. Los valores a los que se da soporte son los mismos que soporta el nodo MQInput (consulte el apartado Nodo MQInput si desea ver más información acerca del nodo MQInput).
messageSetProperty
Atributo opcional que define el identificador de conjunto de mensajes, o el nombre de conjunto de mensajes, en el campo Message Set, sólo si se ha especificado el analizador MRM mediante el atributo messageDomainProperty.
messageTypeProperty
Atributo opcional que define el identificador del mensaje en el campo MessageType, sólo si se ha especificado el analizador MRM mediante el atributo messageDomainProperty.
messageFormatProperty
Atributo opcional que define el formato del mensaje en el campo Message Format, sólo si se ha especificado el analizador MRM mediante el atributo messageDomainProperty.

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 entrada definidos por el usuario

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
Nodo MQInput
Estructura de la lista de excepciones
API de nodo Java definida por el usuario