Getting Started

First of all, getting familiar with the WS-BPEL 2.0 standard is a very good idea. To use ODE, you will need to write processes using the BPEL language. There are several examples in our distributions that you can use to get started, but a decent understanding of the spec is assumed.

Ode can be deployed in two different environments:

  • As a simple Web Service in Axis 2, ODE is bundled in a WAR than can be deployed in any application server and is invoked using plain SOAP/HTTP.
  • As a JBI service assembly, ODE is bundled in a ZIP that can be deployed in any JBI container and is invoked using the NMR.

In a Web Application

Deploying the WAR

Unzip the distribution somewhere on your disk, everything needed is inside.

Get the WAR file in the distribution root directory, rename it to ode.war and copy this file to Tomcat's webapp directory. Start Tomcat and Ode should be up and running. You should get the Axis2 welcome page under http://localhost:8080/ode. The Ode WAR includes its own embedded database (Derby) so you don't have to worry about configuring any external database for now.

Examples

Copy the content of the examples directory in the distribution (the 3 sub-directories) to tomcat/webapps/ode/WEB-INF/processes, this will automatically deploy the 3 example processes. Use the sendsoap command located in the distribution bin directory to send test messages. The messages to run each of the 3 examples are provided in their respective directory (testRequest.soap). For each example type something like:

bin/sendsoap http://localhost:8080/ode/processes/helloWorld examples/HelloWorld2/testRequest.soap

The sendsoap executable can be found in the distribution bin directory. The urls should be updated according to the address defined in the WSDL file for the process service.

Configuring ODE in Tomcat with a MySQL database

The ODE war should have been copied to the webapps directory of Tomcat and the server should have been started at least once before following these instructions. This ensures that the webapp is properly exploded.

  1. Drop the MySQL JDBC driver (MySQL Connector/J) in the common/lib directory of Tomcat.
  2. Add the following stanza to conf/server.xml inside the <Host> element:
    <Context path="/ode" docBase="ode" debug="5" reloadable="true" crossContext="true">
    	 <Resource name="jdbc/ODEDB" auth="Container" type="javax.sql.DataSource"
    		 maxActive="100" maxIdle="30" maxWait="10000"
    		 username="root" password="" driverClassName="com.mysql.jdbc.Driver"
    		 url="jdbc:mysql://localhost:3306/ode?autoReconnect=true"/>
     </Context>
  3. Make sure that MySQL is started and the ODE schema has been loaded in a ode database.
  4. Add a file named ode-axis2.properties under webapps/ode/WEB-INF/conf with the following content:
    ode-axis2.db.mode=EXTERNAL
      ode-axis2.db.ext.dataSource=java:comp/env/jdbc/ODEDB

You're done!

In JBI

Here's a quick overview to deploy PXE/ODE on a JBI container (e.g. ServiceMix)

1) Download the JBI distribution

Check our download page and get the latest JBI-based distribution. Unzip it in a directory of your choice. We'll now refer to this directory as ODE_HOME.

2) Install on JBI container

For example, with ServiceMix you can use file-system deployment:

(from ode/jbi directory)
cp ODE_HOME/ode-jbi-1.1.zip SERVICEMIX_DIR/install

The above will result in ODE being installed with the default settings. You may wish to first modify the ode-jbi.properties file found in the root of the installer ZIP.

3) Deploying Processes

We are assuming that the reader is familiar with JBI deployment concepts. Deploying a process consists of the following steps:

  1. Create a temporary service unit directory for the BPEL processes
  2. Place the relevant .bpel, .wsdl and .xsd files into the temporary directory
  3. Create an ODE deployment descriptor (deploy.xml) and place it in the temporary directory
  4. (Optional) Compile the the BPEL processes using the bpelc command.
  5. Zip up the contents of the temporary directory into a service unit archive
  6. Create a temporary service assembly directory
  7. Place the service unit archive in a temporary service assembly directory
  8. Update the service assembly's jbi.xml descriptor
  9. Zip up the contents of the service assembly directory into a service assembly archive
  10. Copy the service assembly archive into the appropriate deploy directory

Examples

Some JBI examples are available under the examples directory of the ODE distro:

  • HelloWorld2
  • PingPong
  • Async2

Extract the distro-jbi-2.0-SNAPSHOT.zip created in the build instructions above.

To compile the examples, you may wish to define the ODE_HOME environment variable.  The build script does a good job of figuring this out without ODE_HOME set, however.

# On Linux/Unix
export ODE_HOME=/path/to/ode/distribution

# On Windows
set ODE_HOME=C:\Path\To\Ode\Distribution

and run Ant in the example's directory:

cd %ODE_HOME%/examples/PingPong
ant

This will create a JBI service assembly in the "build" subdirectory. With ServiceMix you can simply copy it to the file-system hot deployment directory:

(from PingPong directory)
cp build/PingPing-sa.zip SERVICEMIX_DIR/deploy

Finally, you can test the example by typing:

(from PingPong directory)
ant test

JBI Endpoints

ODE now relies strictly on abstract web service definitions (i.e., without binding/service/port definitions), meaning that you only need abstract WSDLs when compiling processes. Since JBI uses normalized messages (in theory, at least), there is no need to define bindings for the BPEL service engine.

The following is an example of bindings to JBI internal service endpoints using the deployment descriptor (e.g. MyProcess.dd):

<?xml version="1.0" encoding="UTF-8"?>
<dd:deployment-descriptor
        xmlns:dd="http://pxe.fivesight.com/schemas/2006/02/14/bpeldd"
        xmlns:jbi="http://java.sun.com/jbi/end-point-reference">

    <dd:endpoint-refs>

        <!-- Bind process endpoint to a JBI ServiceEndpoint -->
        <dd:endpoint-ref partnerLinkName="SomePartnerLink" partnerLinkRole="myRole">
            <jbi:end-point-reference service-name="{http://mycompany.com/someNamespace}MyService" end-point-name="myEndpoint"/>
        </dd:endpoint-ref>

        <!-- Bind partner service to a JBI ServiceEndpoint -->
        <dd:endpoint-ref partnerLinkName="AnotherPartnerLink" partnerLinkRole="partnerRole">
            <jbi:end-point-reference service-name="{http://example.com/otherNamespace}SomeService" end-point-name="someEndpoint"/>
        </dd:endpoint-ref>

    </dd:endpoint-refs>

</dd:deployment-descriptor>

One may use JBI binding components to make services externally available and therefore providing concrete bindings to those binding components. An example of exposing process services via SOAP/HTTP can be found in the PingPong ping-http service unit.

Database

The generated installer will use an internally-managed embedded Derby database. No configuration is required. To use an external database one needs to modify ode-jbi.properties found in the component installer zip.

Compatibility Caveat

Many binding components are not very good about delivering messages in the correct format for WSDL11 services.

Known Issues with ServiceMix

ServiceMix' so-called lightweight components make it difficult to properly expose process services since they do not fully implement the JBI contract and do not allow the process engine to enquire about its external endpoints.

  • The servicemix-http binding component does not normalize messages

Using Ode

Process Deployment

Deploying a Process in Ode

Each deployment is a directory with all relevant deployment artifacts. At the minimum it will contain the deployment descriptor, one or more process definitions (BPEL or .cbp), WSDL and XSDs (excluding those compiled into the .cbp). It may also contain other files, such as SVGs or XSLs. The deployment descriptor is a file named deploy.xml (see the next paragraoh for its description).

During deployment, the process engine loads all documents from the deployment descriptor. Loading documents allow it to reference processes, service and schema definitions using fully qualified names, and import based on namespaces instead of locations.

To deploy in Ode, just copy the whole directory containing your artifacts (the directory itself, not only its content) in the path %DEPLOYMENT_ROOT%/WEB-INF/processes (in Tomcat it would be %TOMCAT_HOME%/webapps/ode/WEB-INF/processes).

Deployment Descriptor

To deploy your process in Ode you will need to create a simple deployment descriptor with basic information. The deploy.xml file configures one or several processes to use specific services. For each process, deploy.xml must supply binding information for partner links to concrete WSDL services. Every partner link used with a <receive> activity must be matched with a <provide> element, and every partnerLink used in an <invoke> activity must be matched with an <invoke> element in deploy.xml (unless that partnerLink has initializePartnerRole="false").

Formal definition

The XML schema describing ODE's deployment descriptor is available here. The root element, deploy, contains a list of all deployed processes from the deployment directory:

<deploy>
 <process ...>*
 { other elements }
 </process>
</deploy>

Each process is identified by its qualified name and specifies bindings for provided and invoked services:

<process name = QName  fileName = String?  bpel11wsdlFileName = String? >
 (<provide> | <invoke>)*
 { other elements }
</process>

Each process element must provide a name attribute with the qualified name of the process. Optionally, a fileName attribute can be used to specify the location of the BPEL process definition (the .bpel file). The fileName attribute does not need to be provided unless non-standard compilation options are used or the bpel11wsdlFileName attribute is used to specify a WSDL document for a BPEL 1.1 process.

Each <process> element must enumerate the services provided by the process and bind each service to an endpoint. This is done through <provide> elements which associates {{partnerLink}}s with {{endpoint}}s:

<provide partnerLink=NCName>
  <service name = QName port = NCName?>
</provide>

Note, that only one partnerLink can be bound to any specified endpoint.

The port attribute can be used to select a particular endpoint from the service definition.

Examples

A very simple process that would only be invoked would use a deploy.xml very similar to:

<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03" 
	xmlns:pns="http://ode/bpel/unit-test" xmlns:wns="http://ode/bpel/unit-test.wsdl">
	<process name="pns:HelloWorld2">
		<active>true</active>
		<provide partnerLink="helloPartnerLink">
			<service name="wns:HelloService" port="HelloPort"/>
		</provide>
	</process>
</deploy>

See the complete example here.

A deployment including two processes invoking each other and whose execution would be triggered by a first message would look like:

<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03" xmlns:main="http://ode/bpel/unit-test" 
        xmlns:mws="http://ode/bpel/unit-test.wsdl" xmlns:resp="http://ode/bpel/responder">

	<process name="main:MagicSessionMain">
		<provide partnerLink="executePartnerLink">
			<service name="mws:MSMainExecuteService" port="MSExecutePort"/>
		</provide>
		<provide partnerLink="responderPartnerLink">
			<service name="mws:MSMainService" port="MSMainPort"/>
		</provide>
		<invoke partnerLink="responderPartnerLink">
			<service name="mws:MSResponderService" port="MSResponderPort"/>
		</invoke>
	</process>
	<process name="resp:MagicSessionResponder">
                <type>resp:MagicSessionResponder</type>
		<provide partnerLink="mainPartnerLink">
			<service name="mws:MSResponderService" port="MSResponderPort"/>
		</provide>
		<invoke partnerLink="mainPartnerLink">
			<service name="mws:MSMainService" port="MSMainPort"/>
		</invoke>
	</process>
</deploy>

See the complete example here.

Additional settings

In memory execution

For performance purposes, you can define a process as being executed only in-memory. This greatly reduces the amount of generated queries and puts far less load on your database. Both persistent and non-persistent processes can cohabit in Ode.

To declare a process as in-memory just add an in-memory element in your deploy.xml:

<process name="pns:HelloWorld2">
	<in-memory>true</in-memory>
	<provide partnerLink="helloPartnerLink">
		<service name="wns:HelloService" port="HelloPort"/>
	</provide>
</process>

Be aware that in-memory executions introduces many restrictions on your process and what it can do. The instances of these processes can't be queried by using the Management API. The process definition can only include one single receive activity (the one that will trigger the instance creation).

User-defined process properties

Versioning

Introduction

Before starting on what process versioning exactly does, let's see what the world (or at least Ode) would be without versioning. It will be much more easier for you to understand the solution after fully seeing the problem.

So you're starting using Ode and you've just designed you first business process. It's all nice and dandy and works perfectly. It works so well that you let your users start using it. It's not really production but you know, release early, release often, so let's see what users think of it. After a couple of days you realize that a couple of steps are missing, you add them in your process and once again, it executes smoothly. So let's see what our users think of the improvement! Next thing you know, your phone starts ringing and the user on the other side is most likely pretty upset. What happened?

So when you start using a process, executions for it are created, running processes if you like (also called process instances). Depending on the type of your business these could take a variable amount of time to execute but they're usually not instantaneous. So you have all these running processes sometimes doing things, sometimes just waiting there and all of a sudden, a brand new process definition replaces the original one all these executions have been using so far. What is a process engine to do with all these executions? Well, the most logic thing on earth: just nuke them all.

At this time there's no simple automated way to migrate a running process that has been executing using one definition to another new one. Computing the differences between the 2 definitions can be very complex and chances are that they're not even compatible! When you think of all these little tasks that are arranged just so to guarantee a perfect execution using the right data types, even minor alterations can get really tricky to apply on instances without blowing them all.

So here is the crude and sad truth: without having some versioning goodness in it, a process engine will always delete all the running instances when a new process definition is deployed.

How Versioning Works

So if existing executions can't be migrated, what are you going to do with them? Well, just let them be. Versioning is based on the fact that, instead of directly updating the original process definition (leaving its instances to their dreadful fate), another new version of this definition is created. The older one is declared retired so no new executions can be started on that one, the new process is the one to be used now on. But running instances can still finish their job peacefully as the process they've been using to execute so far is still available and unchanged.

However Ode also has the concept of deployment bundles and supports 2 modes of deployment (remotely or manually directly on the filsesystem). Let's see how we get versioning to work under those conditions.

Process Versioning in Ode

In Ode, processes are deployed in what we call a deployment bundle. When you come down to it, it's just a zip file or a directory containing Ode's deployment descriptor (deploy.xml), the processes BPEL and all the other goodies necessary for your BPEL to run (WSDLs, schemas, xsl stylesheets, you name it). And what Ode is using to know you're redeploying the same thing is the deployment bundle name.

So when you're redeploying a deployment bundle in Ode, here is what happens:

  1. A new version is attributed to the bundle by incrementing the version number of the last deployment.
  2. Ode checks whether the same bundle has been deployed before, all processes in those older bundles are retired.
  3. The processes in the bundle are deployed in the engine using the same version number as the bundle itself.
  4. New executions of all newly deployed processes are ready to be started.

There are a couple of additional remarks to make. The first is that the version is a single, sequentially incremented (which is to say that 3 comes after 2 and 2 comes after 1) number. All deployed bundles share the same sequence. The second thing to be aware of is that all processes in a bundle share the same version number and it's the number of their bundle.

Let's use the notation Foo-x(Bar-x, Baz-x) to represent the deployment of the Foo bundle in version x with processes Bar and Baz (sharing the same version number as just explained). The following illustrates a valid deployment sequence:

  1. Coconut-1(Pineapple-1, Mango-1)
  2. Orange-2(Tangerine-2)
  3. Orange-3(Tangerine-3) => retires Orange-2(Tangerine-2)
  4. Coconut-4(Pineapple-4, Mango-4) => retires Coconut-1(Pineapple-1, Mango-1)
  5. Banana-5(Kiwi-5)

That's both tasty and healthy!

There's still a last question left unsolved: what happens if you take your bundle and deploy it under a different name with the same content. If you know a bit about source version control (like CVS or Subversion), that's very close to branching, only you might be executing two branches at the same time. As Ode can't find another bundle with the same, the processes will simply be deployed without retiring anything. You will effectively have twice the same process deployed under different versions. In that scenario you're supposed to know what you're doing.

If two identical process definitions are deployed at the same time, the behavior of the engine is unspecified. Which one of the two process will pick up the message? Who knows!? But this can be a very useful feature in specific cases when you want to deploy the same process twice (by same understand same name and same namespace) but the 2 definitions are actually different and enable different endpoints. This allows the parallel deployment of two different version of the same process provided that they don't overlap in their endpoint implementation.

Remote Deployment vs. Hand-Made Deployment

Ode supports 2 different ways of deploying bundles:

  • using the deployment web service or JBI deployment.
  • dropping bundles as directories under WEB-INF/processes.

The first way works just as described previously. Under the hood, your process bundle is a zip and it gets unzipped in a directory named bundlename-version. The version number is automatically generated by the engine. So you only need to provide the zip itself and a consistent bundle name.

For the second way, it's a bit more tricky. Because you're directly interacting with the deployment directory, you're allowed to create a bundle directory with any name you like (even not numbered at all). In that case Ode will still create a version number, it just won't be shown on the filesystem. However as it won't be able to find the previous bundle to retire, it will just deploy the new bundle along with all other processes, even if you already had some conflicting deployments. Basically, if you don't number your directories properly, every new deployment will be a new branch. In short, you don't really want to do that.

Another thing you're allowed to do with the file system is simply to replace (or remove and copy) all the files in the deployment bundle directory and remove the .deployed marker file to trigger redeployment. In that case Ode will simply consider you've undeployed and deployed the whole thing. So we get back to the situation where we don't have any versioning. Which can be very useful when you're in development mode because you usually don't care much about the running instances and you usually don't want to pile up versions of process definitions.

Management API

Ode has a complete management API to check which processes are deployed, running and completed instances, variables values and more. To see which methods are available, have a look at the ProcessManagement and InstanceManagement interfaces, the javadoc is pretty comprehensive.

These two interfaces are available as web services on the Axis2-based distribution. The corresponding WSDL can be found here.

To invoke these two services, any web service client should work (in a perfect interoperable world). To ease the invocation when using an Axis2 client, a helper class is bundled in ode-axis2.jar: ServiceClientUtil. Usage examples are also available in test classes InstanceManagementTest and ProcessManagementTest. Here is a short example demonstrating the invocation of the listAllProcesses operation:

ServiceClientUtil client = new ServiceClientUtil();
OMElement root = client.buildMessage("listAllProcesses", new String[] {}, new String[] {});
OMElement result = client.send(msg, "http://localhost:8080/ode/processes/ProcessManagement");

We're using XMLBeans to serialize and deserialize the returned values from/to XML so in the previous example. So if you'd like to have objects instead of an Axiom structure in the previous example, just add the following lines of code:

ScopeInfoDocument scopeIndoDoc = ScopeInfoDocument.Factory.parse(result.getXMLStreamReader());

You will need to include ode-bpel-api.jar in your classpath.

Specification

More details are available in the Process Management API specification

ODE Execution Events

ODE generates events to let you track what is exactly happening in the engine and produces detailed information about process executions. These events are persisted in ODE's database and can be queried using the Management API. The default behavior for the engine is to always generate all events for every executed action. However from a performance standpoint it's a good idea to deactivate some of the events you're not interested in (or even all of them). Inserting all these events generates a non-negligeable overhead.

Event types

The following table details each event possibly generated by ODE:

Event Name Process/Scope Description Type
ActivityEnabledEvent Scope An activity is enabled (just before it's started) activityLifecycle
ActivityDisabledEvent Scope An activity is disabled (due to dead path elimination) activityLifecycle
ActivityExecStartEvent Scope An activity starts its execution activityLifecycle
ActivityExecEndEvent Scope An activity execution terminates activityLifecycle
CompensationHandlerRegistered Scope A compensation handler gets registered on a scope scopeHandling
CorrelationMatchEvent Process A matching correlation has been found upon reception of a message correlation
CorrelationNoMatchEvent Process No matching correlation has been found upon reception of a message correlation
CorrelationSetWriteEvent Scope A correlation set value has been initialized dataHandling
ExpressionEvaluationFailedEvent Scope The evaluation of an expression failed dataHandling
ExpressionEvaluationSuccessEvent Scope The evaluation of an expression succeeded dataHandling
NewProcessInstanceEvent Process A new process instance is created instanceLifecycle
PartnerLinkModificationEvent Scope A partner link has been modified (a new value has been assigned to it) dataHandling
ProcessCompletionEvent Process A process instance completes instanceLifecycle
ProcessInstanceStartedEvent Process A process instance starts instanceLifecycle
ProcessInstanceStateChangeEvent Process The state of a process instance has changed instanceLifecycle
ProcessMessageExchangeEvent Process A process instance has received a message instanceLifecycle
ProcessTerminationEvent Process A process instance terminates instanceLifecycle
ScopeCompletionEvent Scope A scope completes scopeHandling
ScopeFaultEvent Scope A fault has been produced in a scope scopeHandling
ScopeStartEvent Scope A scope started scopeHandling
VariableModificationEvent Scope The value of a variable has been modified dataHandling
VariableReadEvent Scope The value of a variable has been read dataHandling

The second column specifies wether an event is associated with the process itself or with one of its scopes. The event type is used for filtering events.

Filtering events

Filtering at the process level

Using ODE's deployment descriptor, it's possible to tweak events generation to filtrate which ones get created. First, events can be filtered at the process level using one of the following stanza:

<dd:process-events generate="all"/> <!-- Default configuration -->

<dd:process-events generate="none"/>

<dd:process-events>
    <dd:enable-event>dataHandling</dd:enable-event>
    <dd:enable-event>activityLifecycle</dd:enable-event>
</dd:process-events>

The first form just duplicates the default behaviour, when nothing is specified in the deployment descriptor, all events are generated. The third form lets you define which type of event is generated, possible types are: instanceLifecycle, activityLifecycle, dataHandling, scopeHandling, correlation.

Filtering at the scope level

It's also possible to define filtering for each scope of your process. This overrides the settings defined on the process. In order to define event filtering on a scope, the scope activity MUST have a name in your process definition. Scopes are referenced by name in the deployment descriptor:

<dd:deploy xmlns:dd="http://www.apache.org/ode/schemas/dd/2007/03">
    ...
    <dd:process-events generate="none">
        <dd:scope-events name="aScope">
            <dd:enable-event>dataHandling</bpel:enable-event>
            <dd:enable-event>scopeHandling</bpel:enable-event>
        </dd:scope-events>
        <dd:scope-events name="anotherScope">
            <dd:enable-event>activityLifecycle</bpel:enable-event>
        </dd:scope-events>
    </bpel:process-events>
    ...
</dd:deploy>

Note that it's useless to enable an event associated with the process itself when filtering events on scopes.

The filter defined on a scope is automatically inherited by its inner scopes. So if no filter is defined on a scope, it will use the settings of its closest parent scope having event filters (up to the process). Note that what gets inherited is the full list of selected events, not each event definition individually.

Event listeners

Ode lets you register your own event listeners to analyze all produced events and do whatever you want to do with them. To create a listener you just need to implement the org.apache.ode.bpel.iapi.BpelEventListener interface.

Then add your implementation in the server's classpath and add a property in ode-axis2.properties giving your fully qualified implementation class name. Something like:

ode-axis2.event.listeners=com.compamy.product.MyOdeEventListener

Start your server and you're done!

Manipulating Endpoints

Introduction

An endpoint reference holds information to call a service. The simplest endpoint reference is usually an URL but it can also be much more complex such as holding a message id, a reply-to address or custom properties.

In BPEL, endpoint references (aka EPRs) are modeled as partner link roles. When defining a partner link, two roles maybe defined, myRole and partnerRole:

<partnerLink name="responderPartnerLink" partnerLinkType="test:ResponderPartnerLinkType"
             myRole="main" partnerRole="responder" initializePartnerRole="yes"/>

Both partnerRole and myRole represent EPRs. So when assigning partner link roles or invoking partners, you are using EPRs behind the scene.

ODE and Endpoint References

The ODE runtime supports 4 types of EPRs:

We recommend the two first solutions to interact with the engine. The first one is just the easiest and for the case where you need more robustness, WS-Addressing is the most popular second choice.

To show you how these EPRs look like and how they can be assigned to partner links roles here are some examples:

<assign>

  <!-- Simple URL -->
  <copy>
    <from>
      <literal>http://localhost:8080/ode/dynresponder</literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>

  <!-- URL in soap:address element -->
  <copy>
    <from>
      <literal>
        <service-ref>
          <soap:address location="http://localhost:8080/ode/dynresponder"/>
        </service-ref>
      </literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>

  <!-- WS-Addressing endpoint reference -->
  <copy>
    <from>
      <literal>
        <wsa:EndpointReference>
          <wsa:To>http://localhost:8080/ode/dynresponder</wsa:To>
        </wsa:EndpointReference>
      </literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>
</assign>

Normally BPEL requires wrapping EPRs with inside a service-ref element, however ODE relaxes this requirement for ease of use and increased interoperability with existing services. ODE automatically detects the different EPR types when assigning to a partner link role. If you need to use WS-Addressing sessions (@see appropriate page), then you will have to use wsa:EndpointReference EPRs.

You can just as well assign EPRs to/from variables to pass them around and enable more dynamic communication patterns.

Passing Endpoint References

To pass endpoint references around and manipulate them, you usually need to assigne them to variables. The EPR can then be sent in a message and reassigned to another partner link. This lets you model complex scenarii where you don't know the address of your partner beforehand or where you select one partner among many others.

The type of the variable that will hold your EPR defines the type of the EPR that it will contain. For example if you define a message in your WSDL document that looks like this:

<wsdl:message name="EndpointMessage">
  <wsdl:part name="payload" element="xsd:string"/>
</wsdl:message>

ODE will automatically put a simple URL EPR when you assign this message part:

<variable name="myEndpoint" messageType="resp:EndpointMessage"/>
...
<assign>
  <copy>
    <from partnerLink="mainPartnerLink" endpointReference="myRole"/>
    <to variable="myEndpoint" part="payload"/>
  </copy>
</assign>

Now if you want to manipulate a WS-Addressing EPR, the only thing you have to change in the above examples is the message part type. So your message will then look like this:

<wsdl:message name="EndpointMessage">
<wsdl:part name="payload" element="wsa:EndpointReference"/>
</wsdl:message>

Once your EPR has been assigned to a variable and set, say, to another process, you just need to reassign it to a partner link partnerRole to use it:

<assign>
  <copy>
    <from variable="eprmessage" part="payload"/>
    <to partnerLink="mainPartnerLink"/>
  </copy>
</assign>
<invoke name="eprcall" partnerLink="mainPartnerLink"
       portType="resp:MSMainPortType" operation="call" inputVariable="eprmessage"/>

For a complete example check DynPartner in the engine examples.

BPEL Extensions

ODE extends the WS-BPEL in areas that aren't covered by the spec. This page details these extensions.

Implicit Correlations

BPEL process instances are stateful — therefore, a client interacting with the BPEL engine must identify the particular instance with which it intends to interact in all of its communications. The BPEL specification defines a mechanism — correlation — which allows the process designer to specify which parts of an incoming message (i.e. a message going from a client to the BPEL server) should be used to identify the target process instance. Correlation is a powerful mechanism — however it is a bit complicated and relies on "in-band" message data to associate a messages with a process instance.

To keep simple cases simple, ODE provides an alternative correlation mechanism — implicit correlation — that automatically handles correlation through "out-of-band" session identifiers. The mechanism is simple: a unique session identifier is associated with every every partner link instance. When a message is sent on a partner link, the session identifier is sent along with the message. The recipient is then able to use the received session identifier in subsequent communications with the process instance. Messages received by the BPEL engine that have a session identifier are routed to the correct instance (and partner link) by that session identifier.

Read more

Activity Failure and Recovery

There are several types of error conditions. In this document we introduce a class of error condition called failures, distinct from faults, and describe how failures are caught and handled by the process engine.

For example, when the process is unable to perform DNS resolution to determine the service endpoint, it generates a failure. An administrator can fix the DNS server and tell the process engine to retry the activity. Had the DNS error been reported as a fault, the process would either terminate or require complex fault handling and recovery logic to proceed past this point of failure.

In short, failures shields the process from common, non-terminal error conditions while retaining simple and straightforward process definitions that do not need to account for these error conditions.

Read more

XPath Extensions

Apache ODE extends the default XPath coverage provided by the WS-BPEL specification mostly by adding support for XPath 2.0 and by offering a few utility extension functions to make some assignments easier.

Read more

External Variables

External variables are an easy way to share data between the process and external systems, by treating those independent entities as BPEL variables that can be used in expressions and assignments. External variables may be records stored in the database, REST resources, etc.

Read more

Headers Handling

There are several situations where one would want to access headers form the wire format in their BPEL process. It could be to assign them to another message and pass them around or to execute some business logic that's header-dependent (often a bad idea but sometimes there's no choice). ODE supports the manipulation of wire format headers in two different ways.

Read more

RESTful BPEL, Part I

Extends the invoke activity to handle RESTful Web services. This extension uses BPEL variables of type xsd:uri and xsd:string instead of partner links, and does away with the WSDL indirection, making it straightforward to develop processes that use RESTful Web services.

Read more

RESTful BPEL, Part II

Extends receive and onEvent to expose RESTful resources that. This extension adds the ability to declare and instantiate resources, and specific handling for the HTTP methods GET, POST, PUT and DELETE.

Read more

How To

Controlling ODE's Memory Footprint

Rational

In most ODE deployments, processes are only used once in a while and the time between each solicitation can be pretty long with respect to the actual execution time. However the default behavior for the engine is to load all processes permanently in memory, including their definition. For environments where memory is scarce or where a large number of processes are deployed, this isn't suitable.

ODE implements two mechanisms in order to reduce the memory footprint of the engine to the strict minimum:

  • Process definitions lazy-loading: processes are loaded in-memory bare, without their runtime definition (the compiled BPEL process). The definition will be loaded and associated to the in-memory process representation only when they are actually invoked. This mechanism is called hydration.
  • Process definitions reaping: process definitions can be disassociated from their in-memory representation if they haven't been used for some time of if there are already too many definitions loaded in memory. This mechanism is called dehydration. A process will automatically rehydrate itself when necessary (when it receives a message for example).

Activating Dehydration Policy

In the Axis2 integration layer, activation of the policy can be done by setting the following property on the ode-axis2.properties located in the WEB-INF/conf directory of ODE's web application:

ode-axis2.process.dehydration=true

The default configuration is to dehydrate processes that haven't been used for 20mn or after the maximum of 1000 process definitions in memory is reached.

Dehydration Policy at IL Level

If you're using your own interface layer or want to do some customization at this level, the default hydration policy is implemented in CountLRUDehydrationPolicy. It should be set on BpelServerImpl and can been configured by setting the process max age or max count (either one will not influence the dehydration if set to 0). For example:

CountLRUDehydrationPolicy dehy = new CountLRUDehydrationPolicy();
dehy.setProcessMaxAge(60000);  // Setting process max age to one minute
dehy.setProcessMaxCount(100);  // Setting maximum hydrated processes to 100
_server.setDehydrationPolicy(dehy);

The dehydration policy is polled every 10s to see if some processes should be dehydrated so a process max age of less than 10 seconds will be effectively of 10 seconds. Alternatively a custom dehydration policy can be used by implementing the DehydrationPolicy interface.

HTTP Authentication (Axis2)

This section explains how to perform authentication against Web services requiring HTTP basic, digest or NTLM authentication mechanisms.

Non-Standard

This mode of authentication is non-standard in the Web service world because the authentication data is passed outside of the SOAP message. This feature is still experimental and requires Ode >1.1

Authentication element and message part

To perform authentication, you must pass an additional message part containing the general authentication element which contains the credentials (as plain-text strings)

Authenticate Message Part Content
<auth:authenticate xmlns:auth="urn:ode.apache.org/authentication">
    <auth:username/>?
    <auth:password/>?
    <auth:domain/>?     <!-- NTLM specific -->
    <auth:realm/>?      <!-- NTLM specific -->
    <auth:token/>?
</auth:authenticate>

This additional message part may be declared in the WSDL definition to allow tools to validate the data structure:

"MyService.wsdl"
<?xml version='1.0' encoding='utf-8'?>
<wsdl:definitions xmlns:tns="http://www.example.com"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:xs="http://www.w3.org/2001/XMLSchema"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:auth="urn:ode.apache.org/authentication"
                  targetNamespace="http://example.com">

    <wsdl:import namespace="urn:ode.apache.org/authentication" location="Authentication.xsd"/>

    <wsdl:message name="MyRequest">
        <wsdl:part name="body" element="tns:HelloText"/>
        <wsdl:part name="authenticate" element="auth:authenticate"/>     <!-- Additional part -->
    </wsdl:message>

</wsdl:definitions>

Notes:

  • The message part does not have to be named authenticate, it is only suggested as descriptive name.
  • The message part should not be referenced as SOAP header or SOAP body in the binding.
  • If you are using Document-Literal style binding, make sure that your body binding references the body part, because you now have more than one part in the message definition.

The "authenticate" element schema.

The schema of the authenticate element follows:

Authenticate.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="urn:ode.apache.org/authentication"
           elementFormDefault="qualified">

    <xs:element name="authenticate">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="username" type="xs:string"  minOccurs="0" maxOccurs="1"/>
                <xs:element name="password" type="xs:string"  minOccurs="0" maxOccurs="1"/>
                <xs:element name="domain" type="xs:string" minOccurs="0" maxOccurs="1"/>
                <xs:element name="realm" type="xs:string" minOccurs="0" maxOccurs="1"/>
                <xs:element name="token" minOccurs="0" maxOccurs="1">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:any minOccurs="1"/>
                        </xs:sequence>
                    </xs:complexType>
                 </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

You can add this schema to your project to allow tools to display/validate the correct element structure.

Using a JNDI DataSource under ServiceMix JBI

These instructions will help you configure a JNDI DataSource for Apache Ode when running inside the ServiceMix JBI container.

1. Edit $SERVICEMIX/conf/jndi.xml

Declare a managed connection factory pointing to your database:

<bean id="odeManagedConnectionFactory" class="org.jencks.tranql.DataSourceMCF">
        <property name="driverName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://hostname/databaseName"/>
        <property name="user" value="username"/>
        <property name="password" value="myPassword"/>
    </bean>

And register the DataSource in the JNDI registry by adding an <entry> under the <util:map> element:

<util:map id="jndiEntries">

    <!-- Ode DataSource -->
    <entry key="java:comp/env/jdbc/ode">
        <bean id="odeDataSource" class="org.jencks.factory.ConnectionFactoryFactoryBean">
            <property name="managedConnectionFactory" ref="odeManagedConnectionFactory"/>
            <property name="connectionManager" ref="connectionManager"/>
        </bean>
    </entry>

    <!-- ... other entries follow... -->

  </util:map>

2. Edit ode-jbi.properties

In ode-jbi.properties, set the following properties:

ode-jbi.db.mode=EXTERNAL
ode-jbi.db.ext.dataSource=java:comp/env/jdbc/ode

(Be sure to match the JNDI lookup name to the one defined in $SERVICEMIX/conf/jndi.xml)

3. Add jencks-2.0-all library

Copy jencks-2.0-all.jar under $SERVICEMIX/lib

4. Restart ServiceMix

And you're done! Don't forget to redeploy your service assemblies since they need to be re-synchronized with Ode.

Extras

Connection Pool Parameters

If you want to manually configure the connection pool parameters, edit $SERVICEMIX/conf/tx.xml and update the "poolingSupport" object. For example,

<jencks:poolingSupport id="poolingSupport"
    connectionMaxIdleMinutes="5"
    connectionMaxWaitMilliseconds="10000"
    poolMaxSize="100"
    poolMinSize="20" />

Writing BPEL Test Cases

ODE has a test framework to automatically run BPEL processes. A big part of our test harness is therefore many different BPEL processes that test specific BPEL configurations or interactions. If you run into a problem with one of your processes that seems to be a bug, the best way to get it fixed is to contribute a test case to the project. We'll run it and keep it to prevent regressions. The more test cases we have, the more robust ODE will be.

This small guide will just explain you how to write and structure a test case to include it in ODE's test suite. For those who rather have examples than explanations, you can already check all existing test cases.

BPEL Constraints

An automated test system has to make some assumptions about your test cases to reduce the complexity of running them. Therefore ODE's test framework can't run absolutely any process. There are a couple of limitations to be aware of:

  • Your process must start with a receive and end with a matching reply. The result produced by the reply is what will be validated.
  • Invoking external web services during the execution is not possible, test cases must be self-contained as BPEL processes. However we'll see later that a couple of predefined mocked services can be invoked.

Other than that your process can do anything and can use all the WSDL, schemas and XSL stylesheets you need.

Test Descriptor

So to begin with your test process must at least have a BPEL file, a WSDL file and the standard deploy.xml deployment descriptor (links are provided for the HelloWorld test case). All of these should be included in a single directory.

Then for the test framework to know what it should do you will also need to write a simple test descriptor. It's a simple properties file saying which service is implemented by the process and which messages should be sent to start it and make it continue. It should me named test?.properties with the '?' being a increasing number. Here is the descriptor for the HelloWorld example, in test1.properties:

namespace=http://ode/bpel/unit-test.wsdl
service=HelloService
operation=hello
request1=<message><TestPart>Hello</TestPart></message>
response1=.*Hello World.*

The 3 first lines specify the namespace, the name and the operation to which messages will be sent. Then comes the request message. It should always be named request?, and should always be wrapped in a message element and another element named like the message part. Finally, and most importantly, comes the response test pattern. It's a regular expression that will be checked against the response produced by the process. If the expression can't be found, the test fails. In the HelloWorld example here we're just testing that our response includes 'Hello World'.

A test descriptor can contain more that one request/response couple, allowing several executions of a same process, testing different input/output combinations. You just need to increase the numbers in the request and response property names. For an example see the descriptor of the flow test.

Also if your process needs to receive several messages, you can have several test descriptors, each corresponding to a message. The files should just be named test1.properties, test2.properties, ... following the order of invocation. Here is an example with 2 descriptors used by the correlation test case:

test1.properties

namespace=http://ode/bpel/unit-test/testCorrelation.wsdl
service=testCorrelationService
operation=request
request1=<message><requestMessageData><testMessage><requestID>Start Test5.1</requestID><requestText>Event Start 
Test5.1</requestText><requestEnd>no</requestEnd></testMessage></requestMessageData></message>
response1=ASYNC

test2.properties

namespace=http://ode/bpel/unit-test/testCorrelation.wsdl
service=testCorrelationService
operation=continue
request1=<message><requestMessageData><testMessage><requestID>Start Test5.1</requestID><requestText>Event Start 
Test5.2.1</requestText><requestEnd>yes</requestEnd></testMessage></requestMessageData></message>
response1=.*Event Start Test5.1 -&gt; loop on receive until message includes requestEnd = yes -&gt; received 
            message -&gt; process complete.*

Finally a response can be marked as being ASYNC in the case no reply is expected for a given receive. This only applies to non instantiating receives:

response1=ASYNC

Mocked Services Toolkit

Writing isolated BPEL processes isn't something easy and for more advanced test cases you often need a bit more. The test framework therefore includes 2 mocked services to help you: the probe service and the fault service. Be aware however that the usage of these services require a bit more understanding on the BPEL that you're going to execute.

Probe Service

The probe service makes it easy to track the path that has been taken by a process execution by appending strings that are specific to one execution case. It basically takes a string that you pass and appends it to a global process execution string that you can test in the end. Let's see with a pseudo-code example:

receive(foo)
probe("received message " + foo.name)
if (foo.value > 50)
  probe("big value")
else
  probe("small value")
end
reply(probeStr)

Once this has been executed you can check whether the probeStr produced as a reply contains both "received message" and "big value" or "received message" and "small value" using a response regular expression.

Practically the probe service takes 2 parts: probeName and probeData. The probeName part should contain what you wnat to append, the probeData part will contain the appended string after the call and shouldn't be modified once it's been initialized. The probeData part will effectively contain the successive appended strings and that's what you're going to test at the end of the execution.

Here is a usage example extracted from the correlation test case:

<receive name="receive1" partnerLink="request" portType="wns:testCorrelationPT" 
         operation="request" variable="request" createInstance="yes">
	<correlations>
		<correlation set="testCorr1" initiate="yes"/>
	</correlations>
</receive>
<!-- Copy input variables to internal accumulators -->
<assign name="assign1">
	<copy>
		<from variable="request" property="wns:testProbeID"/>
        	<to variable="probeInput" part="probeName"/>
	</copy>
	<copy>
		<from variable="request" property="wns:testProbeData"/>
		<to variable="probeInput" part="probeData"/>
	</copy>
</assign>
<assign>
	<copy>
		<from>
			<literal><![CDATA[loop on receive until message 
                                          includes requestEnd = yes]]></literal>
		</from>
		<to variable="probeInput" part="probeName"/>
	</copy>
</assign>
<invoke name="probe" partnerLink="probe" portType="prb:probeMessagePT" operation="probe" 
        inputVariable="probeInput" outputVariable="probeInput"/>

The first assign initializes the probe parts with the input message. The second one places in probeName the text that should be appended. After the call to the probe service, probeData will contain both information appended. Then to return the probeData at the end of the execution:

<assign name="assign2">
	<copy>
		<from variable="probeInput" part="probeName"/>
		<to variable="reply" part="replyID"/>
	</copy>
	<copy>
		<from variable="probeInput" part="probeData"/>
		<to variable="reply" part="replyText"/>
	</copy>
</assign>
<reply name="reply" partnerLink="request" portType="wns:testCorrelationPT" operation="continue" 
       variable="reply"/>

The returned data is finally tested by using a nice regular expression for the response:

response1=.*Event Start Test5.1 -&gt; loop on receive until message includes requestEnd = yes -&gt; 
            received message -&gt; process complete.*

A complete usage example can be found with the correlation test case.

Fault Service

When invoked, the fault service (as the name says) will return a fault. It's mostly used to test fault handlers and compensation. To invoke the fault service just use:

<invoke name="throwTestFault" partnerLink="fault" portType="flt:faultMessagePT" operation="throwFault" 
        inputVariable="fault" outputVariable="faultResponse"/>

The only types of fault that can be thrown for now are FaultMessage1, FaultMessage2 and UnknownFault in the http://ode/bpel/unit-test/FaultService.wsdl namespace. For more details see the example in the implicit fault handler test.