Thursday, September 27, 2007

Using custom headers in BPEL

Sending messages to a BPEL process mostly done via the web service interface. In general the message is packed in a SOAP envelope in the body part. The header part of the SOAP envelope id not touch. This is mostly done automatically between the client and web service.

Sometimes you need information to send or retrieve information to the BPEL process that is not part of the message. This information is mostly technical and not functional related to the message in the SOAP body. In these cases you could manipulate the SOAP header to add your own message.

The next example describes a solution of retrieving headers from the SOAP request. The example is very simple it just an 'HelloWorld' application that reads the SOAP header. If the custom header exists it returns it, otherwise it does not. The BPEL process is show as follows:



Now this is simple. The trick is in the WSDL specification and the 'receive' step in the BPEL process. A few changes have been made in the default WSDL (after you create a default async BPEL process in jDeveloper).

The WSDL is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="HelloHeader"
targetNamespace="http://xmlns.oracle.com/HelloHeader"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:client="http://xmlns.oracle.com/HelloHeader"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing"
xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/">
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://xmlns.oracle.com/HelloHeader"
schemaLocation="HelloHeader.xsd"/>
<import namespace="http://schemas.xmlsoap.org/ws/2003/03/addressing"
schemaLocation="https://dvlp1.eurotransplant.nl/
orabpel/xmllib/ws-addressing.xsd"/>
</schema>
</types>
<!-- SOAP HEADER -->
<message name="RelatesToHeader">
<part name="RelatesTo" element="wsa:RelatesTo"/>
</message>
<message name="MessageIDHeader">
<part name="MessageID" element="wsa:MessageID"/>
</message>
<message name="ReplyToHeader">
<part name="ReplyTo" element="wsa:ReplyTo"/>
</message>
<message name="CustomHeader">
<part name="CustomHeader" element="client:HelloHeaderHeaderMessage"/>
</message>
<!-- EOF SOAP HEADER -->
<message name="HelloHeaderRequestMessage">
<part name="payload" element="client:HelloHeaderProcessRequest"/>
</message>
<message name="HelloHeaderResponseMessage">
<part name="payload" element="client:HelloHeaderProcessResponse"/>
</message>
<portType name="HelloHeader">
<operation name="initiate">
<input message="client:HelloHeaderRequestMessage"/>
</operation>
</portType>
<portType name="HelloHeaderCallback">
<operation name="onResult">
<input message="client:HelloHeaderResponseMessage"/>
</operation>
</portType>
<binding name="HelloHeaderBinding"
type="client:HelloHeader">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="initiate">
<soap:operation style="document" soapAction="initiate"/>
<input>
<soap:header message="client:MessageIDHeader"
part="MessageID" use="literal"/>
<soap:header message="client:ReplyToHeader"
part="ReplyTo" use="literal"/>
<soap:header message="client:CustomHeader"
part="CustomHeader" use="literal"/>
<soap:body use="literal"/>
</input>
</operation>
</binding>
<binding name="HelloHeaderCallbackBinding"
type="client:HelloHeaderCallback">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="onResult">
<soap:operation soapAction="onResult" style="document"/>
<input>
<soap:header message="client:RelatesToHeader"
part="RelatesTo" use="literal" required="false"/>
<soap:body use="literal"/>
</input>
</operation>
</binding>

<plnk:partnerLinkType name="HelloHeader">
<plnk:role name="HelloHeaderProvider">
<plnk:portType name="client:HelloHeader"/>
</plnk:role>
<plnk:role name="HelloHeaderRequester">
<plnk:portType name="client:HelloHeaderCallback"/>
</plnk:role>
</plnk:partnerLinkType>
</definitions>



In the WSDL we overwrite the binding of the web service. Here we specify the custom header. We should not forget to use the normal headers as MessageId and ReplyTo.

Note: In the XSD-file we added a message":
 <element name="
<complextype>
<sequence>
<element name="message" type="string">
</sequence>
</complextype>
</element>

Now the message and the WSDL are correct we could change the BPEL file to add the code to read the SOAP Header. In the variable declaration add:

<variable name="customHeader"
messageType="client:CustomHeader"/>
<variable name="messageID"
messageType="client:MessageIDHeader"/>
<variable name="replyTo"
messageType="client:ReplyToHeader"/>

Now change the receive task to add the "bpelx:headerVariable" statement.

<receive name="receiveInput"
partnerLink="client"
portType="client:HelloHeader"
operation="initiate"
variable="inputVariable"
createInstance="yes"
bpelx:headerVariable="customHeader messageID replyTo"
/>

After the receice step the headers, if the exist, are put into the variables. Now you could use normal assign/copy tasks to use this data.

You can download the example code here.

To test the BPEL process, use a tool as SoapUI.org (open source). Here is a SOAP request with a custom header:

<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:hel="http://xmlns.oracle.com/HelloHeader"
xmlns:add="http://schemas.xmlsoap.org/ws/2003/03/addressing">
<soapenv:Header>
<hel:HelloHeaderHeaderMessage>
<hel:message>This is the custom header</hel:message>
</hel:HelloHeaderHeaderMessage>
</soapenv:Header>
<soapenv:Body>
<hel:HelloHeaderProcessRequest>
<hel:input>Hello World</hel:input>
</hel:HelloHeaderProcessRequest>
</soapenv:Body>
</soapenv:Envelope>

Tuesday, September 18, 2007

Compile error BPEL against SSL

If you compile a BPEL proces that is using partner links based on SSL. You run into an compile error:

ValidatorException: PKIX path building failed

This can be solved by importing the certificate form the server into your local key store. This is the keystore from which JDeveloper is started.

Make sure that you apply this also on the SOA Suite server. The BPEL process is deployed and compiled again on the server.

cd [jdevhome]\jdk\jre\lib\security
..\..\bin\keytool.exe -import -file "Vijfhuizen.cer"
-keystore cacerts -storepass changeit -alias myRootCA

Owner: O=Vijfhuizen, C=Issuer: O=Vijfhuizen, C=com
Serial number: 1
Valid from: Mon Sep 21 08:00:00 CET 2007
until: Thu Feb 28 20:00:34 CET 2012
Certificate fingerprints:
MD5:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
SHA1: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Trust this certificate? [no]: yes
Certificate was added to keystore


Tuesday, September 11, 2007

Cancel workflow / human-task

Work with human work flow in Oracle SOA Suite is very powerful. Within Oracle BPEL you can create a human task for a particular user or user-group. But as these tasks are assigned to users, the BPEL process is waiting on result. Depending on the task configuration, you could assign time-outs and escalation paths.

From business perspective, you could have a rule that says; reject all open task for a particular user or group. So how do we approach this issue? Oracle Suite has a rich Java API to control the human work flow. In this case you could create a java program that collects information from the work flow engine to cancel the tasks. This java program could be a:
  • A java client.
  • A web application, using the java api.
  • Embedded Java in the BPEL process.
But a closer look in the human task call in the BPEL process, shows another solution. Apart of initiating a human task you could also call various other operations:

The advantage to use BPEL code, you do not need any (embedded) Java API calls to fulfill this function. In our case we could invoke the human work flow via the 'updateTaskOutcome' operation. The are a few restrictions. Before you could set the outcome of an task (REJECT, APPROVED, DENIED, etc.) you must known the TaskID for that particular task and the session token of the task. This information is available in the BPEL process that creates this task. You could store this information in a database table to make it available for the process that wants to set the outcome.

A question was asked, how to obtain the TaskID and the session Token. This is easy. When a human-task is created, after the invoke in the human-task scope, a result is send back to BPEL. In this result you can retrieve information on this task. In our case you could read the TaskId and the Token. Saving this data in a table or file, you can use it by other processes to update the outcome.