Creating a message processing or output node in Java

Before you start

WebSphere Message Broker provides the source for two sample user-defined nodes called SwitchNode and TransformNode. You can use these nodes in their current state, or you can modify them.

Conceptually, a message processing node is used to process a message in some way, and an output node is used to output a message as a bit stream. However, when you code a message processing node or an output node, they are essentially the same thing. You can perform message processing within an output node, and likewise you can output a message to a bit stream using a message processing node. For simplicity, this topic mainly refers to the node as a message processing node, however, it discusses the functionality of both types of node.

Creating a new Java project

You can create Java nodes from within the workbench. To do this, you must create a new Java project, as follows:
  1. Switch to the Java perspective.
  2. Click File > New > Project. Select Java from the left menu, and then select Java Project from the right menu
  3. Give the project a name.

    The Java Settings panel is displayed.

  4. Select the Libraries tab, and click Add External JARs.
  5. Select install_dir\classes\jplugin2.jar.
  6. Follow the prompts on the other tabs to define any other build settings.
  7. Click Finish.
You can then develop the source for your Java node within this project.

Declaring the message processing node class

Any class that implements MbNodeInterface and is contained in the broker's LIL path is registered with the broker as a message processing node. When you implement MbNodeInterface, you must also implement an evaluate method for this class. The evaluate method is called by the broker for each message that is passed through the flow.

For example, to declare the message processing node class:
package com.ibm.jplugins;

import com.ibm.broker.plugin.*;

public class BasicNode extends MbNode implements MbNodeInterface
You can do this in the workbench as follows:
  1. Click File > New > Class.
  2. Set the package and class name fields to appropriate values.
  3. Delete the text in the Superclass text field and click the Browse button
  4. Select MbNode and click OK.
  5. Click the Add button next to Interfaces text field, and select MbNodeInterface.
  6. Click Finish.

Defining the node constructor

When the node is instantiated, the constructor of the user's node class is called. This is where you create the terminals of the node, and initialize any default values for attributes.

A message processing node has a number of input terminals and output terminals associated with it. The methods createInputTerminal and createOutputTerminal are used to add terminals to a node when the node is instantiated. For example, to create a node with one input terminal and two output terminals:

public MyNode() throws MbException
{
		// create terminals here
		createInputTerminal ("in");
		createOutputTerminal ("out");
		createOutputTerminal ("failure");
}

Accessing message data

In many cases, the user-defined node needs to access the contents of the message received on its input terminal. The message is represented as a tree of syntax elements. Groups of utility functions are provided for use in the evaluate method that are used for message management, message buffer access, syntax element navigation, and syntax element access.

The MbElement class provides the interface to the syntax elements. For further details of the Java API, see the Javadoc.

For example:

  1. To navigate to the relevant syntax element in the XML message:
        MbElement rootElement = assembly.getMessage().getRootElement();
        MbElement switchElement = 
    			rootElement.getLastChild().getFirstChild().getFirstChild();
  2. To select the terminal indicated by the value of this element:
        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);

Transforming a message object

The received input message is read-only, so before a message can be transformed, you must write it to a new output message. You can copy elements from the input message, or you can create new elements in the output message. New elements are generally in a parser's domain.

The MbMessage class provides the copy constructors, and methods to get the root element of the message. The MbElement class provides the interface to the syntax elements.

For example, where you have an incoming message assembly with embedded messages you could have the following code in the evaluate method of your user-defined node:
  1. To create a new copy of the message assembly and its embedded messages:
        MbMessage newMsg = new MbMessage(assembly.getMessage());
        MbMessageAssembly newAssembly = new MbMessageAssembly(assembly, newMsg);
  2. To navigate to the relevant syntax element in the XML message:
        MbElement rootElement = newAssembly.getMessage().getRootElement();
        MbElement switchElement = 
    			rootElement.getFirstElementByPath("/XML/data/action");
  3. To change the value of an existing element:
      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. To add a new tag as a child of the switch tag:
        MbElement tag = switchElement.createElementAsLastChild(MbElement.TYPE_NAME,
                                                               "PreviousValue",
                                                               elementValue);
  5. To add an attribute to this new tag:
        tag.createElementAsFirstChild(MbElement.TYPE_NAME_VALUE,
                                      "NewValue",
                                      switchElement.getValue());
    
        MbOutputTerminal out = getOutputTerminal("out");
As part of the transformation it might be necessary to create a new message body. To create a new message body, the following methods are available:
createElementAfter(String) 
createElementAsFirstChild(String) 
createElementAsLastChild(String) 
createElementBefore(String) 
createElementAsLastChildFromBitstream(byte[], String, String, String, String, int, int, int) 
These methods should be used because they are specific for assigning a parser to a message tree folder.
When creating a message body, do not use the following methods because they do not associate an owning parser with the folder:
createElementAfter(int)
createElementAfter(int, String, Object) 
createElementAsFirstChild(int) 
createElementAsFirstChild(int, String, Object) 
createElementAsLastChild(int) 
createElementAsLastChild(int, String, Object) 
createElementBefore(int) 
createElementBefore(int, String, Object) 

Propagating the message

Before you propagate a message, you have to decide what message flow data you want to propagate, and which of the node's terminals is to receive the data.

For example:
  1. To propagate the message to the output terminal "out":
    MbOutputTerminal out = getOutputTerminal("out");
            out.propagate(newAssembly);

To clear the memory that is allocated for the message tree, call the clearMessage() function within the final try/catch block.

Declaring the node name

You need to declare the name of the node as it will be identified by the workbench. All node names must end with "Node". You declare the name using the following method:

public static String getNodeName()
{
   return "BasicNode";
}
If this method is not declared, the Java API framework creates a default node name using the following rules:
  • The class name is appended to the package name.
  • The dots are removed, and the first letter of each part of the package and class name are capitalized.
For example, by default, the following class is assigned the node name "ComIbmPluginsamplesBasicNode":
package com.ibm.pluginsamples;
public class BasicNode extends MbNode implements MbNodeInterface
{
   ...

Declaring attributes

You declare node attributes in the same way as Java Bean properties. You are responsible for writing getter and setter methods for the attributes, and the API framework infers the attribute names using the Java Bean introspection rules. For example, if you declare the following two methods:

private String attributeVariable;

public String getFirstAttribute()
{
  return attributeVariable;
}

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

The broker infers that this node has an attribute called firstAttribute. This name is derived from the names of the get or set methods, not from any internal class member variable names. Attributes can only be exposed as strings, so you must convert any numeric types to and from strings in the get or set methods. For example, the following method defines an attribute called timeInSeconds:

int seconds;

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

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

Implementing the node functionality

As described earlier, for message processing or output nodes, you must implement the evaluate method, defined in MbNodeInterface. This is called by the broker to process the message. This method should provide all the processing function for the node.

The evaluate method has two parameters that are passed in by the broker:
  1. The MbMessageAssembly, which contains the following objects that are accessed using the appropriate methods:
    • The incoming message
    • The local environment
    • The global environment
    • The exception list
  2. The input terminal on which the message has arrived.
For example, the following code extract shows how the evaluate method could be written:
public void evaluate(MbMessageAssembly assembly, MbInputTerminal inTerm) throws MbException
  {
    // add message processing code here

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

The message flow data, which consists of the message, global environment, local environment, and exception list, is received at the input terminal of the node.

Deleting an instance of the node

An instance of the node is deleted when either:
  • You shutdown the broker.
  • You remove the node or the message flow containing the node, and redeploy the configuration.
During node deletion, the node might want to be informed so that it can perform any cleanup operations, such as closing sockets. If the node implements the optional onDelete method, this is called by the broker just before the node is deleted.

You implement the onDelete method as follows:

public void onDelete()
{
  // perform node cleanup if necessary
}
Related reference
Exception list structure
Related information
Java user-defined node API