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.

In deploy.xml, you simply define the JBI internal endpoints invoked or provided by your partnerLinks,

<?xml version="1.0" encoding="UTF-8"?>
<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03"
        xmlns:process="urn:/Ping.bpel"
        xmlns:ping="urn:/Ping.wsdl"
        xmlns:pong="urn:/Pong.wsdl">

	<process name="process:Ping">
		<active>true</active>
		<provide partnerLink="PingPartnerLink">
			<service name="ping:PingService" port="PingPort"/>
		</provide>
		<invoke partnerLink="PongPartnerLink">
			<service name="pong:PongService" port="PongPort"/>
		</invoke>
	</process>
    
</deploy>

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

Types of EPRs

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.

HTTP Binding Support

Since version 1.2, ODE supports HTTP Binding. ODE is almost fully compliant with the WSDL 1.1 spec. The few limitations are related to MIME types.
Actually only the following MIME types are supported:

  1. Media types that represent XML MIME entities. Basically any types matching "text/xml", "application/xml" and "*+xml".
  2. Non-XML types will be processed as Text, thus Text Media Types comes de facto but they may have a very limited set of usages.

mime:multipartRelated, mime:soap:body and mime:mimeXml are not supported.

Considering how unsuitable WSDL 1.1 HTTP Binding is for a large majority of services – especially RESTful services – a set of extensions is available.
All the details you want to know are here.

HTTP Binding Extensions for RESTful services

The Resource-Oriented Architecture defines four concepts:

  1. Resources
  2. Their names (URIs)
  3. Their representations
  4. The link bet ween them

and four properties:

  1. Addressability
  2. Statelesness
  3. Connectedness
  4. A uniform interface

HTTP binding as defined in WSDL 1.1 is not well suitable to describe services implementing these concepts and properties, mainly because a port type may access 4 different locations/resources but with only one HTTP method.

To better describe RESTful services, and turn a port type into a "resource type", ODE brings a set of 4 extensions:

  1. one HTTP method per operation (instead of one per binding)
  2. a unique URI Template for all operations
  3. access to HTTP headers
  4. fault support

Further details below.

In this page, we use an imaginary blog service as a use case to illustrate and make things more palpable. We will focus on the resources defined by the following URI template:

http://blog.org/post/{id}

Let's assume that such a resource accept four operations:

  • GET to retrieve a post
  • DELETE to delete it
  • PUT to update the post
  • POST to add a comment to a post
Check out unit tests!
For a complete suite of working examples, please refer to unit tests

One verb per operation

According to the WSDL 1.1 specification, the verb describing the HTTP method has to be at the binding level. Which implies that the same HTTP method is used by all operations of a given port type. But RESTful web services leverage HTTP methods as a uniform interface to describe operation on resources. So for instance, if you want to use the following HTTP operations – GET, POST, PUT, DELETE – for a given resource, four different bindings would be required. And consequently four port types and four ports. Quite verbose and unusable, isn't it?

So, this extension is to push down the HTTP verb at the operation level. And if an operation does not have its own verb, then the verb defined at the binding level will be used.
This extension is declared in the namespace: http://www.apache.org/ode/type/extension/http

Please note that ODE supports GET, POST, PUT, DELETE only.

<definitions ...
                  xmlns:odex="http://www.apache.org/ode/type/extension/http"/>

    <!-- many wsdl elements are ommited to highlight the interesting part -->

    <binding name="blogBinding" type="blogPortType">
        <operation name="GET">
            <odex:binding verb="GET" />
        </operation>
        <operation name="DELETE">
            <odex:binding verb="DELETE"/>
        </operation>
    </binding>
</definitions>

URI Template

A RESTful service exposed a set of resources, each of them being accessible through a uniform interface: HTTP methods for a web service. So we need a way to define four operations (at most) for a single resource.

Moreover it's very likely that the resource URI actually describes a set of resources. For instance, the set of posts contained in our imaginary blog: http://blog.org/post/\{post_id}.

HTTP binding offers the http:operation element to set the path of an operation. While the service address is set in the http:address of the wsdl:port element.
So one could imagine splitting the URL this way:

<definitions ...
                  xmlns:odex="http://www.apache.org/ode/type/extension/http"/>

   <service name="blogService">
        <port name="blogPort" binding="blogPortType">
             <http:address location="http://blog.org"/>
        </port>
    </service>

    <binding name="blogBinding" type="blogPortType">
        <operation name="PUT">
            <odex:binding verb="PUT" />
            <http:operation location="post/(post_id)"/>
            <input>
                 <http:urlReplacement/>
             </input>
             <output/>
        </operation>
    </binding>
</definitions>

However, here 3 issues show up:

  1. the location is not accessible from the End Point Reference. => ODE cannot process it before invoking the external service.
  2. http:urlReplacement is only accepted for GET => what about the uniform interface?!
  3. http:urlReplacement requires all parts of the operation input message to be mentioned in the operation location. Meaning that:
    • => the resource id (in the present example) must be a part of the message.
    • => no parts may be stored in the HTTP body. this conflicts with a PUT operation for instance. With a PUT you would like to set the id in the url and the resource data in the HTTP request body.

To solve this, ODE allows http:operation elements to be omitted or empty, and the full resource URI to be set in a single place, the http:address element.

Please note that curly brackets '{}' are the preferred argument delimiters in URI templates. So that URLs can be dynamically composed using composeUrl, combineUrl and expandTemplate XPath Functions.

In addition, the http:urlReplacement is relaxed: all parts are not required in the URI template anymore. One part could go in the URI, another in the request body.

<definitions ...
                  xmlns:odex="http://www.apache.org/ode/type/extension/http"/>

   <service name="blogService">
        <port name="blogPort" binding="blogPortType">
             <!-- here is the full URI template, using curly brackets -->
             <http:address location="http://blog.org/post/{post_id}"/>
        </port>
    </service>

    <binding name="blogBinding" type="blogPortType">
        <operation name="PUT">
             <odex:binding verb="PUT" />
             <!-- location attribute intentionally blank -->
             <http:operation location=""/>
             <input>
                 <http:urlReplacement/>
                 <!-- an additional part can be mapped to the request body even if urlReplacement is used-->
                 <mime:content type="text/xml" part="post_content"/>
             </input>
             <output/>
        </operation>
    </binding>
</definitions>

HTTP Headers manipulation

HHTP protocal convey a lot of information in Request/Response Headers. Caching information, Content description for instance. All this data is completely left out by WSDL 1.1 HTTP Binding. To fix this, ODE provides a header element. This element can be used to insert a part or a static value into a given HTTP request header (standard or custom). And the way around, a HTTP request header into a part. Also note that all HTTP response headers are inserted into the message headers, and thus are available from the BPEL process.

<definitions ...
                  xmlns:odex="http://www.apache.org/ode/type/extension/http"/>

    <binding name="blogBinding" type="blogPortType">
        <operation name="PUT">
             <odex:binding verb="PUT" />
             <http:operation location=""/>
             <input>
                 <http:urlReplacement/>
                 <mime:content type="text/xml" part="post_content"/>
                 <!-- set a standard request header from a part -->
                 <odex:header name="Authorization" part="credentials_part"/>
                 <!-- set a custom request header with a static value -->
                 <odex:header name="MyCustomHeader" value="ode@apache.org" />
             </input>
             <output>
                 <mime:content type="text/xml" part="post_content"/>
                 <!-- set 1 response header to a part -->
                 <odex:header name="Age" part="age_part"/>
             </output>
        </operation>
    </binding>
</definitions>

For every HTTP response, in addition to HTTP response headers, the Status-Line is passed as a message header. To save some painful XPath string manipulations, the Status-line is already parsed into the following structure:

<Status-Line>
   <HTTP-Version>HTTP/1.1</HTTP-Version>
   <Status-Code>200</Status-Code>
   <Reason-Phrase>OK</Reason-Phrase>
   <!-- the original unaltered Status-Line -->
   <original>HTTP/1.1 200 OK</original>
</Status-Line>

So that you can write the following BPEL lines:

<assign>
            <copy>
                <from variable="postMsg" header="Status-Line"/>
                <to>$statusLine</to>
            </copy>
        </assign>
        <if>
            <condition>number($statusLine/Status-Code) > 200 and number($statusLine/Status-Code) < 300</condition>
            <assign>
                 <copy>
                     <from>'Request successful!!!'</from>
                     <to>$outputVar.TestPart</to>
                 </copy>
             </assign>
        </if>

Fault Support

Another domain completely neglected by WSDL 1.1 HTTP Binding is Fault management. The word is not even mentioned in the HTTP Binding section.
ODE allows you to bind a fault with HTTP Binding. If a 4xx or a 5xx is returned, the following logic is applied:

A failure is thrown if the code is one of these:

Status-Codes triggering a Failure
3xx Redirections
401_UNAUTHORIZED
408_REQUEST_TIMEOUT
503_SERVICE_UNAVAILABLE
504_GATEWAY_TIMEOUT

else ODE checks that a fault is declared in the WSDL for the current operation and that the response body contains the expected XML element then reply with a fault ; else reply with a failure.

Status-Codes that may trigger a Fault if the body element matches a fault declaration
500_INTERNAL_SERVER_ERROR 407_PROXY_AUTHENTICATION_REQUIRED
501_NOT_IMPLEMENTED 409_CONFLICT
502_BAD_GATEWAY 410_GONE
505_HTTP_VERSION_NOT_SUPPORTED 412_PRECONDITION_FAILED
400_BAD_REQUEST 413_REQUEST_TOO_LONG
402_PAYMENT_REQUIRED 414_REQUEST_URI_TOO_LONG
403_FORBIDDEN 415_UNSUPPORTED_MEDIA_TYPE
404_NOT_FOUND 411_LENGTH_REQUIRED
405_METHOD_NOT_ALLOWED 416_REQUESTED_RANGE_NOT_SATISFIABLE
406_NOT_ACCEPTABLE 417_EXPECTATION_FAILED
Useful Information
Note that 3xx errors should be pretty rare since by default the first hundred redirections are followed. You can tweak this value by setting the property http.protocol.max-redirects in the enpoint-configuration.properties of your process.
<definitions ...
                  xmlns:odex="http://www.apache.org/ode/type/extension/http"/>

   <portType name="BlogPortType">
        <operation name="PUT">
            <input message="..."/>
            <output message="..."/>
            <fault name="UpdateFault" message="tns:UpdateFault"/>
        </operation>
    </portType>

    <binding name="blogBinding" type="blogPortType">
        <operation name="PUT">
             <odex:binding verb="PUT" />
             <http:operation location=""/>
             <input> ... </input>
             <output> ...  </output>
             <!-- fault binding -->
             <fault name="UpdateException">
                 <!-- name attribute is optional if there is only one fault for this operation -->
                 <!-- <odex:fault name="UpdateFault"/> -->
                 <odex:fault/>
             </fault>
        </operation>
    </binding>
</definitions>

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.

Endpoint Configuration

endpoint-configuration.properties

SOAP and HTTP external endpoints can be tweaked using a property file named {$process_dir}/endpoint-configuration.properties. A common set of properties are available to configure external services. At run time, ODE will translate these properties and apply them to Axis2 or HttpClient depending of the targetted service uses SOAP binding or HTTP binding.

Dynamic refresh

Properties are dynamically loaded and refreshed at run time.
The timing is the following:
On every request, if the file has not been polled during the last 30 seconds then check the file for updates. If any, reload it.

Consequently, if you have updated properties, you have to wait ~30 seconds and then trigger a request.

Hierarchical properties

The property file is a regular property file except that service name and port name may be used to apply different default values to different services.
All properties follow this pattern:

[nsalias.servicename[.portname].ode.]property

If service name is mentioned but port name omitted, the value will apply to all ports of the service.
If service name and port name are omitted, the value will apply to all services.

Namespace management

A service has to qualified. To so you may define namespace aliases. Aliases will then prefixed the service local name.

alias.ode_ns=http://ode.apache.org

ode_ns.dummyservice.ode.http.request.chunk=true

If your namespace does not collide with the property syntax , you dont have to define an alias. This property file is accepted:

# Next line is commented
# alias.ode_ns=http://ode.apache.org

ode_ns.dummyservice.ode.http.request.chunk=true
Examples

For instance, considering 2 services:

  • the foo-service
  • and the brel-service which has 2 ports: port-of-amsterdam and port-of-hiva-oa

and this property file:

alias.test_ns=http://test.org
http.protocol.max-redirects=5

test_ns.brel-service.ode.http.protocol.max-redirects=40
test_ns.brel-service.port-of-amsterdam.ode.http.protocol.max-redirects=100

The http.protocol.max-redirect property will have the following values:

  • 5 for all ports of foo-service
  • 40 for brel-service.port-of-hiva-oa
  • 100 for brel-service.port-of-amsterdam

Supported properties

Here the list of supported properties, and their descriptions. If the file contains some properties not listed here, they will be available in the property map nevertheless. Values will be strings.
Such unmanaged properties will also be passed to Axis2 options and HttpClient params, see below.

Context Property name Accepted values Description/Notes
Bpel-runtime mex.timeout a long the Ode Message Exchange timout
Outbound Services http.request.chunk true/false This will enable/disable chunking support. Will not apply to http-bound services TBD
  http.protocol.version HTTP/1.1 or HTTP/1.0 the HTTP protocol version used
  http.request.gzip true/false Will not apply to http-bound services TBD
  http.accept.gzip true/false Append gzip to the Accept-Encoding header
  http.protocol.encoding a string  
  http.default-headers.{your-header}   You must define one property per header, prefixed with 'http.default-headers'.
These values will be appended to any previous value already set for a given header.
  http.connection.timeout an int timeout in milliseconds until a connection is etablished
  http.socket.timeout an int timeout in milliseconds for waiting for data
  http.protocol.max-redirects an int the maximum number of redirects to be followed
  http.proxy.host=myproxy.org   To disable proxy set the host to null
  http.proxy.port    
  http.proxy.domain    
  http.proxy.user    
  http.proxy.password    

Sample file

endpoint-configuration.properties.txt

Download, remove the '.txt' prefix, customize then enjoy...

Implementation Details for Outbound services

The properties related to Outbound services have to be applied to Axis2 (for SOAP services) or HttpClient (for HTTP services).
Tables below sum up this information.

SOAP Services (Axis2)

For Axis2, all properties are converted to meet the Options#setProperty() requirements.

Property name Axis2 Description/Notes
http.request.chunk Options.setProperty(HTTPConstants.CHUNKED, ?)  
http.protocol.version Options.setProperty(HTTPConstants.HTTP_PROTOCOL_VERSION, ?)  
http.request.gzip Options.setProperty(HTTPConstants.MC_GZIP_REQUEST, ?)  
http.accept.gzip Options.setProperty(HTTPConstants.MC_ACCEPT_GZIP, ?)  
http.protocol.encoding Options.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING,?)  
http.default-headers.* Options.setProperty(HTTPConstants.HTTP_HEADERS, ?)  
http.connection.timeout Options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, ?)  
http.socket.timeout Options.setProperty(HTTPConstants.SO_TIMEOUT, ?)  
http.protocol.max-redirects   not applied to Axis2 services
http.proxy.* Options.setProperty(HTTPConstants.PROXY, ?);  
HTTP Services (HttpClient)

For HttpClient, all properties are defined by: HttpMethodParams , HostParams, HttpClientParams, HttpConnectionParams and HttpConnectionManagerParams.
The idea is to convert properties into the expected type and push them in a DefaultHttpParams . This property holder is then set as the parent of all other HttpParams used.
If a given property is not listed below, you are still able to set it as long as the expected value is a string. For instance "http.protocol.cookie-policy" can be set seamlessly.

Property name HttpClient Description/Notes
http.request.chunk EntityEnclosingMethod.setContentChunked()  
http.protocol.version HttpParams.setParameter(HttpMethodParams.PROTOCOL_VERSION, ?)  
http.request.gzip   not supported
http.accept.gzip   not supported
http.protocol.encoding HttpParams.setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,?)  
http.default-headers.* HttpParams.setParameter(HTTPConstants.HTTP_HEADERS, ?)  
http.connection.timeout HttpParams.setParameter(HttpConnectionParams.CONNECTION_TIMEOUT, ?)  
http.socket.timeout HttpParams.setParameter(HttpMethodParams.SO_TIMEOUT, ?)  
http.protocol.max-redirects HttpParams.setParameter(HttpClientParams.MAX_REDIRECTS, ?)  
http.proxy.*   Cannot be set with simple properties. Custom code added.

Additional Configuration for SOAP Endpoints

If you deploy ODE in an application server, the Axis2 Integration Layer allows ODE to communicate via Web Service interactions. SOAP Web Services will be managed by Axis2. These web services may be configured dynamically.
For each service, you need to place a [serviceLocalName].axis2 file at the root of the process bundle. Currently, this file can only be added on the server, under var/processes/{$process_dir}.

For example, given this wsdl:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions ...>
  <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">A sample Time service</wsdl:documentation>
 ...
  <wsdl:service name="TimeService">
    <wsdl:port name="TimeServiceSoap" binding="tns:TimeServiceSoap">
      <soap:address location="http://ws.intalio.com/TimeService/" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

You would place a TimeService.axis2 file with:

<service name="TimeService" scope="application" targetNamespace="http://ws.intalio.com/TimeService/">
    <module ref="sandesha2" />
    <module ref="addressing" />
</service>

This will engage Engage sandesha2 and addressing modules.

On every request, if the config file has not been polled during th