Monday, January 20, 2014

# 305 Adaptive Case Management Java API Part 2

Building on from the previous post - let's get some more information -

        Case myCase = new Case();
                List caseInfo =
            new ArrayList();
           caseInfo.add(ICaseConstants.CaseInfo.ALL);   

Note the additional line above -

This gives us access to more case related info such as Activities, Data, Milestones, Comments etc.

            // Case Activities
            CompletedCaseActivityList ccal =
                myCase.getCaseHeader().getCompletedCaseActivityList();
            List cca_l =
                ccal.getCompletedCaseActivity();
            System.out.println("### Nr of Case Activities Completed for this instance =" +
                               cca_l.size());

            for (int a = 0; a < cca_l.size(); a++) {
                CompletedCaseActivity cca = cca_l.get(a);
                String name = cca.getName();
                System.out.println("Completed Case Activity name = " + name);
            }

            System.out.println("### End of Case Header Info ...");

            System.out.println("");
                      // Case Data
            List cd_l = myCase.getCaseData();
            System.out.println("Case Data list size " + cd_l.size());
            for (int b = 0; b < cd_l.size(); b++) {
                CaseData cd = cd_l.get(b);
                System.out.println("Case Data  = " + cd.getData());
                System.out.println("");

            }

            // Case Actions
            List myActions = myCase.getActions();
            for (int c = 0; c < myActions.size(); c++) {
                System.out.println("+++ Actions : " +
                                   myActions.get(c).toString());
                

            }
            System.out.println("");
            
            // Case Comments
            List c_l = myCase.getComments();
            System.out.println("Comment list size " + c_l.size());
            for (int d = 0; d < c_l.size(); d++) {
                Comment comment = c_l.get(d);
                System.out.println("Comment :" + comment.getCaseObjectComment());
            }
            System.out.println("");

          
            List caseDataList = myCase.getCaseData();
            System.out.println("Case Data List size = " + caseDataList.size());
            for (int e = 0; e < caseDataList.size(); e++) {
                CaseData caseData = caseDataList.get(e);
                System.out.println("Case Data Name :" + caseData.caseDataName);
            }
            System.out.println("");
          
            List caseMilestoneList = myCase.getCaseMilestones();
            System.out.println("Case Milestone List size = " +
                               caseMilestoneList.size());

            for (int f = 0; f < caseMilestoneList.size(); f++) {
                CaseMilestone caseMilestone = caseMilestoneList.get(f);
                System.out.println("Case Milestone :" +
                                   caseMilestone.getObjectDisplayName());
                System.out.println("Case Milestone state :" +
                                   caseMilestone.getState());

            }
            System.out.println("");
            


FYI - here is one of my case composites -


         























































I have a test instance running - the case is configured with a rule to automatically reach the milestone ApproveOrderStarted. The Case data below contains my input

I run the above code - and get the following output -

### Case Header Info ...
Composite Name = default/SimpleOrderProcessingCase!1.0*soa_9f7e0a57-cfea-4ce1-8f91-5ca2280c4a13
Case Def. = SimpleOrderProcessingCase
ECM Folder = MyOrders/123
Id Key = 123 
Case Definition Name = SimpleOrderProcessingCase
Application Name = default
Component Name = SimpleOrderProcessingCase
Case Number = 42
Case Definition Id = default/SimpleOrderProcessingCase!1.0/SimpleOrderProcessingCase
### Nr of Case Activities Completed for this instance =0
### End of Case Header Info ...

Case Data list size 1
Case Data  = 123iBikeA1234NiallCMain StIEN
         

+++ Actions : abortCase
+++ Actions : updateCaseHeader
+++ Actions : closeCase
+++ Actions : suspendCase
+++ Actions : getCase
+++ Actions : queryCase

Comment list size 1
Comment :auto-start

Case Data List size = 1
Case Data Name :caseDataName

Case Milestone List size = 4
Case Milestone :OrderApprovalCompleted
Case Milestone state :NOT_ATTAINED
Case Milestone :OrderApprovalStarted
Case Milestone state :ATTAINED
Case Milestone :OrderShippingCompleted
Case Milestone state :NOT_ATTAINED
Case Milestone :OrderShippingStarted
Case Milestone state :NOT_ATTAINED

Case StakeHolder List size = 1
Case StakeHolder Name :CSR
+++ Case StakeHolder Member Name :James Cooper
+++ Case StakeHolder Member Name :John Steinbeck

Revised JDeveloper project available here




# 304 Adaptive Case Management Java API Part 1

Here is the stakeholders definition of my demo order approvals case -















Here is an instance of that case -




















Test API Output


















API code to query the case -

package com.niall;


import java.io.StringWriter;

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

import javax.servlet.http.HttpServletRequest;

import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBElement;

import oracle.bpel.services.bpm.common.IBPMContext;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.ITaskService;

import oracle.bpel.services.workflow.verification.IWorkflowContext;
import oracle.bpel.services.workflow.WorkflowException;

import oracle.bpm.casemgmt.CaseIdentifier;
import oracle.bpm.casemgmt.CaseList;
import oracle.bpm.casemgmt.CaseServiceException;
import oracle.bpm.casemgmt.ICaseService;
import oracle.bpm.casemgmt.persistence.model.Case;
import oracle.bpm.casemgmt.persistence.model.CaseData;
import oracle.bpm.casemgmt.persistence.model.CaseHeader;
import oracle.bpm.casemgmt.persistence.model.CaseMilestone;
import oracle.bpm.casemgmt.persistence.model.CaseStakeHolder;
import oracle.bpm.casemgmt.persistence.model.CaseStakeHolderMember;
import oracle.bpm.casemgmt.persistence.model.Comment;
import oracle.bpm.casemgmt.persistence.model.CompletedCaseActivity;
import oracle.bpm.casemgmt.persistence.model.CompletedCaseActivityList;
import oracle.bpm.casemgmt.persistence.model.ICaseConstants;
import oracle.bpm.client.BPMServiceClientFactory;
import oracle.bpm.services.authentication.IBPMUserAuthenticationService;
import oracle.bpm.services.common.exception.BPMException;
import oracle.bpm.training.acm.apilab.model.Complaint.ObjectFactory;
import oracle.bpm.training.acm.apilab.model.Complaint.TComplaint;
import oracle.bpm.training.acm.apilab.util.CaseHelper;


public class CaseMgtAPI {

    private BPMServiceClientFactory mServiceClientFactory = null;
    private ICaseService mCaseService = null;
    private IBPMContext mBPMUserContext = null;

    private static final String WLS_ADMIN_PASSWORD = "welcome1";
    private static final String WLS_ADMIN_USERID = "jcooper";

    public CaseMgtAPI() {
        super();
    }


    public static CaseMgtAPI getAllCases4User(String cUser,
                                              String cPwd) throws Exception {
        CaseMgtAPI caseMgtAPI = new CaseMgtAPI();

        Map properties =
            new HashMap();
        properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,
                       BPMServiceClientFactory.REMOTE_CLIENT);
        properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,
                       "t3://localhost:7001");
        properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,
                       cPwd);
        properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,
                       cUser);
        caseMgtAPI.mServiceClientFactory =
                BPMServiceClientFactory.getInstance(properties, "default",
                                                    null);
        /* Get Task Query Service */
        ITaskQueryService service =
            caseMgtAPI.mServiceClientFactory.getWorkflowServiceClient().getTaskQueryService();

        /* Get Case Mgt Service */
        caseMgtAPI.mCaseService =
                caseMgtAPI.mServiceClientFactory.getCaseManagementServiceClient().getCaseService();

        caseMgtAPI.mBPMUserContext =
                (IBPMContext)service.authenticate(cUser, cPwd.toCharArray(),
                                                  null);


        if (caseMgtAPI.mBPMUserContext != null) {
            System.out.println("CaseMgtAPI.getAllCases4User() Returning context for:" +
                               caseMgtAPI.mBPMUserContext.getUser());
            // list Case instances
            listCases(caseMgtAPI.mCaseService, caseMgtAPI.mBPMUserContext);

        } else {
            System.out.println("CaseMgtAPI.getAllCases4User()  invalid context");
        }

        return caseMgtAPI;
    }

    public static void listCases(ICaseService caseService,
                                 IBPMContext bpmContext) throws CaseServiceException {
        System.out.println("CaseMgtAPI.listCases()");
        System.out.println("");

        Case myCase = new Case();
        List caseInfo =
            new ArrayList();

        CaseList caseList =
            caseService.queryCase(bpmContext, caseInfo, null, null, 10, 1);
        System.out.println("Total Nr of Case Instances = " +
                           caseList.getTotalNumberOfCases());
        List myCaseList = caseList.getCases();
        System.out.println("Case list size " + myCaseList.size());
        for (int i = 0; i < myCaseList.size(); i++) {
            myCase = myCaseList.get(i);
            System.out.println("");
            System.out.println("Info for Case Instance " + i);
            //
            // Case Header
            System.out.println("");

            System.out.println("### Case Header Info ...");
            System.out.println("Composite Name = " +
                               myCase.getCaseHeader().getCompositeDn());
            System.out.println("Case Def. = " +
                               myCase.getCaseHeader().getCaseDefinitionName());
            System.out.println("ECM Folder = " +
                               myCase.getCaseHeader().getEcmFolder());
            System.out.println("Id Key = " +
                               myCase.getCaseHeader().getIdentificationKey());
            System.out.println("Case Definition Name = " +
                               myCase.getCaseHeader().getCaseDefinitionName());
            System.out.println("Application Name = " +
                               myCase.getCaseHeader().getApplicationName());
            System.out.println("Component Name = " +
                               myCase.getCaseHeader().getComponentName());
            System.out.println("Case Number = " +
                               myCase.getCaseHeader().getCaseNumber());
            System.out.println("Case Definition Id = " +
                               myCase.getCaseHeader().getCaseDefinitionId());
            // Case Activities
            CompletedCaseActivityList ccal =
                myCase.getCaseHeader().getCompletedCaseActivityList();
            List cca_l =
                ccal.getCompletedCaseActivity();
            System.out.println("### Nr of Case Activities Completed for this instance =" +
                               cca_l.size());

            for (int a = 0; a < cca_l.size(); a++) {
                CompletedCaseActivity cca = cca_l.get(a);
                String name = cca.getName();
                System.out.println("Completed Case Activity name = " + name);
            }

            System.out.println("### End of Case Header Info ...");
            System.out.println("");


          
            // Case Actions
            List myActions = myCase.getActions();
            for (int c = 0; c < myActions.size(); c++) {
                System.out.println("+++ Actions : " +
                                   myActions.get(c).toString());
                

            }
            System.out.println("");
            
      
          
            
            // stakeholders
            List caseStakeholderList =
                myCase.getCaseStakeHolders();
            System.out.println("Case StakeHolder List size = " +
                               caseStakeholderList.size());

            for (int f = 0; f < caseStakeholderList.size(); f++) {
                CaseStakeHolder caseStakeholder = caseStakeholderList.get(f);
                System.out.println("Case StakeHolder Name :" +
                                   caseStakeholder.getObjectDisplayName());
                List l_csm =
                    caseStakeholder.getCaseStakeHolderMembers();
                for (int g = 0; g < l_csm.size(); g++) {
                    CaseStakeHolderMember csm = l_csm.get(g);
                    System.out.println("+++ Case StakeHolder Member Name :" +
                                       csm.getStakeHolderDisplayName());
                }


            }

            System.out.println("");
            
        }
    }

    public IBPMContext getBPMContext() {
        return this.mBPMUserContext;
    }


    public BPMServiceClientFactory getServiceClientFactory() {
        return mServiceClientFactory;
    }

    public ICaseService getCaseService() {
        return mCaseService;
    }

    public ITaskService getTaskService() {
        return this.mServiceClientFactory.getWorkflowServiceClient().getTaskService();
    }


}


JDeveloper Project Libraries




















JDev Project download













Available here 

Useful resources/docs:

https://blogs.oracle.com/VenugopalMangipudi/entry/obpm_case_management_api_s


http://docs.oracle.com/cd/E28280_01/apirefs.1111/e25378/oracle/bpm/casemgmt/ICaseInstanceService.html


Wednesday, January 15, 2014

#303 BPM 11g Adaptive Case Management without UCM

Maybe you are trying out ACM and do not have UCM installed.
You enter such document details when creating a case.













Usually this results in the creation of a sub-folder in the parent folder. In my case, a subfolder in NewOrders. This title of this subfolder is based on the incoming custNr.

Great stuff! But what if you don't have UCM installed or do but want to save on resources when developing?

Ensure there is no setting in the SOA config for UCM - Click on Workflow properties























Click on More...













WorkflowConfig...











Amend the UCMIcdUrl value - In this case I just delete it.
Note the port 4444 this is for the Inbound Refinery from UCM - see

http://docs.oracle.com/cd/E21764_01/doc.1111/e14495/configucm.htm
for more details.









Restart your server

Test -






































Here is my case -














I upload a document -









































I start an activity in the case, in my scenario - Approve Order.
I can also view the attachment here.

















File is stored in the following DB table.






Naturally, when you store in the DB you do not have the usual UCM functionality - Check In / Check Out etc.


Monday, January 13, 2014

#302 Using the Human Workflow Event mechanism

Simple scenario - I want to pick up any re-assignment of tasks made in BPM workspace.

Here is my sample process -




All that is really salient for this example is the human task.

So I need to pick up any reassignments - firstly, I activate workflow events.
Amend the task definition as follows -















In the above, I activate for OnAssigned, OnCompleted and OnUpdated.
For my particular use case I only really require OnAssigned.

Once the task has been re-assigned, an EDN event will be thrown. The event definitions are stored in MDS -























This .edl file contains the XSDs of the OnAssigned etc. messages.












I now add a Mediator to my composite to subscribe to this event - As you can see, I simply write the data to a file.



.








So let's now look at the mapping for OnTaskAssigned















You see I am filtering on reassigns only.




















Now to the mapping -

I have defined the file adapter to use the same message type -









Testing

Task is assigned to jcooper






I now reassign to jstein













Output file is generated






































All that remains to be done is to pare down this info into something somewhat simpler.

The above message does not contain the actual task payload. 

So what can I do, if, for example, I want to see the orderNr of the reassigned order?

A simple method is to use the workflow flex fields -























I map the incoming order  nr to 




I re-test -








Monday, January 6, 2014

#301 Invoking BPM processes programmatically - when to use what?

- A big thank you to my colleague David R. for his comments -

For invoking the process (initial BPM process instance creation) we could --

1. Create a web service proxy based on the BPM process WSDL.
http://niallcblogs.blogspot.de/2013/11/283-bpm-11g-web-service-clients.html

2. Create an EJB Service Client to front the BPM process and call this.
http://niallcblogs.blogspot.de/2013/11/284-283-bpm-11g-ejb-service-clients.html

3. Create a direct binding interface to the BPM process.
http://niallcblogs.blogspot.de/2014/01/299-bpm-apis-revisited-directbinding.html

4. Explicitly use the BPM API as per https://java.net/projects/oraclebpmsuite11g/downloads/directory/Samples/bpm-api-101-instance-query

Other variations -

5. Use Technology Adapters

6. Use EDN

Some heuristics

Direct binding enables Java clients to directly invoke composite services, bypassing the intermediate conversion to XML required with web service binding.
Direct binding for propagating identity/transaction context
Direct binding for native connectivity between OSB and SOA/BPM Suite.
Direct binding for HiAv / Clustering support

Web Services when this is the preferred canonical model for integration.

Web Service Proxy - loose coupling, simple to develop.

In each of the above,  the caller is responsible for detecting failures and preserving the original message, The caller is responsible for retrying if required.  

EJB Service Client - useful for those who have standardised on EJBs - allows propagating identity/transaction context

BPM API - tightly coupled - for those who want full control. Not that simple to develop.

Adapters/EDN

Use of the adapters can move error handling/recovery off the client.  For example using JMS adapter or Database adapter.  For example, an integration with Mainframes where WLS/SOA Suite were not running when the process is started on the mainframe. 

Another common pattern would be a local queue with store & forward to the SOA instance, or inserting row into the DB. Then leveraging the DB adapter to start the process.  

EDN (Signal) can also be used. has it’s benefits, but does not currently have durable subscription. EDN thus decreases coupling, but does not ensure the message/event will be handled.


Friday, January 3, 2014

#300 BPM APIs revisited - IInstanceQueryService

The IInstanceQueryService can be used to query instances at runtime -
here is an example output from my Java client.













The code is pretty straightforward -

    public String getAllInstances() {
        System.out.println("getAllInstances() ");
        try {
            List displayColumns = new ArrayList();
            displayColumns.add(IColumnConstants.PROCESS_ID_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_COMPOSITEDN_COLUMN);

            displayColumns.add(IColumnConstants.PROCESS_PROCESSNAME_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_STATE_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_TITLE_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_CREATOR_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_CREATEDDATE_COLUMN);

            IInstanceQueryInput input = new InstanceQueryInput();

            input.setAssignmentFilter(IInstanceQueryInput.AssignmentFilter.ALL);
            Ordering ordering =
                new Ordering(IColumnConstants.PROCESS_NUMBER_COLUMN, true,
                             true);
            IInstanceQueryService queryService =
                getBPMServiceClient().getInstanceQueryService();

            IBPMContext bpmContext = getIBPMContext("jcooper", "welcome1");
            System.out.println("getInstances() queryInstances");

            List processInstances =
                queryService.queryInstances(bpmContext, displayColumns, null,
                                            ordering, input);
            for (IProcessInstance instance : processInstances) {
                System.out.println(instance.getSystemAttributes().getProcessInstanceId() +
                                   "\t" +
                                   instance.getSystemAttributes().getProcessNumber() +
                                   "\t" +
                                   instance.getSystemAttributes().getState() +
                                   "\t" + instance.getTitle() + "\t" +
                                   instance.getCreator() + "\t" +
                                   instance.getSystemAttributes().getCreatedDate().getTime());

            }
        }

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

    }

This can be further refined by adding a predicate to the query - In this case, I only want to check for "OPEN" instances -

    public String getAllOpenInstances() {
        System.out.println("getAllOpenInstances() ");
        try {
            List displayColumns = new ArrayList();
            displayColumns.add(IColumnConstants.PROCESS_ID_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_COMPOSITEDN_COLUMN);

            displayColumns.add(IColumnConstants.PROCESS_PROCESSNAME_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_STATE_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_TITLE_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_CREATOR_COLUMN);
            displayColumns.add(IColumnConstants.PROCESS_CREATEDDATE_COLUMN);

            IInstanceQueryInput input = new InstanceQueryInput();

            input.setAssignmentFilter(IInstanceQueryInput.AssignmentFilter.ALL);
            Ordering ordering =
                new Ordering(IColumnConstants.PROCESS_NUMBER_COLUMN, true,
                             true);

            Predicate pred =
                new Predicate(IColumnConstants.PROCESS_STATE_COLUMN,
                              Predicate.OP_EQ, "OPEN");


            IInstanceQueryService queryService =
                getBPMServiceClient().getInstanceQueryService();

            IBPMContext bpmContext = getIBPMContext("jcooper", "welcome1");
            System.out.println("getInstances() queryInstances");

            List processInstances =
                queryService.queryInstances(bpmContext, displayColumns, pred,
                                            ordering, input);
            for (IProcessInstance instance : processInstances) {
                System.out.println(instance.getSystemAttributes().getProcessInstanceId() +
                                   "\t" +
                                   instance.getSystemAttributes().getProcessNumber() +
                                   "\t" +
                                   instance.getSystemAttributes().getState() +
                                   "\t" + instance.getTitle() + "\t" +
                                   instance.getCreator() + "\t" +
                                   instance.getSystemAttributes().getCreatedDate().getTime());

            }
        }

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

    }

Further reading
http://docs.oracle.com/cd/E28389_01/apirefs.1111/e25378/oracle/bpm/services/instancequery/IInstanceQueryService.html

https://java.net/projects/oraclebpmsuite11g/downloads/directory/Samples/bpm-api-101-instance-query