Thursday, March 22, 2007

Convert A-Synchronous proccess to Synchronous

Introduction

This article how to convert an Asynchronous to a Synchronous BPEL process. Since BPEL determines whether a process is async or sync by just some small settings in the .bpel file (and some dependency in the wsdl) it is rather easy to convert from one to another.

When creating BPEL processes, three files are created by default:
  1. bpel.xml
  2. .bpel
  3. .wsdl
To determine whether a BPEL process is Asynchronous or Synchronous, it looks at the second file: YourBPELProcess.bpel. Generally, when creating a process based on the Asynchronous template, the .bpel file look like this:

<process
name="BPELProcess1"
targetNamespace="http://xmlns.oracle.com/BPELProcess1"
xmlns="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:client="http://xmlns.oracle.com/BPELProcess1"
xmlns:ora="http://schemas.oracle.com/xpath/extension"
xmlns:orcl="http://www.oracle.com
/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc"
xmlns:xp20="http://www.oracle.com
/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20"
xmlns:ldap="http://schemas.oracle.com/xpath/extension/ldap"
xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/">
<partnerLinks>
<partnerLink
name="client"
partnerLinkType="client:BPELProcess1"
myRole="BPELProcess1Provider"
partnerRole="BPELProcess1Requester"/>
</partnerLinks>
<variables>
<variable
name="inputVariable"
messageType="client:BPELProcess1RequestMessage"/>
<variable
name="outputVariable"
messageType="client:BPELProcess1ResponseMessage"/>
</variables>
<sequence name="main">
<receive
name="receiveInput"
partnerLink="client"
portType="client:BPELProcess1"
operation="initiate"
variable="inputVariable" createInstance="yes"/>
<invoke
name="callbackClient"
partnerLink="client"
portType="client:BPELProcess1Callback"
operation="onResult"
inputVariable="outputVariable"/>
</sequence>
</process>

The differences between a synchronous and asynchronous process lie in the steps of invoke callback vs. reply difference to return the response. In a synchronous process, the way a reply message is handed back is through a reply. The asynchronous process invokes a callback method to hand back the reply message.

<process … namespace definitions etc … >
<partnerLinks>
<partnerLink name="client"
partnerLinkType="client:BPELProcess3"
myRole="BPELProcess3Provider"/>
</partnerLinks>
<variables>
<variable name="inputVariable"
messageType="client:BPELProcess3RequestMessage"/>
<variable name="outputVariable"
messageType="client:BPELProcess3ResponseMessage"/>
</variables>
<sequence name="main">
<receive name="receiveInput"
partnerLink="client"
portType="client:BPELProcess3"
operation="process"
variable="inputVariable"
createInstance="yes"/>
<reply name="replyOutput"
partnerLink="client"
portType="client:BPELProcess3"
operation="process"
variable="outputVariable"/>
</sequence>
</process>
As shown in the .bpel xml examples, receive, invoke and reply refer to a portType and a partnerLink attribute. These are specified in the .wsdl and also show some differences.
<portType name="BPELProcess1">
<operation name="initiate">
<input message="client:BPELProcess1RequestMessage"/>
</operation>
</portType>
<portType name="BPELProcess1Callback">
<operation name="onResult">
<input message="client:BPELProcess1ResponseMessage"/>
</operation>
</portType>

<plnk:partnerLinkType name="BPELProcess1">
<plnk:role name="BPELProcess1Provider">
<plnk:portType name="client:BPELProcess1"/>
</plnk:role>
<plnk:role name="BPELProcess1Requester">
<plnk:portType name="client:BPELProcess1Callback"/>
</plnk:role>
An asynchronous process specifies two portTypes, each with its own variable. Also it specifies two partnerLinkTypes, because it uses the same client partnerlink to receive from and invoke to (the callback). A synchronous process only specifies one portType holding both the input aswell as the output variable. It requires just one definition of the client partnerlink, because it doesn’t need to be invoked separately. blok

There is also a difference in the operation names (ie. Initiate, onResult and process). Though these are just labels that are referred to, it is good practice to actually rename the to the labels as shown in the default generated code, when changing from async to sync (and vise versa). This makes the xml code better understandable later on.

Summary

In your Asynchronous BPEL process diagram:
  1. Remove the Invoke process step from the diagram
  2. Drag a Reply process step in that place
  3. Connect the Reply process step to the client partnerlink (on the left lane)
  4. In the Edit reply dialog change the name to replyOutput and click OK
  5. Ignore the yellow exclamation mark for now
Now go to the source view of the .bpel file (and auto indent xml):
  1. Within the sequence element find the receive and reply elements
  2. For both of them, set the operation attribute’s value to process
  3. To the reply element, add the attribute variable and assign as value the name of the output variable, by default this is outputVariable.
Open the .wsdl file in source view (and auto indent xml):
  1. Find the two portType definitions
  2. From the one with operation onResult and name Callback prefixed with your service name, copy the input element and paste it after the input element of the first portType (named your service name)
  3. Rename the element you just copy/pasted from input to output
  4. In the current portType, set the value for operation name to process
  5. Remove the element (including its child elements!) from which you copied the input element (the one that’s called Callback prefixed with the name of your process)
  6. Remove the partnerLink role element - plnk:role - (including its child elements!) that has a portType name equal to the earlier removed portType .
Now go back to your .bpel file and open it in diagram view. The yellow exclamation mark should now be gone. If not, open the Reply process step and click the Apply button. Recompile your project and deploy it.

Friday, March 16, 2007

Tuning BPEL with JVMStat

Since Java version 1.4 a nice tool is available to monitor the behavior of the JVM. This can also be applied on BPEL. While BPEL is running on top of the JVM. The tool is called JVMstat and is available via the Sun website.

It works as follows. On the application server a daemon program must be run. This program is collecting information from the running JVM's. Via a visual tool, supplied with JVMStat, the behavior of the JVM is shown in a graphical way.

We assume that the JVMStat daemon is running on your application server, on which SOA Suite 10.1.3 is running, and the JVMStat visual tool running from your workstation. This is shown in the following diagram.


Download JVMStat from the the Sun website, unzip the file into a directory on your workstation and copy the zip file to your server and unzip it there as well.

Create a policy file on the server. This file is needed to set the appropriate rights for JMVStat. Create a file named "jstatd.policy". The content of the file must be:

grant codebase "file:$ORACLE_HOME/jdk/lib/tools.jar"
{

permission java.security.AllPermission;
};

Replace $ORACLE_HOME with the full directory path.

On the server you can start the daemon process.

jstatd -J-Djava.security.policy=jstatd.policy

Note that the daemon process is using the policy file, we just created.

Now you can start the visual user interface. This is done by starting the program 'visualgc.cmd' on the workstation. Therefore you must known the process-id of the JVM on which the SOA Suite is running. Use, assuming you run a unix environment, the 'ps -ef | grep java' command to found the process id.

Start the visual tool as follows:

visualgc.cmd @

For example:

visualgc.cmd 3219@linuxmachine.local.site

Monday, March 05, 2007

Delete BPEL instances

Running a production environment generates a huge amount of BPEL instances. Unless you have specified that you do not use dehydration :-).

It is possible to purge the instances all at once, as described in the article here (It also describes how to reclaim your space). But this purges all the instances. This is nice during development. But in a production environment you want to have a controlled way. A nice way to do this is via good-old PLSQL. Use the package COLLAXA in the ORABPEL schema.

This package has some nice methods to call:

procedure delete_txs
Deletes all the transactions that belong to a particular cube instance.

procedure delete_ci
Deletes a cube instance and all rows in other Collaxa tables that reference the cube instance. Since we don't have referential integrity on the tables (for performance reasons), we need this method to help clean up the database easily.

You can make a query to select the BPEL instances to delete and call the package:

create or replace
PROCEDURE purge_instances
(
p_domain IN VARCHAR2 DEFAULT '%'
, p_process_name IN VARCHAR2 DEFAULT '%'
, p_revision IN VARCHAR2 DEFAULT '%'
, p_older_than IN NUMBER DEFAULT 999
)
IS
CURSOR c
(
b_domain IN VARCHAR2
, b_process_name IN VARCHAR2
, b_revision IN VARCHAR2
, b_older_than IN VARCHAR2
)
IS
SELECT
cie.cikey cikey
, dmn.domain_id domain_id
, cie.process_id process_id
, cie.revision_tag revision_tag
, cie.modify_date modify_date
, cie.domain_ref domain_ref
FROM
cube_instance cie
, domain dmn
WHERE cie.domain_ref = dmn.domain_ref
--
-- the name of the domain
AND dmn.domain_id LIKE b_domain
--
-- code 5 means completed
AND cie.modify_date < TRUNC(sysdate)-b_older_than
--
AND cie.process_id LIKE b_process_name
AND cie.revision_tag LIKE b_revision;
BEGIN
FOR r in c
(
p_domain
, p_process_name
, p_revision
, p_older_than
)
LOOP
DBMS_OUTPUT.PUT_LINE
(
'Purge '||r.process_id||'('||r.revision_tag||')'||
' at '||to_char(r.modify_date, 'YYYY-MM-DD HH24:MI:SS')
);
collaxa.DELETE_CI(r.cikey);
--
delete from wftask wfn where wfn.instanceid = r.cikey;
--
END LOOP;
--
-- delete invoke calls
-- invoked messages
DELETE FROM invoke_message_bin imn
WHERE EXISTS
(
SELECT 1
FROM invoke_message ime
, domain dmn
WHERE ime.message_guid = imn.message_guid
AND ime.domain_ref = dmn.domain_ref
AND dmn.domain_id LIKE p_domain
AND ime.state > 1
AND ime.process_id LIKE p_process_name
AND ime.revision_tag LIKE p_revision
AND ime.receive_date < TRUNC(sysdate)-p_older_than
);
--
DELETE FROM invoke_message ime
WHERE ime.domain_ref in
(
SELECT dmn.DOMAIN_REF
from domain dmn
WHERE dmn.domain_id LIKE p_domain
)
AND ime.state > 1
AND ime.process_id LIKE p_process_name
AND ime.revision_tag LIKE p_revision
AND ime.receive_date < TRUNC(sysdate)-p_older_than;
--
DBMS_OUTPUT.PUT_LINE ('-> #invoke msg '||SQL%ROWCOUNT);
--
--
-- delete callback calls
DELETE FROM dlv_message_bin dmb
WHERE EXISTS
(
SELECT 1
FROM dlv_message dme
, domain dmn
WHERE dme.message_guid = dmb.message_guid
AND dme.domain_ref = dmn.domain_ref
AND dmn.domain_id LIKE p_domain
AND dme.state > 1
AND dme.process_id LIKE p_process_name
AND dme.revision_tag LIKE p_revision
AND dme.receive_date < TRUNC(sysdate)-p_older_than
);
--
DELETE FROM dlv_message dme
WHERE dme.domain_ref IN
(
SELECT dmn.DOMAIN_REF
from domain dmn
WHERE dmn.domain_id LIKE p_domain
)
AND dme.state > 1
AND dme.process_id LIKE p_process_name
AND dme.revision_tag LIKE p_revision
AND dme.receive_date < TRUNC(sysdate)-p_older_than;
--
DBMS_OUTPUT.PUT_LINE ('-> #callback msg '||SQL%ROWCOUNT);
END;
/

Calling the Operating System (OS) from BPEL

Sometimes you would like to call a script on the operating system to perform some complex tasks that are not possible to do in BPEL. This is often seen in Unix environments, when you want to call some scripts to run batch program for example. The following code is an example that shows how to call the OS. Note that the 'command' contains the full path to the executable script.

<bpelx:exec name="CallOS"
language="Java" version="1.4">
<![CDATA[
Element inVarElem = (Element)getVariableData
( "inputVariable"
, "payload"
, "/CallOSRequest/client:command");
String inVar = inVarElem.getNodeValue();
String command = inVar;
String result;
InputStreamReader isr;
InputStream isForProcess;
String isResult;

try
{
Runtime myProcessRuntime = Runtime.getRuntime();
Process process = myProcessRuntime.exec(command);
int exitVal = process.waitFor();
isResult = new String();

if (exitVal > 0)
{
isForProcess = process.getErrorStream();
}
else
{
isForProcess = process.getInputStream();
}
isr = new InputStreamReader(isForProcess);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
{
isResult = isResult + line;
}

addAuditTrailEntry("Input is: " + command);
addAuditTrailEntry("Output is: " + isResult);
result = "" + exitVal;
addAuditTrailEntry("Exit status: " + result);

setVariableData(
"outputVariable"
, "payload"
, "/client:CallOSResponse/client:result"
, result);

System.out.println("Executed (Input: " + inVar + ")");
}
catch (Exception ex)
{
ex.printStackTrace();
}]]>
</bpelx:exec>