Monday, January 10, 2011

Canceling a running composite instance - example

Firstly I wish all of you - Health, Wealth and Happiness for 2011 and beyond!

Now it's back to work...

Scenario
File Adapter(read) --> Mediator --> BPEL --> human workflow --> DB
Adapter(write).

Step 1 - create the composite
- I started with a simple Mediator, to begin with.




- Here is the XSD I used



- As you can see, I created a composite sensor on the OrderNr.



Ok, let’s add the BPEL & Human Workflow components to the composite



- Define the Human Workflow ( I simply assign the approval task to jcooper)
- Autogenerate task form etc.


Deploy and Test
- Here is my test input XML.




- Login to sqlplus as dev_soainfra
- execute the following
-- select substr(composite_instance_id,1,15),
substr( component_instance_id,1,20),
substr( component_name, 1,20),
substr(sensor_name,1,20),
substr( string_value,1,20)
from composite_sensor_value
/



- So the composite instance id = 180001

- I can now use this value to execute the following query
-- select substr(composite_dn,1,25), state, live_instances from composite_instance where id = 180001
/



- The STATE = 0 which indicates that the instances is open and running.
- Other state values are -->



- We can now access the cube_instance table to get the details of the running bpel instance –

-- Select cikey, state from cube_instance where cmpst_id = 180001;



- State = 1 means bpel instance is open and running –
- Other state values are -->



- Now I check the Human Workflow tables
-- select
substr( title, 1,20),
tasknumber,
taskid,
substr(state,1,10),
substr(assignees,1,20)
from wftask
where compositeinstanceid = 180001;



-- The taskid = 44b2adae-c347-48c4-b5d9-0b76ba722c1d
-- The task number = 200180

Step 2 - Leveraging the Java API to find a particular human task

- To begin with, let's get all the tasks for jcooper -
-- Method getAllTasks()



The task 200180 is the last one in the list.

- Then I look for a specific task - getSpecificTask()



-------------------------------------------------------------------

package processmyorderapi;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;

import java.util.Map;

import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
import oracle.bpel.services.workflow.query.ITaskQueryService;

import oracle.bpel.services.workflow.repos.TableConstants;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

import oracle.bpel.services.workflow.repos.Predicate;

public class HwTask {

public HwTask() {
super();
}

public String getAllTasks(String user){

Map properties = new HashMap();
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL, "t3://localhost:8001");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS, "welcome1");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL, "weblogic");

try
{
//Create JAVA WorkflowServiceClient
IWorkflowServiceClient wfSvcClient =
WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT, properties, null);

//Get the task query service
ITaskQueryService querySvc = wfSvcClient.getTaskQueryService();
//Login as jcooper
IWorkflowContext ctx = querySvc.authenticate("jcooper","welcome1".toCharArray(),null);
//Set up list of columns to query
List queryColumns = new ArrayList();
queryColumns.add("TASKID");
queryColumns.add("TASKNUMBER");
queryColumns.add("TITLE");
queryColumns.add("OUTCOME");
//Query a list of tasks assigned to jcooper
List tasks = querySvc.queryTasks(ctx,
queryColumns,
null, //Do not query additional info
ITaskQueryService.AssignmentFilter.MY,
null, //No keywords
null, //No custom predicate
null, //No special ordering
0, //Do not page the query result
0);

//Get the task service
ITaskService taskSvc = wfSvcClient.getTaskService();
//Loop over the tasks, outputting task information,

for(int i = 0 ; i < tasks.size() ; i ++)
{

Task task = (Task)tasks.get(i);
int taskNumber = task.getSystemAttributes().getTaskNumber();
String title = task.getTitle();
String taskId = task.getSystemAttributes().getTaskId();
String outcome = task.getSystemAttributes().getOutcome();
/* now we could auto-approve tasks, if required!
/* if(outcome == null)
{
outcome = "APPROVED";
taskSvc.updateTaskOutcome(ctx,taskId,outcome);
}*/

System.out.println("Task ID#"+ taskId + "Task Nr#" + taskNumber+" ("+title+") is "+outcome);
}
}
catch (Exception e)
{
//Handle any exceptions raised here...
System.out.println("Caught workflow exception: "+e.getMessage());
}

return "done";
}

public String getSpecificTask(String taskid){
System.out.println("*** getSpecificTask() looking for taskId " + taskid);
Map properties = new HashMap();
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL, "t3://localhost:8001");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS, "welcome1");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL, "weblogic");

try
{
//Create JAVA WorflowServiceClient
IWorkflowServiceClient wfSvcClient =
WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT, properties, null);

//Get the task query service
ITaskQueryService querySvc = wfSvcClient.getTaskQueryService();
//Login as jcooper
IWorkflowContext ctx = querySvc.authenticate("jcooper","welcome1".toCharArray(),null);
//Set up list of columns to query
List queryColumns = new ArrayList();
queryColumns.add("TASKID");
queryColumns.add("TASKNUMBER");
queryColumns.add("TITLE");
queryColumns.add("OUTCOME");

// add predicate for particular taskid

Predicate predicate = new Predicate(TableConstants.WFTASK_TASKID_COLUMN,
Predicate.OP_EQ,
taskid);
System.out.println("*** getSpecificTask() Predicate added for taskId " + taskid);

//Query a list of tasks assigned to jcooper
List tasklist = querySvc.queryTasks(ctx,
queryColumns,
null, //Do not query additional info
ITaskQueryService.AssignmentFilter.MY,
null, //No keywords
predicate, //add custom predicate
null, //No special ordering
0, //Do not page the query result
0);

if (tasklist != null) { // There are tasks
System.out.println("Total number of tasks: " + tasklist.size());
System.out.println("Tasks List: ");
Task task = null;
for (int i = 0; i < tasklist.size(); i++) {
task = (Task) tasklist.get(i);
System.out.println("Task Number: " + task.getSystemAttributes().getTaskNumber());
System.out.println("Task Id: " + task.getSystemAttributes().getTaskId());
System.out.println("Title: " + task.getTitle());
System.out.println("Priority: " + task.getPriority());
System.out.println("State: " + task.getSystemAttributes().getState());
System.out.println();
// Retrive any Optional Info specified
// Use task service, to perform operations on the task
}
}
}
catch (Exception e)
{
//Handle any exceptions raised here...
System.out.println("Caught workflow exception: "+e.getMessage());
}

return "done";
}


}

------------------

Step 2 - Leveraging the Java API to find and purge composite instances

- see method - findComposite()
-- here we call locator.purgeInstance() to purge the instance
--------------------------------------------

package compositeapi;

import java.util.Hashtable;
import java.util.List;
import javax.naming.Context;

import oracle.soa.management.facade.ComponentInstance;
import oracle.soa.management.facade.Composite;
import oracle.soa.management.facade.CompositeInstance;
import oracle.soa.management.facade.Locator;
import oracle.soa.management.facade.LocatorFactory;
import oracle.soa.management.facade.ServiceEngine;
import oracle.soa.management.facade.bpel.BPELServiceEngine;
import oracle.soa.management.util.ComponentInstanceFilter;
import oracle.soa.management.util.CompositeInstanceFilter;


public class CompositeAPI {

private Locator locator = null;

public CompositeAPI() {
super();
}

public void findComposite(){

java.util.Hashtable props = new java.util.Hashtable();
props.put(javax.naming.Context.PROVIDER_URL, "t3://localhost:8001/soa-infra");
props.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
props.put(javax.naming.Context.SECURITY_PRINCIPAL, "weblogic");
props.put(javax.naming.Context.SECURITY_CREDENTIALS, "welcome1");
props.put("dedicated.connection", "true");

try{
locator = LocatorFactory.createLocator(props);
CompositeInstanceFilter coif = new CompositeInstanceFilter();
coif.setId("180001");
List list = locator.getCompositeInstances(coif);
System.out.println("Nr. of Composites found = " + list.size());

for(int i = 0 ; i < list.size() ; i ++){
CompositeInstance coi = (CompositeInstance)list.get(i);
System.out.println("Composite Instance Domain Name = " +coi.getCompositeDN());
System.out.println("Composite Instance State = " +coi.getState());
System.out.println("Composite Instance Status = " +coi.getStatus());

locator.purgeInstance(coif);

}


// Component Instance Filter usage

ComponentInstanceFilter cif = new ComponentInstanceFilter();
cif.setCompositeInstanceId("180001");
List l = locator.getComponentInstances(cif);

System.out.println("Instance found = " + l.size());

for(int i = 0 ; i < l.size() ; i ++){
ComponentInstance ci = (ComponentInstance)l.get(i);
System.out.println("Component Instance ID = " +ci.getCompositeInstanceId());
ServiceEngine se = ci.getServiceEngine();
System.out.println("*** Engine Type " + se.getEngineType());
System.out.println("DN = " + ci.getCompositeDN());
}

String compositeDN = "default/ProcessMyOrder!1.0";
Composite composite = locator.lookupComposite(compositeDN);
System.out.println("*** got composite "+ composite.toString());

// Purge

}
catch(Exception e){
e.printStackTrace();
}

}
}

-------------------------------------------------------------------------------

Step 3 - Check the DB

I run the selects again -

Saturday, December 18, 2010

Canceling a running composite in SOA Suite 11g

Simple scenario -

An order XYZ is being processed in a composite that includes the following
components-
File Adapter(read) --> Mediator --> BPEL --> human workflow --> DB
Adapter(write).

In the meantime the customer cancels the order XYZ by calling the call center.
The order db is updated accordingly via the call center app, but now we
need to kill the soa composite instance that's still running.

I could create a composite sensor based on the unique Order ID.
The info is then written to the SOA_INFRA table composite_sensor_value

From this table I can then get the COMPOSITE_INSTANCE_ID value.
I can save this pair - Order ID and COMPOSITE_INSTANCE_ID to the DB.

Now we know which composite is processing Order ID XYZ.

Regarding cancelling the running process -

We can use the COMPOSITE_INSTANCE_ID to query
the cube_instance table.

e.g. select * from cube_instance where cmpst_id = xxx;

I can then do the usual purge.

But how should we model this from an architectural perspective?
My OCS colleague, Markus, suggested a generic composite that takes care of
the "cancellation" of all running SOA composite instances.

So we could have the user cancellation call to the call center triggering an event that our generic composite processes.

So essentially we have the following -

1. Create an event via the EDN-Framework.
1.1. The payload contains the info required (orderID, CompositeID etc.) to
find the appropriate running instance.

2. Event is picked up by a Mediator which routes on to a BPEL process that does
the actual purging.

Example to follow after the holidays.

Wednesday, December 8, 2010

Human Workflow API Part 3 - ITaskQueryService.AssignmentFilter

leading on from the previous post...

We have a couple of roles in HW.
For example, the user that actually does the "approving" e.g. jverne.

Also we have the process owner e.g. cdickens.

We can leverage the Java API to process the HW tasks from both perspectives.

We just need to set the ITaskQueryService.AssignmentFilter appropriately.




//Query a list of tasks assigned to jcooper
List tasks = querySvc.queryTasks(ctx,
queryColumns,
optionalInfo, // Payload
ITaskQueryService.AssignmentFilter.OWNER,
null, //No keywords
null, //No custom predicate
null, //No special ordering
0, //Do not page the query result
0);

Friday, November 26, 2010

Human Workflow Java API Part 2

Continuing on from the previous post...

The payload I tested with is as follows -



Now I want to access the custName value in the payload.

Here's the code snippet(thanks to my colleaue Mireille) -

Element el= task.getPayloadAsElement();
String custName = null;
custName = getElementValue(el, "custName");
if(el != null){
System.out.println("Payload customer name = " + custName);


}
else{
System.out.println("Payload is null");
}

public static String getElementValue(Element pElement, String pElementName){
String value=null;
NodeList myNodeList = pElement.getElementsByTagName(pElementName);
Element myElement = (Element)myNodeList.item(0);
NodeList myChildNodeList = myElement.getChildNodes();
for (int i=0; i {
value = ((Node)myChildNodeList.item( i )).getNodeValue().trim();
if( value.equals("") value.equals("\r") ){
continue;
}
else{
break;
}
}
return value;
}

Testing...




The complete Java class is as follows -

package com.niall.hwclient;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import java.util.Map;

import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class HwTask {
public HwTask() {
super();
}

public String getTaskdetails(String user){

Map properties = new HashMap();
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL, "t3://localhost:8001");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS, "welcome1");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL, "weblogic");

try
{
//Create JAVA WorflowServiceClient
IWorkflowServiceClient wfSvcClient =
WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT, properties, null);

//Get the task query service
ITaskQueryService querySvc = wfSvcClient.getTaskQueryService();
//Login as jcooper
IWorkflowContext ctx = querySvc.authenticate("cdoyle","welcome1".toCharArray(),null);
//Set up list of columns to query
List queryColumns = new ArrayList();
queryColumns.add("TASKID");
queryColumns.add("TASKNUMBER");
queryColumns.add("TITLE");
queryColumns.add("OUTCOME");

// also get the payload
List optionalInfo = new ArrayList();
optionalInfo.add(ITaskQueryService.OptionalInfo.PAYLOAD);
//
//Query a list of tasks assigned to jcooper
List tasks = querySvc.queryTasks(ctx,
queryColumns,
optionalInfo, // Payload
ITaskQueryService.AssignmentFilter.MY,
null, //No keywords
null, //No custom predicate
null, //No special ordering
0, //Do not page the query result
0);
//Get the task service
ITaskService taskSvc = wfSvcClient.getTaskService();
//Loop over the tasks, outputting task information, and approving any
//tasks whose outcome has not been set...

for(int i = 0 ; i < tasks.size() ; i ++)
{
System.out.println("In task loop...");
Task task = (Task)tasks.get(i);
int taskNumber = task.getSystemAttributes().getTaskNumber();
String title = task.getTitle();
String taskId = task.getSystemAttributes().getTaskId();
String outcome = task.getSystemAttributes().getOutcome();

// Get the payload
Element el= task.getPayloadAsElement();
String custName = null;
custName = getElementValue(el, "custName");
if(el != null){
System.out.println("Payload customer name = " + custName);


}
else{
System.out.println("Payload is null");
}



/* if(outcome == null)
{
outcome = "APPROVED";
taskSvc.updateTaskOutcome(ctx,taskId,outcome);
}*/

System.out.println("Task #"+taskNumber+" ("+title+") is "+outcome);
}
}
catch (Exception e)
{
//Handle any exceptions raised here...
System.out.println("Caught workflow exception: "+e.getMessage());
}

return "done";
}
public static String getElementValue(Element pElement, String pElementName){
String value=null;
NodeList myNodeList = pElement.getElementsByTagName(pElementName);
Element myElement = (Element)myNodeList.item(0);
NodeList myChildNodeList = myElement.getChildNodes();
for (int i=0; i {
value = ((Node)myChildNodeList.item( i )).getNodeValue().trim();
if( value.equals("") value.equals("\r") ){
continue;
}
else{
break;
}
}
return value;
}
}

Monday, November 22, 2010

BPM Signal Events and EDN Part 2

Now we will create another Business Event at the composite level – OrderProcessedEvent







The new event surfaces in the Business Catalogue.



Return to the BPM Design editor and set the implementation type of the End Event to Signal.





Now we have no errors in our process.

Create the required process variables so that it is runnable.

Add a new Business Object of type Order.



Add a Process Data Object variable of this type



Set the Data Associations for Start and End




Ok, so now as soon as the order has been processed, a Mediator component will pick this up and write the order to a file. Here we will leverage the File Adapter(Write) we created earlier.



We then wire this to the File Adapter(Write)
Don't forget to create the required transformation in the Mediator.



Deploy and Test

Sample input file to be dropped into the /in directory



Result

BPM Signal Events and Oracle EDN

Simple scenario –

A new order arrives via file and kicks off the BPM Order process.
This can be easily modelled in Oracle SOA Suite 11g.
Our order has the following xsd –



Steps

Create a couple of directories on the file system




Create a BPM Application in JDeveloper



Go to the Composite view



Create a file adapter – (Read) based on the XSD – pointing to /in directory
Create a file adapter – (Write) based on the XSD – pointing to /out directory



Add a Mediator to pick up the read order



Add a routing rule to publish an event



Create the event definition





Switch to the BPM Project view

The event has been automatically added to the Business catalogue.



Right-mouse click on the Start icon and change the type to Signal






No we have EDN kicking off the BPM process via a Signal event.
I'll flesh this process out in the next post!

Friday, November 19, 2010

JCAPS - SOA Suite 11g Interoperability/Migration

Greetings from South Africa!

Just spent a couple of days around Capetown and am now up in Hermanus whale watching.
Certainly beats the November cold of southern Germany!

Regarding the above, apart from standard integration via web services and JMS, tighter integration can be achieved using the SOA Suite 11g Spring component described in the last post. Essentially we can wrap the JCAPS JCD as a Spring component and leverage it's functionality directly in our composite. Please ping me if you're interested in more details.