SOA Best Practices: The BPEL Cookbook

Building BPEL Processes on the Fly
by Jerry Thomas

Generate BPEL processes on the fly by transforming parameters stored in the database into a BPEL XML definition file via XQuery.

 Review complete BPEL Cookbook index

Downloads for this article:
 Sample Code
 Oracle BPEL Process Manager and Designer
 Saxon XSLT and XQuery Parser

Published November 2005

In Part 3 of this cookbook, you learned how to make BPEL processes dynamic by manipulating partner links using end point references. This approach is certainly useful for building processes that are shielded from partner relationship changes. However, in many processes, various other variables in addition to the partner links may need to change dynamically.

Consider a company that has designed a workplace management solution for managing corporate real estate and facilities that helps clients answer the following types of questions.

  • How much space are different business units using? How can business units share space most efficiently?
  • When will property leases expire? By looking at the company’s growth trajectory, how should leases be renegotiated?
  • How best to plan, approve a move of a business unit to a different floor, or expand into a building across the street?
The workplace management solution automates business processes such as tracking the occupancy of an office cubicle and disassociating that space from the business unit if it has been unoccupied for three months. As the company deploys this process for different clients, it becomes imperative to tailor the process to their business needs—one company might prefer to send an email to the property manager before the cube is taken away from the business unit, whereas another might prefer to wait a longer period before taking any action.

In this situation, creating a business process from scratch for every client would represent a major investment in time, money, and resources. And creating a reusable business process would require a deep understanding of BPEL constructs.

In contrast, adaptive business processes could be quickly customized per the varying requirements of specific organizations, enabling faster automation, reducing development costs, and accelerating time to market. In this approach, you would hide the complexities of BPEL and enable analysts to model business processes that are as similar to "real life" as possible.

This process requires a custom business-process designer, however, that allows analysts to represent business flow without worrying about the complexities of BPEL. This custom designer store the definition of the process within the database. Every time the process needs to be updated, a business analyst uses the custom designer to update the process accordingly.

In essence, once the process definition is loaded into the database, BPEL processes can be constructed "on the fly" from there. This newly constructed business process can in turn be deployed dynamically.

Oracle BPEL Process Manager is an ideal tool for this approach; it works seamlessly with third-party designers and provides the ability to deploy BPEL processes dynamically. For example, my company, CenterStone Software, leverages this approach to provide a solution to rapidly automate workplace business processes and manage corporate real estate and facilities. Our eCenterOne application uses Oracle BPEL to quickly let property managers develop and deploy custom business processes.

In this article, I'll explain how a custom BPEL designer stores process definitions in the database. After reviewing the database schema to store the definition, you will create a BPEL file on the fly from a sample business process using XQuery and the Saxon XQuery parser. Finally, you will learn how to deploy this process dynamically using an Ant script.

The Approach

As I explained previously, the ability to build processes on the fly offers significant benefits. Figure 1 shows the entire life cycle of the business process created on the fly.

figure 1
Figure 1 Business process life cycle


Building on-the-fly processes comprise several steps: An analyst uses a custom designer to graphically model the business process.

  1. The process definition is stored in the database by the custom designer.
  2. The BPEL Generator reads and validates the process definition. It then generates the BPEL XML file from the database representation as well as associated files used for deployment.
  3. The BPEL process is dynamically deployed on the Oracle BPEL Process Manager Server.
Let's use the following sample business scenario to walk though each and every step.

Sample Business Scenario

Consider the sample real-estate management business process described previously. This "Cube Management" business process involves the following activities:

  1. Initiate a business process when an office cube is emptied (the EmployeeCount field for a space is set to 0).
  2. Wait for three months (if the space’s EmployeeCount is set > 0, exit the flow).
  3. Send email to the property manager that the space is no longer being charged to its original business unit.
  4. Update the space so that it no longer belongs to its original business unit.
Process creation. Process creation is the first step for building processes on the fly. Generally, processes are modeled using Oracle Designer or Eclipse Designer and BPEL files are generated automatically.

In our approach, you will replace a conventional IDE with a custom designer. And instead of creating a BPEL file directly, you will store the definition inside a database.

Figure 2 shows how the sample business process is modeled in the designer.

figure 2
Figure 2 Cube Management Business Process in custom process designer


As I described previously, the Oracle BPEL Process Manager engine has the unique capability to integrate with any designer/tool provided the latter can generate valid BPEL files.

Storing the definition in the database. Once the business process has been created, the next step is to store the definition inside the database. Your database schema must be sufficiently flexible to store complex processes and allow for recreation of BPEL processes with multiple BPEL constructs such as flow, pick, invoke, and scope.

Assume that you have designed a data model containing business process design information that corresponds to the real-life view provided by the custom designer. When a business analyst creates a business process using the designer, information defining the structure of the business process is saved to the database. Note that Oracle BPEL Process Manager does not come into play until deployment (more on that later).

Figure 3 contains a high-level UML view of the data model used to persist process designs in our database.

figure 3
Figure 3 Simplified Data schema for storing Cube Management process definition


The WorkflowProcess object stores the process design; it contains parameters and nodes, which define the sequence and type of activities in the process. The various WorkflowNode objects are design-time artifacts for the different types of process activities supported by your designer. Note that these are not BPEL constructs; rather, they are custom constructs that are then mapped to typically more than one BPEL activity. Once deployed using custom designer, the CubeManagement process details will be stored as follows in the database.

WorkflowProcess Table

Name

ID

CubeManagement

1

WorkflowParameter Table

WorkflowProcess_ID

Name

Type

1

CubeParam

Space

WorkflowNode Table

WorkflowProcess_ID

NodeId

NextNode

Type

E-mail

Parameter_ID

UpdateField

UpdateValue

WaitTime

1

1

2

Start






1

2

3

Wait





3 months

1

3

4

Message

max@big.com





1

4

5

Update


1

businessUnit

null


1

5

Null

End






These tables are read by the BPEL Generator to dynamically generate a BPEL file. The next section describes that process.

Dynamically Deploying BPEL

The most complex task in BPEL generation involves reading the process design from the database and generating a BPEL "suitcase" for the process. In this example, you will leverage XQuery to generate BPEL.

XQuery is a powerful and convenient language for processing XML data. It can handle not only files in XML format, but also database data whose structure—nested, with named trees and attributes—is similar to XML.

The following files, which make up a BPEL process suitcase for Oracle BPEL Process Manager, are generated dynamically.

BPEL file

This XML file is the actual business process; it follows the BPEL specification.

WSDL files

Web Service WSDL files describe two interfaces:

  • The interface exposed by your BPEL process hosted in Oracle BPEL Process Manager. This interface is fairly simple, involving a call to start a process instance along with a callback interface.

  • The interface exposed by eCenterOne. For simplicity, this too involved a single interface that could be used to handle any call from the process running in Oracle BPEL Process Manager to our server as well as an associated callback.

bpel.xml

A deployment descriptor XML file that describes the BPEL process to Oracle BPEL Process Manager.


It holds process settings with information specific to Oracle BPEL Process Manager and the following.
  • Location information specifying the location of the eCenterOne Web service

  • Information specifying how retries should be performed should calls to eCenterOne fail (retries).

Ant build.xml

Uses Oracle BPEL Process Manager’s bpelc Ant task to deploy the BPEL process. (More on that later.)

Here we will focus only on how the BPEL file is generated dynamically. This process involves iterating across the nodes stored in the database and generating corresponding BPEL activities for each node:

  1. Iterate across the database.
  2. BPEL generation is implemented in Java code that reads each WorkflowNode for a workflow process and creates a list of all the nodes to be processed.
  3. Create BPEL Activity for each node.
  4. Each workflow node in the list is generated into a number of BPEL activities that are enclosed in a <scope/> activity. Consequently, if the processing for a BPEL activity requires use of a BPEL parameter, that parameter is only visible within that scope (no variable name collisions, and so on). For each node, you must create an XQuery template to generate a corresponding BPEL XML tag. The XQuery processor will process the XQuery template to construct the BPEL with node and instance information and pass back the result.
  5. Create BPEL file.
  6. The calling process combines BPEL activities for each node and created a master BPEL file, which represents the entire process. The <scope/> blocks generated for each WorkflowNode can simply by appended to each other in this manner.
<process>
        <scope>
                <!— start workflow node related activities -!>
        </scope>
        <scope>
                <!— wait workflow node related activities -!>
        </scope>
        <scope>
                <!— mail workflow node related activities -!>
        </scope>
        <scope>
                <!— update workflow node related activities -!>
        </scope>
</process>
Let's analyze Step 2 in greater detail. Every node equates to a series of BPEL activities. All this information is stored in the XQuery template file. There is an XQuery template for each node, which embodies all the BPEL constructs for that specific node. You have created start.xq, wait.xq, message.xq, update.xq and stop.xq corresponding to five nodes in the Cube Management process.

The following table describes the BPEL activities generated for start, wait, message, update, and stop nodes in the example process.

Nodes in Cube Management Process

Equivalent BPEL Constructs

Start

<sequence>






<receive>





<assign>





<scope>






<variables>





<sequence>






<assign>





<invoke>

Wait

<scope>






<variables>





<sequence>






<assign>





<invoke>





<pick>


message

<scope>






<variables>





<sequence>






<assign>





<invoke>


update

<scope>






<variables>





<sequence>






<assign>





<invoke>


End

<scope>






<variables>





<sequence>






<assign>





<invoke>





<terminate>


Note how each process node is generated into a hierarchy of BPEL activities. For example, the BPEL used to generate the message activity looks like this. It is stored in a XQuery file message.xq.

declare namespace ent = "http://www.centerstonesoft.com/schema/entity"; declare variable $nodeKey as xs:string external;

                               
<scope>
        <!-- generation for Message activity -->
        <variables>

        <!-- Define variables input, output and error variables-->
                <variable name="emailRequest" messageType="services:EntityAsyncMsgWSRequestMessage"/>
                <variable name="emailResponse" messageType="services:EntityAsyncMsgWSResultMessage"/>
                <variable name="fault" messageType="services:EntityFaultMessage" />
        </variables>

        <!--Begin the BPEL activities -->
        <sequence>

        <!--This is the first assign activity. It configures the input variable with
   <!-- identification of node ($nodekey) and function to be called -->
        <!-- by the external webService (bpelSendEmail). -->

                <assign>
                        <copy>
                                <from>
                                        <ent:EntityCollectionElem xmlns:ent="http://www.centerstonesoft.com/schema/entity" >
                                                <ent:Header/>
                                                <ent:entityName/>
                                                <ent:EntityElem>
                                                        <ent:entityKey>{ $nodeKey }</ent:entityKey>
                                                        <ent:method >
                                                                <ent:methodName>bpelSendEmail</ent:methodName>
                                                                <ent:parameter>
                                                                        <ent:PropName>thread</ent:PropName>
                                                                        <ent:Type>string</ent:Type>
                                                                        <ent:Value/>
                                                                </ent:parameter>
                                                        </ent:method>
                                                </ent:EntityElem>
                                        </ent:EntityCollectionElem>
                                </from>
                                <to variable="emailRequest" part="payload"/>
                        </copy>
                </assign>

        <!--Second and third assign activity configure the input variable -->
        <!--with identification of actual process instance -->

                <assign>
                        <copy>
                                <from variable="input" part="parameters" query="//ent:Header"/>
                                <to variable="emailRequest" part="payload" query="//ent:Header"/>
                        </copy>
                </assign>
                <assign>
                        <copy>
                                <from variable="threadKey" part="value"/>
                                <to variable="emailRequest" part="payload" query="//ent:parameter[ent:PropName = 'thread']/ent:Value"/>
                        </copy>
                </assign>

                <!--After configuring input variable with node, instance and function to be called,
                       invoke the external service -->
                <invoke name="invoke" partnerLink="EntityAsyncMsgWSLink"
                           portType="services:EntityAsyncMsgWS" operation="invoke" inputVariable="emailRequest"/>

                <!--Check response from external service. If response indicates fault, throw exception. -->
                <!--If no response is received for 10 days, throw exception -->
                <pick name="deadlined_receive" >

                        <!--Set output variable if return result from external webservice is OK-->
                        <onMessage partnerLink="EntityAsyncMsgWSLink" portType="services:EntityAsyncMsgWSCallback"
                              operation="onResult" variable="emailResponse">
                                <empty/>
                        </onMessage>

                <!--On fault, check the fault code and throw  appropriate exception-->

                        <onMessage operation="onFault" partnerLink="EntityAsyncMsgWSLink"
                              portType="services:EntityAsyncMsgWSCallback" variable="fault">
                                <switch>
                                        <case condition="bpws:getVariableData('fault', 'value',
                                              '/EntityFault/faultcode') = 'MailException' ">
                                                <throw faultName="ent:MailException" faultVariable="fault" />
                                        </case>
                                        <otherwise>
                                                <throw faultName="ent:SystemException" faultVariable="fault" />
                                        </otherwise>
                                </switch>
                        </onMessage>

                        <!--If no response for 10 days, throw  exception-->
                        <onAlarm  for="'P10D'">
                                <throw faultName="ent:SystemException"/>
                        </onAlarm>
                </pick>
        </sequence>
</scope>

                            
After reviewing this XQuery template, you'll see how the document generates a single <scope> activity that contains the implementation of the message process node. The <scope> activity begins with a <variables> section where variables are defined for the request as well a normal response or fault response. The variables section is followed by a <sequence> where the request is generated using a sequence of <assign> statements. The first <assign> writes the boilerplate for the request; within this <assign>, the XQuery parameter nodeKey is used to pass node identification to the external Web service.

Note that nodeKey is generation time information. After the first <assign> activity, the following two <assign> activities, deal with run-time information. The second <assign> copies header information from the initial request used to start the process. This header information identifies the eCenterOne and Oracle BPEL Process Manager process instance IDs as well as the customer that the process is for. The third <assign> activity copies the process thread ID. (As mentioned earlier, similar XQuery templates exist for other nodes in the process.)

XQuery processing. Let's quickly look at how all these templates are processed by the XQuery processor and combined to form a BPEL file.

The generateActivity() below accepts the list of nodes for which the BPEL activities need to be generated. For each node, it will locate the right XQuery template using the function XQuery.newXQueryFromResource. The Saxon processor will then process this XQuery template and produce an XML String. If a node has a child, it will be processed in a recursive manner in the while loop.

                               
/**
 * Recursively generates an activity including any child activities.
 * @param activity the node that is being generated, the value is null for the top-level
 * process node
 * @return typically the root element of the generated XML document
 * @throws EntityException
 */
    protected String generateActivity(WorkflowNode activity) throws EntityException
    {
        //Find the next node in the list to be processed
        BpelActivityType activityType = getBpeBpelActivityType(activity);
        //Get the parameters to be passed to node
        HashMap params = calculateParameters(activity, activityType);
        try {
            //Get corresponding XQuery file for the node
            XQuery q = XQuery.newXQueryFromResource("bpelgen/" + activityType.getQuery());
            //Call the Saxon XQuery Processor to run the query.
            XQueryResult result = q.run(null, params, null);
            //Convert the result into a string
            String parentXml = result.serializeString();
            if (logger.isDebugEnabled()) {
                logger.debug("Generated for parent '" + activityType.getName() + "' is \n" + parentXml);
            }
            //If there are child nodes, recursively invoke generate activity
            if (_process.hasChildren(activity)) {
                //Call java relational class
                Iterator it = _process.getChildrenIterator(activity);
                //Recursively call generateActivity for child nodes. For ‘Cube Management Process’, these will be
        //start, wait, message, update & end
                while (it.hasNext()) {
                    WorkflowNode childActivity = (WorkflowNode) it.next();
                    String childXml = generateActivity(childActivity);
                    // now use Saxon XSLT to add the document generated for the children to the result document
        // Get the Transformer used for this transformation
                    Transformer transformer = getPrependTransformer();
                    transformer.setParameter("inserted", DomUtils.getDocument(new ByteArrayInputStream(childXml.getBytes())));
                    StreamResult saveResult = new StreamResult(new ByteArrayOutputStream());
                    try {
                        StreamSource src = new StreamSource(new ByteArrayInputStream(parentXml.getBytes()));
           // Transform the XML Source
                        transformer.transform(src, saveResult);
                    }
                    finally {
                        transformer.clearParameters();
                    }
                    parentXml = ((ByteArrayOutputStream)saveResult.getOutputStream()).toString();
                    if (logger.isDebugEnabled()) {
                        BpelActivityType childType = getBpeBpelActivityType(childActivity);
                        logger.debug("Preprocessed for parent '" + activityType.getName() + "' and child '" +
                                     childType.getName() + "' is \n" + parentXml);
                    }
                }
            }
            // remove the <insert-here> node
            Transformer transformer = getRemovePrependTransformer();
            StreamResult saveResult = new StreamResult(new ByteArrayOutputStream());
            StreamSource src = new StreamSource(new ByteArrayInputStream(parentXml.getBytes()));
            transformer.transform(src, saveResult);
            parentXml = ((ByteArrayOutputStream)saveResult.getOutputStream()).toString();
            if (logger.isDebugEnabled()) {
                logger.debug("Generated for '"+activityType.getName()+"' is \n" + parentXml);
            }
            return parentXml;
        }
        catch (SAXException ex) {
            throw new EntityException("Failed to run transform "+activityType.getQuery(), ex);
        }
        catch (TransformerException ex) {
            throw new EntityException("Failed to run transform "+activityType.getQuery(), ex);
        }
        catch (IOException ex) {
            throw new EntityException("Failed to load query "+activityType.getQuery(), ex);
        }
    }

                            
The generateActivity() method generates the final BPEL file in a string format. It is then written into a file by another Java program. You can download all XQuery templates as well as the BPEL file for the Cube Management process in the sample code file.

Dynamic deployment with Ant. The final step is to use Oracle BPEL Process Manager deployment tools to compile and deploy the BPEL process. For deployment, you need to generate the following simple Ant build.xml file and then execute it (again using XQuery for its support of parameterized XML file).

<?xml version="1.0" encoding="UTF-8"?>
<project default="main" basedir="C:/DOCUME~1/jerry/LOCALS~1/Temp/cs_bpelgen_simple24369" name="BpelDeploy">
   <target name="main">
      <bpelc keepGenerated="true" deploy="jt_dev" rev="1.0" home="c:\orabpel"/>
   </target>
</project>
The final step is to execute Ant programmatically from your application server. To do this, you can use code from the Ant task Execute (found in org.apache.tools.ant.taskdefs). This class provides industrial-strength support for programmatically executing command-line processes and handling good and error returns.

The following code fragment describes how to use code in the Ant Execute task to implement BPEL process deployment.

String args[] = new String[1];
args[0] = "c:/orabpelpm/integration/orabpel/bin/obant.bat";

Execute exe = new Execute();
exe.setCommandline(args);
exe.setWorkingDirectory(_deploydir);
int retval = exe.execute();
if ( retval != 0 ) {
    throw new ExecuteException("process failed to deploy");
In this fragment, the Execute class is used to execute the Oracle BPEL Process Manager command line file, obant, directly. The batch file sets up the Windows environment appropriately and then invokes Ant on the build.xml file shown above.

A non-zero return code from this file is usually due to errors returned by the BPEL compiler. These errors are uncommon however and nearly always due to some internal error in the process implementation.

Conclusion

CenterStone Software lets property managers rapidly customize business processes using a custom designer. These processes are then dynamically converted into BPEL and deployed on Oracle BPEL Server. This approach should be seriously considered by any organization relying heavily on business users to design organizational processes. Not only does the custom designer approach shield users from the complexities of BPEL, but it also provides a unique platform to generate adaptive BPEL process on the fly. This enables organizations to enable and realize the benefits of SOA rapidly.


Jerry Thomas Jerry Thomas [ jthomas@centerstonesoft.com] is Chief Architect at Centerstone Software, which helps many of the world's largest organizations automate and manage their real estate, facilities, personnel, assets, leases, and workplace operations more efficiently. Thomas focuses on CenterStone's enterprise workplace management product and Web services, BPEL, and system infrastructure. Prior to CenterStone, Thomas worked as a consultant and held principal development positions at Riverton, ONTOS, and Hewlett-Packard.

Send us your comments