Tuesday, February 15, 2011

Web Service Orchestration with an ESB

Web service orchestration can be thought as a process of combining and coordinating of different web services based on a predefined pattern to give a desired result.

Web Service Orchestration is one of many features that comes with an ESB (see [1]). Even though Business process servers can be used for this kind of tasks , using a business process server for a simple service orchestration scenario will be a waste of time. So in this blog post i’ll explain how we can use an ESB for service orchestration which will be a much simpler task.. I’ll be using WSO2 ESB 3.0.1 for this.

Let’s 1st come up with a scenario.

There are consultants who can give us some advices on life when we are in need. So lets try to have a consulting service.



But to consult a “Life guru” or the consultant 1st we need to get a ticket and then go to the “Life guru ” with that ticket.

Following is the high level message flow for this scenario




1) Client invoke ESB proxy service with Parameters name and age
2) ESB store the details and invoke Ticketing Service
3) Ticketing service return a ticket
4 ) ESB invoke consulting service with details name , age and ticket number
5) Consulting service returns the life advice
6) ESB pass the life advice to the client.



To implement this 1st we need to have some Services. So i’ll be creating two axis2 pojo services [2] for this.

1. TicketCounterService - input : void output : ticket number
2. ConsultingService - input : name , age , ticketnumber output : life advice

After having those service created and deployed (i used axis2 for service deployment).
Having those services deployed we can go in to the implementation of the ESB logic.

In WSO2 esb we will be creating a proxy service that excepts users name and age.It will then invoke the other services and then return the life advice back to the user.

We can use WSO2 ESB console to create the proxy service. I have choose to create a custom proxy and named it as ConsultingProxyService. and also i’m publishing a wsdl from this proxy so that there will be contract between ESB and the client.

Since ESB proxy must only accept users name and age. I edited the ConsultingServices wsdl by removing the ticket number input parameter.




Final Proxy service configuration will look like this.



<proxy name="ConsultingProxyService" transports="https http" startOnLoad="true" trace="disable">
<target>
<inSequence>
<property xmlns:char="http://charith.wickramarachchi.org" name="cname" expression="//char:name" scope="default"/>
<property xmlns:char="http://charith.wickramarachchi.org" name="cage" expression="//char:age" scope="default"/>
<enrich>
<source type="inline">
<p:getTicket xmlns:p="http://charith.wickramarachchi.org"/>
</source>
<target type="body"/>
</enrich>
<property name="count" value="1" scope="default" type="STRING"/>
<send>
<endpoint name="endpoint_urn_uuid_29C989B1BAE1F0A05F46088096875141764187336">
<address uri="http://localhost:8080/axis2/services/TicketCounterService"/>
</endpoint>
</send>
<log level="custom">
<property name="Status" value="TicketCounterService Invoked"/>
<property name="cname" expression="get-property('cname')"/>
<property name="cage" expression="get-property('cage')"/>
</log>
</inSequence>
<outSequence>
<switch source="get-property('count')">
<case regex="1">
<property name="count" value="2" scope="default" type="STRING"/>
<xslt key="consultingServiceTransfrom">
<property name="cname" expression="get-property('cname')"/>
<property name="cage" expression="get-property('cage')"/>
</xslt>
<log level="full">
<property name="Status" value="Case2 Invoked"/>
</log>
<send>
<endpoint name="endpoint_urn_uuid_29C989B1BAE1F0A05F44795543807521144284854">
<address uri="http://localhost:8080/axis2/services/ConsultingService"/>
</endpoint>
</send>
</case>
<case regex="2">
<send/>
</case>
</switch>
</outSequence>
</target>
<publishWSDL uri="file:///home/charith/Public/blogg/ServiceChaining/ConsultingProxyService.wsdl"/>
</proxy>


When a message comes to the proxy service 1st it will go in to the insequence of the proxy service. Where 1st it will store the name and age details in the message context. and then we will replace the body of the message with getTicket request message to invoke the Ticketconuter service. and then invoke the TocketCounter service. We also add the property named count to track the status of the service orchestration process.



<inSequence>
<property xmlns:char="http://charith.wickramarachchi.org" name="cname" expression="//char:name" scope="default"/>
<property xmlns:char="http://charith.wickramarachchi.org" name="cage" expression="//char:age" scope="default"/>
<enrich>
<source type="inline">
<p:getTicket xmlns:p="http://charith.wickramarachchi.org"/>
</source>
<target type="body"/>
</enrich>
<property name="count" value="1" scope="default" type="STRING"/>
<send>
<endpoint name="endpoint_urn_uuid_29C989B1BAE1F0A05F46088096875141764187336">
<address uri="http://localhost:8080/axis2/services/TicketCounterService"/>
</endpoint>
</send>
<log level="custom">
<property name="Status" value="TicketCounterService Invoked"/>
<property name="cname" expression="get-property('cname')"/>
<property name="cage" expression="get-property('cage')"/>
</log>
</inSequence>



The reply to the getTicket request will comes to the out sequence of the proxy service. Where we use the count value to determine the response type. If the count value is 1 then its a response from the ticket counter service.

<case regex="1">
<property name="count" value="2" scope="default" type="STRING"/>
<xslt key="consultingServiceTransfrom">
<property name="cname" expression="get-property('cname')"/>
<property name="cage" expression="get-property('cage')"/>
</xslt>
<log level="full">
<property name="Status" value="Case2 Invoked"/>
</log>
<send>
<endpoint name="endpoint_urn_uuid_29C989B1BAE1F0A05F44795543807521144284854">
<address uri="http://localhost:8080/axis2/services/ConsultingService"/>
</endpoint>
</send>
</case>



There we set the proerty count to 2 which means we are now going to invoke to ConsultingService. But for that we need to create the request message for that. Thats were we need the message transformation feature in an ESB. WSO2 ESB have few ways of transforming messages.(Ex : Enrichmediator , XSLT mediator) since i have used Enrich mediator before i’ll use XSLT mediator in this.

The request for CunsultingService must contain name , age and ticket number. But we only have ticket number with in the response message body. That’s where we are going to use the message context properties we added in the in-sequence. Since we are in a single communication session that message context properties will be preserved. So I m going to pass them as XSLT mediator properties so that i can use them in the xslt transformation .

following is the xslt transformation .



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" exclude-result-prefixes="fn" version="2.0">
<xsl:param name="cname"/>
<xsl:param name="cage"/>
<xsl:template xmlns:char="http://charith.wickramarachchi.org" match="//char:getTicketResponse ">
<char:consult>
<!--Optional:-->
<char:name>
<xsl:value-of select="$cname"/>
</char:name>
<!--Optional:-->
<char:age>
<xsl:value-of select="$cage"/>
</char:age>
<!--Optional:-->
<char:ticket>
<xsl:value-of select="//char:return"/>
</char:ticket>
</char:consult>
</xsl:template>
</xsl:stylesheet>



The response of the Consulting service will also come to the out sequence but now it will hit the case 2 of the switch since we have set the property count to 2 in the case 1 . In that case we just send the response back to the client


<case regex="2">
<send/>
</case>


Now we are done with the implementation.

Now i m going to invoke the Proxy service with SOAP request


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:char="http://charith.wickramarachchi.org">
<soapenv:Header/>
<soapenv:Body>
<char:consult>
<!--Optional:-->
<char:name>Charith</char:name>
<!--Optional:-->
<char:age>25</char:age>
</char:consult>
</soapenv:Body>
</soapenv:Envelope>


and i received the response


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:consultResponse xmlns:ns="http://charith.wickramarachchi.org">
<ns:return>Hello Charith Since your age is 25 Now its a good time to have some fun</ns:return>
</ns:consultResponse>
</soapenv:Body>
</soapenv:Envelope>



I have uploaded all the source codes , configurations , xslt file , and web service archives to [3] so that you can try out your self.

[1]http://en.wikipedia.org/wiki/Enterprise_service_bus#Salient_characteristics
[2]http://axis.apache.org/axis2/java/core/docs/pojoguide.html
[3]http://rapidshare.com/files/448188843/esb-service-chaining.zip