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.
    $ mysql -u root 
    mysql> create database ode;
    mysql> exit
    $ mysql -u root ode < ode_openjpa_mysql-1.3.2.sql
    
  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
ActivityFailureEvent Scope An activity failed 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>
    </dd: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, without the wrapper -->
  <copy>
    <from>
      <literal>http://localhost:8080/ode/dynresponder</literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>
 
  <!-- Simple URL, wrapped in an 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 EPR, without the wrapper -->
  <copy>
    <from>
      <literal>
        <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
          <wsa:To>http://localhost:8080/ode/dynresponder</wsa:To>
        </wsa:EndpointReference>
      </literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>

  <!-- WS-Addressing EPR, with the wrapper -->
  <copy>
    <from>
      <literal>
        <service-ref>
           <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
             <wsa:To>http://localhost:8080/ode/dynresponder</wsa:To>
           </wsa:EndpointReference>
        <service-ref>
      </literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>

  <!-- WSDL1.1 EPR, without the wrapper -->
  <copy>
    <from>
      <literal>
         <wsdl:service xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="DynService" targetNamespace="http://org.apache.ode/examples/dynservice">
           <wsdl:port name="DynPort">
                   <soap:address location="http://localhost:8080/ode/dynresponder"/>
           </wsdl:port>
         </wsdl:service> 
     </literal>
    </from>
    <to partnerLink="responderPartnerLink"/>
  </copy>

  <!-- WSDL2.0 EPR, with the wrapper -->
  <copy>
    <from>
      <literal>
         <service-ref>
         <wsdl:service xmlns:wsdl="http://www.w3.org/2006/01/wsdl" name="DynService" targetNamespace="http://org.apache.ode/examples/dynservice">
           <wsdl:port name="DynPort">
                   <soap:address location="http://localhost:8080/ode/dynresponder"/>
           </wsdl:port>
         </wsdl:service> 
         </service-ref>
     </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. If the service-ref element is absent, the EPR is automatically wrapped inside one on the fly. Moreover, 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 compose-url, combine-url and expand-template 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
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.

Here what ODE does, if the status code is one of those listed in the next table (500, 501, etc):

  1. check that the operation has at least one fault in its abstract part, and one fault binding
  2. check that the Content-type header describes an xml document ('application/xml', 'application/atom+xml' etc)
  3. check that the body is not empty

If so far everything is fine, the HTTP response body is parsed into an xml document. Then the fault to be thrown is inferred from the qname of the response root element, i.e the fault having a message part matching the root element. This matching process is exactly the same as for a SOAP service.
If one of these steps fails, a failure is thrown.

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

Note that you can't bind a given fault to a specific status code.

<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

Extension Activities & Extensible Assign Operations

Apache ODE provides a plug-in architecture for custom activity implementations and custom variable assignment logic. Such plug-ins can be registered to ODE and can be used according to the WS-BPEL 2.0 extensibility mechnisms (<extensionActivity> & <extensionAssignOperation>).

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

Iterable ForEach

Extends the <forEach> activity so that the counter variable iterates over the items contained in a given sequence. This extension provides an alternative and more dynamic way of generating M branches, where M is the size of the binding sequence. The semantics of the <completionCondition> stays the same, in that the loop is deemed to be complete if B out of the M branches complete (successfully), where B is its actual value.

Read more

Flexible Assigns

Extends the <assign> activity so that it can be made to either ignore or insert data that is missing in the to-spec of a copy operation. This shortcut allows users to handle what would normally be fault scenarios, in a more graceful and intuitive way.

Read more

Process Contexts

Extends BPEL and ODE to allow the circulation of transport metadata (like security tokens, correlation keys, or tracing informations) from messages to processes and then from processes to messages.

Read more

Instance Data Cleanup

Rational

During its execution, a process instance can accumulate a significant amount of data. The running process itself isn't that much of an issue, when the instance is done there's nothing left to execute. But the process data can be rather big, mostly because of the messages it received and sent and its own variables. All of these are XML documents and in some cases, sizable ones.

Levels of cleanup

Cleanup on completion

This feature is only available in Ode 1.3 or later

The easiest approach to get started is simply to wait until the instance execution is finished and then cleanup everything that's related to it. That would include the instance state with its variables, scopes and correlation, but also all the messages it has received and sent. Execution events should also be disposed of. So this description defines 5 different categories: instance, messages and events. We should be able to turn on and off each level separately.

  • instance: ODE_PROCESS_INSTANCE, EXECUTION_STATE
  • variables: ODE_SCOPE, ODE_XML_DATA, ODE_PARTNER_LINK
  • messages: ODE_MESSAGE, ODE_MESSAGE_ROUTE, ODE_MEX_PROPS, ODE_MESSAGE_EXCHANGE
  • correlations: ODE_CORRELATION_SET, ODE_CORSET_PROP
  • events: ODE_EVENTS

dtd

    <xs:element name="cleanup" minOccurs="0" maxOccurs="3" type="dd:tCleanup" />

    <xs:complexType name="tCleanup">
    	<xs:sequence>
    		<xs:element name="category" default="all" minOccurs="0" maxOccurs="unbounded">
    			<xs:simpleType>
    				<xs:restriction base="xs:string">
    					<xs:enumeration value="instance" />
    					<xs:enumeration value="variables" />
    					<xs:enumeration value="messages" />
    					<xs:enumeration value="correlations" />
    					<xs:enumeration value="events" />
    					<xs:enumeration value="all" />
    				</xs:restriction>
    			</xs:simpleType>
    		</xs:element>
    	</xs:sequence>
    	<xs:attribute name="on" use="required">
    		<xs:simpleType>
    			<xs:restriction base="xs:string">
    				<xs:enumeration value="success" />
    				<xs:enumeration value="failure" />
    				<xs:enumeration value="always" />
    			</xs:restriction>
    		</xs:simpleType>
    	</xs:attribute>
    </xs:complexType>

examples

1. no instance data cleanup

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

2. cleaning up all data on either successful or faulty completions of instances

    <process name="pns:HelloWorld2">
	<active>true</active>
	<provide partnerLink="helloPartnerLink">
		<service name="wns:HelloService" port="HelloPort"/>
	</provide>
	<cleanup on="always" />
    </process>

3. cleaning up all data on successful completions of instances and no data cleanup on faulty completions

    <process name="pns:HelloWorld2">
	<active>true</active>
	<provide partnerLink="helloPartnerLink">
		<service name="wns:HelloService" port="HelloPort"/>
	</provide>
	<cleanup on="success" >
                <category>instance</category>
                <category>variables</category>
                <category>messages</category>
                <category>correlations</category>
                <category>events</category>
        </cleanup>
    </process>

4. cleaning up all data on successful completions of instances and only messages and correlations on faulty completions

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

	<cleanup on="success" >
                <category>all</category>
        </cleanup>
        <cleanup on="failure">
                <category>messages</category>
                <category>correlations</category>
        </cleanup>
    </process>

5. an invalid configuration; the instance category should accompany the variable and correlations categories

    <process name="pns:HelloWorld2">
	<active>true</active>
	<provide partnerLink="helloPartnerLink">
		<service name="wns:HelloService" port="HelloPort"/>
	</provide>
	<cleanup on="success" >
                <category>all</category>
        </cleanup>
        <cleanup on="failure">
                <category>instance</category>
        </cleanup>
    </process>

Future Developments

WS-BPEL makes heavy use of scopes, those could be another hook in the execution lifecycle for the cleanup to take place. So instead of waiting until the instance is finished and clean up the whole state, we could proceed by smaller increments and delete the state scope by scope. For short running processes (say less than a few days) the advantages of this approach are minimal but for long running processes (say months), there's potentially a lot of unused state that's just sitting there and will never be used anymore.

Final notes

When we continue along the lines of refining further when the cleanup should occur and what exactly should be cleaned up, we quickly start getting close to the transaction boundaries. Down the road, ideally, we shouldn't persist anything unnecessarily, so that no cleanup is needed when a given piece of data will never be reused. It's often the case for message variables for example, where a process will receive a message, assign some values from it and never use that message variable anymore. So this should never get written, minimizing the writes and deletes.

Direct Process-to-Process Communication

Ode automatically optimizes all process-to-process communication such that all message exchanges happen directly inside the engine and do not go through the integration layer (e.g. Axis2, JBI, ...).

How To

Controlling ODE 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 in the ode-axis2.properties file, which is 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.

However, you may override the time that the process have to remain unused before they can be considered for dehydration by specifying a value, in milliseconds, for the following property in the ode-axis2.properties file:

# wait for 5 minutes instead of 20 minutes
ode-axis2.process.dehydration.maximum.age=300000

Similarly, you may override the maximum number of process definitions that may remain hydrated at any given point in time by specifying a value for the following property in the ode-axis2.properties file:

# allow not more than 500 processes to be in memory at once
ode-axis2.process.dehydration.maximum.count=500

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 property files

SOAP and HTTP external endpoints can be tweaked using some property files. 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 if the targeted service uses SOAP binding or HTTP binding.

Conventions and locations

The extension of these property files must be *.endpoint. These files can be dropped in 2 different locations:

  • the global configuration directory of ODE which is by default ode/WEB-INF/conf. This location might be customized with the system property org.apache.ode.configDir
  • the process deployment directory, ode/WEB-INF/processes/$your_process

Two rules of precedence apply:

  1. Endpoint files in a same directory are loaded in alphabetical order. Meaning that if a property is set in two different files, the value from the "greatest" file name will have precedence.
  2. Endpoint files located in the global conf directory have precedence over files in process deployment directories.
Version Information
This feature is available in ODE > 1.1. For version 1.1, a file named endpoint-configuration.properties may be dropped in process deployment unit directories. For backward compatibility, ODE > 1.1 supports this file too.

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 be qualified. To so do 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
  mex.failure.verbose true(default)/false mute the details of a failure that might occur during a process invocation (to avoid information disclosure for instance)
Outbound Services address a URL overrides the soap:address or http:address.
The order of precedence is: process, property files, wsdl.
  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.content-charset 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    
  jms.reply.destination a string the reply-to destination in an outbound message
  jms.reply.timeout an int timeout in milliseconds for waiting for two-way reply
  ws-adddressing.headers true(default)/false Enable/disable the WS-Addressing headers for outgoing soap requests

Sample file

sample.endpoint.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.content-charset 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, ?);  
jms.reply.destination Options.setProperty(JMSConstants.REPLY_PARAM, ?);  
jms.reply.timeout Options.setProperty(JMSConstants.JMS_WAIT_REPLY, ?);  
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.content-charset 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 the last 30 seconds then check the file for changes and (re)load it if any.

For further details about the type of parameters that can be set, see Axis2 Service Config.

This mechanism is available for external SOAP services – i.e. services that processes invoke – and for SOAP services exposed by ODE – i.e. services exposed by BPEL processes.

The service.xml could be quite powerful. However all options are not accessible this way. Many settings are available programmatically only using the Options class.. ODE exposes these settings through an additional property file described in the previous section.

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.

WS-Security with Axis2 integration layer

How to use WS-Security in ODE?

Only in 1.3.2
The features describe in this section are shipped in ODE 1.3.2 and newer

ODE 1.3.2 introduces support for WS-Security: secure services can now be invoked from a process, and the process service itself might be secured. A first part will explain how to invoke a secured service, a second part how to secure the process service.

ODE has an Integration Layer based on Axis2 so using Rampart, the Axis2 security modules, goes without saying. As a result this section will only focus on Rampart integration. Rampart and WS-Security specifications won't be detailed here. Please refer to their ad-hoc documentations for further details.

Quick Rampart introduction

As any other Axis2 module, Rampart is configurable with Axis2 Service configuration files. For instance a service.xml document, using the parameter based configuration model, might be:

<service>

    <module ref="rampart" />
    
    <parameter name="OutflowSecurity">
      <action>
        <items>Timestamp Signature</items>
        <user>client</user>
        <signaturePropFile>TestRampartBasic/secured-services/client.properties</signaturePropFile>
        <passwordCallbackClass>org.apache.rampart.samples.sample04.PWCBHandler</passwordCallbackClass>
        <signatureKeyIdentifier>DirectReference</signatureKeyIdentifier>
      </action>
    </parameter>

    <parameter name="InflowSecurity">
      <action>
        <items>Timestamp Signature</items>
        <signaturePropFile>TestRampartBasic/secured-services/client.properties</signaturePropFile>
      </action>
    </parameter>
    
 </service>

Another example using WS-Security Policy based configuration model is listed below. See the full document here.

<service>

    <module ref="rampart"/>

    <wsp:Policy wsu:Id="SecConvPolicy2" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:SymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                             <!-- truncated, see original document ny following the link above -->
                    </wsp:Policy>
                </sp:SymmetricBinding>
                <sp:Wss11 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                             <!-- truncated, see original document ny following the link above -->
                    </wsp:Policy>
                </sp:Wss11>
                <sp:Trust10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                             <!-- truncated, see original document ny following the link above -->
                    </wsp:Policy>
                </sp:Trust10>
                <sp:EncryptedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <sp:Body/>
                </sp:EncryptedParts>
                <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
                    <ramp:user>client</ramp:user>
                    <ramp:encryptionUser>service</ramp:encryptionUser>
                    <ramp:passwordCallbackClass>org.apache.rampart.samples.policy.sample04.PWCBHandler</ramp:passwordCallbackClass>
                
                    <ramp:signatureCrypto>
                        <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.file">TestRampartPolicy/secured-services/client.jks</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">apache</ramp:property>
                        </ramp:crypto>
                    </ramp:signatureCrypto>
                    <ramp:encryptionCypto>
                        <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.file">TestRampartPolicy/secured-services/client.jks</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">apache</ramp:property>
                        </ramp:crypto>
                    </ramp:encryptionCypto>
                
                </ramp:RampartConfig>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>

</service>

The important thing to notice is that these documents are plain Axis2 Service configuration files. And as explained in the ODE User Guide, a mechanism to handle these files already exists. So all we have to do is reuse this mechanism, the rest is pure Rampart configuration.

Let's take an example and see the actual required steps.

How to invoke a secure web service?

Prepare your service document

Assuming your process needs to invoke the secure service {http://sample03.policy.samples.rampart.apache.org}Sample03, the first step is to prepare a service document named ${process_bundle_dir}/Sample03.axis2 and containing your desired Rampart configuration.
The second step is to to make sure the resources needed to invoke the services are available to Rampart through ODE webapp classpath. Typical resources are :

  • password callback handler classes
  • Java keystores
  • property files containing keystore information

Add resources to ODE webapp classpath

How you add these resources to ODE classpath might vary depending on your application server, your global architecture or other criteria. So it's up to you to figure this out. However typical locations are:

  • ode/WEB-INF/classes
  • ode/WEB-INF/lib

An alternative for WS-Security Policies

If you're using the policy base configuration model, an alternative is available to you: use the endpoint property mechanism to attach the policy to the service. In that configuration, ODE will engage the Rampart module and load the policy when the service is invoked.
To do so:

  1. save the Policy document (not the service document) in the file of your choice. For instance mypolicy.xml
  2. create an endpoint file linking the service and the policy file. Basically with the two properties listed below. Note that if the path assigned to the "security.policy.file" property is relative it will be resolved against the process bundle directory. Of course if the path is absolute, it will be used as is.
alias.sample03-ns=http://sample03.policy.samples.rampart.apache.org
sample03-ns.sample03-policy.ode.security.policy.file=mypolicy.xml

How to secure the web service exposed by a process?

Applying security to a process service is no different from invoking a secured service. If the process service you're exposing is {http://mycompany.com}AbscenceRequest. All you have to do is prepare a service document named ${process_bundle_dir}/AbscenceRequest.axis2 and containing your Rampart configuration. Once again, it's up to you to add the required resources in ODE webapp classpath.

You can also use the property 'security.policy.file' to secure the process service.

Do I need to install Rampart myself?

No. ODE comes with the following Axis2 modules (and the jars they depend on): Rampart, Rahas and Addressing.

Useful resources

Rampart material

ODE test cases

How to run them

$ cd axis2-war
$ buildr test:Secure

Where are the processes executed by the unit tests?

The executed processes are generated by the build, so run the tests once, then look into the following directories. Process directories are prefixed with "process-".

  • axis2-war/target/test-classes/TestRampartPolicy/secured-services/
  • axis2-war/target/test-classes/TestRampartPolicy/secured-processes/
  • axis2-war/target/test-classes/TestRampartBasic/secured-services/
  • axis2-war/target/test-classes/TestRampartBasic/secured-processes/

the nitty-gritty details

The integration with Rampart described in this section is tested with a decent suite of unit tests. These unit tests are based on the Rampart samples. The related resources were imported into ODE repository.
These tests are divided into two parts: tests using the parameter base configuration model aka "basic tests" and tests using the policy base configuration model aka "policy tests".
ODE test cases reuse these test cases in two different scenarii:

  1. the security configuration is applied to an "external" web service, and a ODE process invokes it.
  2. the security configuration is applied to the web service exposed by a process.

These partitions lead to four resource directories:

Everything describes for TestRampartBasic applies to TestRampartPolicy. So for now on we will mention only TestRampartBasic.

For the "secured-services" scenario, the "external" web services are Axis archives deployed in an Axis2 webapp.

The corresponding unit test classes are SecuredServicesTest.java and SecuredProcessTest.java. Each test class will start a list of processes that must succeed (as many processes as Rampart samples actually).
To avoid duplication these processes are generated by the build based on two process templates: one for the secured-services case, another for the secured-processes case.

The build generates processes into:

  • axis2-war/target/test-classes/TestRampartPolicy/secured-services/process-*
  • axis2-war/target/test-classes/TestRampartPolicy/secured-processes/process-*
  • axis2-war/target/test-classes/TestRampartBasic/secured-services/process-*
  • axis2-war/target/test-classes/TestRampartBasic/secured-processes/process-*