We began the original WebSphere Application Server ESB article series with an example showing how the System Integration Bus (SIBus) acts as the default JMS provider for the application server. That first article showed how to connect a J2EE application client, sending a plain JMS text message, to a JMS service provider running on the application server implemented as a message-driven bean (MDB). In this article, we will use this same example as our starting point -- but instead of sending the message from the client to the SIBus queue, and then the MDB service getting the message from the queue, we will route the message through a mediation running in WebSphere ESB.
To place this example in a business context, we will use our shipping company scenario introduced in Part 1. We will assume that whenever a package has been delivered to a client, a message must be sent to the main system to confirm the delivery. This confirmation message is sent asynchronously; that is, no response to the message is required and the message is simply queued to the main system for processing.
The enhanced architecture
An ESB enables building decoupled systems by offering virtual service interfaces, meaning a client does not access the actual service provider directly; rather, it exchanges messages with the ESB instead. The ESB then routes messages to and from the actual service provider. This is true not just for Web services (that is, services exposed via a SOAP/HTTP binding), but for any service. In our example, we have a J2EE application receiving plain text messages from a JMS queue. In the context of the ESB, we view this application as a service provider. Similarly, we offer a service interface to the JMS client application.
We won't spend a lot of time describing the service requester and provider code that we use for this example (the code is really no different from any typical JMS code; see Resources for a JMS tutorial), but let's go over the basics:
The J2EE client application simply sends a message containing the number of the package that was delivered.
Since this sender code is running in a J2EE client application, we don't hardcode the names of the JMS resources that are used; we use the java:comp/env namespace instead. Both names (one for the connection factory and one for the actual queue) are bound using resource references in the client application deployment descriptor.
The client was originally written to send a simple text message but will be updated to send an XML message.
For the MDB, things are even simpler: each MDB has a method called onMessage(), which is invoked as soon as a message arrives on the queue that the MDB listens to and it prints the message to System.out.
One aspect that makes this interesting is that by routing through WebSphere ESB, we have now decoupled the client JMS configuration and the MDB configuration -- they will both be configured to talk to WebSphere ESB. If the MDB is deployed on a different server, the client application does not need to change. Since the message flows through WebSphere ESB, the message can be mediated in the ESB.
Figure 1 shows the setup when the JMS requester and MDB are directly connected through a SIBus queue (described in Part 3 of our previous series), compared to what we are going to build in this article.
Figure 1. Sample scenario setup
In the updated architecture, note that we are using two queues: one between the client and the WebSphere ESB mediation, and one between the mediation and the actual receiver (that is, the service provider). Figure 1 also shows how WebSphere ESB artifacts still run within WebSphere Application Server, which provides the JMS runtime and hosts the message-driven bean. For simplicity, we are running the MDB in the same server as WebSphere ESB; in a production environment, the MDB would typically be run on a separate server requiring additional configuration steps.
A mediation flow component in WebSphere ESB is just another type of service component defined in the Service Component Architecture (SCA). SCA requires a service interface definition that describes the service endpoint on the bus (the interface the client application will call). Having a formal (WSDL) description enables us to view the JMS message exchange as a service invocation. (See Resources for an introduction to SCA.)
Overview of ESB creation and configuration
Before beginning, here is an overview of the steps you will perform in this article. (If you prefer to minimize the work you need to do, the EARs for the service requester and service provider, and a project exchange file, with the completed WebSphere ESB mediation, are included in the download materials with this article. If you choose to use the project exchange file, you will still need to configure the deployment descriptors of the client and MDB applications, as well as the server itself.)
Create the service interface. You will use WebSphere Integration Developer to create a WSDL description of the service interface for the service requester to send messages onto the bus, and by the MDB as the service provider.
Create the mediation. You will create the mediation module and construct the mediation flow component. The mediation export and import will both be configured with the service interface you create in Step 2, above. JMS bindings will be created for the export and import. The mediation will simply log the message as it flows through.
Set up the service requester. You will modify the original test client that sent a simple text string to send an XML message. You will set up the JMS configuration of the client application and WebSphere Application Server, in which WebSphere ESB runs and to which the client connects. SIBus queues generated by WebSphere ESB will be used as part of the configuration.
Set up the service provider. You will modify the MDB EAR configuration using SIBus queues generated by WebSphere ESB.
Use the latest version of WebSphere Integration Developer and WebSphere Enterprise Service Bus that are available. To write this article, we used WebSphere Integration Developer V22.214.171.124 and WebSphere ESB V126.96.36.199. A fixpack to bring both products to version 6.0.2 will be available in late December 2006 with significant functional and performance enhancements, so use these versions when they become available.
A. Create the WebSphere ESB server
Create a test server in WebSphere Integration Developer that you will use for testing.
On the Servers panel in WebSphere Integration Developer, select New => Server.
Select WebSphere ESB Server v6.0, accept the defaults, and then Finish.
If you have already created a test server, you can reuse it, but be aware that there may be preexisting artifacts defined on it that could cause conflict.
B. Create the service interface
A service component interacts with external partners via imports and exports. In our case, the export interacts with the JMS client application, and the import interacts with the MDB.
Figure 2. The SCA assembly of the mediation
Both the export and the import need an interface definition that describes the exact format of the data that is exchanged. Both also have a binding that describes the specifics of the underlying protocol over which the data is sent. In this example, the import and export interface will be the same.
Let's start with the interface. In the original example, we simply sent a string (the "package received" notifier). In our updated version, we want to formalize that by defining an XML schema that contains the definition of the structure of the message. This will make it easier to process the message in the mediation, as well as in the final receiver. We will use the business object editor and the interface editor in WebSphere Integration Developer to create the interface. The business object editor is used to describe the content of the message, and the interface editor describes how the message is wrapped into an operation envelope. These graphical tools generate schema and WSDL that describe the interface, and that can be given to service requesters and providers.
Open WebSphere Integration Developer in the Business Integration perspective.
Create a new mediation module, called
PackageReceivedModule. A mediation module serves as the container for a mediation component and its included logic, and is mapped into a deployable EAR project under the covers.
Open the business object editor by selecting the Data Types node in the navigation tree. Right-click on it to create a new business object.
In the New Business Object wizard, name the business object
PackageReceivedBOand leave the default values for all other fields. Click Finish.
When the business object editor opens, add an attribute called
messageto the business object and make it type string. The final definition looks like Figure 3.
Figure 3. Business object definition
This message attribute will store the payload of the message as it flows through WebSphere ESB. By the way, the business object definition is stored as an XML schema in an .xsd file. You can further modify it in any XML schema editor. For our purposes, however, there is no need to do that.
Save your changes.
You are now ready to create the actual service interface. Right-click on the Interfaces node in the navigation tree and select Create a new Interface.
Name the new interface
PackageReceivedIFand keep all remaining default values. Click Finish. The interface editor opens.
In the interface editor, select Add One Way Operation and call it
Add Input parameter, and name it
packageReceivedBOof type business object PackageReceivedBO (browse for this type, which you created above). The interface should now look like Figure 4.
Figure 4. Interface definition
Save your changes.
To view the WSDL that is generated, select PackageReceivedIF and choose Open with => XML Source Page Editor. Similarly, you can open the PackageReceivedBO schema.
You are now ready to build the actual mediation component.
C. Create the mediation
In WebSphere Integration Developer, double-click on the PackageReceivedModule assembly icon to open the assembly editor. A default mediation flow component called Mediation1 is created. Rename this component to
Next, you need to create an import that connects the mediation to the actual service provider (in our case, the MDB). Drag an import from the palette and drop it to the right of the mediation flow component. Rename the import to MDBImport. Right-click on the import and select Add Interface.
In the next dialog, choose the PackageReceivedIF interface that you created earlier. What basically happens here is that you assign the interface that you created to be used to send a message to the MDB; the message being sent from the ESB must conform to this interface.
Connect the import to the mediation flow component using the wire tool. (Click OK on any pop-up window that asks if it is okay to generate a reference on the mediation flow component.)
The service provider is implemented as an MDB so we are going to communicate with it over JMS; thus we are required to define a JMS binding on the import. Right-click the import and select the Generate Binding... => JMS Binding menu option. In the resulting dialog window, we define a set of critical values, as shown in Figure 5.
Figure 5. Configure JMS import service
JMS messaging domain -- Leave the default value, Point-to-Point, because we are using a specific queue to which messages are sent, as opposed to a pub-sub topic.
Configure new messaging provider resources -- Selecting this means that all the relevant JMS resources, including queues and their respective SIBus artifacts, will be generated automatically for you once the mediation module is deployed to a server.
Serialization type -- Messages that flow through the mediation are turned into a business object when they are received via an export, and they are transformed into the appropriate output format when going out via an import. In the case of JMS, you need to tell the system which class to use to do this transformation. WebSphere ESB provides a couple of pre-built classes that take specifically formatted XML messages and transform them to and from the business object format. In our case, you will use a class that takes a JMS TextMessage and converts it into a business object. The text message must follow a specific format so that the converter class can determine the right business object to use. We will revisit this later.
(WebSphere ESB V6.0.2 will introduce support for JMS messages that do not contain XML, that can be mapped to a business object but can have arbitrary content instead. For a more detailed description of how to create your own serialization logic for JMS content, see Building a powerful, reliable SOA with JMS and WebSphere ESB.)
JMS Function Selector -- Remember that we explained how we view the application receiving the JMS message as a service? This means that it supports one or more operations. In the case of a JMS-based service, one queue can be reused for multiple operations. Hence, you need to tell the system which type of message to map to which operation on the service. WebSphere ESB does this by setting a JMS header field, called TargetFunctionName, that contains the name of the invoked operation on the outgoing message. The receiving application, in our case the PackageReceivedMDB, can now distinguish between operations by inspecting this header. In our scenario, however, we are not using this, since the MDB only supports one operation anyway. Therefore, uncheck this option.
After setting all values as described above, click OK to generate the binding.
Select the import in the assembly editor and go to the Properties tab at the bottom of the tool. Within the Properties view, select the Summary tab. Your view should now look like Figure 6.
Figure 6. Import properties
Note how the name of the send queue is set to PackageReceivedModule/MDBImport_SEND_D. This name was generated when you created the binding, and the respective artifacts will be generated at deployment time. You will need to use this queue name later when you configure the MDB.
There is one thing left to do, namely define the appropriate JMSType on the outgoing message. The MDB only handles messages that have the JMSType header field set to package_received (this is defined in the MDB's deployment descriptor). Define the JMSType in the Method bindings tab of the Properties view, as shown in Figure 7.
Figure 7. Method bindings
Go back to the assembly editor and drag and drop an export onto the canvas, to the left of the mediation component, and name it
JMSClientExport. This export will expose the mediation to the JMS test client.
Add the PackageReceivedIF interface to the export and connect it to the mediation flow component. This will also create an interface on the mediation flow component. Connect this export to the PackageRecievedMediation using the wire tool.
Similar to the import, we need to generate JMS bindings to the export, since that is how the client will communicate with the mediation. Right-click the export and select the Generate Binding... => JMS Binding menu option. The resulting dialog window looks almost exactly like the one for the import. You need the same definitions here, with one exception, as you can see in Figure 8.
Figure 8. JMS bindings for export
For Serialization type, be sure to select Business Object XML using JMS TextMessage. The export will use the default JMS function selector class. As described above, the incoming JMS message must be mapped to a specific operation on the service interface, and the default selector class expects the TargetFunctionSelector JMS header field to be set to the name of the invoked operation. You will update the client application below to set this JMS header field.
Click OK to generate the binding and look at its Properties view.
Select the Binding - Endpoint configuration tab, and within that tab, the JMS Destinations tab. Expand Receive Destination Properties. The view should look like Figure 9.
Figure 9. Receive Destination Properties
Note how the name of the queue that receives messages for the mediation is set to "PackageReceivedModule.JMSClientExport_RECEIVE_D". We will be configuring the client application to use this queue name.
After you have saved all the changes, you are ready for the final step, which is to create the flow component's implementation. Right-click PackageReceivedMediation in the assembly editor and select Generate Implementation. This will open the mediation flow editor.
For our example, we will limit this to very simple mediation logic: you will log each message that goes through the mediation. This will help you visualize the messages flowing through the ESB. To do so, connect the package_received operation on PackageReceivedIF on the left (this represents the interface -- connected to the export -- of the mediation flow component) with the package_received operation on PackageReceivedIFPartner (this represents the reference -- connected to the import -- of the mediation flow component). In the bottom half of the editor, drop the Message Logger mediation primitive onto the canvas and wire the message flow so that each message goes through the logger, as shown in Figure 10.
Figure 10. Wire each message through the logger
Save all changes, both in the mediation flow editor and the assembly editor. The mediation module is complete and ready to be deployed on the server. Before you do that, however, you still have to adjust the requester code and provider configurations.
D. Set up the service requester
To complete this task, you need to perform these general steps:
- Import the J2EE test client application into WebSphere Integration Developer.
- Configure the queue name in the deployment descriptor to be the queue generated by the export of the WebSphere ESB mediation module.
- Update the application code to send an XML message and to set the JMSHeader TargetFunctionName, as needed by WebSphere ESB.
- Create a JMS connection factory on the WebSphere ESB server for the client to use to get a connection.
Now, the details:
Import the test client application, which is located in the PackageReceivedClient.ear file. This is an updated EAR file based on the one we have used in our previous article. The updates are explained below.
- When you select Import => EAR, you may be asked to enable Base J2EE Support if you have not already done so. If so, answer OK and switch to the J2EE perspective.
- During the import, make sure that you name the EAR project
PackageReceivedClientEARand select WebSphere ESB Server v6.0 as the target server. The new Application Client project, named
PackageReceivedClient, has a Main.java file in it that contains the code for the test client.
Next, change the name of the queue that the test message will be sent to since, instead of sending the message to the queue the MDB listens on, you want the message sent to the WebSphere ESB export. This name is referenced in the deployment descriptor for the client project, so open it by double-clicking it. The deployment descriptor contains a reference called jms/PackageReceivedQueue. Change its WebSphere Bindings => JNDI name to
PackageReceivedModule/JMSClientExport_RECEIVE_D, which is the name of the queue WebSphere ESB generated for its export.
In the original test client, we sent a plain string to the queue. In our updated scenario, you will send an XML-formatted string that represents the business object that we defined in the mediation module. This enables the default serializer to transform this XML message into a business object we can manipulate in the mediation. Following the interface and business object definition schema you created earlier, the XML string should look like this:
<?xml version="1.0" encoding="UTF-8"?> <ns:package_received xmlns:ns="http://PackageReceivedModule/PackageReceivedIF"> <packageReceivedBO> <message>Package Received - 24595023</message> </packageReceivedBO> </ns:package_received>
Therefore, the updated Main.java file sends this new message:
// private final static String messageText = "Package Received - 24595023"; private final static String messageText = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " + "<ns:package_received xmlns:ns=\"http://PackageReceivedModule/PackageReceivedIF\"> " + " <packageReceivedBO>" + " <message> Package Received - 24595023</message> " + " </packageReceivedBO> " + " </ns:package_received> ";
Moreover, remember that in WebSphere ESB, you are using the default JMS function selector that picks the appropriate operation on the service interface to call. You need to add a JMS header field (TargetFunctionName) to indicate the operation you are invoking (package_received):
... // Create MessageProducer and TextMessage MessageProducer queueSender = session.createProducer(q); TextMessage outMessage = session.createTextMessage(); outMessage.setText(messageText); // set target operation name for WESB default JMS function selector outMessage.setStringProperty("TargetFunctionName", "package_received"); // Set type and destination and send outMessage.setJMSType("package_received"); outMessage.setJMSDestination(q); ...
You will note that we have already made those code changes to the code, so there is nothing for you to do here.
You also need to do some JMS configuration to enable the J2EE client application (the service requester) to connect to the WebSphere ESB server using its JMS resources. Open the admin console for the test server (start the server if isn't already started), by going to the Servers view. Right-click on the WebSphere ESB v6.0 server and select Run administrative console.
The J2EE client application uses a JMS connection factory to connect to the JMS queue. You will create this connection factory on the server and then let the client application retrieve it from there. The client then opens a connection to the JMS queue on that server. Note that the client uses a specific port to communicate with the messaging engine on the server, which by default is 7276. However, the test server running within the tool will most likely use a different port, so you must configure the connection factory with the correct port number. To find out which port number your server uses, go to the administrative console and click on the Servers => Application Servers... node. Select the server named server1. The screen will now show information about that server, with a link called Ports, as shown in the Figure 11.
Figure 11. Find ports used by server
Select the Ports link to retrieve a list of the ports that your server is using. The port number that is used for JMS communication is called SIB_ENDPOINT_ADDRESS. In Figure 12, the port number is 7278.
Figure 12. Ports used by servers
Remember this port number.
Now, in the administrative console, navigate to Resources => JMS Providers => Default Messaging. Click the JMS connection factory link, and then New. Enter these values:
- JNDI name:
- Bus name:
- Provider endpoints:
The Provider endpoints definition should contain the port number for your messaging engine (which you looked up earlier); in our example, it was 7278. (The specific cell name varies based on individual configuration, so the name of the default SCA application bus may vary between installations.)
You can find more details about connecting to a non-default JMS provider port in the WebSphere Application Server Information Center.
Leave all other fields with their defaults and click on OK.
Save your changes and close the administrative console.
E. Set up the service provider
As we have mentioned earlier, the service provider that receives the JMS message is implemented using a message-driven bean. We will use the same application that we used in the previous article series; the appropriate enterprise archive (EAR) file is included in the download materials for this article.
To complete this task, you need to perform these general steps:
Import the EAR with the MDB into WebSphere Integration Developer.
Configure the deployment descriptor to listen on the queue generated by the WebSphere ESB mediation module's import.
Create an ActivitationSpec for the MDB on the WebSphere ESB server (since it is the same server being used to run the MDB EAR).
Now, the details:
In WebSphere Integration Developer, switch to the J2EE perspective. Before you import the PackageReceived.ear file, start up the WebSphere ESB test server in WebSphere Integration Developer, if it is not already started.
- When importing the EAR file, make sure you name the EAR project PackageReceivedEAR.
- Select WebSphere ESB Server v6.0 as the target server for the project. Leave all other fields as their defaults.
- After the import has completed, you should see a new EJB project called PackageReceived, with one MDB in it called PackageReceived.
Change the MDB's deployment descriptor to configure it to get messages from the queue on which WebSphere ESB puts them. Double-click the deployment descriptor to open it in the editor.
On the Bean tab for the PackageReceived MDB, remove the Message destination entry. You also need to change the WebSphere Bindings => Destination JNDI name of the queue that the message is received from to PackageReceivedModule/MDBImport_SEND_D, which was generated by WebSphere ESB for the mediation module's import. (If you look at the value of the messageSelector, you will see it is set to the JMSType that you set in the import binding of the mediation.) The deployment descriptor should now appear as shown Figure 13.
Figure 13. Updated MDB deployment descriptor
If you see any additional compile errors after the import, select Project => Clean... to rebuild the project and solve those errors.
You also need to do some JMS configuration on the server the MDB runs on, which for simplicity is our WebSphere ESB server. Open the admin console for the test server, by going to the Servers view. Right-click on the WebSphere ESB v6.0 server and select Run administrative console (assume the server is started; if it isn't started, do so now).
The service provider application uses a message-driven bean to receive messages from the ESB. This MDB is configured to use an activation specification that contains the definition of the queue, among other things. This is all standard J2EE business, and so we need to configure the activation specification before deploying the application. In the administrative console, expand Resources => JMS Providers => Default messaging.
- In the lower right section of the screen, click the link named JMS activation specification.
- On the following screen, click on New and enter the following values for the new activation specification:
- JNDI name:
- Destination JNDI name:
- Bus name:
(Again, the actual bus name may vary based on individual configuration.)
Figure 14. Activation specification
Leave all other default values and click OK. Save your changes.
This is all you need to do for the service provider.
F. Run an end-to-end test
You are finally ready to deploy the entire example to the test server and run it.
In WebSphere Integration Developer, from the top menu, select Project => Clean... => Clean all projects => OK. This action ensures consistency across the code generated for the project. In the Servers view, right-click on WebSphere ESB Server v6.0 and select Add and remove projects....
In the resulting dialog, select PackageReceivedModuleApp first, then click on Add >.
Next, add PackageReceivedEAR to the server, then click Finish. By adding them in this order, you ensure that the PackageReceivedModuleApp is loaded first, which is necessary because it sets up the destinations that are used by the MDB EAR. If the projects are loaded in the wrong sequence, you might see errors on loading of the ActivationSpec that the destination is not found.
Figure 15. Add available projects to configured projects
Note that the client application does not get deployed to the server, since it runs as a client and thus is not deployed.
When the publish step is completed, stop and start your server, using the Restart or the Stop and Start buttons in the tool. When restarted, both applications should start without problems. One way to ensure the applications have been started successfully is to launch the administrative console again and select Applications => Enterprise Applications. It should show a list of applications, including the two you just installed -- and all of them should be started (Figure 16).
Figure 16. Application status
To run the test client application, select Run => Run... from the WebSphere Integration Developer main menu.
In the following dialog, select the WebSphere v6.0 Application Client configuration, then click on New.
In this new configuration, select WebSphere ESB Server v6.0 as the WebSphere runtime, enter PackageReceivedClientEAR as the Enterprise application, and use PackageReceivedClient as the application client module. Also, check the Enable application client to connect to a server checkbox, and select Use specific server with the WebSphere ESB Server v6.0 option, as shown in Figure 17.
Figure 17. Select configuration
You can now click Run to start the client. Once the client completes, it will show the following in the console:
Figure 18. Application status messages in console
These console messages indicate that the message has been delivered to the first queue, from where it will be picked up by the mediation flow component, be logged, and then forwarded to the second queue, which delivers it to the MDB.
You can switch the console view between different processes, showing output for both the application client, as well as the server process:
Figure 19. Alternate console view
Switch your console output to the test server process.
Both the mediation flow component and the MDB run in the same server, so their output shows up in the console together.
Figure 20. Console messages for mediation flow component and message-driven bean
The database related printouts indicate that the message logger primitive has executed and the System.out messages come from the MDB.
This article described how to turn a simple, point-to-point JMS scenario, with a sender application and a receiver application, into one where you establish an ESB mediation between them. The ESB provides decoupling and separation of concerns, since things like logging can now be handled within the ESB mediation, rather than needing to be done in the provider and/or consumer code itself, thus establishing two core principles of SOA.
In our scenario, we used WebSphere ESB and its support for JMS protocol bindings to create the mediation. Even though we used JMS as the protocol on both sides of the ESB in this example, we will show in a future installment how you can run different protocols into and out of the mediation component, leaving it up to the ESB to handle the protocol switch.
In the next article, we will focus on a more Web services-oriented scenario, before we continue and tie both of these worlds (JMS/MQ and Web services) together. Stay tuned!
Oh, one more thing: As of Part 3 in this series, we will begin leveraging the new 6.0.2 release of both WebSphere ESB and the WebSphere Integration Developer tool!
More in this series
- Part 1: An introduction to using WebSphere ESB, or WebSphere ESB vs. SIBus
- Part 3: Adding Web services and promoting properties
|Sample application||PackageReceivedModule.zip||20 KB|
|Sample application EAR file 1||PackageReceivedClientEAR.ear||4 KB|
|Sample application EAR file 2||PackageReceived.ear||4 KB|
- Building an ESB with WebSphere Application Server V6 Part 3: A simple JMS messaging example
- An introduction to the IBM Enterprise Service Bus
- JMS Tutorial
- WebSphere Enterprise Service Bus product page
- Getting started with WebSphere Enterprise Service Bus and WebSphere Integration Developer
- Developing custom mediations for WebSphere Enterprise Service Bus
- Building a powerful, reliable SOA with JMS and WebSphere ESB
- Dynamic routing at runtime in WebSphere Enterprise Service Bus
- Tutorial: Invoking a Web service with a JMS client
- Service Component Architecture
- Redbook: Enabling SOA Using WebSphere Messaging