Skip to main content

Using MQ Headers to Dynamically Determine Data Handler Behavior

Ensuring a flexible SOA integration

Philip Norton (nortonp@uk.ibm.com), Software Engineer, WebSphere ESB Development Team, IBM
Philip Norton
Philip Norton is a software engineer at IBM Hursley Lab. He works on the WebSphere ESB Development team. His expertise includes Java and JMS for WebSphere MQ. He is a certified Java Programmer and he has a degree in Computer Science from Canterbury University. You can reach Philip at nortonp@uk.ibm.com
Alex Wood (wooda@uk.im.com), Software Engineer, Websphere ESB Development Team, IBM
Alex Wood
Alex Wood works as a software developer on the WebSphere Business Integration suite of products based at IBM Hursley Lab in England. He has extensive experience in development on many of the WebSphere products. Including WebSphere MQ, Websphere Message Broker, WebSphere Enterprise Service Bus (WESB) and WebSphere Process Server (WPS). He received a BSc in Physics with Astrophysics from Birmingham University in UK in 1998.
Fenglian Xu (xufengli@uk.ibm.com), Software Engineer, WebSphere ESB Development Team, IBM
Fenglian Xu
Dr. Fenglian Xu works as a software developer in the WebSphere Enterprise Service Bus development team at IBM Hursley Lab in the UK. Her expertise includes the JMS and Web services over JMS bindings in WebSphere Enterprise Service Bus and WebSphere Application Server. She has worked in various IT companies from middleware to application for 10 years. She has gained her main expertise in Java, J2EE, and SOA. She obtained her PhD degree in computer science from the University of Southampton in 1998.

Summary:  The format of the data in an MQ message can differ and is often determined by the format field in the MQ header, in this case the Data Handler must behave dynamically based on the values stored in this header.

Date:  02 Apr 2009
Level:  Intermediate PDF:  A4 and Letter (190KB | 15 pages)Get Adobe® Reader®
Activity:  4255 views

Introduction

This article is aimed at integration developers whose role involves integrating MQ messaging engines using Service Component Architecture (SCA). It describes how to use a Data Handler to change the transformation logic based on an MQ header, while ensuring the handler remains protocol neutral. Topics that will be covered are:

  • Explanation of a Data Handler
  • Accessing MQ headers from a Data Handler
  • Altering the behavior of my Data Handler based on a header
  • Ensuring the Data Handler remains protocol neutral

After reading this article you will be able to author reusable data handlers that dynamically transform data based on a MQ header.


Data Handlers

A Data Handler is responsible for transforming the input and output types of an operation to the format used on the wire and visa versa. Previously the MQ binding used Data Bindings to perform this function. A Data Binding would have access to the wire format i.e. MQ message, and could read or write directly to or from the message respectively. This connected each Data Binding to a particular binding type e.g. a MQ Data Binding could not be used on a JMS binding.

Data Handlers provide a generic way of transforming data by using canonical formats. The transform method is used for reading data and one of the following types is passed to read the input:

  • InputStream - for reading bytes
  • Reader - for reading text
  • Object - for reading Java Objects

The transformInto method is used for writing data and one of the following types is passed in to write the output:

  • OutputStream - for writing bytes
  • Writer - for writing text
  • Object - for writing Java Objects

This makes the wire format of the message transparent to the Data Handler allowing them to be reused by any of the supported bindings.

Data Handlers and Message Headers

When authoring a Data Binding the message headers were passed into the read or write method, allowing transformation logic to be determined by a field in the protocol header. However, for Data Handlers the method input parameters do not include the protocol headers, so how do we access them?

In WebSphere Enterprise Service Bus (WESB) V6.2, protocol headers are stored in a repository called ContextService. The ContextService is accessed using a common SPI and can be referenced from any Java code, including POJOs, Function Selectors, or Data Handlers. Example code for obtaining the MQHeader from the ContextService is shown in Listing 1.


Listing 1. Accessing Protocol Headers using Context Service
HeadersType headers = ContextService.INSTANCE.getHeaders();
MQHeaderType mqHeader = headers.getMQHeader();		
		


Using the MQHeader in a Data Handler

Now we know how to access the MQ Header in the Data Handler lets make a transformation decision, based on one of the fields. The format field of the MQMD is often used to determine the message body format. The format, encoding and coded character set of the message body are stored in the MQControl structure, contained in the MQ Header. The code shown in Listing 2 demonstrates how to modify the transformation logic depending on the value of the format field in the MQMD.


Listing 2. Changing Behaviour based on MQMD format field
package com.ibm.custom.datahandlers;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

import com.ibm.websphere.bo.BOFactory;
import com.ibm.websphere.bo.BOXMLDocument;
import com.ibm.websphere.bo.BOXMLSerializer;
import com.ibm.websphere.sca.ServiceManager;
import com.ibm.websphere.sca.mq.structures.MQControl;
import com.ibm.websphere.sibx.smobo.HeadersType;
import com.ibm.websphere.sibx.smobo.MQHeaderType;
import com.ibm.wsspi.session.ContextService;
import commonj.connector.runtime.DataHandler;
import commonj.connector.runtime.DataHandlerException;
import commonj.sdo.DataObject;

@SuppressWarnings("serial")
public class MQHeaderDataHandler implements DataHandler {

	Map bindingContext;
	String delimiter = "%";

	/**
	 * Reads a DataObject from an InputStream (Bytes support only)
	 */
	public Object transform(Object source, Class target, Object options) 
			        throws DataHandlerException {
		System.out.println("MyDataHandler: Calling 
			transform(" + source + "," + target + "," + options + ")");

		DataObject output = null;
		InputStream is = (InputStream) source;
		
		//Handle InputStreams and DataObjects
		if (source instanceof InputStream && 
			target.getName().equals("commonj.sdo.DataObject")) {
                if (("MQXML").equals(getMQMessageFormat())) {
				//If the format field of the incoming message 
                            is XML use BOXMLSerializer
				ServiceManager serviceMgr = new ServiceManager();
				BOXMLSerializer xmlSerializer = 
                        (BOXMLSerializer) serviceMgr
						.locateService
                        ("com/ibm/websphere/bo/BOXMLSerializer");
				try {
					BOXMLDocument xmlDoc = xmlSerializer.
                        readXMLDocumentWithOptions
                            (is , options);
					output = xmlDoc.getDataObject();
					((InputStream)source).close();
				} catch (IOException e) {
					throw new 
						DataHandlerException
                        ("Exception reading DataObject " 
                        + e.getLocalizedMessage());
				}
			} else {
				//Read delimited data
				byte[] ch = new byte[2];
				StringBuffer buffer = new StringBuffer();
				 
				try {
					int len = is.read(ch);
					while (len != -1) {
						buffer.append(new String(ch), 0, len);
						len = is.read(ch);
					}
				} catch (IOException e) {
					throw new DataHandlerException(e);
				}
				//Split the values using the delimiter
				String[] values = buffer.toString().split(delimiter);
				System.out.println("Found name: "+values[0]+" and id: "
                        +values[1]);
				//Build the CustomerType
				ServiceManager serviceManager = new ServiceManager();
                        BOFactory bofactory = 
						(BOFactory) serviceManager.locateService
                                ("com/ibm/websphere/bo/BOFactory");
				output =
					bofactory.create("http://
                        MQHeaderDataHandlerModule", "CustomerType");
				output.setString("name", values[0]);
				output.setInt("id", Integer.parseInt(values[1]));
			}
		} else {
			throw new 
				DataHandlerException("Source type " + 
                    source.getClass().getName() + " or Target type "
					+ target.getName() + " unsupported by 
                                            MyDataHandler");
		}
		return output;
	}

	/**
	 * Writes the DataObject out to a OutputStream (Bytes support only)
	 */
	public void transformInto(Object source, Object target, Object options)
						throws DataHandlerException {
		System.out.println("Calling 
			transformInto(" + source + "," + target + "," + options + ")");

		if (source instanceof DataObject && target instanceof OutputStream) {
			OutputStream os = (OutputStream) target;
			DataObject bo = (DataObject) source;
			
			if (("MQXML").equals(getMQMessageFormat())) {
				//If the format field of the outgoing message is XML use 
                            BOXMLSerializer
				ServiceManager serviceMgr = new ServiceManager();
				BOXMLSerializer xmlSerializer = (BOXMLSerializer) 
                      serviceMgr.locateService("com/ibm/websphere/
                                               bo/BOXMLSerializer");
								
				try {
					xmlSerializer.writeDataObject(bo, bo.getType().
                             getURI(), bo.getType().getName(), os);
				} catch (IOException e) {
					throw new 
						DataHandlerException("Exception writing 
                           DataObject " + e.getLocalizedMessage());
				}
			} else {
				//Write delimited data				
				String name = bo.getString("name");
				int id = bo.getInt("id");				
				String message = name+delimiter+id;
				
				try {
					os.write(message.getBytes());
				} catch (IOException e) {
					throw new 
						DataHandlerException("Exception writing 
                        DataObject " + e.getLocalizedMessage());
				}
			}			
		} else {
		
			throw new 
				DataHandlerException("Source type " + source.getClass()
                    .getName() + " or Target type "
					+ target.getClass().getName() + 
                    " unsupported by MyDataHandler");
		}
	}
		
	/**
	 * Returns the format of the MQHeader or null if there is no MQHeader
	 * @return
	 */
	private String getMQMessageFormat() {
		String format = null;
		
		HeadersType headers = ContextService.INSTANCE.getHeaders();
		
		MQHeaderType mqHeader = null;   
	    if(headers!= null){
	    	mqHeader = headers.getMQHeader();
	    	if (mqHeader != null) {
	    		MQControl mqControl = mqHeader.getControl();
	    		if (mqControl != null) {
	    			format = mqControl.getFormat();
	    			if (format != null) {
	    				format = format.trim();
	    			}
	    		}
	    	}
	    }
		
		return format;
	}
	
	public void setBindingContext(Map context) {
		bindingContext = context;
	}

}
		

This handler uses the MQHeader format field and if it is set to MQXML the message body is treated as XML, otherwise the body will be treated as delimited data. If this Data Handler was used with a non MQ binding then the MQHeader will not be defined in the ContextService. In this case the getMQMessageFormat() method will return null, resulting in the body being treated as delimited data, therefore the Data Handler remains protocol neutral.


Testing the Data Handler

First of all lets create a simple module with an MQ binding. It is assumed that WebSphere MQ has been installed and is accessible within WebSphere ESB.

  1. In WID create a new module called MQHeaderDataHandlerModule
  2. In the MQHeaderDataHandlerModule project create a new Data Type called CustomerType

Figure 1. CustomerType Business Object
CustomerType
  1. Add a String field called name and an int field called id
  2. Save and close the new data type
  3. Create a new Interface called CustomerInterface
  4. Add a one way operation called processCustomer that accepts CustomerType as the input parameter

Figure 2. Customer Interface
CustomerInterface
  1. Add a new export to the assembly diagram
  2. Add the CustomerInterface to the export
  3. On the export configure an MQ binding
    • Set the request queue manager name
    • Set the receive destination
    • Set the connection details appropriately for your WebSphere MQ Queue Manager
    • For the default request data format, click select
    • Select Select your custom data format transformation from the workspace and click select
    • Choose the MQHeaderDataHandler and click ok
    • Tick the Add custom class to binding registry and click next (this will make it available in the existing data format list)
    • Enter the name MQHeaderDataHandler
    • Add a description
    • Select the type of bindings it applies to, for now tick MQ and JMS

Figure 3. Setting Data Format
Setting Data Format
  1. On the import configure an MQ binding with the same data format as above
  2. To ensure the DataObject is being created correctly we will print it out using a Java component, so add one to the canvas
  3. Wire the Java component to the export and the import, your module should now look like Figure 4

Figure 4. MQHeaderDataHandlerModule
MQHeaderDataHandlerModule
  1. Double click on the Java component to create an implementation
  2. Replace the implementation of processCustomer with the code from Listing 3

Listing 3. Printing Data Object
BOFactory factoryService = (BOFactory) new 
	ServiceManager().locateService("com/ibm/websphere/bo/BOFactory");
BOXMLSerializer xmlSerializerService =(BOXMLSerializer) new 
	ServiceManager().locateService("com/ibm/websphere/bo/BOXMLSerializer");
try {
	xmlSerializerService.writeDataObject(input1, input1.getType().getURI(), 
	input1.getType().getName(), System.out);
} catch (IOException e) {
	e.printStackTrace();
}

locateService_CustomerInterfacePartner().processCustomer(input1);
		

  1. Save the Java component
  2. Save the module

Running the Module

To test the module we will use RFHUtil which allows us to create MQ messages and put them to the exports receive destination. We can then use the server log view in WID to see the DataObject printed by the Java component and use RFHUtil again to view the message output by the import.

  1. Create a file called CustomerType.xmlin the MQHeaderDataHandlerModule project
  2. Set the contents to the XML shown in Listing 4

Listing 4. CustomerType XML Data
		
<?xml version="1.0" encoding="UTF-8"?>
<p:CustomerType xmlns:p="http://MQHeaderDataHandlerModule" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://MQHeaderDataHandlerModule CustomerType.xsd ">
<name>Tim</name>
<id>114923</id>
</p:CustomerType>
		

  1. Create a file called CustomerType.txt in the MQHeaderDataHandlerModule project
  2. Set the contents to the XML shown in Listing 5

Listing 5. CustomerType Delimited Data
		
Tim%114923
		

  1. Deploy your module to the server
  2. Start RFHUtil, and set the queue manager and the queue used by the export
  3. Use Read File to read in CustomerType.txt, you should be able to see the message body under the data tab
  4. Press the WriteQ button to send the message to the queue
  5. Check the Server Logs view in WebSphere Integration Developer for the print out of the DataObject
  6. Back in RFHUtil Change the queue to that used by the import and click ReadQ
  7. Flick to the Data tab to see the message body which should look like Figure 5

Figure 5. Output Delimited Message
Delimited Message
  1. Flick back to the Main tab in RFHUtil
  2. Use Read File to read in CustomerType.xml, you should be able to see the message body under the data tab
  3. Go to the MQMD tab and set the message format to MQXML

Figure 6. Setting MQXML message format
MQXML message format
  1. Back on the Main tab press WriteQ to send the message
  2. Change the queue to that used by the import and click ReadQ
  3. Flick to the Data tab to see the message body which should look like Figure 7

Figure 7. Output XML message
XML Message

Conclusion

Congratulations, you have successfully created and tested a protocol neutral Data Handler whose behavior is controlled by a field in the MQ Header. We can see that if the format field is set to MQXML then the message is treated as XML. If there is no MQ Header, then the data would also be treated as delimited. Try using this data handler with another binding and check the behavior.

In this article we concentrated on using the MQ format field to change the data handler logic, we could also have used fields in any of the other headers, such as JMS Header or HTTP Header if dealing with these protocols. It is worth noting that the headers are only available to the Data Handler if Propagate protocol header is enabled, which it is by default. To view the setting check the Propagation tab in the binding properties panel.



Download

DescriptionNameSizeDownload method
Project Interchange for MQHeaderDataHandlerModuleMQHeaderDataHandlerModulePI.zip14KB FTP

Information about download methods


Resources

About the authors

Philip Norton

Philip Norton is a software engineer at IBM Hursley Lab. He works on the WebSphere ESB Development team. His expertise includes Java and JMS for WebSphere MQ. He is a certified Java Programmer and he has a degree in Computer Science from Canterbury University. You can reach Philip at nortonp@uk.ibm.com

Alex Wood

Alex Wood works as a software developer on the WebSphere Business Integration suite of products based at IBM Hursley Lab in England. He has extensive experience in development on many of the WebSphere products. Including WebSphere MQ, Websphere Message Broker, WebSphere Enterprise Service Bus (WESB) and WebSphere Process Server (WPS). He received a BSc in Physics with Astrophysics from Birmingham University in UK in 1998.

Fenglian Xu

Dr. Fenglian Xu works as a software developer in the WebSphere Enterprise Service Bus development team at IBM Hursley Lab in the UK. Her expertise includes the JMS and Web services over JMS bindings in WebSphere Enterprise Service Bus and WebSphere Application Server. She has worked in various IT companies from middleware to application for 10 years. She has gained her main expertise in Java, J2EE, and SOA. She obtained her PhD degree in computer science from the University of Southampton in 1998.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services, WebSphere
ArticleID=379749
ArticleTitle=Using MQ Headers to Dynamically Determine Data Handler Behavior
publish-date=04022009
author1-email=nortonp@uk.ibm.com
author1-email-cc=
author2-email=wooda@uk.im.com
author2-email-cc=
author3-email=xufengli@uk.ibm.com
author3-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers