Saturday, March 22, 2008

Programatically controlling the length of expiration/escalation on BPEL Human Task

By default Oracle BPEL lets you expire or escalate tasks on a pre-determined duration basis. But consider the situation where you want the task duration to be 8 working hours before expiration, and working hours are only the time between 8 am an 5 pm, Monday through Friday -- how can you do this?

The options in the standard task dialog don't really accommodate this, but you can work around it. You can provide a variable to the task that has this information. Here are the five basic steps to making this work:

1) Create a BPEL variable of type string. For this example I use "strExpirationMinutes". This will hold the number of minutes to wait before expiring a task.

2) In your human task definition, define a parameter of type string. For this example let's call it "ExpirationTime"

3) In your human task block, assign the value of strExpirationMinutes ( bpws:getVariableData('strExpirationMinutes') ) to the parameter ExpirationTime

4) In your human task definition, set the expiration/escalation policy to be "By Expression" and provide the XPath to reference the passed in parameter. For this example the XPath would be: /task:task/task:payload/task:ExpirationTime

5) Prior to the human task block in the BPEL flow assign a value to strExpirationMinutes of the form "PTxM" where x is the number of minutes before expiration. I typically do this via a embedded Java block.

Here is the Java code for our example:

String tmp = "PT";   
int iMins = 0;
int hrs8 = 60*8;
int hrs23 = 60*23;
int hrs24 = 60*24;

java.util.Calendar calNow = java.util.Calendar.getInstance();

int hours = calNow.get( java.util.Calendar.HOUR_OF_DAY );
int minutes = calNow.get( java.util.Calendar.MINUTE );
int dow = calNow.get( java.util.Calendar.DAY_OF_WEEK );

// if a M-T
if (dow>=2 && dow<=5) {
// if starting before 8 am - add 8 hours plus the interval between now and 8 am
if (hours<8) {
iMins = (60-minutes) + ( (8-hours-1)*60 )+ hrs8;

// if starting between 8 am and 9 am - just add 8 hours
} else if (hours==8 || ( hours==9 && minutes==0) ) {
iMins = hrs8;

// if starting between 9 am and 5 pm - just add 23 hours
} else if (hours>=9 && (hours<17 || (hours==17 && minutes==0)) ) {

iMins = hrs23;

// if starting after 5 pm - add 8 hours plus the interval between now and 8 am
} else {
iMins = (60-minutes) + ( (24-hours-1)*60 ) + hrs8 + hrs8;
}

// if Fri
} else if (dow==6) {
// if starting before 8 am - add 8 hours plus the interval between now and 8 am
if (hours<8) {
iMins = (60-minutes) + ( (8-hours-1)*60 )+ hrs8;

// if starting between 8 am and 9 am - just add 8 hours
} else if (hours==8 || ( hours==9 && minutes==0) ) {
iMins = hrs8;

// if starting between 9 am and 5 pm - add 23 hours plus the two we days
} else if (hours>=9 && (hours<17 || (hours==17 && minutes==0)) ) {

iMins = hrs23 + hrs24 + hrs24;

// if starting after 5 pm - add 8 hours duration plus the interval between
// now and 8 am saturday, plus the 2 we days
} else {
iMins = (60-minutes) + ( (24-hours-1)*60 ) + hrs8 + hrs8 + hrs24 + hrs24;
}

// if Sat
} else if (dow==7) {
// add 8 hrs duration, plus the time between now and midnight sat, plus
// all of sunday, plus 8 hrs for monday before 8 am
iMins = (60-minutes) + ( (24-hours-1)*60 ) + hrs8 + hrs8 + hrs24;

// if Sun
} else {
// add 8 hrs duration, plus the time between now and midnight sun,
plus
// 8 hrs for monday before 8 am
iMins = (60-minutes) + ( (24-hours-1)*60 ) + hrs8 + hrs8;
}

// finish build the time string
tmp = tmp + iMins + "M";

// save the string to bpel proc and audit
setVariableData("strExpirationMinutes", tmp);
addAuditTrailEntry("strExpirationMinutes:",tmp);

Programmatically determining BPEL manual recovery status

It's been a while since I've written an article, and I hope no one has been waiting all this time for part 2 of my BAM/BPEL alert article. I promise to complete that write up soon.

Today, however, I'd like to talk about an interesting problem I ran across this week. If anyone has experience with Oracle's BPEL product you know that sometimes BPEL processes can go to manual recovery. The two most common issues are: 1) an issue on the invoke of the process (BPEL server Out of Memory error for example), and 2) an issue with the callback on an asynchronous BPEL process.

BPEL process manager has a nice UI for looking at and managing these, but what if we need to be alerted when a process goes into one of these states? Well, BPEL PM doesn't have that capability, so we need to write our own. Another useful thing might be to always clear the processes, especially those that are in manual recovery for callback. Typically in that situation the callback failed, most often because the original calling process is no longer around to receive the callback.

In the rest of the article we talk about finding processes in both invoke and callback manual recovery. For both cases, it will be necessary to get a reference to the Locator object via the BPEL Java API. I talk about that in an earlier post, so I've assumed that you've read up on that before continuing.

Getting process manual recovery for inoke:

            String Buffer buf = new StringBuffer();

WhereCondition where = new WhereCondition(buf.append(SQLDefs.IM_state).append( " = " ).append(IDeliveryConstants.STATE_UNRESOLVED ).toString() );

IInvokeMetaData imd[] = locator.listInvokeMessages(where);

String ids[] = new String[imd.length];

// print out the partial process information
// for processes in manual recovery status on invoke
for (i = 0; i <>
{
System.out.println("ConversationId=" +
imd[i].getConversationId());
System.out.println("ProcessId=" +
imd[i].getProcessId());
System.out.println("State=" + imd[i].getState());

ids[i] = imd[i].getConversationId();
}

// if we wanted to automatically recover these processes
// we would uncomment the following line:
// locator.lookupDomain().recoverInvokeMessages(ids);

That's it! For callbacks the code flow is the exact same. Instead of IInvokeMetaData, we get an array of ICallbackMetaData using the locator.listCallbackMessages(where) method. One other slight change is the where clause itself -- for callbacks the where clause should be built as:

WhereCondition where = new WhereCondition(buf.append(SQLDefs.DM_state).append( " = " ).append(
IDeliveryConstants.TYPE_callback_soap ).toString() );

The last change for callbacks is how they are recovered -- locator.lookupDomain.recoverCallbackMessages(ids) will do the trick!

Using the API's above you can see how we could build a custom notification process or an automatic or work flow based recovery method that does not involve the actual BPEL PM.

Oracle BAM - Calling an external web service as an alert - Part 1 - BAM Configuration

Oracle's BAM tool is very useful not only for reporting on enterprise data, but also in taking action on certain events.

For instance, suppose you have a BAM object defined and other processes are interested in the fact that the BAM object has been populated with a new row. In the SOA world, there are many ways we could notify another process -- via a message queue or simply by calling a web service. It is the second technique that we will be discussing in this and a follow up article.

In my example I have a BPEL process that needs to be called every time my BAM data object has a new row added to it. BAM doesn't know anything about using the BPEL Java API, but it can call a web service via an external action hooked to an alert. We'll talk about the BPEL process in the next post -- the rest of this post will described how to set up the external action and alert in BAM.

Step 1 - Creating an External Action in BAM


The external action will define the actual call to the web service. External actions are created using the BAM Architect module.

1) Select 'Data Objects' from the architect drop down
2) Expand the 'System' folder and then click the 'Alert' folder
3) Click on the 'External Actions' data object link
4) If you select the 'Layout' link you'll see that an external action has five fields that must be filled in:
  1. Name - The name that will appear in the alert's dialog
  2. Description - A description of the action
  3. Library Name - The library that contains the action class
  4. Class Name - The class which implements the external action
  5. Argument - Argument to pass to the action class
5) Click the 'Contents' link and then the 'Edit Contents' button.
6) Click the 'Add' button to add a new row. The name and description fields are self-explanatory. The library name field should be set to 'Internal' and the class name field should be set to 'WebServiceCaller'. Finally the argument field needs to be set to 'URL=' followed by the complete endpoint URL to the web service -- for example:
'URL=http://myhost:myport/orabpel/mydomain/bpelprocessname/1.0,SOAPAction=process'
7) Verify everything and click 'Save' to save your new row

Note: Once you have defined your new external actions, you should restart the BAM Event Engine service.

Step 2 - Creating the Alert in BAM to Call the Action

The alert will set up the conditions under which our alert is called. Alerts are created using the BAM Architect module.

1) Select 'Alerts' from the architect drop down
2) Click the 'Create a New Alert' link
3) A new window will pop up, click the 'Create Rule' button
4) Give the rule a meaningful name in the 'Rule Name' field
5) Select the trigger event. For my example I want to call the web service everytime a row is added to a data object, so I would select 'When a data field changes in a data object'
6) Click the 'Selet data field' link
7) Pick the appropriate data object and data field.
8) By default, BAM will add a frequency constraint that will launch your rule no more than once every 5 seconds. You can change this frequency constraint to any number of minutes, hours, or seconds, or you can disable the constraint by unchecking the 'Constraint Enabled' box. Make sure that you uncheck the box if you need the alert to fire on every single event (BAM will warn you about this and ask you to click 'OK' if you want the constraint disabled).
9) Click the 'Next' button
10) You can add any time conditions if required and most importantly you can now select the action to perform. If you restarted the event service after you created you external action, you should now see your external action as an option at the end of the list. Select it.
11) Click 'OK'. Your new alert will be created and appear in the alert list.

Note: You can enable and disable alerts, simply by checking or unchecking the 'Activate' box by the alert.

If you've filled in everything correctly, your alert should fire when you add a row to the data object that you selected. If you're reading this in anticipation of calling a BPEL process, here's an important point in advance of my next post -- BAM can only call synchronous BPEL processes.

JNDI URL in High Availability Environments

In high availability environments you'll want to specify more than one source for your JNDI provider. How can you do that?

With a simple URL change you can switch from only specifying one provider:

jndiProviderURL = "ormi://localhost/Service"

to a list of nodes which can serve as the provider:

jndiProviderURL = "opmn:ormi://host1:port1:oc4j/Service, opmn:ormi://host2:port2:oc4j/Service"

Debugging a failing BPEL process - How to force dehydration

Sometimes a failing BPEL process will fail without much trace, not even showing that an instance was ever created in Oracle process manager. In this case it may be useful to force dehydration to aid debugging.

You can try to force dehydration in your process by:

1) Adding a Java execution block to your process at the point you wish to dehydrate the process
2) Calling the checkpoint() method

You should not that this method should only be used for debugging purposes, and that future versions of Oracle BPEL may not support it.

Changing an Asynchronous Oracle BPEL process to be Synchronous

One of the top suggestions Oracle has for tuning BPEL processes is to make sure that processes that can be synchronous are. This is because synchronous processes can be considered transient within the BPEL engine -- and the BPEL engine has several tunable parameters for transient processes that determine if and when they are pushed to the dehydration store. Fewer dehydration trips means better performance.

An obvious question then becomes "I just created a bunch of BPEL processes and they are all asynchronous, do I have to recreate them?" The short answer is: NO!

If you take a look at an asynchronous process, you'll notice that the last step is typically a callback client, while for a synchronous process it is a reply. This is because the client of a synchronous process is blocking during the call and a simple reply response is made to that blocked client. An asynchronous process makes a callback to the non-blocking client using the partner link (although of using a different partnerLinkType).

To start modifying your process, open the process .bpel file in design mode using JDeveloper. Delete the callback client step at the end of the process, this will be an invoke block connected back to the client. Drag a new reply block to that space. Now connect the reply block to the client partner link. You should change the reply name to "replyOutput" or something similarly useful.

Next, we need to make some further changes to the process .bpel file, so switch to source view. Scroll down the file until you come to the sequence element. Find the receive and reply elements within the sequence and change the operation attribute value of each one to "process". You will also need to add variable attribute to the reply element. Set this to the value of the output variable - by default this would be "outputVariable". Sample receive and reply elements look like this:


<receive name="receiveInput" partnerLink="client"
portType="client:BPELProcess2" operation="process"
variable="inputVariable" createInstance="yes"/>


<reply name="replyOutput" partnerLink="client"
portType="client:BPELProcess2" operation="process"
variable="outputVariable"/>


For the last changes, you need to open up your process' .wsdl file in the source editor. The WSDL edits are a bit more involved so, follow closely....

First, find the two portType definitions. One of them, typically the second one, should have the name attribute set to something like <yourprocessname>Callback and the operation set to "onResult". Copy the input element and paste it under the existing input element in the first portType definition. Change the element you just copied from "input" to "output". Change the operation name of this portType to "process".

Going back to the Callback portType, delete the entire portType definition. If you'd rather you can simply comment it out.

Now find a the partnerLinkType element. You'll want to delete the plnk:role element within that has the same portType name attribute as the portType you just deleted. Make sure you remove the entire plnk:role element and not just the plnk:portType.

Here is an excerpt of my changed WSDL, with comments where I made changes:


<portType name="BPELProcess2">
<operation name="process">
<input message="client:BPELProcess2RequestMessage"/>
<!-- copied from old call back, change input to output -->
<output message="client:BPELProcess2ResponseMessage"/>
</operation>
</portType>

<!-- removed/comment out to make synchronous
<portType name="BPELProcess2Callback">
<operation name="onResult">
<input message="client:BPELProcess2ResponseMessage"/>
</operation>
</portType>
-->

<plnk:partnerLinkType name="BPELProcess2">
<plnk:role name="BPELProcess2Provider">
<plnk:portType name="client:BPELProcess2"/>
</plnk:role>

<!-- remove/comment out to make synchronous
<plnk:role name="BPELProcess2Requester">
<plnk:portType name="client:BPELProcess2Callback"/>
</plnk:role>
-->

</plnk:partnerLinkType>


You should now be able to save your changes and recompile the project -- see that was easy wasn't it?

Tips on the Oracle BPEL Java API - Part 2 - Using the Locator class to get a list of processes

In part 1, we saw how to use the Server class to connect to the BPEL server and get a list of BPEL domains configured on that server. The Server class is useful for performing administrative tasks on the BPEL server. If we want to perform process oriented tasks, we need to use the Locator class and it's methods.

First, you'll need to import several BPEL API classes:

import com.oracle.bpel.client.IBPELProcessHandle;
import com.oracle.bpel.client.Locator;

And, as in our discussion in part 1, next task is to configure a java.util.Properties object with the connection information for your BPEL server, as well as a String object containing the security credentials (password):

Properties props = new java.util.Properties();
props.put("orabpel.platform", "ias_10g" );
props.put("java.naming.factory.initial",
"com.evermind.server.rmi.RMIInitialContextFactory" );
props.put("java.naming.provider.url",
"opmn:ormi://bpelserver:6003:home/orabpel" );
props.put("java.naming.security.principal", "oc4jadmin" );
props.put("java.naming.security.credentials", "password123" );

String securityCredentials = "password123";

When we use the Locator class to search for processes, we need to specify which BPEL domain to look in. Here we initialize a String to use the default domain:

String selectedDomain = "default";

Now we can get our instance of the Locator. We'll need to pass it the domain we're interested in, the server password, and our Properties object to make the connection:

Locator locator = new Locator(selectedDomain, securityCredentials, props);

Once we have our Locator reference, we can lookup processes, instances, and services. For our example, we'll be getting a list of processes, represented as an array of IBPELProcessHandle objects, loaded in our default domain:

IBPELProcessHandle procs[] = locator.listProcesses();

We can now iterate through our array of process handles and print out the name of each one:

for (int i=0; i<procs.length; i++ ) {
System.out.println( "Process Name="+procs[i].getProcessId().getProcessId() );
}

You should note that calling locator.listProcesses() does not guarantee that an array of fully loaded process handle objects will be returned to the caller. If you intend to use the methods in the IBPELProcessHandle interface, you should first ensure that the process handle was loaded by calling the method isLoaded() first.

Sometimes Toplink's mergeEntity doesn't....

If you use JDeveloper and you want to use a JPA, odds are you'll pick Toplink. Toplink works well for me, but if you use a session facade bean to access your Toplink queries and objects you may have had a run in, like I have in the past, with the mergeEntity() method.

It seems like sometimes this method just will not save the data to the database, which can be very frustrating. I'll list some common causes and remedies below:

1) Toplink will not update values that compose the key pf an object. The only way I know around this is to delete the old object and create a new one.

2) You are calling mergeEntity() against objects created by a ReadAllQuery. In this case, in your session facade bean method you should make sure that you uncomment the code that detaches the results. It will be directly below a couple of comments that should look like these:
// Uncomment the following lines of code if the results from this query will be mutated.
// See SessionFactory.detach() for more information.

3) The read and the merge are using different transactions. Change the code to use the same transaction for reading/updating.

Tips on the Oracle BPEL Java API - Part 1 - Using the Server class to get a list of domains

In this post I'll describe some of the common things that need to be done to call the Oracle BPEL PM API from Java, and I'll show you how to put that to use to get a list of the current BPEL domains defined on you server. We'll be using the Server class, which is in the com.oracle.bpel.client package, and is useful for manipulating BPEL at the server level.

First, for this example, you'll need to import several BPEL API classes:

import com.oracle.bpel.client.BPELDomainStatus;
import com.oracle.bpel.client.Server;
import com.oracle.bpel.client.auth.ServerAuth;
import com.oracle.bpel.client.auth.ServerAuthFactory;

The next task is to configure a java.util.Properties object with the connection information for your BPEL server, as well as a String object containing the security credentials (password):

Properties props = new java.util.Properties();
props.put("orabpel.platform", "ias_10g" );
props.put("java.naming.factory.initial",
"com.evermind.server.rmi.RMIInitialContextFactory" );
props.put("java.naming.provider.url",
"opmn:ormi://bpelserver:6003:home/orabpel" );
props.put("java.naming.security.principal", "oc4jadmin" );
props.put("java.naming.security.credentials", "password123" );

String securityCredentials = "password123";

We are now ready to get a ServerAuth object from the ServerAuthFactory:

ServerAuth auth = ServerAuthFactory.authenticate(securityCredentials, null, props);


Next, we get an actual client connection to the server by creating a Server object and passing the constructor our ServerAuth object:


Server srv = new Server(auth);

Finally, we can get the list of domain status information by calling the getAllDomainStatus() method of the server:

BPELDomainStatus domainList[] = srv.getAllDomainStatus();

Now that you have the status information you can do any number of things, in our example we can print out the domain ID of each domain on the server with this code:

for (int i=0; i<domainList.length; i++ ) {
System.out.println( "Domain ID="+domainList[i].getDomainId() );
}

A short note about our new site home

This site contains the posts from my previous location for Charles' SOA Blog ( http://soablog.info ) through the end of 2007.

Additional posts will be made here as the other account is phased out.