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

Creating a sample application for a common scenario

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 is the second of three articles on integrating JMS messaging with WebSphere ESB, and describes use case scenarios that set the stage for building and deploying a test application for demonstrating this integrated messaging.

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.


developerWorks Master author
        level

22 March 2006

Also available in Chinese

From the IBM WebSphere Developer Technical Journal.

Introduction

In Part 1 of our series, we introduced a number of concepts to you, including the concept of the Enterprise Service Bus (ESB), and how a reliable, standardized API like Java Message Service (JMS) can help provide Quality of Services to the communication between service consumers, service providers, and the bus. We also looked at how the WebSphere ESB product provides you with an implementation of ESB based on a new programming model, called Service Component Architecture (SCA). SCA describes the notion of export and import bindings, which we can use to interact with mediation flow components via JMS. All of the required artifacts can be built and deployed with the IBM WebSphere Integration Developer tool.

In Part 2, we will start applying these concepts and show you how to build a real application.


Use cases

In real SOA business applications -- especially those which have heterogeneous IT infrastructures involved, and which want to compose workflows among services presented by these IT infrastructures to form loosely coupled interactions -- messages flowing between service consumers and providers do not need to be handled synchronously. JMS is often used as the preferred protocol for synchronous and asynchronous service invocation because of its popularity as a standard for message-oriented middleware.

There are a number of typical scenarios where a connection between services can utilize JMS as a transport: e-government, e-business, or industrial manufacturing are a few examples.

Example 1

One concrete use case is the handling of documents in an accounting system. Documents that influence a company's finances (for example, invoices for purchased goods) flow across multiple systems in the enterprise. Many of them must be handled in a way that allows later auditing, and in some cases, this is legally required. This means that the flow of such documents must be traceable, which requires a transport protocol that is reliable. Moreover, many of the interactions between the involved systems are implementing workflows that are asynchronous in nature. Hence, a protocol is selected that offers support for both reliable, transactional exchange of messages, as well as a variety of messaging patterns, for asynchronous and synchronous invocations plus publish/subscribe messaging.

At the same time, all messages are directed through the Enterprise Service Bus so that additional functionality can be applied; for example, content-based dynamic routing or transformation of data, as well as journaling and logging.

This accounting example serves as a scenario where a solution is built with service-oriented architecture in mind, but without necessarily utilizing Web services; in our case, services communicate by exchanging plain JMS messages. Deploying an ESB for such a solution is still very applicable.

Example 2

Let us look at another scenario, this one being in the manufacturing industry: A company manufactures products using a large number of parts, some of which are supplied from internal plants, while others are purchased from external suppliers. This company wants to speed up its stock turnover and decrease its stock inventory to reduce internal cost and improve time-to-market of its products. One way to realize this goal is to establish a closer relationship with its business partners, including its suppliers.

Usually, the manufacturer will utilize a production planning system (PPS) to coordinate internal production and its external supply chain. In the PPS, when the stock of an internal part is running low, a production request is generated to get this part produced.

Figure 1. Production planning system - High level architecture
Figure 1. Production planning system - High level architecture

Besides using parts supplied from internal plants, the manufacturer purchases other parts from external suppliers. To increase the level of integration between the involved parties, the manufacturer needs to integrate its PPS with several of its suppliers' systems, such that they can exchange demand and supply information automatically. Connecting the PPS system to any number of suppliers, each with its own protocol and data format, can lead to a significant amount of code that must be developed.

Many of the existing systems that are to be integrated use JMS as the external messaging protocol. Others, especially recently-built systems, may support Web services. In this case, the manufacturer decides to establish an ESB to handle the transformation of protocols and message formats centrally, and thus will keep any impact on the existing systems to a minimum. For example, an existing system that leverages JMS at its external interface can interact with a Web service, and delegate the details of handling the different protocols to the ESB.


The test application

Above, we saw how JMS as a protocol is relevant in a SOA-based solution, both when a system is established from scratch, and in the situation when existing systems must be integrated. In this section, we will present a test application implementation that lets us focus on the functionality in WebSphere ESB that makes the scenarios described above possible. This implementation includes:

  • a JMS client, which sends requests in different JMS message formats.
  • a service provider, which accepts JMS format service requests and replies also with a JMS message.
  • a WebSphere ESB mediation module with a mediation flow to perform actual message mediation actions.

Figure 2 outlines the structure of the test application. Note that a total of four queues are used for the communication with the ESB.

Figure 2. Test application
Figure 2. Test application

Before showing you how to build the actual mediation flow and explain how to insert the custom binding code (which we will do in Part 3 of our series), we will take you through the test client and test service provider, which will send and receive the JMS messages respectively.

By the way, you will notice that there is nothing WebSphere ESB specific in the client and provider code. This is by design, because we assume that neither the service consumer nor the service provider need any knowledge of the ESB that exists between them.

The client

We will not take you through the detailed steps of importing the EAR file into WebSphere Integration Developer. The application follows plain J2EE standards and can be imported and handled just like any other Web application. Furthermore, we will not explain the entire source code in detail, but rather will focus on those parts that are important to our example. Finally, we will assume that you will deploy all of the shown code to a WebSphere ESB server; for example, the test server environment that is included in the WID tool.

The test client application is based on a set of JavaServer Pages and servlets in a Web application, and enables sending and receiving JMS messages of different types to and from a couple of JMS queues. It is packaged in the JMSTestClientV1_6.ear file, which you can find in the Download section for this article.

Figure 3. JMS client project in WebSphere Integration Developer workspace
Figure 3. JMS client project in WebSphere Integration Developer workspace

In Figure 3, you can see the structure of the client project after you imported it into your WebSphere Integration Developer workspace. Notice that there are two servlets, named ReadQueue and WriteQueue, which we will look at in more detail below.

Listing 1. WriteQueue.java JMS message creation
//WriteQueue.java
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);

if (msg_type.equals("TextMessage")) {
	TextMessage message = session.createTextMessage(msg_text);
	producer.send(message);
}
else if (msg_type.equals("BytesMessage")) {
... ...
}
else if (msg_type.equals("StreamMessage")) {
... ...
}
else if (msg_type.equals("MapMessage")) {
... ...
}
else if (msg_type.equals("ObjectMessage")) {
	... ...
}

The servlet WriteQueue (Listing 1), produces all kinds of JMS messages that are sent to a designated queue destination. It uses standard JMS API calls to construct JMS messages according to the input passed in by the JSP pages. The input is formatted depending on the indicated JMS message type. The code example above explicitly shows how a TextMessage is built. You can find the respective code for the other types in the downloadable source. The following extract shows how a reference to the appropriate JMS destination queue is resolved:

Listing 2. WriteQueue.java JMS message creation
InitialContext ic = null;
try {
	//Get the jndi initial context
	ic = new InitialContext();
Context myEnv = null;

	//Get the context that is specific for this WAR
	//This holds items such as resource references, ejb references, etc
	//from the web.xml
	myEnv = (Context) ic.lookup("java:comp/env");

	//Get the resource reference for the JMS QueueConnectionFactory
	//that is defined in the web.xml with the folllowing attributes:
	// Name: jms/qcf Type: javax.jms.QueueConnectionFactory
	// Authentication: Container
	// WebSphere Bindings JNDI Name: jms/qcf
	qcf = (QueueConnectionFactory) myEnv.lookup("jms/qcf");

	//Get the resource reference for the JMS QueueConnectionFactory
	//that is defined in the web.xml with the folllowing attributes:
	// Name: jms/queue Type: javax.jms.Queue
	// Authentication: Container
	// WebSphere Bindings JNDI Name: jms/queue
	queue = (Queue) myEnv.lookup("jms/queue_sender");

} catch (NamingException e2) {
...
}

In Listing 2, you can see that jms/qcf is used as the JNDI name for the Queue Connection Factory, and jms/queue_sender is used as the JNDI name for the actual queue. You will need to set those two names up before you can run the application (more on that below).

Listing 3. ReadQueue.java JMS handling
//ReadQueue.java
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(queue);
con.start();
Message msg = consumer.receive(3000L);

String exceptionMark = "";
while(msg != null){
	exceptionMark = "";
	if (msg.propertyExists("IsBusinessException")) {
		if (msg.getBooleanProperty("IsBusinessException")) {
			exceptionMark = "Exception ";
		}
	}					

	if(msg instanceof TextMessage){
		TextMessage textMessage = (TextMessage) msg;
		if (textMessage.getText() != null){
		res.add(exceptionMark + "TextMessage#" + textMessage.getText());
		}
	}
	else if (msg instanceof BytesMessage){
	... ...
	}
	else if (msg instanceof StreamMessage){
		... ...
	}
	else if (msg instanceof MapMessage){
		... ...
	}
	else if (msg instanceof ObjectMessage){
		... ...
	}
}

The servlet ReadQueue.java, an extract of which is shown in Listing 3, consumes all kinds of JMS messages from a designated queue destination. It receives and parses all JMS messages that exist in the queue, and sends the parsed content to the appropriate JSP pages for display. If the JMS message has an IsBusinessException property and the value is true, it is an exception message. Normal messages and exception messages are displayed differently. The ReadQueue servlet also uses jms/qcf to resolve to the Queue Connection Factory, and it uses jms/queue_receiver as the name of the response queue.

During the installation of the JMSTestClientV1_6.ear enterprise application to a WebSphere ESB runtime, all options use default values. You can use the WebSphere ESB test environment that is included with the tool, as well as any separately installed runtime. After installation, you need to execute these configuration steps in the WebSphere ESB administrative console:

  1. Create two queue destinations by selecting Buses => SCA.APPLICATION.esbCell.Bus => Destinations. The queue names are JMSCustomBinding_Export_Receive_Q and JMSCustomBinding_Export_Send_Q. Leave all other options with their default values.

  2. Create a queue connection factory by selecting Default messaging provider => JMS queue connection factory. Enter or select the following values:

    • Name: JMSCustomBindingFactory
    • JNDI name: jms/JMSCustomBindingFactory
    • Bus name: SCA.APPLICATION.esbCell.Bus
  3. Create two queue destinations by selecting Default messaging provider => JMS queue. The first queue has these settings:

    • Name: JMSCustomBinding_Export_Receive_Q
    • JNDI name: jms/JMSCustomBinding_Export_Receive_Q
    • Queue name: JMSCustomBinding_Export_Receive_Q
    • Bus name: SCA.APPLICATION.esbCell.Bus

    The second queue has these settings:

    • Name: JMSCustomBinding_Export_Send_Q
    • JNDI name: jms/JMSCustomBinding_Export_Send_Q
    • Queue name: JMSCustomBinding_Export_Send_Q
    • Bus name: SCA.APPLICATION.esbCell.Bus

    The JMS resources created in steps 1 through 3 will be shared with the mediation module. The queue connection factory created in step 2 will also be shared by the mediation module and by the service provider (mentioned next). Hence, once you get to those parts, you won't have to create these artifacts again.

  4. Map resource references by selecting Enterprise Applications => JMSTestClient => Map resource references to resources.

    • Reference binding: jms/queue_receiver
    • JNDI name: jms/JMSCustomBinding_Export_Send_Q
    • Reference binding: jms/queue_sender
    • JNDI name: jms/JMSCustomBinding_Export_Receive_Q
    • Reference binding: jms/qcf
    • JNDI name: jms/JMSCustomBindingFactory
    • Login configuration: esbNode/CommonEventInfrastructureJMSAuthAlias

    These values should be already configured by default.

  5. After the application is installed and configured correctly, running the Web application on the server will result in the display of Figure 4.

    Figure 4. Initial Web page for JMSTestClient
    Figure 4. Initial Web page for JMSTestClient
  6. Select Click here to post a TextMessage, and a new page appears (Figure 5).

    Figure 5. TextMessage input Web page for JMSTestClient
    Figure 5. TextMessage input Web page for JMSTestClient
  7. Do not click on the Post Message button just yet. We don't have the other required pieces of the application installed, so clicking this button will not give the expected result. Feel free, however, to select the link labeled Click here to post a JMS message of the other type, and experiment with the different types of JMS messages and their respective input JSPs.

The service provider

The service provider supporting JMS is implemented in WebSphere Integration Developer as a regular message-driven bean (MDB), which will receive JMS messages from a defined JMS queue and then simply send the same message back to a response queue. The application is provided in the download ZIP file as JMSTestMDBV1_6.ear. After importing this EAR file into the tool, you will notice an EJB project named JMSMDBServiceEJB, which contains the desired MDB JMSServiceMDB, as shown in Figure 6.

Figure 6. JMS service provider projects in WebSphere Integration Developer workspace
Figure 6. JMS service provider projects in WebSphere Integration Developer workspace

The onMessage() method of this MDB acts as the service provider implementation. The following code excerpt shows a digest for the method implementation (the full source code should be evaluated in the download file included with this article):

Listing 4. onMessage method implementation of JMSServiceMDBBean

Click to see code listing

Listing 4. onMessage method implementation of JMSServiceMDBBean

public void onMessage(javax.jms.Message msg) {
	// get the message in the correct format 
	ObjectMessage objMsg = null;
	try {
		InitialContext context = new InitialContext();
		Queue q = (Queue) context.lookup("java:comp/env/test/sender");
		QueueConnectionFactory qcf = (QueueConnectionFactory) context.lookup("java:comp/env/test/qcf");
		QueueConnection conn = qcf.createQueueConnection();
		conn.start();
		QueueSession session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
		QueueSender sender = session.createSender(q);
			
		String MessageID = msg.getJMSMessageID();
		msg.setJMSCorrelationID(MessageID);

		if (msg instanceof TextMessage) {
			System.out.println("MDB: TextMessage Received");
				
			System.out.println("MDB: TextMessage Content:");
			String payload = ((TextMessage)msg).getText();
			System.out.println(payload);
				
			if(payload.equals("exception"))
			{
				msg.clearProperties();
				msg.setBooleanProperty("IsBusinessException",true);
			}
		}
		else if (msg instanceof ObjectMessage) {
			......
		}
		else if (msg instanceof BytesMessage){
			......
		}
		else if (msg instanceof StreamMessage){
			......
		}
		else if (msg instanceof MapMessage){
			......
	} catch (Exception e) {
		e.printStackTrace();
	}
}

In the first part of the method implementation, we build up the JMS message transmission context by looking up or creating relevant JMS resources, like a message queue, a queue factory, a queue connection, a queue session, and finally a queue sender. They will be used by the MDB to send back reply messages (remember, the service provider in our case simply sends received messages back to a reply queue). All of this is using the regular JMS APIs.

In the second part, the received JMS message (which is passed as a method parameter) is handled according to its JMS message format. The MDB supports all five types of messages, which are mentioned in the JMS specification:

  • Text
  • Object
  • Stream
  • Map
  • Bytes.

In spite of the format difference, there is a common handling mechanism for each message: print MDB: XXXMessage Received , extract the message content, and then print MDB: XXXMessage Content: with the actual content appended (where XXX can be one of the message types listed above). Finally, the original message is simply sent back to the reply queue. (Note that a regular JMS application would read the name of the response queue from the ReplyTo field in the request message.)

There is a special section in the processing of the Text message that enables testing exceptional cases: a message containing the string "Exception" will be regarded as a request that causes an exception in the provider. In this case, we add a header field called IsBusinessException to the JMS response message, and set its value to "true". In Part 3, we will show you how such exception messages are handled in the mediation module and how they are converted into an error that is sent back to the original client.

After installing the JMSTestMDBV1_6.ear EAR file in your WebSphere ESB server runtime, you need to perform the following configuration steps in the WebSphere ESB administrative console:

  1. Create two queue destinations by selecting Buses => SCA.APPLICATION.esbCell.Bus => Destinations. The queue names will be JMSCustomBinding_Import_Receive_Q and JMSCustomBinding_Import_Send_Q. Leave all other options with their default value.

  2. As for the queue connection factory used by this MDB, it will use the one you already created in step 2 of the JMS client application installation.

  3. Create a JMS activation specification by selecting Default messaging =>JMS activation specification, with the following settings:

    • Name: JMSCustomBinding_Import_Send_AS
    • JNDI name: jms/JMSCustomBinding_Import_Send_AS
    • Destination JNDI name: jms/JMSCustomBinding_Import_Send_Q
    • Bus name: SCA.APPLICATION.esbCell.Bus
  4. Create two queue destinations by selecting Default messaging provider => JMS queue. The first queue has the following settings:

    • Name: JMSCustomBinding_Import_Receive_Q
    • JNDI name: jms/JMSCustomBinding_Import_Receive_Q
    • Queue name: JMSCustomBinding_Import_Receive_Q
    • Bus name: SCA.APPLICATION.esbCell.Bus

    The second queue has the following settings:

    • Name: JMSCustomBinding_Import_Send_Q
    • JNDI name: jms/JMSCustomBinding_Import_Send_Q
    • Queue name: JMSCustomBinding_Import_Send_Q
    • Bus name: SCA.APPLICATION.esbCell.Bus
  5. Bind message destination references by selecting Enterprise Applications => JMSTestMDB => Bind message destination references to administrated object.

    • Message destination object: test/sender
    • JNDI name: jms/JMSCustomBinding_Import_Receive_Q
    • Message destination object: test/qcf
    • JNDI name: jms/JMSCustomBindingFactory

    These values should be already configured by default.

  6. Provide listening bindings by selecting Enterprise Applications => JMSTestMDB => Provide listening bindings for message-driven beans.

    • Activation specification
    • JNDI name: jms/JMSCustomBinding_Import_Send_AS
    • Destination JNDI name: jms/JMSCustomBinding_Import_Send_Q

    These values should also be already configured by default.

After these steps, both the client and service provider of our test application are ready for use. The only thing left to do is to develop and install the piece that connects them: the mediation module that runs on WebSphere ESB, using custom bindings to handle the incoming and outgoing JMS messages. We will leave this to the last part of our series.


Conclusion

In this article, we took you through a couple of use case examples that leverage WebSphere ESB to handle messages that are exchanged between service consumers and service providers, both using JMS as the underlying messaging mechanism.

We also showed you how to build a test application that allows flexible testing of the created mediation module. This includes a JSP-based JMS client to send messages into the mediation module, as well as a Message-driven Bean (MDB) to receive messages from the module.

In the final part of this series, we will get to the most interesting part: we will show you how to build the mediation module itself, and explain the custom mediation code that is deployed to handle the different types of JMS messages.


More articles in this series


Download

DescriptionNameSize
Code sampleJMSTestV1_6.zip  ( HTTP | FTP )36 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=106348
ArticleTitle=IBM WebSphere Developer Technical Journal: Building a powerful, reliable SOA with JMS and WebSphere ESB -- Part 2
publish-date=03222006