ESB in Practice

Create an Enterprise Service Bus in WebSphere Application Server V6

This article describes how to create a simple instance of an Enterprise Service Bus that performs transformation and routing, using IBM® WebSphere® Application Server Version 6 Messaging Resources.

Greg Flurry, Senior Technical Staff Member, IBM

Greg FlurryGreg Flurry is a Senior Technical Staff Member in IBM's Enterprise Integration Solutions group. His responsibilities include working with customers on service-oriented solutions and advancing IBM's service-oriented products.


developerWorks Professional author
        level

21 September 2005

Also available in Russian

Introduction

There are many ways to implement an ESB with IBM's products and other middleware vendor's products. IBM WebSphere Application Server Version 6 provides a particularly interesting platform for building an ESB. [Reinitz1] introduces the WebSphere Messaging Resources (also known as the SIBus) available in Version 6. The SIBus can be used to construct many ESB topologies. That article covers some concepts and terminology, while [Reinitz2] describes the set-up required to allow the SIBus to interact with SOAP/HTTP Web services. You can also find additional information about the SIBus from the WebSphere Application Server V6 InfoCenter under the Service Integration heading.

Two of the most commonly mentioned capabilities of an ESB are message transformation and message routing. This article builds on the techniques introduced in [Reinitz1] and [Reinitz2] and walks you through the creation of an instance of an ESB that implements message transformation and routing.

Figure 1 outlines the scenario we'll implement. The figure identifies a service requestor that uses, perhaps via a JAX-RPC compliant proxy, a service that offers a publish operation with the signature shown. The figure also identifies a service provider that offers a notify operation with the signature shown. You can see that there is a mismatch between what the service requestor wants and what the actual service provider can give. The role of the ESB that we'll build is to perform the necessary message transformation so that the service requestor can successfully communicate with the service provider. More precisely, the ESB we build must perform WSDL portType mapping.

Figure 1. PortType Mapping Scenario
PortType Mapping Scenario

Solution Analysis

First let's analyze the scenario to understand what sort of SIBus (hereafter called bus) topology we have to create. From an initial analysis, we must:

  1. Get the SOAP/HTTP request into the bus from the requestor. To do this, we'll need to create an inbound service in the bus using the requestor's WSDL. In order to define an inbound service, we'll first need to install the SOAP/HTTP endpoint listener application, as described in [Reinitz2].
  2. Transform the request from the requestor's form to the provider's form. This requires a mediation of a destination to allow message processing, in this scenario, transformation of the message. We have to create and install a mediation or mediation handler list to perform the transformation; in this scenario, we'll use XSLT to perform the transformation.
  3. Get the request out of the bus to the provider. To do this, we'll need to create an outbound service using the provider's WSDL. A prerequisite to defining an outbound service is the installation of the SOAP/HTTP service invoker, which is installed with endpoint listener.
  4. Transform the response from the provider's form to the requestor's form. Even though the response is void in both cases, a response is expected. The responses have namespace differences that must be handled. Since there is no real content, however, we'll cheat and insert into the response message a hard-coded message appropriate for the requestor. Even this requires creation and installation of mediation handler list to process the response.

This analysis gives us a good overall picture of what we need to do. We'll benefit by doing a bit deeper analysis, however, based on a more detailed understanding of the bus. Step 1 above requires that we create an inbound service. As shown in Figure 2, an inbound service consists of an endpoint listener that routes messages to an inbound service destination (represented by the yellow rectangle). The inbound service destination must exist prior to creating the inbound service. When we create an inbound service, the reply destination shown in the figure gets created automatically. This reply destination is used to route replies back to the requestor via the endpoint listener. There is a single default reply destination for all inbound services created using the Application Server administration console.

These facts of inbound service creation mean we have to make a couple of design decisions regarding the inbound service destination and the reply destination. For example, what destination do we use when we create the inbound service, and where (at what destination) do we transform the response message? Before answering, we need to study outbound service creation.

Figure 2. Inbound service
Inbound service

Step 3 requires that we create an outbound service. Doing so automatically creates destinations that represent the service and each port in the service definition, and connects them to a service invoker, as shown in Figure 3. In addition, creating an outbound service automatically routes replies from the service invoker to the default reply destination.

Figure 3. Outbound service
Outbound service

We have two options for creating the inbound service; we can reuse the outbound service destination created for us, or we can create a new destination to receive messages for the inbound service, and route those messages to the outbound service destination. Both options work; but in our case, there is no reason to introduce an additional destination, so we'll reuse the outbound services destination. This means, however, that we need to create the outbound service before creating the inbound service.

To do Step 4, we need to mediate a destination to transform the reply message. Since we'll create our bus topology with the administration console, that topology will use the single bus-wide default reply destination. In our simple scenario, we could mediate the default reply destination. In a realistic environment, however, that wouldn't work, since all replies for all inbound services flow through that single default reply destination. Therefore, we'll insert a reply intercept destination in the reply path to intercept and transform the reply message from the outbound service before that reply message gets to the default reply destination and eventually back to the requestor.

The net result is that we'll create a runtime topology like the one shown in figure 4. First we'll create the outbound service, which automatically creates the outbound service and outbound port destinations. We then create the inbound service, identifying the outbound service destination as the target destination for the inbound service. We can mediate the outbound service destination to transform the request message (a mediation is represented by a blue hexagon). We need to create the reply intercept destination manually. We can mediate the reply intercept destination to transform the response. It's important to understand that creating the reply intercept mediation does not impact the default routing of the response from the service invoker to the default reply destination. To force the reply to go first to the reply intercept destination, we need to modify the reverse routing path that is part of the context of the request message. We'll modify the reverse routing path in the mediation on the outbound service destination.

Figure 4. Runtime topology
Runtime topology

There is one final, but important, decision we need to make. We could build the topology in Figure 4 in an existing SIBus instance or create a new instance. For this simple ESB scenario, we'll create a new bus, since we don't need to interact with any additional bus artifacts.

We've completed our analysis and now we can start building! For this article, we'll perform all of the steps necessary to implement our solution within the WebSphere Test Environment provided the Rational V6 development tools, either Application Developer, or Software Architect. These tools allow you to use a fully functional WebSphere Application Server version 6, and leverage all the benefits of an integrated development environment. In this case, we will use the tool to create the topology in figure 4, develop the mediation handlers and test the whole thing. This article assumes familiarity with the Rational tooling, and we won't cover the details of development practices not specific to the SIBus.


Create the outbound service

Before we can create the outbound service, we need to create a new bus instance and call it TypeMap. See [Reinitz1] for a description of the procedure for creating a new bus instance using the administrative console.

To create the outbound service, we need to provide the administration console with a WSDL document describing the service provider. Listing 1 shows the important snippets of the WSDL file for a service named SubscriberS1T1Service with portType SubscriberS1T1. It follows the best practice of being defined according to the wrapped document literal form. It also follows the best practice of importing the schema for the parameters rather than embedding the schema directly. Listing 2 shows the schema.

Listing 1. WSDL for SubscriberS1T1Service
<wsdl:definitions xmlns:impl="http://pubsubhub.ibm.com"
    xmlns:intf="http://pubsubhub.ibm.com" ... >

 <wsdl:types>
  <schema xmlns:sub="http://www.sub1.com/sub1doc" ... >
   <import namespace="http://www.sub1.com/sub1doc" 
      schemaLocation="sub1Topic1.xsd"/>
   <element name="notify">
    <complexType>
       <sequence>
        <element name="doc" nillable="true" type="sub:Sub1Topic1Doc"/>  
       </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>

 <wsdl:message name="notifyRequest">
      <wsdl:part element="impl:notify" name="parameters"/>
 </wsdl:message>

 <wsdl:portType name="SubscriberS1T1">
      <wsdl:operation name="notify">
         <wsdl:input message="impl:notifyRequest" 
        name="notifyRequest"/>
         <wsdl:output message="impl:notifyResponse" 
        name="notifyResponse"/> 
      </wsdl:operation>
   </wsdl:portType>
  ...
 <wsdl:service name="SubscriberS1T1Service">
      <wsdl:port binding="impl:SubscriberS1T1SoapBinding" 
    name="SubscriberS1T1">
         ...
      </wsdl:port>
 </wsdl:service>
Listing 2. Schema for SubscriberS1T1Service
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.sub1.com/sub1doc" ... >
  <complexType name="Sub1Topic1Doc">
    <sequence>
      <element name="firstName" type="string"/>
      <element name="lastName" type="string"/>
    </sequence>
  </complexType>
  <element name="Sub1Topic1Doc" 
     type="tns:Sub1Topic1Doc"></element>
</schema>

To create the outbound service, do the following:

  1. In the administration console, select Service integration => Buses => TypeMap => Outbound Services => New.
  2. In the New Outbound Service dialog, identify the location of the WSDL document defining the service provider represented by the outbound service. We'll provide a URL, as shown.
  3. Click Next.
    Figure 5. Create an outbound service, step 1
    Creating outbound service, step 1
  4. In the next dialog, we need to select one service, since there can be multiple services defined in the document. The WSDL for SubscriberS1T1Service contains only a single service, so we'll use the default choice. Click Next.
  5. In the Select Ports dialog, select the port on which to send messages. Again, while there can be multiple ports, SubscriberS1T1Service contains only a single port, which should be selected by default. Click Next.
    Figure 6. Creating outbound service, step 3
    Creating outbound service, step 3
  6. In the Name the Outbound Service and Destinations dialog shown in Figure 7, do the following:
    1. Enter an Outbound service name, as shown. This is an administrative name for your convenience and can be anything you wish, but it must be unique in the bus instance.
    2. Enter Service destination name. The administration console creates a default name, which is a combination of the WSDL target namespace and the service name. In this case, we'll use the default name.
    3. Finally, enter a name for the outbound port destinations in the Port destination name field. Again, the administration console picks default names, which are a combination of the WSDL target namespace, the service name and the port name. The name for the single port is fine for this case.
    4. Click Next.
      Figure 7. Creating outbound service, step 4
      Creating outbound service, step 4
  7. In the next dialog, click Finish.

We've now created the outbound service. It is a good idea to save the configuration after this step and after every critical step in creating the topology shown in Figure 4.


Create the inbound service

To define the inbound service, we need to provide the administration console a WSDL document describing the service requestor's interface expectations. Listing 3 shows the important snippets of the WSDL file for a service named PublisherS1T1Service with portType PublisherS1T1. It follows the best practice of being defined according to the wrapped document literal form. It also follows the best practice of importing the schema for the parameters, rather than embedding the schema directly. Listing 4 shows the schema. Comparing the WSDL for SubscriberS1T1Service and PublisherS1T1Service, you can see that we'll need to transform two aspects of the request message: the operation and the body, including the namespace.

Listing 3. PublisherP1T1Service WSDL
<wsdl:definitions xmlns:impl="http://pubsubhub.ibm.com" 
    xmlns:intf="http://pubsubhub.ibm.com" ... >
 <wsdl:types>
  <schema xmlns:sub="http://www.sub1.com/sub1doc" ... >
    <import namespace="http://www.pub1.com/pub1doc" 
        schemaLocation="pub1Topic1.xsd"/>
 <element name="publish">
    <complexType>
       <sequence>
        <element name="doc" nillable="true" type="sub:Pub1Topic1Doc"/>  
       </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>

<wsdl:message name="publishRequest">
      <wsdl:part element="impl:publish" name="parameters"/>
 </wsdl:message>
 <wsdl:portType name="PublisherS1T1">
      <wsdl:operation name="publish">
         <wsdl:input message="impl:publishRequest" name="publishRequest"/>
         <wsdl:output message="impl:publishResponse" name="publishResponse"/> 
      </wsdl:operation>
  </wsdl:portType>
  ...
   <wsdl:service name="PublisherP1T1Service">
      <wsdl:port binding="impl:PublisherP1T1SoapBinding" name="PublisherP1T1">
         ...
      </wsdl:port>
  </wsdl:service>
Listing 4. PublisherP1T1Service schema
<?xml version="1.0" encoding="UTF-8"?>
<schema 
 targetNamespace="http://www.pub1.com/pub1doc" ... >
  <complexType name="Pub1Topic1Doc">
    <sequence>
      <element name="firstName" type="string"/>
      <element name="lastName" type="string"/>
      <element name="address" type="string"/>
   </sequence>
  </complexType>
  <element name="Pub1Topic1Doc" type="tns:Pub1Topic1Doc"></element>
</schema>

To crete the inbound service, do the following:

  1. In the administration console, select Service integration => Buses => TypeMap => Inbound Services => New.
  2. In the New Inbound Service dialog, we need to identify the inbound service destination and the location of the WSDL document defining the service requestor's expectations. As described above, we'll select the outbound service destination created automatically during the creation of the outbound service, and provide a URL for the WSDL, as shown below.
  3. Click Next.
    Figure 8. Define an inbound service, step 1
    Define an inbound service, step 1
  4. In the next dialog, we need to select a service. The WSDL for PublisherS1T1Service contains only one service, so we'll use the default. Click Next.
  5. You'll see the dialog shown in Figure 9, do the following:
    1. Enter an Inbound service name. This is an administrative name for your convenience and can be anything you wish. The administration console picks a default name, which is a combination of information about the binding, the destination name, and other names; it is unique, but generally long and unintelligible. In our case, we'll use the name of the service.
    2. Select an endpoint listener and click Next.
      Figure 9. Define inbound service, step 3
      Define inbound service, step 3
    3. In the next dialog, click Finish.

We've now created the inbound service.


A bit of fix-up

Because of a limitation in Application Server V6.0.1 and earlier, the best practice of using a separate schema for the parameters in a WSDL document results in problems you won't discover until run-time. The administrative console automatically loads WSDL documents into the SDO repository used by the SIBus to process Web services messages. (See [Reinitz2] and the Application Server InfoCenter for more information on the SDO repository.) However, it doesn't load separate schemas referenced by WSDL documents into the repository. This means we need to load the schemas manually using the command line tool wsadmin. The general form of the wsadmin commands used is:

set sdoRepBean [$AdminControl queryNames *:*, type=SdoRepository]
puts [$AdminControl invoke $sdoRepBean importResource 
{"resourceName" "sourceFileName"}]

First, open a command shell and go to the directory <install_path>/profiles/<profile>/bin where <install_path> refers to the path where the Application Server is installed and <profile> refers to the profile you are using (probably default). Type wsadmin to start the tool; then enter the set command shown above. The following commands are used to load the schemas for the two services via a file name:

puts [$AdminControl invoke $sdoRepBean importResource {"pub1Topic1.xsd"
"C:/workspace/MyServices/WebContent/wsdl/com/ibm/pubsubhub/pub1Topic1.xsd"}]
puts [$AdminControl invoke $sdoRepBean importResource {"sub1Topic1.xsd" 
"C:/workspace/MyServices/WebContent/wsdl/com/ibm/pubsubhub/sub1Topic1.xsd"}]

It may be better to use a URL to locate the schema, but the file system locations used above seem to work. You need to issue the above commands only one time. If you want to determine whether something is already in the SDO repository, you can use the following command:

puts [$AdminControl invoke $sdoRepBean listResources].


Create the intercept reply destination

We need to create a destination that intercepts the reply so that the message can be transformed appropriately. To do this, complete the following steps:

  1. In the administration console, select Service integration => Buses => TypeMap => Destinations => New.
  2. In the Create New Destination dialog, make sure that Queue is selected, then click Next.
    Figure 10. Create a destination
    Create a destination
  3. In the Create New Queue dialog, enter FixReply in the Identifier field. Add an optional description and click Next.
    Figure 11. Create a destination, step 1
    Create a destination, step 1
  4. In the next dialog, you must assign the queue to a bus member. There is only one for our scenario, so simply click Next.
  5. In the following dialog, click Finish.

We've now created the intercept destination.


Mediate the inbound service destination

Now we need to transform an incoming message in a form appropriate for PublisherP1T1Service to a form appropriate for SubscriberS1T1Service. To do so, we'll mediate the inbound service destination, as shown in Figure 4. A mediation consists of a list of mediation handlers; in our case, the list contains only the single mediation handler described in this section. We must develop a mediation handler to transform the message from one form to another. Since this requires XML-to-XML transformation, the mediation handler will use XSLT to do the actual transformation. [Reinitz3] provides more detail on writing and deploying mediations.

The XSLT must do two things. First it must deal with the SOAP wrapper around the message; that means ignoring the wrapper around the incoming message and inserting the wrapper around the outgoing message. Second it must transform the body of the message; that means transforming a document described by the type Pub1Topic1Doc in namespace http://www.pub1.com/pub1doc to a document described by the type Sub1Topic1Doc in the namespace http://www.sub1.com/sub1doc. The Rational tools include an XML-to-XML mapping tool that can help with creation of the XSLT, shown below.

Listing 5. XSLT to transform request message
<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform
    version="1.0" xmlns:xalan=http://xml.apache.org/xslt
    xmlns:sub1="http://www.sub1.com/sub1doc" 
    xmlns:pub1="http://www.pub1.com/pub1doc" 
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:q0="http://pubsubhub.ibm.com" exclude-result-prefixes="pub1">
  <xsl:output method="xml" encoding="UTF-8" indent="yes" 
      xalan:indent-amount="2" /> 
  <xsl:strip-space elements="*" /> 
  <xsl:template match="soapenv:Envelope/soapenv:Body/q0:publish">
    <soapenv:Envelope 
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:q0="http://pubsubhub.ibm.com" 
  xmlns:q1="http://www.sub1.com/sub1doc" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:tns="http://www.sub1.com/sub1doc" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
        <q0:notify>
          <q0:doc>
            <sub1:firstName>
              <xsl:value-of select="pub1:doc/pub1:firstName/text()" /> 
            </sub1:firstName>
            <sub1:lastName>
              <xsl:value-of select="pub1:doc/pub1:lastName/text()" /> 
            </sub1:lastName>
          </q0:doc>
        </q0:notify>
      </soapenv:Body>
    </soapenv:Envelope>
  </xsl:template>
 </xsl:stylesheet>

Mediation handlers run in an EJB container. We'll use Rational tooling to develop the mediation handler and deploy it for use in the bus.

First create an EJB project. In that project, create a Java class (not an EJB!) named P1S1Transform that extends the interface com.ibm.websphere.sib.mediation.handler.MediationHandler. Listing 6 shows the framework of the mediation handler named P1S1Transform used to transform the message. It first gets two context properties that will be set on the inbound/outbound service destination; these context properties will be set on the destination and allow customization of the mediation behavior based on the destination for which it runs. The property traceOn causes the mediation handler to write information in the log, or if running in a development tool, in the console. The property xsltName gives the mediation handler the name of the file containing the XSLT definition.

Listing 6. P1S1Tranform mediation handler
public class P1S1Transform implements MediationHandler {

    protected String xsltName = null;
    protected boolean traceOn = false;
    protected SIMessageContext siMC = null;
    protected SIMessage message = null;
    protected String defaultOutputFormatString = SIApiConstants.JMS_FORMAT_BYTES;

    public boolean handle(MessageContext messageContext) throws
     	MessageContextException {

	   String mediationName = "P1S1Transform";
	   // get the needed context properties
	   traceOn = ((Boolean)messageContext.getProperty
		("traceOn")).booleanValue();
	   xsltName = (String) messageContext.getProperty
		("xsltFileName");	
	   if (traceOn) {
	    	System.out.println("**** Entering " + 
		  mediationName + " Mediation ****");
	    System.out.println
	    	  ("on Destination - " 	+ 
		  ((SIMessageContextProxyImpl) 
		  messageContext).getSession().getDestinationName());
	    System.out.println
	    	  ("reading properties - traceOn, xsltFileName");
	    System.out.println
                  ("traceOn is " + traceOn + "  xsltFileName is " + xsltName);
		}
		&
	}
}

Listing 7 shows the important aspects of transforming the message. We first get the message as a byte array, the necessary input for the XSLT transformation. Next, we get the XSLT definition and transform the message using a javax.xml.transform. The details of using the transformer are not shown. Next we replace the current message with the transformed message. We then modify the format of the message from that of the inbound service to that of the outbound service. This step deserves a bit of explanation.

Listing 7. P1S1Tranform mediation handler, transformation code
// Convert the MessageContext into an SIMessageContext
	siMC = (SIMessageContext) messageContext;
	// Retreive the message from the session
	message = siMC.getSIMessage();
		
	// Transform the message 
	try {
		DataGraph graph = 
            message.getNewDataGraph(SIApiConstants.JMS_FORMAT_BYTES);
		DataObject body = graph.getRootObject();
		if (body.isSet("data")) {
			// Grab the bytes
			byte[] payload = body.getBytes("data/value");

			// Transform the bytes from P1 format to S1 format
			String transform = readFileAsString(xsltName);
			payload = XSLTTransform.transform(payload, transform);

		       // Replace the payload in the data graph
		       body.setBytes("data/value",payload);
		        
		       // Replace the contents of the message with the new graph
		       String outputFormatString = 
"SOAP:dest:TypeMap:http://pubsubhub.ibm.com:SubscriberS1T1Service,
http://pubsubhub.ibm.com,SubscriberS1T1Service,SubscriberS1T1";
		       message.setDataGraph(graph, SIApiConstants.JMS_FORMAT_BYTES);
		       DataGraph newGraph = message.getNewDataGraph(outputFormatString);
		       message.setDataGraph(newGraph, outputFormatString);
			
		} else {
			...
		}
			
		return true;
	} catch (Exception e) {
		...
	}

The bus uses a format string to understand the format of the message. See the Application Server InfoCenter for more detail. The format string identifies, for example, the WSDL and schema that define the message. You can think of the format string as a pointer into the SDO repository. The bus uses the format string to produce an SDO that a mediation can use to examine and change the message. For SOAP messages, the format string syntax is:

SOAP:<SDO repository key>,<service namespace>,<service name>,<port name>


The <SDO repository key> format for an inbound/outbound service is:

in:<busname>:<inbound service name>
dest:<busname>:<service destination name>


Therefore, to put the message into the proper format for the outbound service, P1S1Transform must use the format string:

SOAP:dest:TypeMap:http://pubsubhub.ibm.com:SubscriberS1T1Service,
http://pubsubhub.ibm.com,SubscriberS1T1Service,SubscriberS1T1

Remember that we must modify the reverse routing path (the path taken by the reply or response) so that the reply goes to the reply intercept destination before going to the default reply destination. Listing 8 shows the code that accomplishes that. It is placed in the code segment of Listing 7 just before the catch statement. The code first gets the current reply or reverse routing path. It then inserts the reply intercept destination called FixReply in the path before the default reply destination. Finally, it replaces the message's reverse routing path with the modified path.

Listing 8. P1S1Tranform mediation handler, routing the reply
// set reply (reverse routing) path
List rrp = message.getReverseRoutingPath();
SIDestinationAddress fixup = 
   SIDestinationAddressFactory.getInstance().
      createSIDestinationAddress("FixReply", false);
// insert before default reply
rrp.add(0, fixup);
message.setReverseRoutingPath(rrp);

Now the development of the mediation handler is complete, and we need to deploy it to create a mediation or mediation list. To do this, complete the following steps:

  1. Find the deployment descriptor for the EJB project and open it.
  2. At the bottom of the deployment descriptor editor, select the Mediation Handlers tab.
  3. You'll see an empty list of mediation handlers. Click Add.
  4. In the Define Mediation Handler dialog, we'll enter the mediation handler attributes. The two that are important for our scenario are Name and Handler class. The name can be anything, but it's a good idea to give it the name of the mediation handler class (without the package). The class must be the fully-qualified class name. Click Browse and type the first few letters of the handler class name, then select it from the list.
  5. Click Finish.
    Figure 12. Define a mediation handler in deployment descriptor
    Define a mediation handler in deployment descriptor

    The deployment descriptor editor automatically places the mediation handler in a list with the same name as the mediation handler; the result is shown in Figure 13. You can put the handler in other lists, but that's not necessary for our scenario.


    Figure 13. Deployed mediation handler
    Deployed mediation handler
  6. Save the deployment descriptor. Make sure the EAR containing the mediation handler EJB project is deployed on your server.

Now we need to tell the bus about the mediation handler list. To do so, complete the following steps:

  1. Select Service integration => Buses => TypeMap => Mediations => New.
  2. Enter a name in the Mediation name field. It's a good idea for the name to match the name of the mediation handler list.
  3. In the Handler list name field, enter the name of a mediation handler list defined in a deployed EAR.
  4. Click OK.
    Figure 14. Define the mediation
    Define the mediation

Next we need to mediate the inbound/outbound service destination. To do that:

  1. Select Service integration => Buses => TypeMap => Destinations.
  2. In the Destinations dialog, select the outbound service destination whose type is Web service, and click Mediate.
    Figure 15. Destinations
    Destinations
  3. In the Mediate Destinations dialog, you can select the mediation for the destination. Since there's only one mediation defined at this time, it is already selected. Click Next.
    Figure 16. Select a mediation, step 1
    Select a mediation, step 1
  4. In the next dialog, select the node where the mediation will run. In the test environment, there is only one, so just click Next.
  5. The last dialog confirms the choices made in the previous dialogs. Click Finish.
  6. The dialog shown in Figure 17 displays, confirming that the outbound service destination is mediated by P1S1Transform.
    Figure 17. Confirmation of mediation
    Confirmation of mediation

Mediate the reply intercept destination

Remember that we need to transform the response as well as the request. In reality, we'll simply replace the response from the actual service provider with the response expected by the inbound service. We can do this because of the void response for both the requestor and provider. We'll mediate the reply intercept destination FixReply. We need to create a new mediation handler, called P1VoidReply. For this scenario, We'll create the class in the same EJB project as P1S1Transform to minimize the number of applications running on the application server. As a mediation handler, P1VoidReply has the same general form as P1S1Transform, but requires only a single context property, traceOn. P1VoidReply must access the reply message in the same way P1S1Transform accessed the request message.

Listing 9 shows the code snippet for P1VoidReply that does the work of replacing the message. The handler simply replaces the reply body with the string newBody, as shown. Finally the new body must be stored in the format appropriate for the inbound service; you can see the format string used to accomplish that. The format string follows the syntax described above for an inbound service.

Listing 9. FixReply mediation handler
   try {
	String newBody = "<env:Envelope 
  xmlns:ns1=\"http://pubsubhub.ibm.com\" 
  xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">
  <soapenv:Body xmlns:xsi=
  \"http://www.w3.org/2001/XMLSchema-instance\" 
  xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
  xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" 
  xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
  <p895:publicshResponse 
  xmlns:p895=\"http://pubsubhub.ibm.com\"/>
  </soapenv:Body></env:Envelope>";

	// Replace the contents of the message with the new graph
	DataGraph newGraph = 
    message.getNewDataGraph(SIApiConstants.JMS_FORMAT_TEXT);
	    newGraph.getRootObject().set("data/value", newBody);
message.setDataGraph(newGraph, SIApiConstants.JMS_FORMAT_TEXT);

// get the message represented as a SOAP message 
	String format = "SOAP:inport:TypeMap:PublisherP1T1Service:SOAPHTTPChannel1InboundPort,
http://www.ibm.com/websphere/sib/webservices/flurryNode01Cell/TypeMap/Service,
PublisherP1T1Service,SOAPHTTPChannel1InboundPort";
	DataGraph soap = message.getNewDataGraph(format);
	message.setDataGraph(soap, format);
   } catch (Exception e) {
	...
   }
   return true;

Set the context properties

Remember that we've coded the mediation handlers to depend on some context properties that must be set on the mediated destinations. If a mediation handler tries to get context properties that aren't defined, you may experience runtime failures that are difficult to diagnose.

To set a context property, do the following:

  1. Select Service integration => Buses => TypeMap => Destinations=>destination_name=>Context Properties=>New.
  2. Enter a name, type and value, then click OK. Figure 18 shows the result of defining the context properties traceOn and xsltFileName for the outbound service destination.
    Figure 18. Outbound service destination context properties
    Outbound service destination context properties
    Figure 19 shows the result of defining the context property traceOn for the reply intercept destination.
    Figure 19. Reply intercept destination context properties
    Reply intercept destination context properties

Test the bus scenario

After completing all the set-up described in the previous sections, be sure to save the configuration and restart the server for the configuration changes to take effect.

The Rational development environments are a great place to test. You can leverage all the debug capabilities available in those tools, such as breakpoints, examination of variables, and so on. You can create a client of the inbound service defined on the bus by using the WSDL explorer that is part of the Rational tools, or by creating a client using the Web service client wizard, also part of the Rational tools.

You can access the WSDL for the inbound service on the bus using the generic syntax http://<server:port>/sibus/wsdl/<bus_name>/<inbound_service_name>. For Application Server V6.0.1 or earlier, you should drive the tooling with the WSDL that you used to define the inbound service, and change the endpoint address. This is due to the limitation regarding external schema described earlier, which prevents tool usage of the WSDL created automatically by the bus to describe the inbound service.

One other hint that may be useful when testing and debugging scenarios that use the WebSphere Messaging Resources. If you set the trace capabilities of the application server to *=info: SIBMessageTrace=all, you'll see some useful diagnostic information placed in the trace.log file for the application server profile. This trace information is especially useful in detecting invalid format strings.


Conclusion

This article described how to use Application Server V6 Messaging Resources to create a simple ESB scenario that does transformation and routing in the context of Web services. The article described the details of setting up the required inbound and outbound services, and the required destinations and mediation handlers. This basic example should help you create more sophisticated ESB scenarios. For more complex scenarios, check out the IBM Redbook Patterns: SOA with an Enterprise Service Bus in WebSphere Application Server V6 for a discussion of more complex scenarios.

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 WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, SOA and web services, Architecture
ArticleID=94194
ArticleTitle=ESB in Practice
publish-date=09212005