Using IBM WebSphere Enterprise Service Bus (hereafter called ESB) integration developers can create mediations to support intelligent interconnectivity between service requestors and service providers. In Websphere ESB, mediation modules provide service virtualization to insulate the requestors from the interface, protocol, and identity of the providers.
Virtualization of identity implies that a requestor sees the mediation module, or mediation, as the provider, and that the mediation can route that request to a "real" provider. For requestors and providers interacting via Web services, integration developers can use ESB to identify the endpoint address of the real provider at development time. However, ESB does't currently provide a convenient way to change that endpoint address at runtime.
Why does this matter? In many situations the choice of the real provider must be made by a mediation at runtime rather than at development time or deployment time. Consider, for example, driving the choice of the real provider using information derived from a services registry. In this situation, the mediation queries the registry and expects the registry to give it service metadata containing the endpoint address of the provider matching the query. As an alternative, the mediation might expect the registry to give it a list of service metadata containing endpoint addressees matching the query, and use additional information, perhaps from the request itself, to choose the proper service and endpoint address. In such situations, the endpoint address used at development time is simply a placeholder, and is rarely used.
In this article you'll learn how to circumvent the current restrictions in ESB and implement dynamic routing at runtime for Web services. First we'll describe the normal technique for building an ESB mediation that allows only static, or development time, routing, then show you the technique used to achieve dynamic routing at runtime. The approach uses a custom mediation primitive that replaces some of the built-in actions of the ESB mediation framework. Note that you must be using WebSphere ESB V6.0.1.1 (or WebSphere Process Server V6.0.1.1, which includes ESB) to use the technique shown in this article.
This section shows very briefly how to construct an ESB mediation module that supports only static routing. This section assumes you're familiar with the general activities involved in developing an ESB mediation module using WebSphere Integration Developer. Check out "Getting started with WebSphere Enterprise Service Bus and WebSphere Integration Developer" for a great introduction to developing mediation modules using Integration Developer.
Listing 1 shows the WSDL for the Web service (SOAP/HTTP) that acts as the real provider; the provider simply echoes a complex data structure. This WSDL file is placed in a Business Integration Library project called Resources, which makes it easy to reference the WSDL file from multiple projects in an Integration Developer workspace.
Listing 1. BigEcho service definition
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://big.com"
xmlns:impl="http://big.com"
xmlns:intf="http://big.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace="http://big.com"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:impl="http://big.com" xmlns:intf="http://big.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<complexType name="BEData">
<sequence>
<element name="one" nillable="true" type="xsd:string"/>
<element name="two" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="echoResponse">
<complexType>
<sequence>
<element name="echoReturn" nillable="true" type="impl:BEData"/>
</sequence>
</complexType>
</element>
<element name="echo">
<complexType>
<sequence>
<element name="d" nillable="true" type="impl:BEData"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
<wsdl:message name="echoRequest">
<wsdl:part element="impl:echo" name="parameters"/>
</wsdl:message>
<wsdl:message name="echoResponse">
<wsdl:part element="impl:echoResponse" name="parameters"/>
</wsdl:message>
<wsdl:portType name="BigEcho">
<wsdl:operation name="echo">
<wsdl:input message="impl:echoRequest" name="echoRequest"/>
<wsdl:output message="impl:echoResponse" name="echoResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="BigEchoSoapBinding" type="impl:BigEcho">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="echo">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="echoRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="echoResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="BigEchoService">
<wsdl:port binding="impl:BigEchoSoapBinding" name="BigEcho">
<wsdlsoap:address location="http://localhost:9081/BigEcho/services/BigEcho"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
Figure 1 shows the Business Integration perspective view after copying the WSDL file into the Resources Library project. Note the presence of the port (named BigEcho), the interface (WSDL port type, also named BigEcho) and the data type (BEData) used in the interface.
Figure 1. BigEcho in Library
Create a mediation module named StaticRoute that references the Resources Library in its dependencies, using the techniques demonstrated in "Getting started with WebSphere Enterprise Service Bus and WebSphere Integration Developer". StaticRoute has a single export, a single import, and the mediation component. Figure 2 shows the Integration Developer Assembly Diagram that is the result of:
- Creating the mediation module that contains a mediation component automatically named Mediation1.
- Adding to the mediation module an import automatically named Import1 and an export automatically named Export1.
- Assigning the BigEcho interface to Import1 and Export1.
- Generating a Web services binding (HTTP) for Export1.
- Generating a Web services binding (HTTP) for Import1.
- Selecting BigEcho.wsdl for the binding for Import1.
- Wiring Export1 to Mediation1 and Mediation1 to Import1.
Figure 2. StaticRoute mediation module assembly
Figure 3 shows the binding information for Import1. You can see the confirmation of the information from the BigEcho WSDL in Listing 1.
Figure 3. Import1 binding
Figure 4 shows the operation mapping for the mediation module in the Mediation Flow editor, which in this case is very simple: the echo operation on the export maps to the echo operation on the import.
Figure 4. Operation mapping for StaticRoute
Figure 5 shows the simple request flow where the Input node (representing the request from Export1) is wired to the Callout node (representing the request to Import1). This arrangement passes an incoming request directly from the export to the import. If any additional processing of the request, such as transformation, is needed, the mediation primitives to perform the processing would be wired between the Input and the Callout.
Figure 5. StaticRoute Request flow
Figure 6 shows the simple response flow where the CalloutResponse node (representing the response from Import1) is wired to the InputResponse node (representing the response to Import1). This arrangement passes an incoming response directly from the import to the export. If any additional processing of the response, such as transformation, was needed, the mediation primitives to perform the processing would be wired between the CalloutResponse and the InputResponse.
Figure 6. StaticRoute Response flow
Once the mediation has been saved and deployed to the test server (either ESB or WebSphere Process Server), you can test the mediation module. We used a simple Java™ client that calls a proxy generated from the WSDL file created for Export1.
To generate a proxy from the Business Integration perspective, do the following:
- Open the Physical Resource view and expand the StaticRoute project, as shown in Figure 7.
- Right-click on the interface Export1_BigEchoHttp_Service.wsdl (which describes the mediation module as a Web service and is automatically generated when creating the mediation module), and select Web Services => Generate Client
- Put the client proxy in a convenient project.
We created a simple Java client that invokes the proxy, which in turn, invokes the mediation module.
Figure 7. Physical resources view of StaticRoute
The BigEcho service implementation we used ran in the same test environment as the mediation and so prints the following message to the test environment console "+++ Got to BigEcho" showing that the mediation module invokes the BigEcho service.
Consider what needs to happen if you want to change the provider used by the StaticRoute mediation. The most obvious solution is to change the endpoint address used by the Import1 binding at runtime. Unfortunately, the current version of ESB doesn't support such actions. You could modify the StaticRoute mediation and add a second import that supplies an alternative provider; the choice of which import to use could be made at runtime. While adding new alternatives at development time is fine in some situations, it won't work in all situations, such as a case where the actual provider endpoint address is to be derived from a services registry, such as an implementation of UDDI.
We'll implement a form of dynamic routing using a mediation containing custom mediation primitives. "Developing custom mediations for WebSphere Enterprise Service Bus" describes the basic techniques for using custom mediation primitives. We'll create a DynamicRoute mediation module that references the Resources library in its dependencies. DynamicRoute has a single export, a single import, and the mediation. After doing the following:
- Creating the mediation module that contains a mediation component automatically named Mediation1
- Adding to the mediation module an import automatically named Import1 and an export automatically named Export1
- Assigning the BigEcho interface to Import1 and Export1
- Generating a Web services binding (HTTP) for Export1
- Generating a Web services binding (HTTP) for Import1
- Selecting BigEcho.wsdl for the binding for Import1
- Wiring Export1 to Mediation1 and Mediation1 to Import1
you see that the Assembly Diagram appears just like that of the StaticRoute mediation in Figure 2.
The operation mapping for the DynamicRoute mediation module, again very simple, is the same as for the StaticRoute mediation shown in Figure 4. The simple response flow where the CalloutResponse is wired to the InputResponse is also identical to the StaticRoute mediation, as shown in Figure 6. The significant difference is in the request flow, where we introduce the custom mediation primitive, as shown in Figure 8.
Figure 8. DynamicRoute request flow
We have inserted a custom mediation primitive named Router, and wired the Input node to its input terminal, as expected. Note, however, that the output terminal of Router is wired to the InputResponse node, not to the Callout node. This means that the request flows to Router, but does not flow through it to Import1. Instead, Router must invoke the Web service represented by Import1. Further, since Import1 is not used, the response cannot come back into the response flow via Import1, so the response flow is unused. Instead, Router sends the response it receives to Export1 via the InputResponse node.
- Select the Router mediation primitive and show its properties.
- Click the Details tab, and click Define.
- On the Define Custom Mediation dialog of the Define Custom Mediation wizard, click Next..
- In the Specify Message Types dialog, make sure Message Root is set to / and click Next. Using / as the message root allows the mediation primitive to see the entire Service Message Object, including any context and headers.
- In the Create a new interface dialog, click Next.
- In the Generate Java Implementation dialog, click Finish, and save the mediation flow.
- After saving the mediation flow, return to the Assembly Diagram and save it.
- Right-click on Mediation1 and select Merge Implementation
- Click OK in the resulting dialog to merge the implementation. You'll see the Merge Implementation wizard, as shown in Figure 9.
Figure 9. Merge Implementation Wizard
- Ensure that the box under Create Java Component is checked and then click OK.
- The Mediation Flow editor displays. Return to the Assembly Diagram and save the mediation.
- In the main menu, click Project => Clean.
- Click OK in the pop-up dialog. The mediation in the Assembly Diagram should now look like Figure 10.
Figure 10. Mediation showing custom mediation
- The CustomMediation1Partner represents the Router custom mediation primitive. Now you need to wire CustomMediation1Partner to Import. In the first Add Wire dialog, click Yes to create a matching interface.
- In the second Add Wire dialog, shown in Figure 11, click No. This simplifies the job of the router by avoiding mapping between Java objects and Service Message Objects.
Figure 11. Add Wire dialog
- The Assembly Diagram should look like Figure 12. Save the mediation module.
The basic requirements for routing via a custom mediation primitive are now in place. Now let's look at how to implement the same sort of static routing described above, then we'll make the additions necessary to create dynamic routing.
Figure 12. Mediation with custom mediation wired to import
- Return to the Mediation Flow editor.
- Select the Router primitive and view the properties.
- Select the Implementation tab.
- Click Open Java Editor and click OK in the resulting dialog.
- Scroll to the
execute()method. - Now you can enter the code that allows the Router mediation primitive to invoke the Web service represented by Import1. It uses the Service Component Architecture framework (SCA) that ESB is built on. Listing 2 shows the necessary code. You need to understand that the DataObject input parameter (input1) is the entire SMO, so that the mediation primitive can see the context and headers, as well as the body. Similarly, the DataObject returned must be the entire SMO. Of course, the input parameter contains the input message, or payload, from the original requestor, while the returned parameter contains the response from the provider.
Listing 2. Service invocation in custom mediation primitive with static addresspublic DataObject execute(DataObject input1) { System.out.println("... In Router mediation primitive"); // (1) Create endpoint reference EndpointReference eRef = EndpointReferenceFactory.INSTANCE .createEndpointReference(); eRef.setAddress("http://localhost:9081/BigEcho/services/BigEcho"); // (2) invoke service and extract response payload Service echoService = (Service) ServiceManager.INSTANCE.getService("BigEchoPartner", eRef); DataObject request = input1.getDataObject("body/echo"); DataObject response = (DataObject) echoService.invoke("echo", request); DataObject responseBody = response.getDataObject("echoReturn"); // (3) create response message and response BOFactory boFactory = (BOFactory) ServiceManager.INSTANCE .locateService("com/ibm/websphere/bo/BOFactory"); DataObject message = boFactory.createByMessage("http://big.com", "echoResponse"); DataObject myRes = boFactory.createByElement("http://big.com", "echoResponse"); // (4) add response and return myRes.setDataObject("echoReturn", responseBody); message.setDataObject("parameters", myRes); input1.setDataObject("body", theMes); return input1; }
Code snippet (1) in Listing 2 creates an endpoint reference. The address in the endpoint reference is hard-coded to be that of the BigEcho service, as shown in the WSDL file in Listing 1.Code snippet (2) gets a proxy for the BigEcho service at the address in the endpoint reference. Notice in Figure 4 that the import is represented by a symbol labeled BigEchoPartner. The symbolic name of the import is needed to perform the look-up. The wire between CustomMediation1Partner and Import1 allows Router to successfully use the symbolic name in the look-up. The snippet next extracts the payload of the request message. Because the message passed in is the entire SMO, the XPath expression
body/echois used to access the request payload. The snippet then invokes the service using the payload. It finally extracts the response payload.Code snippet (3) creates a response message and a response from the proper elements defined by the WSDL in Listing 1.
Finally, code snippet (4) inserts the payload from the original response into the response message structure. Note that the snippet then replaces the request message in the incoming SMO with the response message. This allows any headers or context to flow on through the Router mediation primitive. The snippet then returns the response, which then flows to the InputResponse node, as described above.
- Save the code shown in Listing 2.
- Save the mediation flow and the mediation module, and clean again.
- Deploy the mediation module.
- To test the DynamicRoute mediation module, you can use the same test client you created to test StaticRoute by simply modifying the endpoint address used by the proxy in the test client to point to DynamicRoute instead of StaticRoute. For example, if the endpoint address for StaticRoute is http://localhost:9080/StaticRouteWeb/sca/Export1, then the endpoint address for DynamicRoute is http://localhost:9080/DynamicRouteWeb/sca/Export1. Run the test client. If you examine the server console view, you'll see that the mediation module and the BigEcho service were invoked, as shown in Listing 3.
Listing 3. Console output after mediation and service invocationSystemOut O ... in Router mediation primitive SystemOut O +++ Got to BigEcho.
Enhanced dynamic routing for SOAP/HTTP
The approach described above assumes the actual endpoint address is somehow derived in the same custom mediation primitive that uses the endpoint address. However, that've not very re-usable. Let's look at a more re-usable dynamic routing approach. The approach leverages the SMO transient context, which is a mechanism for communicating between mediation primitives. For more information, check out the WebSphere Enterprise Service Bus Information Center. We'll introduce another mediation primitive in the request flow that sets the desired endpoint address in the transient context. We'll also modify the Router mediation primitive to extract the address from the transient context and invoke a service provider at that address.
- First create a new business object named EPR in the Resources library, as shown in Figure 13. This business object will define the transient context used. The EPR business object contains a single field named address, and we use the default namespace. In a more realistic implementation, the structure defined might be based on the WS-Addressing specification, for example.
Figure 13. Transient business object
- Enable a transient context in the request flow by opening the Mediation Flow editor for the DynamicRoute mediation module.
- Select the Request flow tab.
- Select the Input node and open its properties.
- Select the Details tab. You'll see something like Figure 14.
- Click Browse to the right of Transient context.
- In the Data Type Selection dialog, select EPR and then click OK. The details for the Input node now should display with the transient context set, as shown in Figure 14.
Figure 14. Transient context set
- Restart the DynamicRoute project to ensure the transient context mechanism is engaged.
- Next add another custom mediation primitive that sets the context. In the Mediation Flow editor for DynamicRoute, delete the wire between the Input node and Router.
- Add a new custom mediation primitive and name it Setter.
- Wire the Input node to the Setter input terminal and wire the Setter output terminal to the Router input terminal.
- Open the Setter properties, select the Detail tab and click Define.
- As when defining Router, make sure the root is set to /, then click Finish. The mediation request flow should now appear as in Figure 15.
Figure 15. DynamicRoute request flow with Setter node
- Save the mediation flow, return to the Assembly Diagram, and merge the implementation as described above.
- Clean the mediation. You should see something like Figure 16. The CustomMediation2Partner represents the Setter custom mediation primitive.
Figure 16. Dynamic Route with a setter and router
- Now you need to create an implementation for Setter custom mediation primitive that puts the desired endpoint address in the transient context passed into Setter. Listing 4 shows the necessary code. Code snippet (1) simply sets the endpoint address to the desired value. The XPath expression
context/transient/addressreflects that the SMO has a child calledcontext, thatcontexthas a child calledtransient, and that, since we've declared thattransienthas a structure defined by EPR,transienthas a child calledaddressthat contains the desired endpoint address. In a more realistic situation, the address would be set by deriving information from a services registry, for example.
Listing 4. Setting transient context to support dynamic routingpublic DataObject execute(DataObject input1) { System.out.println("... In Setter mediation primitive"); // (1) set EPR address input1.setString("context/transient/address", "http://localhost:9081/BigEcho/services/BigEcho"); return input1; }
- Modify the Router mediation primitive to extract the desired endpoint address from the transient context, and use it as the endpoint address for service invocation. Listing 5 shows the necessary modifications to the static routing code for Router custom mediation primitive:
Listing 5. Service invocation in custom mediation primitive with dynamic addresspublic DataObject execute(DataObject input1) { System.out.println("... In Router mediation primitive"); // (0) extract address String address = input1.getString("context/transient/address"); // (1') Create endpoint reference EndpointReference eRef = EndpointReferenceFactory.INSTANCE .createEndpointReference(); eRef.setAddress(address); // (2) invoke service and extract response body ... // (3) create response message and add response ... // (4) add response and return ... }
Code snippet (0), added to the static form shown in Listing 2, extracts the desired endpoint address from the transient context; the XPath expression is identical to that used to insert the endpoint address in Setter. Code snippet (1) creates an endpoint reference using the address extracted from the transient context. Code snippets (2) through (4) are identical to those in Listing 2.
-
If you run the test client again, you should see something like the output shown in Listing 6. You'll see that the Setter mediation primitive sets the desired endpoint address and the Router mediation primitive uses that to invoke the BigEcho service.
Listing 6. Console output after mediation and service invocation with enhanced mediationSystemOut O ... In Setter mediation primitive SystemOut O ... In Router mediation primitive SystemOut O +++ Got to BigEcho.
This example shows that the selection of the endpoint address and the actual use of the address can be separated, offering a much more flexible and reusable approach to dynamic routing. Note that a more realistic scenario might require additional processing of either the request or response or both. In the mediation flow shown in Figure 15, any request processing must occur before Router custom mediation primitive, and any response processing must occur before the InputResponse node.
You've learned how to enable dynamic routing for a SOAP/HTTP Web services binding. You can use the same technique for a SOAP/JMS Web services binding, as well. However, there are a few caveats. First, consider the SCA module for a SOAP/JMS service, consisting of an export and the business components. We found that the name of the queue used for the export, and the name of the WSDL file generated by Integration Developer are derived solely from the name of the interface and the name of the export in the module. This means that when you want to create two modules with the same interface, you must give the exports different names or the names of the queues and WSDL files will collide, with undesirable results.
Second, we found that the WSDL file (or Web Service Port) bound to an import in a module must be available to that module. We simply copied the information into the Resources library referenced by the module.
Finally, take a look at Listing 7, which contains the service element from the automatically generated WSDL for a SOAP/JMS module containing an export named JS1Export1 that uses the BigEcho port type from Listing 1. Notice that the location attribute for the soap:address element in the wsdl:port element of the wsdl:service element contains a URI string listing values for the queue (destination) name, the connection factory name, and the port name. The string & is used as a separator between these values. The URI string used in the EndpointReference.setAddress() method (see Listing 2 and Listing 5) must use & as the separator.
Listing 7. WSDL snippet for SOAP/JMS Web service
<wsdl:service name="JS1Export1_BigEchoJmsService">
<wsdl:port name="JS1Export1_BigEchoJmsPort"
binding="this:JS1Export1_BigEchoJmsBinding">
<soap:address
location=
"jms:/queue?destination=jms/JS1Export1
&connectionFactory=jms/JS1Export1QCF
&targetService=JS1Export1_BigEchoJmsPort"
/>
</wsdl:port>
</wsdl:service>
|
The technique also works for SCA bindings. For SCA bindings, no WSDL files are generated. Note that the form of the address needed for creating the endpoint reference is sca://<project name>/<export name>.
This article showed you how to implement dynamic routing for Web services in the WebSphere ESB V6. This can be critical in many real world enterprise service bus scenarios. This article also showed you how to use the SCA framework and the ESB mediation context. All of these techniques can enhance your real-world ESB solutions using WebSphere ESB.
The author would like to thank to Rob Phippen for hours of discussion on dynamic routing in WebSphere ESB that lead to the technique described in this article.
Learn
-
WebSphere Enterprise Service Bus product page: Get product information.
-
Getting started with WebSphere Enterprise Service Bus and WebSphere Integration Developer: Learn more about WebSphere Enterprise Service Bus server and its accompanying tooling, WebSphere Integration Developer. This article describes how to develop a mediation flow providing a basic Web service; develop an intermediate flow, to connect to this service, with more complex routing logic provided by several of the pre-built mediation functions offered by the tooling; and deploy and test these flows using both the tooling test facilities and a standalone JSP-based front-end.
-
Developing custom mediations for WebSphere Enterprise Service Bus: This article introduces the use of custom mediations using the WebSphere Integration Developer V6 environment for WebSphere Enterprise Service Bus V6. The article will walk you through the development of three types of custom mediations in a simple scenario.
-
Specifications: Service Component Architecture (SCA) and Service Data Objects (SDO)
-
WebSphere Enterprise Service Bus Information Center: Get product documentation.
-
developerWorks SOA and Web services zone: Get technical resources and downloads for IBM SOA and Web services.
-
developerWorks WebSphere Web Services zone: Get technical resources and downloads for IBM WebSphere Web services.
Get products and technologies
- Build your next development project with IBM trial software, available for download directly from developerWorks.
Discuss
- Participate in developerWorks blogs and get involved in the developerWorks community.

Greg Flurry is a Senior Technical Staff Member in IBM's SOA Advanced Technology group. His responsibilities include working with customers on service-oriented solutions and advancing IBM's service-oriented products. If you have comments for the Greg about this article, send them to wsdd@us.ibm.com.





