There are two things which allow us to dynamically invoke Web Services in WS-BPEL: 1) using contract first approach when implementing Web Services 2) WS-BPEL allows you to change the EPR of a
partnerLink. These two can be used to dynamically invoke Web Services, for example based on quality and performance factors you can dynamically at runtime pick up the optimal Web Service. And as long as the interface and exchanged messages are the same, you're at home.
Today I'll show you how to do this using ServiceMix4/CXF and Apache ODE.
Prerequisites: two Web Services
Using the following archetype:
servicemix-osgi-cxf-code-first-archetype I created two OSGi CXF Web Services.The first one:
group id: org.xh.studies.ode.servicemix
artefact id: chemical-service-1
And the second one:
group id: org.xh.studies.ode.servicemix
artefact id: chemical-service-2
I let both have the same implementation apart of the sign (line 11). First implementation had + sign, the second one had - sign:
package org.xh.studies.ode.servicemix.chemical_service;
import javax.jws.WebService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@WebService
public class ChemicalComputationsService {
private static final Log log = LogFactory.getLog(ChemicalComputationsService.class);
public Double[] computeParameters(String substance, Double temperature) {
Double[] parameters = new Double[3];
for (int i = 0; i < parameters.length; i++) {
parameters[i] = (i + 1) * Math.log(Math.exp(temperature));
log.info(i + " parameter = " + parameters[i]);
}
return parameters;
}
}As you can see both Web Services had the same package. Which in my case resulted in the very same namespace for both of them. Yes, for this simple scenario I used code first approach, but I made myself sure that the generated WSDL was exactly the same. Finally I updated the Spring configuration, for both projects respectively:
<jaxws:endpoint id="HTTPEndpoint"
implementor="org.xh.studies.ode.servicemix.chemical_service.ChemicalComputationsService"
address="/ChemicalComputationsService1"/>and <jaxws:endpoint id="HTTPEndpoint"
implementor="org.xh.studies.ode.servicemix.chemical_service.ChemicalComputationsService"
address="/ChemicalComputationsService2"/>That was it. I built them and deployed to ServiceMix 4.2. Now it was turn to write a WS-BPEL process. Writing WS-BPEL process
I decided to use Eclipse plugin called BPEL Designer. I used it 2 years ago and found out it wasn't working at all. (That is why I write all my WS-BPEL processes by hand. I will take a look at the NetBeans WS-BPEL plugin, I read somewhere that it's much better than Eclipse BPEL Designer.) And after those 2 years I must say that nothing really changed in that matter. Eclipse BPEL Designer still sucks. And sucks very much. Everything I created using the GUI I had to amend by hand... There is only one thing that worked without any problems. It was a visual editor for Apache ODE Deployment Descriptor. The rest of the BPEL Designer is rubbish.
To cut long story short. I wrote a simple WS-BPEL process that based on ODE instance ID mod 2 was choosing one or another Web Service at runtime. Please note that implementing something like a load balancer in WS-BPEL is not a good idea. Load balancing of backing Web Services is a matter of infrastructure not the business logic. In my example I'm choosing between 2 different/alternative implementations of the same problem.
Here's the source code:
<bpel:process name="DynamicEPRChemicalProcess"
targetNamespace="http://dynamiceprchemicalprocess.wsbpel.examples.xh.org"
suppressJoinFailure="yes"
xmlns:tns="http://dynamiceprchemicalprocess.wsbpel.examples.xh.org"
xmlns:bpel="http://docs.oasis-open.org/wsbpel/2.0/process/executable"
xmlns:ode="http://www.apache.org/ode/type/extension" xmlns:csn="http://chemical_service.servicemix.ode.studies.xh.org/">
<bpel:import namespace="http://chemical_service.servicemix.ode.studies.xh.org/"
location="ChemicalComputationsService1.wsdl" importType="http://schemas.xmlsoap.org/wsdl/"></bpel:import>
<bpel:import location="DynamicEPRChemicalProcessArtifacts.wsdl"
namespace="http://dynamiceprchemicalprocess.wsbpel.examples.xh.org"
importType="http://schemas.xmlsoap.org/wsdl/" />
<bpel:partnerLinks>
<bpel:partnerLink name="client"
partnerLinkType="tns:DynamicEPRChemicalProcess" myRole="DynamicEPRChemicalProcessProvider" />
<bpel:partnerLink name="ChemicalComputationsService1PartnerLink"
partnerLinkType="csn:ChemicalComputationsService1PartnerLinkType"
partnerRole="ChemicalComputationsService1ProcessProvider"></bpel:partnerLink>
</bpel:partnerLinks>
<bpel:variables>
<bpel:variable name="input"
messageType="tns:DynamicEPRChemicalProcessRequestMessage" />
<bpel:variable name="output"
messageType="tns:DynamicEPRChemicalProcessResponseMessage" />
<bpel:variable name="chemicalComputationsServiceInputMessage"
messageType="csn:computeParameters"></bpel:variable>
<bpel:variable name="chemicalComputationsServiceOutputMessage"
messageType="csn:computeParametersResponse"></bpel:variable>
</bpel:variables>
<bpel:sequence name="main">
<bpel:receive name="receiveInput" partnerLink="client"
portType="tns:DynamicEPRChemicalProcess" operation="process"
variable="input" createInstance="yes" />
<bpel:assign validate="no" name="Init messages">
<bpel:copy>
<bpel:from>
<bpel:literal>
<wrapperCanBeAnything>
<arg0 xmlns="">xxx</arg0>
<arg1 xmlns="">28</arg1>
</wrapperCanBeAnything>
</bpel:literal>
</bpel:from>
<bpel:to variable="chemicalComputationsServiceInputMessage"
part="parameters"></bpel:to>
</bpel:copy>
<bpel:copy>
<bpel:from>
<bpel:literal>
<oneMoreWrapper>
<tns:result />
</oneMoreWrapper>
</bpel:literal>
</bpel:from>
<bpel:to variable="output" part="payload" />
</bpel:copy>
<bpel:copy>
<bpel:from part="payload" variable="input">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[tns:input]]></bpel:query>
</bpel:from>
<bpel:to part="parameters" variable="chemicalComputationsServiceInputMessage">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[arg0]]></bpel:query>
</bpel:to>
</bpel:copy>
</bpel:assign>
<bpel:if name="Check if instance ID even">
<bpel:condition><![CDATA[substring($ode:pid, 3) mod 2]]></bpel:condition>
<bpel:assign validate="no" name="Set EPR to ChemicalService1">
<bpel:copy>
<bpel:from>
<bpel:literal>http://localhost:8181/cxf/ChemicalComputationsService1</bpel:literal>
</bpel:from>
<bpel:to partnerLink="ChemicalComputationsService1PartnerLink" />
</bpel:copy>
</bpel:assign>
<bpel:else>
<bpel:assign validate="no" name="Set EPR to ChemicalService2">
<bpel:copy>
<bpel:from>
<bpel:literal>http://localhost:8181/cxf/ChemicalComputationsService2</bpel:literal>
</bpel:from>
<bpel:to partnerLink="ChemicalComputationsService1PartnerLink" />
</bpel:copy>
</bpel:assign>
</bpel:else>
</bpel:if>
<bpel:invoke name="Invoke"
partnerLink="ChemicalComputationsService1PartnerLink" operation="computeParameters"
portType="csn:ChemicalComputationsService" inputVariable="chemicalComputationsServiceInputMessage"
outputVariable="chemicalComputationsServiceOutputMessage" />
<bpel:assign validate="no" name="Copy results">
<bpel:copy>
<bpel:from>
<![CDATA[concat('You have invoked me with the following argument: ', $input.payload/tns:input, '. The sum of all computation results is ', sum($chemicalComputationsServiceOutputMessage.parameters/*))]]>
</bpel:from>
<bpel:to part="payload" variable="output">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[tns:result]]></bpel:query>
</bpel:to>
</bpel:copy>
</bpel:assign>
<bpel:reply name="replyOutput" partnerLink="client"
portType="tns:DynamicEPRChemicalProcess" operation="process"
variable="output" />
</bpel:sequence>
</bpel:process>TestingI wrote a simple test project with tests to confirm that both chemical Web Services and the WS-BPEL process worked.
Source code download
The source code of both Web Services, WS-BPEL process, and test project can be found here: WS-BPEL-Dynamic-EPR.zip. It assumes that CXF Web Services deployed to Karaf are running on 8181 port and Apache ODE is running on Tomcat 8383.
Summary
So now you know how to dynamically change the EPR of a Web Service. If you'd like you can retrieve the EPR from an external Web Service, the return type should be
wsa:Endpoint. The external Web Service could determine the EPR based on, what I wrote at the very beginning of this post, performance and quality factors.Stay tuned more WS-BPEL to come.
thanks,
Łukasz

1 comments:
Few months ago I created some simple BPEL snippets with Swordfish Eclipse Designer and it worked fine. Did you choose correct discovery site (http://swordfish-tooling.googlecode.com/svn/trunk/org.eclipse.swordfish.third-parties/bpel)?
Post a Comment