SOA Best Practices: The BPEL Cookbook

Making BPEL Processes Dynamic
by Sean Carey

Learn how to achieve dynamic binding by manipulating endpoint references at runtime.

 Review complete BPEL Cookbook index

Downloads for this article:
 Oracle BPEL Process Manager and Designer

Web services and service-oriented architecture (SOA) allow business processes to be easily extended through interaction with other business processes and applications. BPEL processes define this interaction through partner links, which define the interface (messages and operations), transport protocol, and most important, the location of each service to be used.

In most basic process designs, partner links are static; they refer to a single external process selected by the developer at design time. This approach is appropriate for highly targeted or constrained systems. However, in larger systems business processes are more complex. They interact with multiple external services and define multiple partner links, and some of these partner links might not be known at design time. As a result, all potential callouts and logic for deciding which partner links to use must be built inside the business process itself—unnecessarily complicating that process. Furthermore, as additional partner links are added, the resulting process grows more and more unwieldy, as any changes to the partner links require modification of the entire business process.

Fortunately, the BPEL language supports the concept of dynamic binding of partner links. Dynamic binding allows the developer to add new services through configuration or run-time inputs. This approach eliminates the need to anticipate and manage all parent-child relationships at design time.

In this installment of The BPEL Cookbook, I will outline the effective strategy of shielding BPEL processes from partner relationship changes by letting the system manage partner links dynamically at runtime. I will also explain how to invoke multiple BPEL processes dynamically, either sequentially or in parallel.

Dynamic Binding Overview

Similar to object-oriented analysis and design in the traditional programming world, dynamic binding of partner links allows for modularization of code and run-time binding between processes. The benefits of this approach include:

  • Support for team-oriented development by breaking down functional components into individual units of work
  • Creation and deployment of additional subprocess components without the need to modify and redeploy parent processes
  • Less need to use, maintain, and enhance individual overlapping processes
  • Changes and enhancements to subprocesses are automatically accessible to parent processes
In essence, dynamic processes allow the system to adapt to conditions that would not otherwise be known at design time. For example, process flow can be determined by data content—if the data is insufficient, external data sources such as a relational database can be invoked at runtime to determine process flow.

The use of dynamic processing can also have a significant advantage in organizations where there is a division of responsibility between development versus configuration and maintenance of high-level process flow. The development team can be responsible for understanding the details of the BPEL implementation and creation of the process components, and then domain experts such as business analysts or support teams can assemble these components into individual workflows without needing detailed knowledge of partner links, namespaces, WSDL, XPATH, and other technical details.

Building Dynamic BPEL Processes

As I explained previously, partner links describe interfaces to business processes or other services. BPEL processes call these external services using information stored in the partner links.

The partner link defines operations and message types that make up the interface to the service using portTypes in WSDL. As illustrated in Figure 1, portTypes also indirectly define the transport used to communicate with the service (bindings) and the location of the service (services).

Figure 1 portTypes in action

In a static BPEL process, the partner link information is defined at design time. However, there are scenarios where all the partner link information is not known to the developer or needs to change at runtime to adapt to data or other dynamic requirements.

Consider the example of a loan-processing scenario where you want to select the loan provider based on input data like geographic region, loan amount, or credit history. This data is not available at runtime, and if many possible loan providers are involved, it may be difficult to model the process to manage all the different services available to you statically.

Selecting the providers dynamically eliminates this problem. The WS-Addressing standard provides a mechanism called endpoint references that allow you to select one of the available services in the WSDL, or even define new services at runtime. The business process is statically dependent on the interface information defined in the portType whereas an endpoint reference (which maps the binding to the service) allows you to redefine the service location dynamically. In essence, the endpoint reference is a dynamic alternative to the static service element defined in the WSDL. In many cases, the process designer can remain isolated from the decision about which services to call as long as those services all conform to a standard interface. A good starting point for understanding these topics is the DynamicPartnerLink sample provided with Oracle BPEL Process Manager. Let's explore that sample step by step; then, you'll learn how to build a dynamic process from scratch. (Note: I recommend that you become familiar with the standard LoanFlow tutorial before working with this sample.)

Understanding the DynamicPartnerLink Sample

The DynamicPartnerLink sample is a great resource for understanding the underlying concepts of partner links and endpoint references. It allows you to specify one of three loan service providers (United, Star, and American) and makes a dynamic call to the appropriate service based on the process input.

For this discussion you will be using the sample provided in the GA release of Oracle BPEL Process Manager 10.1.2; you will find it in directory [BPEL_HOME]\samples\references\DynamicPartnerLink. I developed and tested the code discussed here under Patch 1 of version 10.1.2.

Note: When you first load and deploy the Dynamic Partner Link sample, do not make any modifications to the code in the Oracle JDeveloper visual designer--just deploy it as-is. If you were to make and save such changes, JDeveloper would reformat the BPEL code based on its standard XML layout by introducing newline characters. JDeveloper modifies the <Address> and <ServiceName> tags inside the <EndpointReference> data, adding a newline character before the </Address> and </ServiceName> close tags. The newline appended to the data inside these elements breaks the binding. If necessary, you can correct the problem by removing the newline character before the close tag on the service and address. Later I will present an alternative method for populating the endpoint reference that is not affected by the formatting applied by JDeveloper.

When you launch the sample from the console, you are asked for the standard loan application data for the loan flow demo (SSN, email, and so on) along with a "provider" field. Specify one of the following values in the provider string: united, american, or star. Run the sample with each of them to see how it works. The process will dynamically make calls to appropriate loan provider. It is also interesting to try it with some other value in the provider string, as well as with no value at all.

To understand how this dynamic process works, it is first necessary to analyze the DynamicPartnerLink.bpel file. The first interesting thing in this file is the loan service partner link:

                               
<partnerLink name="LoanService" partnerLinkType="
                                
services:LoanService"
  myRole="LoanServiceRequester" partnerRole="LoanServiceProvider"/>
                              
                            
Rather than defining a specific loan service (like UnitedLoan), a generic loan service name and type is specified ( services:LoanService). The LoanService partner link is defined in the LoanService.wsdl file; this file is imported by adding it to the bpel.xml file in the <partnerLinkBindings> section as shown below:
<partnerLinkBinding name="LoanService">
  <property name="wsdlLocation">LoanService.wsdl</property>
</partnerLinkBinding>
You'll observe in the LoanService.wsdl file that each of the available loan providers is defined as a <service> within this single WSDL file, as shown below.
<service name="StarLoan">
 <port name="LoanServicePort" binding="tns:LoanServiceBinding">
  <soap:address location="http://localhost:9700/orabpel/default/StarLoan"/>
 </port>
</service>

<service name="UnitedLoan">
 <port name="LoanServicePort" binding="tns:LoanServiceBinding">
  <soap:address location="http://localhost:9700/orabpel/default/UnitedLoan"/>
 </port>
</service>

<service name="AmericanLoan">
 <port name="LoanServicePort" binding="tns:LoanServiceBinding">
  <soap:address location="http://localhost:9700/orabpel/default/AmericanLoan"/>
 </port>
</service>
It is important to understand that there is no "real" service called "LoanService." Rather, LoanService is a template from which you select one of the real loan provider services (UnitedLoan, AmericanLoan, StarLoan). This approach works as long as the real services all support the same interface (same data types, messages, roles, ports, and partner link types) as that defined in the template WSDL. It is important to define this template interface carefully because a change here can affect many processes down the line.

The LoanService.wsdl file defines all the service options, which the parent process can elect to call dynamically. This model requires a redeployment of the WSDL file as each new service is added. This approach represents a remarkable improvement over modifying the parent process to include new partner links and routing logic for each new service. (Later you will see how to disassociate the service endpoints from the WSDL file as well.)

Returning to the DynamicPartnerlink.bpel file, the next feature we want to look at is the partnerReference variable:

<variable name="partnerReference" element="wsa:EndpointReference"/>
This variable is of the type EndpointReference. It has a namespace wsa: defined at the top of the BPEL file as
xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing"
The WS-Addressing standard provides the schema for the EndpointReference type. You can <assign> variables of this type to a partner link in order to modify the address and service information, thus providing the ability to modify the partner link at runtime.

The DynamicPartnerLink process basically consists of a switch. It inspects the "provider" string passed in by the caller. Then, it assigns an EndpointReference xml data structure to the partnerReference variable containing the information relevant to the service that you're requesting. After the switch, the partnerReference variable is assigned to the LoanService partner link and the partner link is invoked.

Here's how to accomplish this task when the input string (the service provider) is "united":

<assign>
 <copy>
  <from>
   <EndpointReference xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing">
   <Address>http://localhost:9700/orabpel/default/UnitedLoan</Address>
   <ServiceName
     xmlns:ns1="http://services.otn.com">ns1:UnitedLoan</ServiceName>
   </EndpointReference>
  </from>
  <to variable="partnerReference"/>
 </copy>
</assign>
Everything between the <from> and </from> tags is literal XML that you're assigning to the partnerReference variable. This data will override the address and service specified in the LoanService partner link when you assign the partnerReference variable to that link.

Now that you've explored the use of the LoanService partner link and LoanService.wsdl to invoke services selected at run time, you can move on to building a dynamic process.

Creating a Dynamic BPEL Process

Now, let's create a dynamic BPEL process from scratch.

1. Create a new BPEL project.
Create a new asynchronous BPEL Process Project in JDeveloper and name it "MyDL".

2. Import the LoanService.wsdl file from the DynamicPartnerLink sample.
Copy the LoanService.wsdl file from the DynamicPartnerLink sample into the working directory of MyDL project ([BPEL_HOME]\integration\jdev\jdev\mywork\Workspace1\MyDL by default). (This approach will save you the time and trouble of creating your own dynamic WSDL and subprocess services.) Then right-click the MyDL project in the Applications Navigator and select Add to Project... Pick the LoanService.wsdl file from the directory and click on OK.

The LoanService.wsdl file has not yet been added to the bpel.xml file. You won't do that until much later in the process when you implement the EndpointReference variable.

3. Create the loan service partner link template.
Right-click one of the swim lanes and select Create Partner Link.... Fill in the dialog as shown in Figure 2. To fill in the WSDL File location you'll use the Browse WSDL Files from Local File System button (left of the flashlight) and select the LoanService.wsdl file from your MyDL project directory. Click on OK to create the partner link.

Figure 2 "Create Partner Link" dialog box

4. Create the invoke and receive actions to call out to the DynamicLoanService partner link.
Drag one each of the invoke and receive actions from the component palette into your process (between the receiveInput and callbackClient actions). Drag one of the arrows from invoke to the DynamicLoanService partner link and create the input variable. Do the same for receive. Variables should be called loanInput and loanOutput.

5. Configure input data loanInput.
Typically you would modify the MyDL.wsdl file to get the loan input data from the user. For the sake of simplicity here you'll just hard-code an assign to populate the loanInput variable. Place the assign after the receiveInput action and create a copy rule that puts the value "123456789" (this is a string, not a number, so don't forget to quote it) into the SSN element of loanInput as follows:

<assign name="PopulateSSN">
 <copy>
  <from expression="'123456789'"/>
  <to variable="loanInput" part="payload" query="/ns2:loanApplication/ns2:SSN"/>
 </copy>
</assign>
6. Create the partnerReference variable.
In the Structure window, expand the Variables tree, then Process, and select the Variables item (see Figure 3).

Figure 3 Expanding the "Variables" tree

Right-click on Variables and select Create Variable.... Set the name of your variable to "partnerReference" and set the type to "Element." Click on the flashlight next to the element box to bring up the type chooser. Find the type "EndpointReference" under Project WSDL Files → LoanService.wsdl → Inline Schemas → schema (see Figure 4).

Figure 4 Selecting "EndpointReference"

7. Setup the partnerReference variable.
Create another assign before the DynamicLoanService invoke. Use this assign to setup the partnerReference variable. Initially you'll hard-code it to the UnitedLoan service but you'll make it dynamic in the next section.

Here, you can avoid the issue encountered in the DynamicPartnerLink sample by reformatting the EndpointReference xml data. Create a copy rule that populates the partnerReference variable with this empty EndpointReference:

<EndpointReference xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing"
  xmlns:ns1="http://services.otn.com">
     <Address/>
     <ServiceName/>
</EndpointReference>
In the "from" block of copy rule, be sure to select the type "XML Fragment" before entering the information above. You have to make this copy in order to establish the namespace information for the partnerReference because the partnerReference variable is treated as a separate XML document when it is copied over to the DynamicLoanService partner link. Otherwise, expect to get a null pointer exception when you try to assign the partnerReference variable to the partner link.

Now you can populate the ServiceName and Address elements of the partnerReference variable with standard copy rules. Be sure to specify the same namespace for the service ( ns1) as what is defined in your blank endpoint reference. The <assign> should look like this:

                               
<assign name="SetupPartnerlink">
  <copy>
    <from>
      <EndpointReference
        xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing"
         xmlns:ns1="http://services.otn.com">
        <Address/>
        <ServiceName/>
      </EndpointReference>
    </from>
    <to variable="partnerReference"/>
  </copy>
  <copy>
    <from expression="'ns1:UnitedLoan'"/>
    <to variable="partnerReference" query="/ns3:EndpointReference/ns3:ServiceName"/>
  </copy>
  <copy>
    <from expression="'http://localhost:9700/orabpel/default/UnitedLoan'"/>
    <to variable="partnerReference" query="/ns3:EndpointReference/ns3:Address"/>
  </copy>
</assign>

                            
Also note that it is not until this point, when you use the partnerReference variable, that the LoanService.wsdl file is added to your bpel.xml file (so that the EndpointReference schema can be accessed).

8. Copy the partnerReference variable into the DynamicLoanService partner link.
Create the new <assign> between the SetupPartnerlink action and the <invoke> for the DynamicLoanService. Create a new copy rule and set it up as shown in Figure 5.

Figure 5 Creating a new copy rule

After these steps are completed, a dynamic BPEL process is created. The process has hard-coded address assignments. You can remedy this situation by replacing the last two copy rules in Step 7 with information collected at runtime. The BPEL Process diagram is shown in Figure 6.

Figure 6 The new BPEL process

Increasing the Effectiveness of Dynamic Processes

As you've seen in the previous example, LoanService.wsdl lists all the possible services called dynamically at run time. You can enrich this dynamism of business process by eliminating the need to modify the business process every time a new service is added. New services are defined in WSDL and WSDL is re-deployed to make the new service available.

You could take this dynamism even one level higher: A WSDL-driven approach requires the knowledge of the location of new services at design time, but you can go one step further and make processes WSDL independent. This approach will eliminate the need to re-deploy the WSDL each time a service is added.

Eliminating address dependency at run time. Service addresses can change frequently, but you can shield dynamic processes from these changes at runtime. If you assign only a service name and not an address, then the address for the service will be retrieved from the WSDL instead. To demonstrate, remove the stub for the address (<Address/>) from the XML fragment in the template copy rule. Make a backup copy of the MyDL.bpel file before you do so, as you will want the address information back shortly. Also remove the copy rule that manipulates the address from your SetupPartnerLink <assign> statement. The SetupPartnerlink <assign> should now look like this:

                               
<assign name="SetupPartnerlink">
  <copy>
    <from>
      <EndpointReference xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing"
           xmlns:ns1="http://services.otn.com">
        <ServiceName/>
      </EndpointReference>
    </from>
    <to variable="partnerReference"/>
  </copy>
  <copy>
    <from expression="'ns1:UnitedLoan'"/>
    <to variable="partnerReference" query="/ns3:EndpointReference/ns3:ServiceName"/>
  </copy>
</assign>

                            
Now, deploy and run the MyDL process again. Despite the lack of an address, it still makes a successful call to the UnitedLoan subprocess. This action can be verified by looking at the process tree view in the BPEL console. The result is that it is possible to modify the behavior of dynamic processes by simply deploying a new WSDL, which will contain modified address information for the service. The tradeoff is that in order to add new services the WSDL will need to be modified and redeployed.

WSDL-independent services. In some cases, in addition to having many services to manage, you may also have a situation where the service addresses change frequently or where you want to avoid frequent updates to the WSDL file. Allowing the process to specify the address of the endpoint reference at runtime can solve this problem.

Go back to the previous version of the MyDL.bpel file that has the address manipulation copy rules. Instead of removing the address information, remove the service information both from the template XML fragment and the ServiceName copy rule. The <assign> should now look like this:

                               
<assign name="SetupPartnerlink">
  <copy>
    <from>
      <EndpointReference xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing"
          xmlns:ns1="http://services.otn.com">
        <Address/>
      </EndpointReference>
    </from>
    <to variable="partnerReference"/>
  </copy>
  <copy>
    <from expression="'http://localhost:9700/orabpel/default/UnitedLoan'"/>
    <to variable="partnerReference" query="/ns3:EndpointReference/ns3:Address"/>
  </copy>
</assign>

                            
When you run the sample, the process makes the correct call to the UnitedLoan service, even though the service name is not specified. You can create the DynamicPartnerLink WSDL with only a single dummy service and call out to other services not listed in the WSDL as long as the addresses of those services is known at runtime. If you don't specify an address for some reason, it will use the address of the default service in the WSDL. Therefore, it may be a good idea to have that service point to a real BPEL process, possibly one that logs an error or sends a notification.

One application of this technique is in building a framework for exception handling. If you have more than one available address where a given service is available (such as a local server and a remote redundant server), you can roll-over to the secondary address when a call to the primary fails by using an exception handler to override the address information in the endpoint reference and retry the invocation of the service.

Invoking multiple dynamic processes. In some cases, a single data set may need to be passed to multiple sub-processes either in sequence or in parallel. You can use one or more while loops to implement this type of behavior.

Let's look at a quick example. A loan service provider's availability could be based on the day of the week. This information is stored in the database. A loan request arrives on Monday, and when the database is queried, it returns with a list of available Loan Service Providers (United and Star). To process the loan, the United and Star subprocesses will need to be called, either sequentially or in parallel. The database query returns the following result:

                               
<dbOutput>
 <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="response-
   headers">null</part>
 <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   name="DynamiclinksCollection">
 <n:DynamiclinksCollection
    xmlns:n=http://xmlns.oracle.com/pcbpel/adapter/db/top/MyDynamicLink
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Dynamiclinks>
   <address>http://localhost:9700/orabpel/default/UnitedLoan</address>
   <day>monday</day>
   <uid>1</uid>
  </Dynamiclinks>
  <Dynamiclinks>
   <address>http://localhost:9700/orabpel/default/StarLoan</address>
   <day>united</day>
   <uid>4</uid>
 </Dynamiclinks>
</n:DynamiclinksCollection>
</part>
</dbOutput>

                            
In order to call these in sequence, you would create a while loop. This while loop gets the address from the collection and performs a dynamic invoke/receive on each service.
                               
<!-- first setup the counter variable "i" -->
<assign name="CounterReset">
  <copy>
    <from expression="1"/>
    <to variable="i"/>
  </copy>
</assign>

<!-- while loop goes until all link collection notes are done -->
<while name="LoanLoop" condition="bpws:getVariableData('i') <=
      count(bpws:getVariableData('dbOutput','DynamiclinksCollection',
        '/ns3:Dynamiclin ksCollection/Dynamiclinks'))">
    <sequence name="Sequence_1">

<!-- reset the endpoint with the usual xml fragment -->
    <assign name="ClearEndpoint">
      <copy>
        <from>
          <EndpointReference
             xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing"
             xmlns:ns1="http://services.otn.com">
            <Address/>
          </EndpointReference>
        </from>
        <to variable="partnerReference"/>
      </copy>
    </assign>

<!-- set the address in the endpoint variable
        based on the current node -->
    <assign name="SetEndpoint">
      <copy>
        <from variable="dbOutput" part="DynamiclinksCollection"
                     query="/ns3:DynamiclinksCollection/Dynamiclinks
             [number(bpws:getVariableData('i'))]/address"/>
        <to variable="partnerReference"
          query="/wsa:EndpointReference/wsa:Address"/>
      </copy>
    </assign>

<!-- copy the endpoint variable into the partner link -->
    <assign name="DoPartnerlink">
      <copy>
        <from variable="partnerReference"/>
        <to partnerLink="LoanService"/>
      </copy>
    </assign>
<!-- invoke the partner link -->
    <invoke name="Invoke_2" partnerLink="LoanService"
       portType="ns2:LoanService" operation="initiate"
       inputVariable="loanInput"/>

<!-- be sure to increment your counter or you have an infinite loop -->
    <assign name="CounterIncrement">
      <copy>
        <from expression="bpws:getVariableData('i')+1"/>
        <to variable="i"/>
      </copy>
    </assign>
  </sequence>
</while>

                            
In the above example you're calling asynchronous services. It is possible to call them in parallel by removing the <receive> from the <invoke> while loop and giving it a while loop of its own. Responses from each of the callout processes will queue up until a <receive> is run to catch them. The receive task will collect the responses in the order that they return. This approach will prevent a response from a short-running task from being queued up behind the response from a long-running task.

It is not recommended to proceed out of the <receive> while loop until all of asynchronous responses have been collected.

Conclusion

As you've seen here, by binding dynamically using endpoint referencing, BPEL processes can become more agile and adapt to changing business conditions quickly. By decoupling business logic from partner addressing, you can make processes much more adaptive and portable.


Sean Carey Sean Carey is a Software Architect at SPS Commerce, a leader in hosted EDI. Sean has over seven years of experience in mission-critical e-commerce implementations and 15 years of industry experience in software design.

Send us your comments