Finding which activities will execute next in a process instance

We have had a few queries lately about how to find out what activity (or activities) will be the next to execute in a particular process instance.  It is possible to do this, however you will need to use a couple of undocumented APIs.  That means that they could (and probably will) change in some future release and break your code.  If you understand the risks of using undocumented APIs and are prepared to accept that risk, read on…

The way to do this is to look at two things:

  • The model of the process itself, i.e. what tasks and connections exist in the process model, and
  • The audit trail for the specific process instance the we are interested in.

By comparing these two pieces of information, we can work out where the process instance is currently (by finding all the activities that have started but have not yet ended) and what the next activities are (by following the connections that start with these unfinsihed activities to see where they go).

I am using plurals here because, of course, you can have multiple parallel execution paths in a process, for example when you use an inclusive or complex gateway, or a multi-instance embedded subprocess, or even a non-interupting event subprocess.

Here is the sample code.  You will need to edit this to suit your own environment.


package nextactivity;

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

import oracle.bpel.services.bpm.common.IBPMContext;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;

import oracle.bpm.client.BPMServiceClientFactory;
import oracle.bpm.project.SequenceFlowImpl;
import oracle.bpm.project.model.ProjectObject;
import oracle.bpm.services.client.IBPMServiceClient;
import oracle.bpm.services.instancemanagement.model.IProcessInstance;
import oracle.bpm.services.instancequery.IAuditInstance;
import oracle.bpm.services.instancequery.IInstanceQueryService;
import oracle.bpm.services.internal.processmodel.model.IProcessModelPackage;

public class NextActivity {

    private static BPMServiceClientFactory bpmscf;

    public NextActivity() {
        super();
    }

    public static void main(String[] args) {

        try {

            // check that we have a process instance ID
            if (args.length != 1) {
                System.out.println("You must specify the instance ID");
                System.exit(0);
            }
            String instanceId = args[0];

            // get the BPMServiceClient
            IBPMServiceClient bpmServiceClient =
                getBPMServiceClientFactory().getBPMServiceClient();

            // authenticate to the BPM engine
            IBPMContext bpmContext =
                getBPMServiceClientFactory()
                .getBPMUserAuthenticationService()
                .authenticate("weblogic", "welcome1".toCharArray(), null);

            // get details of the process instance
            IInstanceQueryService instanceQueryService =
                bpmServiceClient.getInstanceQueryService();
            IProcessInstance processInstance =
                instanceQueryService.getProcessInstance(bpmContext,
                                                        instanceId);

            if (processInstance == null) {
                System.out.println("Could not find instance, aborting");
                System.exit(0);
            }

            // get details of the process (not a specific instance of it,
            // but the actual process definition itself)
            // WARNING WARNING WARNING
            // The ProcessModelService is an UNDOCUMENTED API - this means
            // that it could (and probably will) change in some future
            // release - you SHOULD NOT build any code that relies on it,
            // unless you understand and accept the risks of using an
            // undocumented API.
            IProcessModelPackage processModelPackage =
                bpmServiceClient
                .getProcessModelService()
                .getProcessModel(bpmContext,
                                 processInstance.getSca().getCompositeDN(),
                                 processInstance.getSca().getComponentName());

            // get a list of the audit events that have occurred in this instance
            List auditInstances =
                bpmServiceClient
                .getInstanceQueryService()
                .queryAuditInstanceByProcessId(bpmContext, instanceId);

            // work out which activities have not finished
            List started = new ArrayList();
            for (IAuditInstance a1 : auditInstances) {
                if (a1.getAuditInstanceType().compareTo("START") == 0) {
                    // ingore the process instance itself, we only care
                    // about tasks in the process
                    if (a1.getActivityName().compareTo("PROCESS") != 0) {
                        started.add(a1);
                    }
                }
            }
            next:
            for (IAuditInstance a2 : auditInstances) {
                if (a2.getAuditInstanceType().compareTo("END") == 0) {
                    for (int i = 0; i < started.size(); i++) {
                        if (a2.getActivityId()
                              .compareTo(started.get(i).getActivityId()) == 0) {
                            started.remove(i);
                            continue next;
                        }
                    }
                }
            }
            System.out.println("\n\nLooks like the following have started but not ended:");
            for (IAuditInstance s : started) {
                System.out.println(s.getActivityId() + "\nwhich is a "
                                   + s.getActivityName() + "\ncalled "
                                   + s.getLabel() + "\n");
            }

            // now we need to find what is after these activities...
            // WARNING WARNING WARNING
            // The ProcessModel, ProcessObject, etc. are UNDOCUMENTED APIs -
            // this means that they could (and probably will) change
            // in some future release - you SHOULD NOT build any code
            // that relies on them, unless you understand and
            // accept the risks of using undocumented APIs.
            List nextActivities = new ArrayList();
            next2:
            for (ProjectObject po : processModelPackage.getProcessModel().getChildren()) {
                if (po instanceof SequenceFlowImpl) {
                    for (IAuditInstance s2 : started) {
                        if (((SequenceFlowImpl)po).getSource()
                                                  .getId().compareTo(s2.getActivityId()) == 0) {
                            nextActivities.add(po);
                            continue next2;
                        }
                    }
                }
            }
            System.out.println("\n\nLooks like the next activities are:");
            for (ProjectObject po2 : nextActivities) {
                System.out.println(((SequenceFlowImpl)po2).getTarget().getId() 
                                   + "\nwhich is a "
                                   + ((SequenceFlowImpl)po2).getTarget().getBpmnType() 
                                   + "\ncalled "
                                   + ((SequenceFlowImpl)po2).getTarget().getDefaultLabel() 
                                   + "\n");
            }

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

    protected static BPMServiceClientFactory getBPMServiceClientFactory() {

        if (bpmscf == null) {

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

            bpmscf = BPMServiceClientFactory.getInstance(properties, null, null);

        }
        return bpmscf;

    }

}

To run the sample, you will need to put some JAR files on the CLASSPATH.  These may not all be needed, but here are the ones I am using:

Oracle_SOA1\soa\modules\oracle.soa.workflow_11.1.1\bpm-services.jar
Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1\bpm-infra.jar  
Oracle_SOA1\soa\modules\oracle.bpm.client_11.1.1\oracle.bpm.bpm-services.client.jar  
Oracle_SOA1\soa\modules\oracle.bpm.client_11.1.1\oracle.bpm.bpm-services.interface.jar  
oracle_common\webservices\wsclient_extended.jar   
oracle_common\modules\oracle.xdk_11.1.0\xmlparserv2.jar
oracle_common\modules\oracle.xdk_11.1.0\xml.jar
wlserver_10.3\server\lib\wlthint3client.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.model.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.io.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.ui.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.draw.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.diagram.draw.jar
Oracle_SOA1\soa\modules\oracle.bpm.workspace_11.1.1\oracle.bpm.ui.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.compile.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.catalog.jar
Oracle_SOA1\soa\modules\oracle.bpm.project_11.1.1\oracle.bpm.project.jar
Oracle_SOA1\soa\modules\oracle.bpm.runtime_11.1.1\oracle.bpm.core.jar
Oracle_SOA1\soa\modules\oracle.bpm.runtime_11.1.1\oracle.bpm.lib.jar
Oracle_SOA1\soa\modules\oracle.bpm.runtime_11.1.1\oracle.bpm.xml.jar

This will produce output like this:

Looks like the following have started but not ended:
ABSTRACT_ACTIVITY1824320344446
which is a USER_TASK
called ChooseNextUser

Looks like the next activities are:
ABSTRACT_ACTIVITY1824321141176
which is a USER_TASK
called DoSomething

When run on a process like this:

About Mark Nelson

Mark Nelson is an Architect ("IC6") in the Platform Architecture Team in Oracle Development. Mark's focus area is continuous delivery, configuration management and provisioning - making it simple to manage the configuration of complex environments and applications built with Oracle Database, Fusion Middleware and Fusion Applications, on-premise and in the cloud. Before joining the Platform Architecture team, Mark was a senior member of the A-Team since 2010, and worked in Sales Consulting at Oracle since 2006 and various roles at IBM since 1994.
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s