Archiving Audit Diagrams as Images in Oracle SOA Suite BPM Processes

In my previous post I had described how we can create a custom Java Class to save custom business indicators in a formatted PDF file. It might be useful in cases where businesses wants process data be presented in a meaningful way.

Another practical case that i had encountered in the past while designing business processes is to save the Audit diagram of the process once it is complete. The audit instance diagram can be viewed in the EM console by clicking on Flow Trace for any process instance.

In the good all Oracle BPM 10g days (prior to Oracle SOA/BPM Suite 11g) we had a fully documented API’s for interacting with process instances. PAPI interfaces were available both as web service and java API’s to connect to a in-flight or completed instance and retrieve all audit data from it.

Getting an audit image from a business process instance using PAPI was a cake walk. The below code sample shows its ease

ProcessService processService = null;
ProcessServiceSession session = null;
try {
processService = ProcessService.create(configuration);
session = processService.createSession(USERNAME, PASSWORD, HOSTNAME);
for (String processId : session.processesGetIds())
{
 fuego.papi.Process process = session.processGet(processId);
 ProcessDiagram diagram = session.processGetDiagram(processId);
 diagram.setTheme(ProcessDiagram.THEME_COLOR_BPMN);
 diagram.setDrawFlowOnly(true);
 Image image = diagram.getImage();
 File pngImage = new File(createPngFilename("image"));
 image.write(pngImage, ImageExtension.PNG);
}
catch (Exception e)
{
 e.printStackTrace();
}
finally
{
 session.close();
 processService.close();
}

Whoa! Pretty Simple and elegant.

However if we want to achieve something similar in Oracle SOA Suite 11g it is a lot more challenging.

Oracle BPM Suite 11g doesn’t have any published API’s that developers can refer. This makes it an even bigger nightmare.

The process instance data in Oracle SOA suite 11g is stored in the dehydration store in the SOA_INFRA schema. In all practical scenarios this store will be subjected to purging and maintenance. So many a people/project might need to archive the flow trace of a process instance as an image. As we all know how significant is historical data for business process improvements and reengineering.

In this blog post I will show how the same functionality of getting an instance image from a process can be achieved using Oracle SOA Suite 11g and explain the code in steps.

Assuming we are using Oracle SOA Suite 11g PS3 that has a running domain and a BPM process deployed with a couple of running/completed instances.

Create a Generic Java project in JDeveloper say ArchiveInstanceImage. Create a Java class of the same name inside it. Right click on the project and add the following JAR’s to the project’s classpath.

Oracle.bpm.bpm-services.client.jar
Oracle.bpm.bpm-services.interface.jar
Oracle.bpm.client.jar
Bpm-infra.jar
Bpm-services.jar
Oracle.bpm.project.model.jar
Oracle.bpm.project.draw.jar
Oracle.bpm.project.catalog.jar
Wlfullclient.jar
Wsclient_extended.jar
Oracle.bpm.core.jar
Oracle.bpm.lib.jar
Oracle.bpm.papi.jar
Oracle.bpm.xml.jar
Oracle.bpm.diagram.draw.jar
Oracle-bpm.jar
Oracle.bpm.bpm-services.implementation.jar
Oracle.bpm.bpm-services.internal.jar
Oracle.bpm.bpmobject.jar
Oracle.bpm.runtime.jar
Oracle.bpm.ui.jar

All these above JAR’s can be found at the following directories

<JDevHome>\soa\modules\oracle.bpm.client_11.1.1
<JDevHome>\soa\modules\oracle.soa.fabric_11.1.1
<JDevHome>\soa\modules\oracle.soa.workflow_11.1.1
<JDevHome>\soa\modules\oracle.bpm.project_11.1.1
<MiddlewareHome>\wlserver_10.3\server\lib
<MiddlewareHome>\oracle_common\webservices
<JDevHome>\soa\modules\oracle.bpm.runtime_11.1.1
<JDevHome>\soa\modules\oracle.bpm.workspace_11.1.1

You can created Wlfullclient.jar as under

Change directories to the server/lib directory.

cd <MiddlewareHome>wlserver_10.3/server/lib

Use the following command to create wlfullclient.jar in the server/lib directory:

java -jar wljarbuilder.jar

You can now copy and bundle the wlfullclient.jar with client applications.

Add the wlfullclient.jar to the client application’s classpath.

See here for more information

http://download.oracle.com/docs/cd/E12840_01/wls/docs103/client/jarbuilder.html

First and foremost like any remote client we need to get an instance of the SOA server runtime to be able to gain access to any running processes inside it. This is pretty simple. The following lines of code demonstrates how we can use BPMServiceClientFactory class to get an instance of the server runtime.

Next initialize an IBPMContext from BPMServiceClientFactory.

// URL of the SOA Server and PORT on which the application is deployed
private static String soaURL = "t3://localhost:4003";

public static BPMServiceClientFactory getBPMServiceClientFactory()
{
Map<IWorkflowServiceClientConstants.CONNECTION_PROPERTY,String> properties = new HashMap<IWorkflowServiceClientConstants.CONNECTION_PROPERTY,String>();
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,WorkflowServiceClientFactory.REMOTE_CLIENT);
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,soaURL);
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,"weblogic");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,"welcome123");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
return BPMServiceClientFactory.getInstance(properties, null, null);
}

public static IBPMServiceClient getBPMServiceClient(){
return getBPMServiceClientFactory().getBPMServiceClient();
}
public static IBPMContext getIBPMContextForAuthenticatedUser() throws Exception{
return getBPMServiceClientFactory().getBPMUserAuthenticationService().getBPMContextForAuthenticatedUser();
}

We also have to get a handle to the IBPMServiceClient interface. Create a self initializing constructor for our main class to get a hook to this interface.

// Get a handle to the IBPMServiceClient interface in the ArchiveInstanceImage class
public ArchiveInstanceImage(IBPMServiceClient bpmServiceClient)
{
this.bpmServiceClient = bpmServiceClient;
}

Using the IBPMContext and a searchable instance id from the process we can get process data using the IProcessInstance interface.

See the code snippet below that shows how we can retrieve the audit diagram for a process instance

public InputStream getProcessAuditImage(IBPMContext bpmContext, String instanceId)
throws BPMException
{
IInstanceQueryService instanceQueryService = this.bpmServiceClient.getInstanceQueryService();
IProcessInstance processInstance = instanceQueryService.getProcessInstance(bpmContext, instanceId);

if (processInstance == null) {
return null;
}
IProcessModelPackage processModelPackage = this.bpmServiceClient.getProcessModelService().getProcessModel(bpmContext, processInstance.getSca().getCompositeDN(), processInstance.getSca().getComponentName());

AuditProcessDiagrammer auditProcessImage  = new AuditProcessDiagrammer(processModelPackage.getProcessModel());
return getProcessImage(auditProcessImage);
}

private InputStream getProcessImage(AuditProcessDiagrammer processImage)
throws BPMException
{
InputStream processImageStream  = null;
ByteArrayOutputStream auditImageOutputStream = new ByteArrayOutputStream();
try {
// Get base64 encoded image String from processImage
String base64Image = processImage.getImage();
Image image = Image.createFromBase64(base64Image);
BufferedImage bufferedImage = (BufferedImage)image.asAwtImage();
// Use the Image Extension that suites you from .PNG, .JPG and .GIF
ImageIOFacade.writeImage(bufferedImage, ImageExtension.PNG, auditImageOutputStream);
processImageStream  = new ByteArrayInputStream(auditImageOutputStream.toByteArray());
// Archives the Process Image at any suitable location
archiveDiagramToFile(processImageStream);
}
catch (Exception e)
{
throw new BPMException(e);
}
finally {
}
return processImageStream;
}

// Utility method to Archive the InputStream into a PNG File
private void archiveDiagramToFile(InputStream istream) throws IOException {
File outputFile = new File("C:\\Arrun\\ProcessAuditImage\\ProcessImage.png");
OutputStream out = new FileOutputStream(outputFile);

// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = istream.read(buf)) > 0) {
out.write(buf, 0, len);
}
istream.close();
out.close();
}

Finally to test the code that we had written add a main method to invoke getProcessAuditImage(iBPMContext, instanceId) to see the process audit image created.

public static void main (String args[]) throws BPMException, Exception
{
ArchiveInstanceImage instImage = new ArchiveInstanceImage(getBPMServiceClient());
// Use any instanceId that is existing for the process in the server
InputStream istream= instImage.getProcessAuditImage(getIBPMContextForAuthenticatedUser(),"840001");
}

Running the standalone Java program from inside JDeveloper creates the following process image in the target archive directory.

image

A quick look at the image and we will realize what it is lacking. Off course we have been quite able to get the image for the process (similar to what we used to get using PAPI in OBPM 10g). However we don’t see the flow trace i.e the sequence of activities that the instance traversed in its flow.

To get that we have get a List of DiagramEvent and highlight the process image by passing this list to it.

Create another private function to get a list of all Events that the instance encountered in its flow as below.

private List<DiagramEvent> getHighlightEvents(Process processModel, IAuditInstance auditInstance)
{
List events = new ArrayList();

String activityId = auditInstance.getActivityId();
Date eventDate = auditInstance.getCreateTime().getTime();
DiagramEvent nodeEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.FLOW_NODE_IN, activityId, eventDate);

events.add(nodeEvent);
String sourceActivity;
String targetActivity;
if (auditInstance.getAuditInstanceType().equalsIgnoreCase("START")) {
FlowNode flowNode = (FlowNode)processModel.findDescendant(FlowNode.class, auditInstance.getActivityId());

if (flowNode != null) {
sourceActivity = auditInstance.getSourceActivity();
Sequence<SequenceFlow> incommingSequenceFlows = flowNode.getIncomingSequenceFlows();
if ((incommingSequenceFlows != null) && (!incommingSequenceFlows.isEmpty()) && (sourceActivity != null)) {
Iterator<SequenceFlow> seqIterator  = incommingSequenceFlows.iterator();
while(seqIterator.hasNext())
{
SequenceFlow sequenceFlow= seqIterator.next();
if (sequenceFlow.getSource().getId().equalsIgnoreCase(sourceActivity)) {
DiagramEvent sequenceEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.SEQUENCE_FLOW, sequenceFlow.getId(), eventDate);
events.add(sequenceEvent);
}}
}}
}
else if (auditInstance.getAuditInstanceType().equalsIgnoreCase("END")) {
FlowNode flowNode = (FlowNode)processModel.findDescendant(FlowNode.class, auditInstance.getActivityId());

if (flowNode != null) {
targetActivity = auditInstance.getTargetActivity();
Sequence<SequenceFlow> outgoingSequenceFlows = flowNode.getOutgoingSequenceFlows();

if ((outgoingSequenceFlows != null) && (!outgoingSequenceFlows.isEmpty()) &&
(targetActivity != null)) {
Iterator<SequenceFlow> seqIterator  = outgoingSequenceFlows.iterator();
while(seqIterator.hasNext())
{
SequenceFlow sequenceFlow= seqIterator.next();
if (sequenceFlow.getTarget().getId().equalsIgnoreCase(targetActivity))
{
DiagramEvent sequenceEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.SEQUENCE_FLOW, sequenceFlow.getId(), eventDate);
events.add(sequenceEvent);
}}
}}
}

Add these following line to the getProcessAuditImage(IBPMContext bpmContext, String instanceId) method before the return statement to highlight the image

List auditInstances = this.bpmServiceClient.getInstanceQueryService().queryAuditInstanceByProcessId(bpmContext, instanceId);
List diagramEvents = new ArrayList();

for (int IAuditInstance=0; IAuditInstance< auditInstances.size(); IAuditInstance++)
{
diagramEvents.addAll(getHighlightEvents(process, (IAuditInstance)auditInstances.get(IAuditInstance)));
}
auditProcessImage.highlight(diagramEvents);

Run the Java program once again and view the image created this time.

image

This time you can see that the activities and transitions that the instance took are highlighted in the image.

The complete Java Class can be found below

package blog.beatechnologies.soasuiteutil;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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

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

import oracle.bpm.client.BPMServiceClientFactory;
import oracle.bpm.collections.Sequence;
import oracle.bpm.draw.diagram.AuditProcessDiagrammer;
import oracle.bpm.draw.diagram.DiagramEvent;
import oracle.bpm.project.model.processes.FlowNode;
import oracle.bpm.project.model.processes.Process;
import oracle.bpm.project.model.processes.SequenceFlow;
import oracle.bpm.services.client.IBPMServiceClient;
import oracle.bpm.services.common.exception.BPMException;
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;
import oracle.bpm.ui.Image;
import oracle.bpm.ui.utils.ImageExtension;
import oracle.bpm.ui.utils.ImageIOFacade;

public class ArchiveInstanceImage
{
private IBPMServiceClient bpmServiceClient;
// URL of the SOA Server and PORT on which the application is deployed
private static String soaURL = "t3://soasitapp03.us.dell.com:5411";

public ArchiveInstanceImage(IBPMServiceClient bpmServiceClient)
{
this.bpmServiceClient = bpmServiceClient;
}

public InputStream getProcessAuditImage(IBPMContext bpmContext, String instanceId)
throws BPMException
{
IInstanceQueryService instanceQueryService = this.bpmServiceClient.getInstanceQueryService();
IProcessInstance processInstance = instanceQueryService.getProcessInstance(bpmContext, instanceId);

if (processInstance == null) {
return null;
}
IProcessModelPackage processModelPackage = this.bpmServiceClient.getProcessModelService().getProcessModel(bpmContext, processInstance.getSca().getCompositeDN(), processInstance.getSca().getComponentName());

AuditProcessDiagrammer auditProcessImage  = new AuditProcessDiagrammer(processModelPackage.getProcessModel());
List auditInstances = this.bpmServiceClient.getInstanceQueryService().queryAuditInstanceByProcessId(bpmContext, instanceId);

List diagramEvents = new ArrayList();

for (int IAuditInstance=0; IAuditInstance< auditInstances.size(); IAuditInstance++)
{
diagramEvents.addAll(getHighlightEvents(processModelPackage.getProcessModel(), (IAuditInstance)auditInstances.get(IAuditInstance)));
}
auditProcessImage.highlight(diagramEvents);
return getProcessImage(auditProcessImage);
}

private InputStream getProcessImage(AuditProcessDiagrammer processImage)
throws BPMException
{
InputStream processImageStream  = null;
ByteArrayOutputStream auditImageOutputStream = new ByteArrayOutputStream();
try {
// Get base64 encoded image String from processImage
String base64Image = processImage.getImage();
Image image = Image.createFromBase64(base64Image);
BufferedImage bufferedImage = (BufferedImage)image.asAwtImage();
// Use the Image Extension that suites you from .PNG, .JPG and .GIF
ImageIOFacade.writeImage(bufferedImage, ImageExtension.PNG, auditImageOutputStream);
processImageStream  = new ByteArrayInputStream(auditImageOutputStream.toByteArray());
// Archives the Process Image at any suitable location
archiveDiagramToFile(processImageStream);
}
catch (Exception e)
{
throw new BPMException(e);
}
finally {
}
return processImageStream;
}

private List<DiagramEvent> getHighlightEvents(Process processModel, IAuditInstance auditInstance)
{
List events = new ArrayList();

String activityId = auditInstance.getActivityId();
Date eventDate = auditInstance.getCreateTime().getTime();
DiagramEvent nodeEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.FLOW_NODE_IN, activityId, eventDate);

events.add(nodeEvent);
String sourceActivity;
String targetActivity;
if (auditInstance.getAuditInstanceType().equalsIgnoreCase("START")) {
FlowNode flowNode = (FlowNode)processModel.findDescendant(FlowNode.class, auditInstance.getActivityId());

if (flowNode != null) {
sourceActivity = auditInstance.getSourceActivity();
Sequence<SequenceFlow> incommingSequenceFlows = flowNode.getIncomingSequenceFlows();

if ((incommingSequenceFlows != null) && (!incommingSequenceFlows.isEmpty()) && (sourceActivity != null)) {
Iterator<SequenceFlow> seqIterator  = incommingSequenceFlows.iterator();

while(seqIterator.hasNext())
{
SequenceFlow sequenceFlow= seqIterator.next();
if (sequenceFlow.getSource().getId().equalsIgnoreCase(sourceActivity)) {
DiagramEvent sequenceEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.SEQUENCE_FLOW, sequenceFlow.getId(), eventDate);
events.add(sequenceEvent);
}}
}}
}
else if (auditInstance.getAuditInstanceType().equalsIgnoreCase("END")) {
FlowNode flowNode = (FlowNode)processModel.findDescendant(FlowNode.class, auditInstance.getActivityId());

if (flowNode != null) {
targetActivity = auditInstance.getTargetActivity();
Sequence<SequenceFlow> outgoingSequenceFlows = flowNode.getOutgoingSequenceFlows();

if ((outgoingSequenceFlows != null) && (!outgoingSequenceFlows.isEmpty()) && (targetActivity != null)) {
Iterator<SequenceFlow> seqIterator  = outgoingSequenceFlows.iterator();
while(seqIterator.hasNext())
{
SequenceFlow sequenceFlow= seqIterator.next();
if (sequenceFlow.getTarget().getId().equalsIgnoreCase(targetActivity)) {
DiagramEvent sequenceEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.SEQUENCE_FLOW, sequenceFlow.getId(), eventDate);
events.add(sequenceEvent);
}}
}}
}
return events;
}
public static BPMServiceClientFactory getBPMServiceClientFactory() {
Map<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, String> properties = new HashMap<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, String>();
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,WorkflowServiceClientFactory.REMOTE_CLIENT);
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,soaURL);
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,"arun_pareek");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,"#Jannu12");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
return BPMServiceClientFactory.getInstance(properties, null, null);
}
public static IBPMServiceClient getBPMServiceClient(){
return getBPMServiceClientFactory().getBPMServiceClient();
}
public static IBPMContext getIBPMContextForAuthenticatedUser() throws Exception{
return getBPMServiceClientFactory().getBPMUserAuthenticationService().getBPMContextForAuthenticatedUser();
}

public static void main (String args[]) throws BPMException, Exception
{
ArchiveInstanceImage instImage = new ArchiveInstanceImage(getBPMServiceClient());
InputStream istream= instImage.getProcessAuditImage(getIBPMContextForAuthenticatedUser(),"10002");
}
void archiveDiagramToFile(InputStream istream) throws IOException {
File outputFile = new File("C:\\Arrun\\process.png");
OutputStream out = new FileOutputStream(outputFile);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = istream.read(buf)) > 0) {
out.write(buf, 0, len);
}
istream.close();
out.close();
}
}

This was all about getting this Java API’s to work with SOA Suite 11g PS3. I also tried to test the same with a  PS2 domain and with PS2 libraries.

Everything was almost same except for a few things.

There is no class called IProcessModelPackage that I could find in PS2 BPM jar’s

So the process image was obtained using IProcessModelService class like below

IProcessModelService processModelService =  this.bpmServiceClient.getProcessModelService();
Process process = processModelService.getProcessModel(bpmContext, processInstance.getSca().getCompositeDN(), processInstance.getSca().getComponentName());
AuditProcessDiagrammer auditProcessImage = new AuditProcessDiagrammer(process);

The additional libraries that we need to put in the project’s classspath are

Oracle.bpm.project.io.jar                    <JDevHome>\soa\modules\oracle.bpm.project_11.1.1
Oracle.bpm.project.jar                         <JDevHome>\soa\modules\oracle.bpm.project_11.1.1
Oracle.bpm.project.compile.jar        <JDevHome>\soa\modules\oracle.bpm.project_11.1.1
Oracle.bpm.vfilesystem.jar                 <JDevHome>\soa\modules\oracle.bpm.runtime_11.1.1

In case you would need the entire class here is what you should use in case you are on Oracle SOA Suite 11g PS2

package blog.beatechnologies.soautil;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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

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

import oracle.bpm.client.BPMServiceClientFactory;
import oracle.bpm.collections.Sequence;
import oracle.bpm.draw.diagram.AuditProcessDiagrammer;
import oracle.bpm.draw.diagram.DiagramEvent;
import oracle.bpm.project.model.processes.FlowNode;
import oracle.bpm.project.model.processes.Process;
import oracle.bpm.project.model.processes.SequenceFlow;
import oracle.bpm.services.client.IBPMServiceClient;
import oracle.bpm.services.common.exception.BPMException;
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.IProcessModelService;
import oracle.bpm.services.util.AuditTrail;
import oracle.bpm.ui.Image;
import oracle.bpm.ui.utils.ImageExtension;
import oracle.bpm.ui.utils.ImageIOFacade;

public class ArchiveInstanceImage
{
private IBPMServiceClient bpmServiceClient;
// URL of the SOA Server and PORT on which the application is deployed
private static String soaURL = "t3://wxp-fxhp7bs.blr.amer.dell.com:4003";

// Get a handle of IBPMServiceClient through the class constructor
public ArchiveInstanceImage(IBPMServiceClient bpmServiceClient)
{
this.bpmServiceClient = bpmServiceClient;
}

public InputStream getProcessAuditImage(IBPMContext bpmContext, String instanceId)
throws BPMException
{
IInstanceQueryService instanceQueryService = this.bpmServiceClient.getInstanceQueryService();
IProcessInstance processInstance = instanceQueryService.getProcessInstance(bpmContext, instanceId);

System.out.println("Composite DN " + processInstance.getSca().getCompositeDN());
System.out.println("Composite DN " + processInstance.getSca().getComponentName());

if (processInstance == null) {
return null;
}

IProcessModelService processModelService =  this.bpmServiceClient.getProcessModelService();
Process process = processModelService.getProcessModel(bpmContext, processInstance.getSca().getCompositeDN(), processInstance.getSca().getComponentName());
AuditProcessDiagrammer auditProcessImage = new AuditProcessDiagrammer(process);
List auditInstances = this.bpmServiceClient.getInstanceQueryService().queryAuditInstanceByProcessId(bpmContext, instanceId);

List diagramEvents = new ArrayList();

for (int IAuditInstance=0; IAuditInstance< auditInstances.size(); IAuditInstance++)
{
diagramEvents.addAll(getHighlightEvents(process, (IAuditInstance)auditInstances.get(IAuditInstance)));
}
auditProcessImage.highlight(diagramEvents);
//auditProcessImage.getImage()
return getProcessImage(auditProcessImage);
}

private InputStream getProcessImage(AuditProcessDiagrammer processImage)
throws BPMException
{
InputStream processImageStream = null;
ByteArrayOutputStream auditImageOutputStream = new ByteArrayOutputStream();
try {
// Get base64 encoded image String from processImage
String base64Image = processImage.getImage();
Image image = Image.createFromBase64(base64Image);
BufferedImage bufferedImage = (BufferedImage)image.asAwtImage();
// Use the Image Extension that suites you from .PNG, .JPG and .GIF
ImageIOFacade.writeImage(bufferedImage, ImageExtension.PNG, auditImageOutputStream);
processImageStream = new ByteArrayInputStream(auditImageOutputStream.toByteArray());
// Archives the Process Image at any suitable location
archiveDiagramToFile(processImageStream);
}
catch (Exception e)
{
throw new BPMException(e);
}
finally {
}
return processImageStream;
}

private List<DiagramEvent> getHighlightEvents(Process processModel, IAuditInstance auditInstance)
{
List events = new ArrayList();
String activityId = auditInstance.getActivityId();
Date eventDate = auditInstance.getCreateTime().getTime();
DiagramEvent nodeEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.FLOW_NODE_IN, activityId, eventDate);
events.add(nodeEvent);
String sourceActivity;
String targetActivity;
if (auditInstance.getAuditInstanceType().equalsIgnoreCase("START")) {
FlowNode flowNode = (FlowNode)processModel.findDescendant(FlowNode.class, auditInstance.getActivityId());

if (flowNode != null) {
sourceActivity = auditInstance.getSourceActivity();
Sequence<SequenceFlow> incommingSequenceFlows = flowNode.getIncomingSequenceFlows();

if ((incommingSequenceFlows != null) && (!incommingSequenceFlows.isEmpty()) && (sourceActivity != null)) {
Iterator<SequenceFlow> seqIterator  = incommingSequenceFlows.iterator();
while(seqIterator.hasNext())
{
SequenceFlow sequenceFlow= seqIterator.next();
if (sequenceFlow.getSource().getId().equalsIgnoreCase(sourceActivity)) {
DiagramEvent sequenceEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.SEQUENCE_FLOW, sequenceFlow.getId(), eventDate);
events.add(sequenceEvent);
}}
}}
}
else if (auditInstance.getAuditInstanceType().equalsIgnoreCase("END")) {
FlowNode flowNode = (FlowNode)processModel.findDescendant(FlowNode.class, auditInstance.getActivityId());

if (flowNode != null) {
targetActivity = auditInstance.getTargetActivity();
Sequence<SequenceFlow> outgoingSequenceFlows = flowNode.getOutgoingSequenceFlows();

if ((outgoingSequenceFlows != null) && (!outgoingSequenceFlows.isEmpty()) &&
(targetActivity != null)) {
Iterator<SequenceFlow> seqIterator  = outgoingSequenceFlows.iterator();
while(seqIterator.hasNext())
{
SequenceFlow sequenceFlow= seqIterator.next();
if (sequenceFlow.getTarget().getId().equalsIgnoreCase(targetActivity))
{
DiagramEvent sequenceEvent = DiagramEvent.create(DiagramEvent.DiagramEventType.SEQUENCE_FLOW, sequenceFlow.getId(), eventDate);
events.add(sequenceEvent);
}}
}}
}
return events;
}
public static BPMServiceClientFactory getBPMServiceClientFactory() {
Map<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, String> properties = new HashMap<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, String>();
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,WorkflowServiceClientFactory.REMOTE_CLIENT);
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,soaURL);
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,"weblogic");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,"welcome123");
properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
return BPMServiceClientFactory.getInstance(properties, null, null);
}
public static IBPMServiceClient getBPMServiceClient(){
return getBPMServiceClientFactory().getBPMServiceClient();
}
public static IBPMContext getIBPMContextForAuthenticatedUser() throws Exception{
return getBPMServiceClientFactory().getBPMUserAuthenticationService().getBPMContextForAuthenticatedUser();
}
public static void main (String args[]) throws BPMException, Exception {

ArchiveInstanceImage instImage = new ArchiveInstanceImage(getBPMServiceClient());
InputStream istream= instImage.getProcessAuditImage(getIBPMContextForAuthenticatedUser(),"840001");
}
// Utility method to Archive the InputStream into a PNG File
private void archiveDiagramToFile(InputStream istream) throws IOException {
File outputFile = new File("C:\\Arrun\\ProcessAuditImage\\ProcessImage.png");
OutputStream out = new FileOutputStream(outputFile);

// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = istream.read(buf)) > 0) {
out.write(buf, 0, len);
}
istream.close();
out.close();
}
}

Now there are a couple of ways to use this Java Code in real practical cases

  1. Use it from any Custom UI/ Workflow UI to access the process image if need be.
  2. Create a Web service wrapper over the Java Class and use it as a WS API that can be invoked from any BPM process and hence it can be reused across multiple BPM processes.
  3. In case you would want to limit the use inside a composite then create a Spring SCA component for this custom Java class and then invoke it from inside a BPM process

In my future blogs I will try to come up with more interesting utilities with these BPM API’s. This will probably be of great use to folks who have worked with the PAPI API’s and find it a great miss in Oracle SOA Suite 11g.

Meanwhile also wondering what is stopping Oracle to publish a well document API for these interfaces. 🙂

.

10 thoughts on “Archiving Audit Diagrams as Images in Oracle SOA Suite BPM Processes

      • Hello guys, how could I get an IBPMServiceClient reference within an ADF backing bean (for a LOCAL_CLIENT, using user’s context and not specifying user and password).

        I’m actually getting a reference to user’s context like this:

        FacesContext context = FacesContext.getCurrentInstance();
        String ctx = (String)context.getApplication().evaluateExpressionGet(context, “#{pageFlowScope.bpmWorklistContext}”, String.class);
        String tskId = (String)context.getApplication().evaluateExpressionGet(context, “#{pageFlowScope.bpmWorklistTaskId}”, String.class);
        IWorkflowServiceClient workflowSvcClient = WorkflowService.getWorkflowServiceClient();
        ITaskQueryService wfQueryService = workflowSvcClient.getTaskQueryService();
        ITaskService taskSvc = workflowSvcClient.getTaskService();
        IWorkflowContext wfContext = wfQueryService.getWorkflowContext(ctx);

        How could I follow on and get a IBPMServiceClient reference?

        Thanks in advance, best regards,

        Like

  1. Hello guys, how could I get an IBPMServiceClient reference within an ADF backing bean (for a LOCAL_CLIENT, using user’s context and not specifying user and password).
    I’m actually getting a reference to user’s context like this:

    FacesContext context = FacesContext.getCurrentInstance();
    String ctx = (String)context.getApplication().evaluateExpressionGet(context, “#{pageFlowScope.bpmWorklistContext}”, String.class);
    String tskId = (String)context.getApplication().evaluateExpressionGet(context, “#{pageFlowScope.bpmWorklistTaskId}”, String.class);
    IWorkflowServiceClient workflowSvcClient = WorkflowService.getWorkflowServiceClient();
    ITaskQueryService wfQueryService = workflowSvcClient.getTaskQueryService();
    ITaskService taskSvc = workflowSvcClient.getTaskService();
    IWorkflowContext wfContext = wfQueryService.getWorkflowContext(ctx);

    How could I follow on and get an IBPMServiceClient reference?
    Thanks in advance, best regards,

    Like

          • Use this

            public static BPMServiceClientFactory getBPMServiceClientFactory() {
            Map properties = new HashMap();
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,WorkflowServiceClientFactory.REMOTE_CLIENT);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,soaURL);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,”userName”);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,”password”);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY, “weblogic.jndi.WLInitialContextFactory”);
            return BPMServiceClientFactory.getInstance(properties, null, null);
            }

            public static IBPMServiceClient getBPMServiceClient(){
            return getBPMServiceClientFactory().getBPMServiceClient();
            }

            public static IBPMContext getIBPMContextForAuthenticatedUser() throws Exception{
            return getBPMServiceClientFactory().getBPMUserAuthenticationService().getBPMContextForAuthenticatedUser();
            }

            private IBPMServiceClient bpmServiceClient;

            // URL of the SOA Server and PORT on which the application is deployed
            private static String soaURL = “t3://host:managedServerPort”;

            public ArchiveInstanceImage(IBPMServiceClient bpmServiceClient)
            {
            this.bpmServiceClient = bpmServiceClient;
            }

            and then

            IInstanceQueryService instanceQueryService = this.bpmServiceClient.getInstanceQueryService();

            Like

  2. Arun,
    This is a great post!

    Just wanted to let you know, to get the code working with the 11.1.1.5 PS4, I needed to make the following changes:

    When I included all the libraries you mentioned in the classpath of my project, I was getting error 87, which is classpath being too long. So, I had to remove everything you referenced from classpath and add them one by one. I finally arrived at the finite list. The attached image gives you a list of minimal set of require libraries.

    And I needed to make one code change:
    The IProcessModelService did not have the method that returns the Process object directly, in the newer version, its returning Impl, which had a method called getProcessModel.

    Process process = processModelService.getProcessModel(bpmContext, processInstance.getSca().getCompositeDN(), processInstance.getSca().getComponentName());

    To

    Process process = processModelService.getProcessModel(bpmContext, processInstance.getSca().getCompositeDN(), processInstance.getSca().getComponentName()).getProcessModel();

    Like

  3. Pingback: How to generate audit process image file on Oracle BPM 12c | Troubadour

If you have any comments, suggestions or feedback about the post, please feel free to type it here and I will do my best to address them asap

Fill in your details below or click an icon to log in:

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