Invoking Synchronous Services Dynamically (In-Parallel) using BPEL (forEach Parallel)
By Malkit-Oracle on Jun 06, 2008
Before delving into the details of Dynamic Service Composition, a quick background is appropriate to introduce some of the terms and concepts. Please jump to next section if you are familiar with Web Services - WSDL/BPEL and Netbeans SOA Pack.
A web service is defined using a WSDL. WSDL makes important distinction between port (concrete communication endpoints) and PortTypes (definition of operations and messages structures thereof). This sort of description allows reuse and cleaner implementations.
In BPEL, PartnerLinks are used to model partner relationships. Each Partner Link is characterized with a single or two roles (in case both partners are interacting with each other, especially asynchronously). A role is linked to the PortType as defined in the service WSDL. The key to understanding is that from the perspective of business process, PortType(s) forms the interface that defines the operations (with the associated message structures) that this service (BPEL Process) exposes or how the partner service is viewed (from BPEL Process). Each PortType, in turn can be exposed as one or many services each bound to same of different communication protocols (using binding definition of WSDL).
Netbeans SOA Pack contains modules that can be used to create business process for orchestrating your web services. SOA pack with Glassfish Bundle comes with very powerful BPEL Editor and runtime support. The runtime for BPEL Engine is based on JBI - Java Business Integration Specification. This specification defines a framework to support two major functions in the world of integration – Service Engines (such as BPEL Engine) and Binding Component (such as HTTP, for communication protocol specific handling). Large number of SE's and BC's are being developed under project Open-ESB. Also check out the developer wiki resource page.
Composing Services Dynamically
BPEL Language provide constructs (INVOKE) for making asynchronous (one-way) or synchronous (request-response) calls to web-services. It also comes with constructs such as FLOW that allows you to create branches in you business process that can be executed in parallel. Using FLOW, multiple simultaneous invocations can be made to external services. During process definition/design stage you need to identify the services that the business process needs to interact with and also specify the specific communication points.
While all of the above is good, for some advanced use cases the above may not be sufficient. Suppose you have requirement to call a particular service, among multiple that implement the same interface (PortType in WSDL Language), the concrete endpoint of which is only known at runtime. Also, to make the use case more complex, let’s extend the requirements to say that actual number of services to call cannot be determined at the design time. Too bad, we cannot use flow here (why?). This calls for some advanced solutions, which I will discuss next.
Two use cases
You have defined multiple partners in you business process, but would like to call one or the other based on some runtime data. Here the endpoint address of all the partners is known at design time.
The endpoint address of the partner is not known at design time and will only be available at runtime. In addition, the number of services to call is known at design time and they need to be called simultaneously.
Solution for 1: Every invoke activity defines a PartnerLink to point to the partner to call. BPEL Language allows for assignment of PartnerLinks, in effect providing mechanism to alter the partner to call (from invoke activity).
Solution for 2: BPEL Language defines a construct – ForEach. This allows for processing the contained activities defined number of times either serially or in-parallel. Also, it allows for defining forced exit condition (normal exit condition defined using start and end counter values). So, for our use case 2, we use combination of ForEach construct configured for parallel and dynamic addressing should do the job.
There is one limitation though. Currently, BPEL Service Engine (part of Open-ESB Project) has not yet implemented the ForEach in parallel. Well, serial calls may not be acceptable - what if each service takes up to 30 seconds to respond, and if we were to make call to 10 services, a total response time of 5 minutes may not be acceptable. So, how do we go about this? There is a workaround, and that is one of the reasons for this blog entry. The work-around is to use combination of Sub-Process, Correlation and Event-Handlers to make simultaneous calls possible.
I created one project using Netbeans BPEL Editor to demonstrate the above. I also added mechanisms that can be used to used to timeouts for predicable response and prevent the process instance from hanging forever, should some service(s) take longer than allowed response time.
Details on this project next. This project consist of many parts that demonstrates the power of the languages and usage of the tool.
Working Sample Project
You can download the working sample project from here - DynamicServiceParallelCalls.zip
The zip contain three projects.
- BPEL Project (BPELProject)
- Comp App Project(CompositeApp)
- EJB Project (ExternalEJBServices)
The EJB Project represents external services and defines 4 services. These are simple echo services and are configured with wait time of 5, 10, 15 and 30 seconds.
Running the project:
NOTE: While building the comp app you will see some warnings (like multiple services defined for same port type), ignore them.
Build and deploy the Comp App and EJB Project.
Testing the project:
NOTE: The result of test run will say test failed. This is expected as I added the start and end time for the test and everytime the test is run, the output value will be different. So, verify the outputs and compare with the expected output.
Created four test cases.
- First test case: It feed address and payload of one service and gets response for that service. This service is configured to respond in 5 seconds.
- Second test case: Feeds a message that contain address and payload for two services. The second service takes 10 to respond. So, expect the result of this test in about 10 seconds.
- Third test case: Feeds address of 3 services and the test completed in about 15 seconds.
- Fourth test case: Feeds address of 4 services, but there is timeout (see details below) defined. Hence the result will have one service as timed-out.
Verifying the results:Since the invocations will be made in parallel, the successful completion of the test is noted by the total time it took to finish the test. The total time should be slightly more than the time taken by the service that take longest to respond.
Quick overview of the project:
In order to achieve the synchronous service invocations in parallel (without using forEach-parallel) we will break down the call into two processes – Main Process and Sub-Process. The main process will pass-on the dynamic endpoint address to a sub-process using one-way (asynchronous call). The Sub-Process will make the external call and send the results back to the Main Process, again using one-way (asynchronous) call.
- Takes input as array of dynamic service address and payload information and call a business process.
- This business process timestamps the incoming message and has a forEach loop that would make as many one-way (asynchronous) calls to a sub-process, passing the dynamic address and the payload info. Hence if the array size is 3, it will make 3 calls to sub-process.
- The sub-process will further construct a dynamic partnerlink (based on the passed address) and make two-way (synchronous) call to this address.
- The sub-process will get the response and make one way call back to the main process.
- Since there in asynchronous communication between the main and sub-process, correlation is used to correlate the message back from sub-process to right instance of main process.
- The main process after making calls to sub-process goes in a while loop and wait for collecting all the responses back from the sub-process.
- The return type from main process is also a array of partner responses (consists of the partner address and the response). The while loop keep filling the responses received from the sub-processes in the return variable.
- After all the responses are received, the main process timestamps the response and replies back. The time stamping is to determine the total time it took to process the request.
- Since in dynamic call some partners may not respond in time, I also created one time-out mechanism. The sub-process defines an event handler with alarm (timeout value 20 Seconds). So, when the sub-process call some external service, if the response is not obtained in defined period, it send back response to main processes indicating this fact.
- The main process's while loop is also attached one event handler with an alarm (of value 25 seconds). So, in essese if for whatever reason, any of the sub-process were not to respond, the main process instance can still complete.