WSO2 ESB by Example - Service Chaining

Archived Content
This article is provided for historical perspective only, and may not reflect current conditions. Please refer to relevant product page for more up-to-date product information and resources.
  • By Supun Kamburugamuva
  • 24 Feb, 2011

Applies To

WSO2 ESB 3.0.1 and above
WSO2 Application Server 4.0 and above

Contents

Introduction

Service chaining is a popular usecase in WSO2 ESB. In lot of usecases, business functionalities defined in different services are exposed as a single service to the outside world. This is an integration scenario that can be implemented using an ESB. The WSO2 ESB allows for easy-to-manage central control for exposing the services as a single service and usually the configuration of the ESB can be changed depending on the service requirement changes.

Since two or more services are aggregated using the ESB, a request to the ESB is served by the help of multiple services. Usually, the ESB has to call these services in a particular order to create the response.

This kind of sequential service calling is being recognized as service chaining. In this article, we will look at the creation of a service chain using the WSO2 ESB.

In the sample scenario we have taken, WSO2 ESB exposes a virtual service that requires two other real web services to fullfil a single request. For a single request, the two web services will be called in order because they have data dependencies between them.

This article is intended for users who are familiar with the basic concepts of the WSO2 ESB. The WSO2 ESB configuration language and WSO2 ESB samples in the product documentation will be a good starting place if you are not familiar with WSO2 ESB concepts.

The sample services used in this article are Axis2 Web Services deployed in WSO2 Application Server. We will also use soapUI to generate the request to the ESB.

The full ESB configuration for the example; the two sample services, WSDL files and XSLT templates can be found in ESB-Sample-02.zip (attached).

The Scenario

ESB will receive a credit request from a proxy service called CreditProxy. The request has the ID of the requestor of the operation and the credit amount. But, to call the CreditService deployed in the WSO2 Application Server, the request must also be enriched with the name and address information of the requestor. This information can be obtained from the PersonInfoService deployed in the WSO2 Application Server. So, first, we need to call the PersonInfoService and enrich the request with the name and address before calling the CreditService.

The Scenario

Services

In this example, we have two back-end services and one proxy service. WSDLs for the examples can be found in the ESB-Sample-02.zip.

CreditProxy

This service is in the WSO2 ESB and it is exposed to customers. This service accepts an ID and a credit amount for its credit operation. A request coming to this service is served by two back-end services.

PersonInfoService

The PersonInfoService provides the name and address information about a requestor when the ID is given. So, this is the first service being called by the CreditProxy service deployed in the ESB.

CreditService

The CreditService is the actual service that does the crediting. It is called by the CreditProxy after getting the required information from the PersonInfoService.

Set-up

The article shows ESB-Sample-02.zip which contains all the necessary configurations for the scenario. The scenario needs WSO2 ESB 3.0.1 and WSO2 Application Server 4.0. The files in the zip should be copied or deployed into these servers. Now, let's look at the contents of the zip file and how to use them to create the scenario.

Back-End services

Deploy the esb-samples-1.0-SNAPSHOT.jar as a jar service in to the WSO2 App Server. This jar has two POJO services. The required classes that need to be exposed as web services are

  • org.wso2.esb.samples.CreditService
  • org.wso2.esb.samples.PersonInfoService

ESB Configuration

  1. Copy the synapse-config folder in the sample zip to the repository/conf of WSO2 ESB and replace the synapse-config folder.
  2. Copy the personToCredit.xslt in the sample zip to resources directory of WSO2 ESB.
  3. Copy the CreditProxy.wsdl in the sample zip to the resources directory of the WSO2 ESB.
  4. If you are running the WSO2 ESB in the sample machine as the WSO2 Application Server, change the ports of the WSO2 ESB in respository/conf/transport-mgt.xml. Otherwise there will be port conflicts.

Now we have the complete configuration for the scenario, let's go through the ESB configuration step by step to understand how it works.

Proxy Service

  • Receive a request with only credit amount and ID of the requestor.
  • Send the id to the PersonInfoService to get the Address and Name of the requestor
  • Use the credit amount, ID, address and name to create a request to the credit service and call the CreditService

The request to the proxy service is:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://samples.esb.wso2.org">
    <soapenv:Body>
        <sam:credit>         
            <sam:id>99990000</sam:id>
            <sam:amount>1000</sam:amount>
        </sam:credit>
    </soapenv:Body>
</soapenv:Envelope>

inSequence

The following request goes to inSequence of CreditProxy:

<inSequence>
    <log level="full">
        <property name="sequence" value="inSequence - request for CreditProxy"/>
    </log>
    <property xmlns:sam="http://samples.esb.wso2.org" name="ORG_ID" expression="//sam:credit/sam:id"/>
    <property xmlns:sam="http://samples.esb.wso2.org" name="ORG_AMOUNT" expression="//sam:credit/sam:amount"/>
    <enrich>
        <source type="inline" clone="true">
            <sam:get xmlns:sam="http://samples.esb.wso2.org">
                <sam:id>?</sam:id>
            </sam:get>
        </source>
        <target type="body"/>
    </enrich>
    <enrich>
        <source type="property" property="ORG_ID"/>
        <target xmlns:sam="http://samples.esb.wso2.org" xpath="//sam:get/sam:id"/>
    </enrich>
    <log level="full">
        <property name="sequence" value="inSequence - request for PersonInfoService"/>
    </log>
    <property name="STATE" value="PERSON_INFO_REQUEST"/>
    <send>
        <endpoint key="PersonInfoEpr"/>
    </send>
</inSequence>

The information in the original request is required to call the two back-end services. When we do the first request, the information from the original request will be lost if we don’t preserve it. So, we need to store the required information from the original request in the message context. In this case, we are only going to store the ID and the credit amount in the message context. But, it is possible to store any part of the message in the context. For example, in some scenarios, the whole message is stored.

We store the request ID and amount by extracting them using property mediator with XPath.

<property xmlns:sam="http://samples.esb.wso2.org" name="ORG_ID" expression="//sam:credit/sam:id"/>

<property xmlns:sam="http://samples.esb.wso2.org" name="ORG_AMOUNT" expression="//sam:credit/sam:amount"/>

After this step, the ID and the credit amount are available in the Message Context properties named ORG_ID and ORG_AMOUNT respectively.

Now, we are ready to do the first request to the PersonInfoService. We need the XML for this request and, so, will insert it into the message using the Enrich Mediator.

<enrich>
    <source type="inline" clone="true">
        <sam:get xmlns:sam="http://samples.esb.wso2.org">
        <sam:id>?</sam:id>
        </sam:get>
    </source>
    <target type="body"/>
</enrich>

As you can see, the inserted XML doesn’t have the ID from the original request so we need to insert it from where it was stored in the message context into this newly created message. To do this, we use the enrich mediator again.

<enrich>
    <source type="property" property="ORG_ID"/>
    <target xmlns:sam="http://samples.esb.wso2.org" xpath="//sam:get/sam:id"/>
</enrich>

Now, we have the complete message prepared for the PersonInfoService invocation.

This scenario involves two service calls and both the response go to the outSequence of the proxy service. So in the outSequence, we need a mechanism to distinguish the different external service calls. To do this, we set a property for the message context before asking to identify the request. In this way, when we get the response, we can correlate it to the request.

<property name="STATE" value="PERSON_INFO_REQUEST"/>

Here, we set the property STATE to PERSON_INFO_REQUEST to identify we are doing a PERSON_INFO_REQUEST.

Now, we use the send mediator to send the request out to the PersonInfoService.

<send>
    <endpoint key="PersonInfoEpr"/>
</send>

outSequence

The response to this request comes to the outSequence of the proxy service. Since the outSequence of the proxy service can receive two responses from two different service invocations, we use the switch mediator to distinguish these two responses using the STATE property that we set before sending the request out.

<switch source="get-property('STATE')">

Now, if the STATE is equal to PERSON_INFO_REQUEST, we invoke the first case.

<case regex="PERSON_INFO_REQUEST">
    <log level="full">
        <property name="sequence" value="outSequence - STATE 01 - response from PersonInfoService"/>
    </log>
    <xslt key="xslt">
        <property name="amount" expression="get-property('ORG_AMOUNT')"/>
    </xslt>
    <log level="full">
        <property name="sequence" value="outSequence - STATE 01 - request for CreditService"/>
    </log>
    <property name="STATE" value="CREDIT_REQUEST"/>
    <send>
        <endpoint key="CreditEpr"/>
    </send>
</case>

Now, we have to create a request to the CreditService using the information in the response as well as the original information which was a part of the client request.

The response from PersonInforService is as follows:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns:getResponse xmlns:ns="http://samples.esb.wso2.org">
            <ns:return xmlns:ax21="http://samples.esb.wso2.org/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ax21:PersonInfo">
                <ax21:address>59, Flower Road, Colombo 07, Sri Lanka</ax21:address>
                <ax21:id>99990000</ax21:id>
                <ax21:name>WSO2</ax21:name>
            </ns:return>
        </ns:getResponse>
    </soapenv:Body>
</soapenv:Envelope>

The request to the CrediService has the following XML format:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://samples.esb.wso2.org" xmlns:xsd="http://samples.esb.wso2.org/xsd">
   <soapenv:Body>
      <sam:credit>
         <sam:info>
            <xsd:amount>78</xsd:amount>
            <xsd:personInfo>
               <xsd:address>?</xsd:address>
               <xsd:id>?</xsd:id>
               <xsd:name>?</xsd:name>
            </xsd:personInfo>
         </sam:info>
      </sam:credit>
   </soapenv:Body>
</soapenv:Envelope>

To create the request to this CrediService, we use the following XSLT with the XSLT mediator. Note, we are using the ORG_ID that we stored in this XSLT as a XSLT parameter and using the XSLT mediator as well.

<xsl:stylesheet version="2.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
        xmlns:ns="http://samples.esb.wso2.org"
        xmlns:ax21="http://samples.esb.wso2.org/xsd"
        exclude-result-prefixes="ns fn">
<xsl:param name="amount"/>
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="/">
  <xsl:apply-templates select="//ns:getResponse" />
</xsl:template>

<xsl:template match="ns:getResponse" xmlns:ns="http://samples.esb.wso2.org">
<sam:credit xmlns:sam="http://samples.esb.wso2.org" xmlns:xsd="http://samples.esb.wso2.org/xsd">
    <sam:info>
        <xsd:amount><xsl:value-of select="$amount"/></xsd:amount>
        <xsd:personInfo>
            <xsd:address><xsl:value-of select="ns:return/ax21:address"/></xsd:address>
            <xsd:id><xsl:value-of select="ns:return/ax21:id"/></xsd:id>
            <xsd:name><xsl:value-of select="ns:return/ax21:name"/></xsd:name>
        </xsd:personInfo>
    </sam:info>
</sam:credit>
</xsl:template>
</xsl:stylesheet>

When the XSLT is done, we have the request to the CreditService. Then we set the STATE to CREDIT_REQUEST and send the request to the CreditService.

The response from the CreditService also comes to the outSequence of the proxy service. Since, the STATE is now set to CREDIT_REQUEST, the second case is invoked.

<case regex="CREDIT_REQUEST">
    <log level="full">
        <property name="sequence" value="outSequence - STATE 02 - response from CreditService"/>
    </log>
    <send/>
</case>

Now, we just send the response coming from the CreditService to the client because we have the same schema for the CreditService response and CreditProxy response. If they did not match, we'd have to do a XSLT transformation.

Running The Scenario

After configuring the ESB and WSAS to run the scenario, users can use the SOAP UI. Create a SOAP UI project by using the WSDL in CreditProxy and then call this proxy service using the SOAP UI.

Improvements

  • This scenario doesn’t have any error handling. To introduce the error handling, an error handling sequence must be introduced to the proxy service.
  • The proxy service outSequence is quite complex so it is better to break it down into several sequences with meaningful names. 
  • Endpoints are specified inside synapse.xml but it is a 'best practice' to store the endpoints in the Governance Registry of the WSO2 ESB
  • All the WSDLs and XSLTs are stored in the files system, but it is better to move them to the Governance Registry of the WSO2 ESB

Resources

References

Author

Supun Kambrugamuva

Technical Lead & Product Manager - WSO2 ESB; WSO2, Inc.

supun@wso2.com

About Author

  • Supun Kamburugamuva
  • Technical Lead
  • WSO2 Inc