IBM WebSphere Developer Technical Journal: Building a powerful, reliable SOA with JMS and WebSphere ESB -- Part 3

The WebSphere ESB mediation

The Java™ Message Service (JMS) standardizes reliable messaging on the J2EE™ platform. The recently released IBM® WebSphere® Enterprise Service Bus (ESB) product offers functionality that is at the core of any environment supporting a service-oriented architecture. This conclusion to a three-part series on integrating JMS messaging with WebSphere ESB describes how to build a JMS custom binding to a mediation flow component in WebSphere ESB.

Share:

Chuan Feng Li (lichuanf@cn.ibm.com), Software Engineer, IBM

Chuan Feng Li photoChuan Feng Li is a Software Engineer working at the SOA Design Center in IBM's China Software Development Lab. He has several years of work experience in the IT industry, and is strongly interested in software development and application. Currently, he is engaged in development work relevant to ESB and SCA.



Xin Peng Liu, Software Engineer, IBM

Xin Peng LiuXin Peng Liu is a Staff Software Engineer working at the SOA Design Center in IBM’s China Software Development Lab. Upon Joining IBM, he has been experienced with SOA-IF, SOA policy, SOA governance, and enterprise cloud-enabled SOA solution engineering. His interests include J2EE, SOA, MDA/MDD, AOP, Semantic Web, Cloud Computing, and more. He is currently performing design and development work related to WebSphere CloudBurst-based SOA solution enablement and a customer technical pilot.



Andre Tost, Senior Technical Staff Member, IBM

Andre TostAndre Tost works as a Senior Technical Staff Member in the Software Group's Enterprise Integration Solutions organization, where he helps IBM's customers establishing Service-Oriented Architectures. His special focus is on Web services technology. Before his current assignment, he spent ten years in various partner enablement, development and architecture roles in IBM software development, most recently for the WebSphere Business Development group. Originally from Germany, he now lives and works in Rochester, Minnesota. In his spare time, he likes to spend time with his family and play and watch soccer whenever possible.



19 April 2006

Also available in Chinese

From the IBM WebSphere Developer Technical Journal.

Introduction

In Part 1, we introduced the concept of the Enterprise Service Bus (ESB), and in Part 2, we showed how to set up the test application client and provider. Now, in the conclusion of this three-part series on connecting service providers and consumers through JMS and IBM WebSphere ESB, we are ready to look at the mediation flow component with custom JMS bindings to finally make the connection.

If you recall from Part 1, we talked about the programming model that forms the basis for WebSphere ESB, namely the Service Component Architecture (SCA). In this architecture, different types of components are all viewed as services and can be accessed the same way. A WebSphere ESB mediation flow is one of those component types. Like any SCA component, you access a mediation flow through exports that it provides, and the mediation flow forwards messages to other external services via imports. Special kinds of imports and exports for JMS, called JMS bindings, enable users to specify the binding configuration and write data handling code themselves. The mediation flow itself consists of a series of mediation primitives that manipulate messages as they flow through the bus.

Two other developerWorks articles, Getting started with WebSphere Enterprise Service Bus and WebSphere Integration Developer and Developing custom mediations for WebSphere Enterprise Service Bus will take you through the basic scenario of creating a mediation module. It would be a good idea to read (or at least browse) through these articles before continuing with this one.

In a nutshell, this final article will:

  • Show you how to write data handling plug-ins to the JMS binding that handle each of the JMS message types.
  • Explain how to set up an export and an import that take advantage of the custom binding code.
  • Describe the implementation of a simple mediation flow using WebSphere Integration Developer.
  • Pull all of the pieces together in a mediation module, which you can deploy to the WebSphere ESB runtime, and which will enable you to run the entire application.

A complete Project Interchange ZIP file, which you can import into WebSphere Integration Developer and includes all of the code and other artifacts needed to run the example, is included in the Download section. To save time, and to enable us to focus on the topic at hand, download and import this file before continuing. The remainder of this article assumes you have done this. When describing the individual pieces of the solution, we will indicate where in WebSphere Integration Developer each will reside after the download file has been imported.


JMS custom binding

When creating a JMS binding for an import or export, you need to identify two Java classes that will handle incoming and outgoing messages. One class is called the function selector, and the other one is called data binding. We also need to define the names of the JMS queues that will be used for inbound and outbound JMS traffic. Let's look at these pieces in more detail.

Function selector

WebSphere ESB mediations, like all SCA service components, have an interface. In WebSphere ESB, the interface is expressed as a WSDL PortType, which means that each mediation flow component supports one or more operations (or methods; both terms mean the same in this context). However, a JMS message contains only data and carries no indication about the target operation for which it is intended. Therefore, we need to map a certain JMS message to a certain operation of the targeted service interface. This is what the function selector does.

In our example, we have defined a service interface with one operation for each JMS message type (find this in JMSCustomBindingLibrary/JMSCustomBindingInterface.wsdl). For example, a JMS Text message should be sent to the handleText() operation, a Stream message to the handleStream() operation, and so forth. Later, you will see that we can assign different data bindings for different operations.

Listing 1 shows an extract of the function selector code that comes with our example:

Listing 1. Function selector code snippet
public class SimpleJMSFunctionSelector implements FunctionSelector {
	public String generateEISFunctionName(Object[] arguments) {

	... ...
	Message message = (javax.jms.Message) arguments[0];
	functionName = message.getJMSType();

	if (functionName == null) {
		if (message instanceof BytesMessage)
			messageBodyType = "Bytes";
		else {
			if (message instanceof TextMessage)}
				messageBodyType = "Text";
	... ...
	functionName = "handle" + messageBodyType;
	... ...
}

Notice how the generateEISFunctionName() method returns the name of the function (or operation or method) that is associated with the given message. In our case, the function name is "handle" concatenated with the message type. For example, for JMS Text messages, "handleText" is returned. In other words, whenever a JMS text message is received by the mediation, it will be sent to the handleText() method.

(You can find the full source code in the SimpleJMSFunctionSelector.java file in the JMSCustomBindingClasses project.)

The function selector is defined in the binding properties of the export, under the Advanced settings of the Connection tab, as shown in Figure 1.

Figure 1. Function selector setting in WebSphere Integration Developer
Figure 1. Function selector setting in WebSphere Integration Developer

WebSphere Integration Developer also provides a default function selector, called com.ibm.websphere.sca.jms.selector.impl.JMSFunctionSelectorImpl, which gets assigned a string value from a header property, TargetFunctionName, from the incoming JMS message as the function name.

Data binding

The data binding implementation is a key part of the JMS custom bindings. In the case of an export binding, the format and structure of each incoming message must be known by this data binding implementation, which turns it into the appropriate form for the mediation flow; that is, into an SDO DataObject. In the case of an import binding, the reverse occurs; namely, the outgoing SDO DataObject is turned into the JMS message that is sent to the external service.

SCA mandates that each service component has an interface, which can be a Java interface, or described as a WSDL portType (the WebSphere ESB MediationFlow component supports WSDL interfaces). To receive any arbitrary JMS message into a mediation flow, we must define a set of operations that represents these messages. For example, the JMS Text message is represented by the following WSDL portType operation:

Listing 2. portType operation for JMS text message
p<wsdl:portType name="JMSCustomBindingInterface">
  <wsdl:operation name="handleText">
      <wsdl:input message="wsdl1:JMSTextMessage"/>
      <wsdl:output message="wsdl1:JMSTextMessage"/>
      <wsdl:fault message="wsdl1:JMSTextMessage" name="TextFault"/>
  </wsdl:operation>
... ...
</wsdl:portType>

(The complete list of operations can be found in the JMSCustomBindingInterface.wsdl file in the JMSCustomBindingLibrary project.)

Once you have defined the interface for an export or import binding, you need to provide the data binding class that handles the conversion between DataObject and JMS. This can either be one class for the entire export/import, or one class per operation. We have defined several operations in our interface (one per JMS message type), hence we define method-level data binding, as shown in Figure 2.

Figure 2. Method level data binding definition
Figure 2. Method level data binding definition

Each data binding class must implement the com.ibm.websphere.sca.jms.data.JMSDataBinding interface.

In our test application, there are five JMSDataBinding implementations:

  • JMSTextDataBinding
  • JMSBytesDataBinding
  • JMSStreamDataBinding
  • JMSMapDataBinding
  • JMSObjectDataBinding.

You can find their source files and the other utility classes in the JMSCustomBindingClasses project.

A data binding implementation must provide implementations for the following methods:

  • setDataObject() sets the value of the DataObject that represents the JMS message payload. The DataObject must conform to the corresponding schema definition.

  • getDataObject() returns a DataObject that represents the JMS message payload, and which conforms to the corresponding schema.

  • getMessageType() returns type of JMS message (defined in the JMSDataBinding interface).

  • read(Message message) sets the value of the DataObject properties from the JMS message, which is supplied as the method parameter.

  • write(Message message) sets the value of the payload of the JMS message, which is supplied as the method parameter, from the DataObject's properties.

  • isBusinessException() states whether or not the DataObject represented by this instance of JMSDataBinding is to be treated as a business exception or not.

  • setBusinessException() sets whether or not the DataObject represented by this instance of JMSDataBinding is to be treated as a business exception.

Figure 3. Method execution flow for SCA import/export with JMS binding
Figure 3. Method execution flow for SCA import/export with JMS binding

In Figure 3, the JMS-->SDO flow occurs when a JMS message is sent to the mediation module (that is, an export), and the SDO-->JMS flow occurs when a JMS message is sent from the mediation module (that is, an import). Notice that the response message for an import with JMS binding does not pass through the function selector.

Listing 3 shows parts of the source for the com.ibm.websphere.sibx.samp.jms.JMSTextDataBinding class, which handles all JMS text messages coming in and going out.

Listing 3. Code sample of JMSTextBinding
public class JMSTextDataBinding extends AbstractJMSDataBindingImpl implements JMSDataBinding {
private String payload = null;
private DataObject jmsData = null;

public int getMessageType() {
	return JMSDataBinding.TEXT_MESSAGE;
}
public void read(Message message) throws JMSException {
... ...
TextMessage textMessage = (TextMessage) message;
	payload = textMessage.getText();
... ...
// construct a DataObject based on the schema definition for
	// "JMSTextMessage"
	jmsData = DataFactory.INSTANCE.create("com.ibm.ws.sib.mfp/schema","JMSTextBody");
... ...
	// set the "value" property to the String value of the message payload
	jmsData.setString("value", payload);
... ...
}


public void write(Message message) throws JMSException { ... ... TextMessage textMessage = (TextMessage) message; // Clears the body of the JMS message textMessage.clearBody(); // Sets the value of the JMS message payload from the DataObject's // 'value' property payload = jmsData.getString("value"); textMessage.setText(payload); ... ... } public DataObject getDataObject() throws DataBindingException { return jmsData; } public void setDataObject(DataObject jmsData) throws DataBindingException { this.jmsData = jmsData; } }

The read() method transforms a JMS TextMessage into a data object, and the write() method transforms a data object back into a JMS TextMessage. In the read() method, a new data object is created using the corresponding schema, which is made available to the WebSphere ESB runtime during deployment of the mediation module. In the write() method, the code reads the content of the data object and stores it in the passed "message" parameter. The type of the message is determined via the getMessagetype() method.

The payload of the DataObject is read using this line:

payload = jmsData.getString("value");

This code is using standard SDO API calls. The name of the retrieved property, "value", stems from the schema definition for JMSTextBody (in the JMSCustomBindingLibrary/JMSBodyModels.xsd file):

Listing 4.
  <xsd:complexType name="JMSTextBody">
         <xsd:sequence>
            <xsd:element name="value" type="xsd:string"/>
          </xsd:sequence>
  </xsd:complexType>

The JMSTextBody type is actually referred to in the WSDL interface for the mediation. The portType messages shown in Listing 2 point to this schema. In other words, the mediation's interface defines the structure of the SDO that the data binding class must handle. Hence, any JMS custom data binding implementation is closely related to the schema that is associated with the interface of the mediation flow component!

The data binding classes for the other JMS message types are similar. We will leave it up to you to analyze those classes. Again, each data binding implementation is closely related to the respective schema definition given in the JMSBodyModels.xsd file. The JMSStreamDataBinding and JMSMapDataBinding classes are slightly more complex because they need to retain type information about each individual element in the stream or map. Check the complex type definitions in the JMSBodyModels.xsd files for more details.

All the custom JMS data bindings have a common super class, called AbstractJMSDataBindingImpl, which implements some common functionality; for example, methods for exception handling.

The JMSDataBindingLogger class is responsible for logging. To activate logging in the custom binding code, set the Trace details in the WebSphere ESB runtime to com.ibm.websphere.sibx.samp.jms.*=fine.

JMS parameters

We already created the actual queue destinations and queue connection factories for these JNDI names in Part 2 of this article series. We are simply reusing them here.

The last step in completing the JMS custom binding definition is to select the proper JMS resources for both incoming and outgoing traffic.

The necessary JMS queue destination and connection factory references are also defined in the binding properties. As shown in the following figures, the definition of the connection factory for the JMS export binding component is under the Advanced settings of the JMS Export Binding tab:

Figure 4. Connection Factory Reference definition for JMS export binding
Figure 4. Connection Factory Reference definition for JMS export binding

The definition of the queue destination references is under the JMS Destinations tab:

Figure 5. Queue Destination Reference definition to JMS export binding
Figure 5. Queue Destination Reference definition to JMS export binding

We won't show the corresponding screenshots for the import binding component because they are almost the same, except that the connection factory definition can be found under the JMS Import Binding tab. All of these values should already be set for you when you imported the project interchange file for this article.


Mediation flow

Once we have coded the custom binding for both export and import, we can start to focus on the mediation flow component itself. In the WebSphere Integration Developer assembly editor, open the JMSCustomBindingMediationModule and double-click JMSCustomBindingMediationComponent1 to open the mediation flow editor. In this editor, each operation on the flow component's interface is represented by a request and a response flow (Figure 6).

Figure 6. Mediation flow editor
Figure 6. Mediation flow editor

These flows are entirely independent from the bindings that are used in the import(s) and export(s). In fact, that is the point of having a conversion into an SDO DataObject instance outside of the flow implementation: flows can be built without knowledge of the protocol and format with which messages are sent to and from the mediation module.

We will elaborate here only on one of the flows as an example, namely the flow for the JMS TextMessage, because this is the most common JMS message type. We also added some specific exception handling mechanisms to the response flow for this message type. (The flows for the other message types are very similar.)

Let's describe the processing of a JMS TextMessage through the mediation module step by step:

  1. When a JMS TextMessage is sent from the client to the mediation module, it is placed on the receive queue destination of the JMS export binding component. Because this export component is implemented as a JMS custom binding, the message is handled by our customized function selector. As we described above, the selector returns the function name "handleText", based on the incoming message type.

  2. Due to the method level data binding definition of the export component (as shown in Figure 2), the JMSTextDataBinding class is selected to handle the data transformation. This class extracts the text payload from the incoming JMS TextMessage, and creates an SDO DataObject instance following the appropriate XML data type, namely JMSTextBody. The "value" string property in the DataObject is set to the value of the JMS TextMessage's payload.

  3. The new DataObject is then passed to the mediation flow component. Because it is passed to the handleText operation, the flow handling this message is the one you see when you select the handleText link in the mediation flow editor (see also Figure 6).

  4. The message is logged in the request flow using the MessageLogger mediation primitive.

  5. When the message leaves the request flow, it enters the JMS import binding component, represented by the JMSCustomBindingPartnerInterface in the mediation flow editor. Here, the DataObject is transformed back into a JMS TextMessage and finally sent on the send queue destination of the import component.

  6. The service provider (that is, the message-driven bean in the case of our test application) listens for messages on this queue destination. Its onMessage() method simply prints the content of the received message. Some special code was added to simulate exception processing: if the content of the text message is "exception", the response JMS TextMessage will have a header property called IsBusinessException set to "true".

  7. Now, the response JMS TextMessage is sent back to the mediation module and will undergo the same processing as before: the JMS import binding component will transform it to an SDO DataObject. If a TextMessage with the IsBusinessException property set to "true" is sent in, it will be passed into the response flow via the fault callout fault callout, rather than the normal response callout normal callout.

  8. As also shown in Figure 6, in the response flow, if the message enters via the fault callout fault callout, it will simply be logged (through MessageLogger3) and forwarded to the fault input fault callout. In all other cases, the message will pass through a customized mediation primitive called BusinessExceptionCustomMediation, where we have added logic to generate another exception if the content of the text message is "Error". In that case, the message will flow through the custom mediation primitive's fail terminal, be logged, and again be forwarded to the fault input fault input.

  9. All "normal" messages (that is, messages for which no exception processing is performed) are simply logged and forwarded to the response input terminal input terminal.

  10. Finally, the response DataObject reaches the JMS export component, is transformed back into a JMS TextMessage, and placed on the send queue destination. There it can be retrieved by any JMS client. If the export component receives the response message via the fault input fault input of the mediation flow, it not only performs the normal data transformation, but also adds a header, named IsBusinessException, to the generated JMS TextMessage. The client can use this to recognize whether there was an exception during the processing of the message.


Pulling it all together

Now that we have completed the definition of the export and import bindings and provided the mediation flow implementation, we can finally deploy and run the entire example. In Part 2, we showed you how to install and deploy the test application client and the test provider. All that remains to be installed is the mediation module.

There are two ways to install the mediation module onto a WebSphere ESB run time server:

  1. Using the WebSphere Integration Developer WebSphere ESB test environment:

    1. In WebSphere Integration Developer, open the Servers view, and make sure the WebSphere ESB Server v6 is created and started.

    2. Right click on this server and select Add and remove projects… from the context menu.

    3. On the next dialog, select the JMSCustomBindingMediationModuleApp project and add it to the server.

  2. Using the WebSphere ESB administrative console:

    1. In WebSphere Integration Developer, switch to the J2EE perspective and select JMSCustomBindingMediationModuleApp under Enterprise Applications.

    2. Export this project to an EAR file.

    3. Open the Servers view, right click WebSphere ESB Server v6 and select Run administrative console from the context menu.

    4. In the admin console, install the exported EAR into the runtime through Applications => Install New Application.

To test the application, open a Web browser, and browse to http://<HOSTNAME>:<HOSTPORT>/JMSTestClientWeb/index.html (<HOSTNAME> and <HOSTPORT> are specific to your local server configuration; you can typically use "localhost" and port 9080). Follow the instructions given in Part 2 to send different kinds of JMS messages, then select Click here to select messages on the reply queue to see the response message. The screen should look like Figure 7.

Figure 7. Result Web page for JMSTestClient after successful sending of a TextMessage
Figure 7. Result Web page for JMSTestClient after successful sending of a TextMessage

Conclusion

In this article, we have shown you a concrete example of how to build a JMS custom binding to a mediation flow component in WebSphere ESB. We discussed the data binding and function selector code that is required to handle incoming and outgoing JMS messages, as well as the necessary binding properties for the export and import. We looked at how the mediation flow component can be built visually and, most importantly, independent from the protocol that was used to communicate with the mediation flow.

With this, we conclude our series of articles on this subject. Stay tuned for more articles on WebSphere ESB and how to build advanced solutions with it and its tool, WebSphere Integration Developer!


More articles in this series


Download

DescriptionNameSize
Code sampleWESBCustomJMSPart3 PI.zip  ( HTTP | FTP )69 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Business process management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Business process management, WebSphere, SOA and web services, Java technology
ArticleID=108677
ArticleTitle=IBM WebSphere Developer Technical Journal: Building a powerful, reliable SOA with JMS and WebSphere ESB -- Part 3
publish-date=04192006