Wednesday 28 February 2018

Generate BPM process audit through API




package com.poc.bpm.task;

import com.poc.WfAudit;
import com.poc.ProcessUtils;
import java.io.IOException;
import oracle.bpm.services.instancequery.IAuditConstants;
import oracle.bpm.collections.Sequence;
import java.util.ArrayList;
import java.util.List;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import oracle.bpel.services.bpm.common.IBPMContext;
import oracle.bpel.services.workflow.IWorkflowConstants;
import oracle.bpel.services.workflow.WorkflowException;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.repos.Ordering;
import oracle.bpel.services.workflow.repos.Predicate;
import oracle.bpel.services.workflow.repos.TableConstants;
import oracle.bpel.services.workflow.task.ITaskAssignee;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.impl.TaskAssignee;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpm.project.model.processes.Activity;
import oracle.bpm.project.model.processes.SendTask;
import oracle.bpm.project.model.processes.conversation.Conversation;
import oracle.bpm.project.model.processes.conversation.ProcessCallConversationDefinition;
import oracle.bpm.services.instancemanagement.model.IProcessInstance;
import oracle.bpm.services.instancequery.IAuditInstance;
import oracle.bpm.services.instancequery.IColumnConstants;
import oracle.bpm.services.instancequery.IInstanceQueryInput;
import oracle.bpm.services.instancequery.IInstanceQueryService;
import oracle.bpm.services.instancequery.impl.InstanceQueryInput;
import oracle.bpm.services.internal.processmodel.model.IProcessModelPackage;
import oracle.bpm.services.processmetadata.ProcessMetadataSummary;
import oracle.bpel.services.workflow.repos.Column;
import oracle.bpm.services.common.exception.BPMException;
public class AuditServiceClient {
   
    private static final Logger LOGGER = Logger.getLogger("oracle.soa.Logger");
    private ProcessUtils _processUtils;
  
    private static final String AUDIT = "AUDIT";
    private static final String COMPLETE = "COMPLETE";
    private static final String TASKNUMBER = "TASKNUMBER";
    private static final String TITLE = "TITLE";
    private static final String PRIORITY = "PRIORITY";
    private static final String STATE = "STATE";
    private static final String ENDDATE = "ENDDATE";
   
    IBPMContext ctx = null;
    public AuditServiceClient(String adminUser, String adminPassword, String host, String port) { //
        try {
            _processUtils = new ProcessUtils(ProcessUtils.getServiceClientFactory(host, port));
            ctx =
                (IBPMContext) _processUtils.getWorkflowServiceClient().getTaskQueryService().authenticate(adminUser,
                                                                                      adminPassword.toCharArray(),
                                                                                      null);
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "\r\ne: " + e.getMessage());
            e.printStackTrace();
        }
    }
   
    public static void main(String[] argv) {
        AuditServiceClient c = new AuditServiceClient("weblogic", "password1$", "localhost", "7001");
        long startTm = System.currentTimeMillis();
        List<WfAudit> auditTrail = c.getAuditEntries("2d520f16-0665-4d8a-b9c3-4fe0f71595b6-0001930e");
        long endTm = System.currentTimeMillis();
        System.out.println(endTm-startTm);
       
        for(WfAudit a : auditTrail){
            System.out.println(a.getActivity()+"::"+a.getAction()+"::"+a.getParticipant()+"::"+a.getDt());
        }
        //List<WfAudit> auditTrail =  c.prepareInstanceAudit("140010");//30002, ,140010
    }
   
    public  List<WfAudit> getAuditEntries(String workItemId) {
        List<WfAudit> auditTrail = null;
        try{
            String instanceId = this.getInstanceId(workItemId);
            auditTrail =  this.prepareInstanceAudit(instanceId);
            auditTrail.sort(Comparator.comparing(o -> o.getDt()));
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return auditTrail;
    }
   
   
    private String getInstanceId(String ecid) throws Exception {
       
        List<Column> columns = new ArrayList<Column>(); 
        columns.add(IColumnConstants.PROCESS_ID_COLUMN); 
        columns.add(IColumnConstants.PROCESS_ECID_COLUMN);
        columns.add(IColumnConstants.PROCESS_INSTANCEID_COLUMN);
   
        Ordering ordering = new Ordering(IColumnConstants.PROCESS_INSTANCEID_COLUMN, true, true); 
        Predicate pred = new Predicate(IColumnConstants.PROCESS_ECID_COLUMN, Predicate.OP_EQ, ecid);
       
        IInstanceQueryInput input = new InstanceQueryInput(); 
        input.setAssignmentFilter(IInstanceQueryInput.AssignmentFilter.ALL); 
       
        List<IProcessInstance> instances =
            _processUtils.getBPMServiceClient().getInstanceQueryService()
                .queryProcessInstances(ctx, columns, pred,ordering, input);
       
        if(!instances.isEmpty())
            return instances.get(0).getSystemAttributes().getProcessInstanceId();
        return null;
    }
    private List<WfAudit> prepareInstanceAudit(String instanceId) {
        List<WfAudit> auditTrail = new ArrayList<>();
        try {
            List<IAuditInstance> auditInstances =
                _processUtils.getBPMServiceClient().getInstanceQueryService().queryAuditInstanceByProcessId(ctx,
                                                                                                            instanceId);
            for (IAuditInstance audit : auditInstances) {

                if("USER_TASK".equals(audit.getActivityName())){
                    WfAudit a = new WfAudit();
                    a.setActivity(audit.getLabel());
                    a.setDt(audit.getCreateTime().getTime());
                    a.setAction("START".equals(audit.getAuditInstanceType())?"ASSIGNED":"COMPLETED");
                    a.setParticipant(audit.getParticipant());
                    auditTrail.add(a);
                }
                if (audit.getActivityName().equals("SEND_TASK") && audit.getAuditInstanceType().equals("START")) {
                    String invokedProcessName = getInvokedProcess(audit.getActivityId(),audit.getProcessName());
                    searchInstance(invokedProcessName,audit.getFlowId(), auditTrail);
                }
            }
        } catch (Exception we) {
            we.printStackTrace();
        }
       
        return auditTrail;
    }
   
    private String getInvokedProcess(String activityId, String processName) throws Exception {
        ProcessMetadataSummary metadata =
            _processUtils.getMetadataForProcess(ctx, processName, null); //TODO: "1.2"
        IProcessModelPackage p = _processUtils.getBPMServiceClient().getProcessModelService().getProcessModel(ctx, metadata.getCompositeDN(), processName);
           
        Iterator<Activity> it = p.getProcessModel().getActivities().iterator();
        Sequence<Conversation> conversations = p.getProcessModel().getConversations();
       
        while(it.hasNext()){
            Activity act = it.next();
            if(act.getId().equals(activityId)){
                SendTask sendTask = act.as(SendTask.class);
                for(Conversation conv : conversations){
                    if(conv.getId().equals(sendTask.getConversation().getId())){
                        ProcessCallConversationDefinition pcallConvDef  = (ProcessCallConversationDefinition)conv.getConversationDefinition();
                        return pcallConvDef.getProcessRef().getName();
                    }
                }
            }
        }
        return null;
    }
    private void searchInstance(String processName,long flowId, List<WfAudit> auditTrail) throws Exception {
        String expression = IAuditConstants.COLUMN_COMPONENTNAME + " like '%"+processName +"%'";// and "+IAuditConstants.COLUMN_QUERYID +" = "+flowId;
           
        List<IAuditInstance> auditInstances =
            _processUtils.getBPMServiceClient().getInstanceQueryService().queryAuditInstances(ctx, expression);
        for (IAuditInstance audit : auditInstances) {
            if (audit.getFlowId()==flowId){
                if("USER_TASK".equals(audit.getActivityName())){
                  
                    WfAudit a = new WfAudit();
                    a.setActivity(audit.getLabel());
                    a.setDt(audit.getCreateTime().getTime());
                    a.setAction("START".equals(audit.getAuditInstanceType())?"ASSIGNED":"COMPLETED");
                    a.setParticipant(audit.getParticipant());
                    auditTrail.add(a);
                }
                else if ("SEND_TASK".equals(audit.getActivityName()) && "START".equals(audit.getAuditInstanceType())){
                    String invokedProcessName = getInvokedProcess(audit.getActivityId(),audit.getProcessName());
                    searchInstance(invokedProcessName,audit.getFlowId(), auditTrail);
                }
            }
        }
    }
}

Using Credential Store to store the UserId/password

While using Oracle BPM APIs in a project, quite often most of us come access the requirement of storing the BPM Administrator user's credential to be used to create the BPMContext. One of most commonly used technique is to store the username and password in a properties file. For security reasons, the password is encrypted before storing in the properties file. This addresses the purpose in most of the cases.

In some of my recent engagements I faced challenge with this approach as SOA Infrastructure Team (Managed by Client) said that they would not handover the BPM Administrator user credential to the Application Team to store it at the application side. They also often change the password of the BPM Administrator user, which would require higher co-ordination in case it is stored at the application side ( may be in a properties file).

The solution for this scenario was using WebLogic Credential Store.  We created a credential map through Enterprise Manager, where SOA Infrastructure Team is going to key in the BPM Administrator user name and password. As and when they change the password of the user, it is upto them to login to EM and update the password in the Credential Store. We will programmatically access the username and password from this Credential Store.

Step 1 - Create Credential Store

a. Log on to Enterprise Manager.
b. Go to Weblogic Domain>>Security>>Credentials



c. Create a map and a key within the map –

Remember the map and the key name. In the code we will refer to this map and key to retrieve the username and password.

Step 2 - Edit system-jazn-data.xml file
Open <MW_HOME>\user_projects\domains\<DOMAIN NAME>\config\fmwconfig\system-jazn-data.xml
Scroll to the end of file, add the following block within  <jazn-policy></jazn-policy>
<grant>
                <permissions>
                    <permission>
                        <class>oracle.security.jps.service.credstore.CredentialAccessPermission</class>
                       <name>context=SYSTEM,mapName={mapName},keyName={keyname}</name>
                        <actions>read</actions>
                    </permission>
                </permissions>
                <permission-set-refs>
                </permission-set-refs>
            </grant>

Replace { mapName } and {keyname} with the one that you have configured in step 2


Step 3 - Set BpelcClasspath property

a. Login to Enterprise Manager
b. Goto SOA Infrastructure > SOA Administrator > BPMN properties


c. Goto “More BPMN Configuration Properties...”


d. set the BpelcClasspath property value to <MW_HOME>/oracle_common/modules/oracle.jps/jps-manifest.jar

Step 4 - Edit WebLogic.policy file

a. Open /wlserver/server/lib/weblogic.policy
b. Add the following lines in the file; remember to replace the Middleware Home and Domain name. c. Do this change for weblogic.policy in all the servers/machines of the cluster

grant codeBase "file:/user_projects/domains//servers/-" { permission oracle.security.jps.service.credstore.CredentialAccessPermission "context=SYSTEM,mapName={mapName},keyName={keyName}", "read"; };


Step 5 - Restart the server ( admin and managed servers)
Step 6 - java code to retrieve the credential

Create a java project and add “BC4J Securities library” in the class path.
Create a class CredentialStorePoc as follows -


package com.poc.bpm;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import oracle.security.jps.JpsException;
import oracle.security.jps.service.JpsServiceLocator;
import oracle.security.jps.service.ServiceLocator;
import oracle.security.jps.service.credstore.PasswordCredential;
import oracle.security.jps.service.credstore.CredentialStore;


public class CredentialStorePoc {

    public CredentialStorePoc() {
        super();
    }

    private static PasswordCredential _getCredentials(String map, String key) throws JpsException {
        ServiceLocator locator = JpsServiceLocator.getServiceLocator();
        CredentialStore credentialStore = JpsServiceLocator.getServiceLocator().lookup(CredentialStore.class);
        return (PasswordCredential) credentialStore.getCredential(map, key);
    }

    public static String getCredentials(final String map, final String key) {
        PasswordCredential credentials;
        PrivilegedExceptionAction<PasswordCredential> action = new
                PrivilegedExceptionAction<PasswordCredential>() {
            public PasswordCredential run() throws JpsException {
                                return _getCredentials(map, key);
            }
        };

        try {
            credentials = AccessController.doPrivileged(action);
        } catch (PrivilegedActionException e) {
            throw new RuntimeException(e);
        }

        System.out.println(credentials.getName());
        System.out.println(credentials.getPassword());
        return credentials.getName();
    }
}


Promoting Views/Flex Fields defined using workspace in lower environment to higher environment

Often we use views and protected flex fields in a BPM project. These are one of those few artifacts that resides outside of your main source code (BPM Application and BPM project in your jdeveloper). During development the BPM developers login to bpm workspace running on their local bpm runtime (most cases a local compact bpm domain or bpm domain on integrated weblogic ).

So the question is, how we promote these configuration to another environment. Certainly manually making the same changes in the each of the higher environment is erroneous and thus ruled out. The suggested option is to export the configuration from local environment into XML files, store it in the source control system and deploy through script to other environment. the step are illustrated as below -

Export Flex fields and Views
Login to the server from which you want to export.
> cd <MW_HOME>\soa\bin
> set ANT_HOME=<Your Path>\apache-ant-1.10.1
> set PATH=%ANT_HOME%\bin\;%PATH%

Export Flex fields
ant -v -f ant-t2p-worklist.xml -Dbea.home={ MW_HOME } -Dsoa.home={ MW_HOME/soa } -Dsoa.hostname={hostname} -Dsoa.rmi.port={port} -Dsoa.admin.user={admin user name}-Dsoa.admin.password={admin password} -Drealm=jazn.com -Dmigration.file={full path with filename  to store generated flex_data.xml} -Dmap.file=={full path with filename  to store generated flex_data_map.xml } -DoperationType=EXPORT -DobjectType=TASK_PAYLOAD_FLEX_FIELD_MAPPING -Dname=ALL -Duser={bpm admin user} -DgrantPermission=true  -DmigrateAttributeLabel=true
Parameters -
-Dbea.home=<MW_HOME>
-Dsoa.home=<MW_HOME>/soa
-Dsoa.hostname=Server ip/name
-Dsoa.rmi.port=Server port
-Dsoa.admin.user=admin user
-Dsoa.admin.password=admin user p
-Drealm=realname
-Dmigration.file=file in which the data will be exported
-Dmap.file=File  in which mapping data will be exported
Example -
ant -v -f ant-t2p-worklist.xml -Dbea.home=C:/Oracle/Middleware/Oracle_Home -Dsoa.home=C:/Oracle/Middleware/Oracle_Home/soa -Dsoa.hostname=localhost -Dsoa.rmi.port=7001 -Dsoa.admin.user=weblogic -Dsoa.admin.password=password1$ -Drealm=jazn.com -Dmigration.file=C:/workspace/ViewMigration/data/flex_data.xml -Dmap.file=C:/workspace/ViewMigration/data/flex_data_map.xml -DoperationType=EXPORT -DobjectType=TASK_PAYLOAD_FLEX_FIELD_MAPPING -Dname=ALL -Duser=weblogic -DgrantPermission=true  -DmigrateAttributeLabel=true


To export Views
ant -v -f ant-t2p-worklist.xml -Dbea.home={ MW_HOME } -Dsoa.home={ MW_HOME/soa } -Dsoa.hostname={hostname} -Dsoa.rmi.port={port} -Dsoa.admin.user={admin user name}-Dsoa.admin.password={admin password} -Drealm=jazn.com -Dmigration.file={full path with filename  to store generated view_data.xml} -Dmap.file=={full path with filename  to store generated view_data_map.xml } -DoperationType=EXPORT -DobjectType= VIEW -Dname=ALL -Duser={bpm admin user} -DgrantPermission=true  -DmigrateAttributeLabel=true
Parameters -
Most of the parameters remains same as Export Flex Fields
-DobjectType=VIEW
Example –
ant -f ant-t2p-worklist.xml -Dbea.home=C:/Oracle/Middleware/Oracle_Home -Dsoa.home=C:/Oracle/Middleware/Oracle_Home/soa -Dsoa.hostname=localhost -Dsoa.rmi.port=7001 -Dsoa.admin.user=weblogic -Dsoa.admin.password=password1$ -Drealm=jazn.com -Dmigration.file=C:/workspace/ViewMigration/data/view_data.xml -Dmap.file=C:/workspace/ViewMigration/data/view_data_map.xml -DoperationType=EXPORT -DobjectType=VIEW -Dname=ALL -Duser=weblogic -DgrantPermission=true

Import Flex fields and Views
Login to the server in which you want to import.
> cd <MW_HOME>\soa\bin
> set ANT_HOME=<Your Path>\apache-ant-1.10.1
> set PATH=%ANT_HOME%\bin\;%PATH%

Import Flex fields
ant -v -f ant-t2p-worklist.xml -Dbea.home={ MW_HOME } -Dsoa.home={ MW_HOME/soa } -Dsoa.hostname={hostname} -Dsoa.rmi.port={port} -Dsoa.admin.user={admin user name}-Dsoa.admin.password={admin password} -Drealm=jazn.com -Dmigration.file={full path with filename  of flex_data.xml} -Dmap.file=={full path with filename  of  flex_data_map.xml } -DoperationType=IMPORT -DobjectType=TASK_PAYLOAD_FLEX_FIELD_MAPPING -Dname=ALL -Duser={bpm admin user} -DgrantPermission=true  -DmigrateAttributeLabel=true

Parameters -
-Dbea.home=<MW_HOME>
-Dsoa.home=<MW_HOME>/soa
-Dsoa.hostname=Server ip/name
-Dsoa.rmi.port=Server port
-Dsoa.admin.user=admin user
-Dsoa.admin.password=admin user p
-Drealm=realname
-Dmigration.file=file from which data will be imported
-Dmap.file=File  from which mapping data will be imported

Example –
ant -v -f ant-t2p-worklist.xml -Dbea.home=C:/Oracle/Middleware/Oracle_Home -Dsoa.home=C:/Oracle/Middleware/Oracle_Home/soa -Dsoa.hostname=localhost -Dsoa.rmi.port=9001 -Dsoa.admin.user=weblogic -Dsoa.admin.password=password1$ -Drealm=jazn.com -Dmigration.file=C:/workspace/ViewMigration/data/flex_data.xml -Dmap.file=C:/workspace/ViewMigration/data/flex_data_map.xml -DoperationType=IMPORT -DobjectType=TASK_PAYLOAD_FLEX_FIELD_MAPPING -Dname=ALL -Duser=weblogic -DgrantPermission=true -DmigrateAttributeLabel=true

Import Views
ant -v -f ant-t2p-worklist.xml -Dbea.home={ MW_HOME } -Dsoa.home={ MW_HOME/soa } -Dsoa.hostname={hostname} -Dsoa.rmi.port={port} -Dsoa.admin.user={admin user name}-Dsoa.admin.password={admin password} -Drealm=jazn.com -Dmigration.file={full path with filename  of view_data.xml} -Dmap.file=={full path with filename  of  view_data_map..xml } -DoperationType=IMPORT -DobjectType= VIEW  -Dname=ALL -Duser={bpm admin user} -DgrantPermission=true  -DmigrateAttributeLabel=true
  
Parameters -
Most of the parameters remains same as above except
-DobjectType=VIEW

Example
ant -f ant-t2p-worklist.xml -Dbea.home=C:/Oracle/Middleware/Oracle_Home -Dsoa.home=C:/Oracle/Middleware/Oracle_Home/soa -Dsoa.hostname=localhost -Dsoa.rmi.port=9001 -Dsoa.admin.user=weblogic -Dsoa.admin.password=password1$ -Dhwf.t2p.config.file=C:/workspace/ViewMigration/source_cfg/config.properties -Drealm=jazn.com -Dmigration.file=C:/workspace/ViewMigration/data/view_data.xml -Dmap.file=C:/workspace/ViewMigration/data/view_data_map.xml -DoperationType=IMPORT -DobjectType=VIEW -Dname=ALL -Duser=weblogic -DgrantPermission=true