/** * Copyright (c) 2015 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wso2.carbon.bpmn.rest.service.stats; import org.activiti.engine.ActivitiObjectNotFoundException; import org.activiti.engine.HistoryService; import org.activiti.engine.RepositoryService; import org.activiti.engine.history.HistoricProcessInstance; import org.activiti.engine.history.HistoricProcessInstanceQuery; import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.impl.RepositoryServiceImpl; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.pvm.process.ActivityImpl; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.task.Task; import org.activiti.engine.task.TaskQuery; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.bpmn.rest.common.utils.BPMNOSGIService; import org.wso2.carbon.bpmn.rest.model.stats.TaskInstanceAverageInfo; import org.wso2.carbon.bpmn.rest.model.stats.ProcessInstanceAverageInfo; import org.wso2.carbon.bpmn.rest.model.stats.ProcessInstanceStatInfo; import org.wso2.carbon.bpmn.rest.model.stats.ProcessInstanceStatusCountInfo; import org.wso2.carbon.bpmn.rest.model.stats.InstanceCountInfo; import org.wso2.carbon.bpmn.rest.model.stats.ResponseHolder; import org.wso2.carbon.bpmn.rest.model.stats.TaskInfo; import org.wso2.carbon.bpmn.rest.model.stats.UserTaskCountInfo; import org.wso2.carbon.bpmn.rest.model.stats.UserTaskDuration; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; import javax.jws.WebService; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; /** * Service class which includes functionality related to processes and tasks */ @WebService @Path("/") public class ProcessStatisticsService { private static final Log log = LogFactory.getLog(ProcessStatisticsService.class); private static final String[] MONTHS = {"Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"}; /** * Get the deployed processes count * * @return a list of deployed processes with their instance count */ @GET @Path("/process-instances/count") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder getProcessInstanceCount() { List<ProcessDefinition> processDefinitionList = BPMNOSGIService.getRepositoryService(). createProcessDefinitionQuery().processDefinitionTenantId(getTenantIdStr()).list(); List<Object> bpmnProcessInstancesList = new ArrayList<>(); ResponseHolder response = new ResponseHolder(); for (ProcessDefinition processDefinition : processDefinitionList) { InstanceCountInfo instanceCountInfo = new InstanceCountInfo(); instanceCountInfo.setProcessDefinitionId(processDefinition.getId()); long historicInstanceCount = getCompletedProcessInstanceCount(processDefinition.getId()); long runningInstanceCount = getActiveProcessInstanceCount(processDefinition.getId()); long noOfInstances = historicInstanceCount + runningInstanceCount; instanceCountInfo.setInstanceCount(noOfInstances); bpmnProcessInstancesList.add(instanceCountInfo); } response.setData(bpmnProcessInstancesList); return response; } /** * Get the completed process instance count for a given process definition * * @param processDefinitionName process definition name * @return number of process instances in the system */ private long getCompletedProcessInstanceCount(String processDefinitionName) { return BPMNOSGIService.getHistoryService().createHistoricProcessInstanceQuery() .processDefinitionId(processDefinitionName).finished().count(); } /** * Get the number of active process instances for a given process definition * @param processDefinitionName process definnition id * @return count of active process instances */ private long getActiveProcessInstanceCount(String processDefinitionName) { return BPMNOSGIService.getRuntimeService() .createProcessInstanceQuery().processDefinitionId(processDefinitionName).count(); } /** * Get the number of processInstances with various States * States: Completed , Active, Suspended, Failed * * @return list with the states and the count of process instances in each state */ @GET @Path("/process-instances/state/all/count/") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder getCountOfProcessInstanceStatus() { List<Object> processCountList = new ArrayList<>(); ResponseHolder response = new ResponseHolder(); long completedInstanceCount = BPMNOSGIService.getHistoryService(). createHistoricProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()).finished().count(); long activeInstanceCount = BPMNOSGIService.getRuntimeService() .createProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()).active().count(); long suspendedInstanceCount = BPMNOSGIService.getRuntimeService() .createProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()).suspended().count(); long failedInstanceCont = BPMNOSGIService.getManagementService() .createJobQuery().jobTenantId(getTenantIdStr()).withException().count(); if ( completedInstanceCount == 0 && activeInstanceCount == 0 && suspendedInstanceCount == 0 && failedInstanceCont == 0) { response.setData(processCountList); } else { processCountList.add(new ProcessInstanceStatusCountInfo("Completed", completedInstanceCount)); processCountList.add(new ProcessInstanceStatusCountInfo("Active", activeInstanceCount)); processCountList.add(new ProcessInstanceStatusCountInfo("Suspended", suspendedInstanceCount)); processCountList.add(new ProcessInstanceStatusCountInfo("Failed", failedInstanceCont)); response.setData(processCountList); } return response; } /** * Get the number of Task Instances with various states * States: Completed , Active, Suspended, Failed * * @return list with the states and the count of task instances in each state */ @GET @Path("/task-instances/status/all/count") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder getCountOfTaskInstanceStatus() { List<Object> taskCountList = new ArrayList<>(); ResponseHolder response = new ResponseHolder(); ProcessInstanceStatusCountInfo completedTaskInstances, activeTaskInstances, suspendedTaskInstances, failedTaskInstances; TaskQuery taskQuery = BPMNOSGIService.getTaskService().createTaskQuery(); long completedTaskInstanceCount = BPMNOSGIService.getHistoryService(). createHistoricTaskInstanceQuery().taskTenantId(getTenantIdStr()).finished().count(); long activeTaskInstanceCount = taskQuery.taskTenantId(getTenantIdStr()).active().count(); long suspendedTaskInstanceCount = taskQuery.taskTenantId(getTenantIdStr()).suspended().count(); //Check on this long failedTaskInstanceCount = BPMNOSGIService.getManagementService() .createJobQuery().jobTenantId(getTenantIdStr()).withException().count(); if (completedTaskInstanceCount == 0 && activeTaskInstanceCount == 0 && suspendedTaskInstanceCount == 0 && failedTaskInstanceCount == 0) { response.setData(taskCountList); } else { taskCountList.add(new ProcessInstanceStatusCountInfo("Completed", completedTaskInstanceCount)); taskCountList.add(new ProcessInstanceStatusCountInfo("Active", activeTaskInstanceCount)); taskCountList.add(new ProcessInstanceStatusCountInfo("Suspended", suspendedTaskInstanceCount)); taskCountList.add(new ProcessInstanceStatusCountInfo("Failed", failedTaskInstanceCount)); response.setData(taskCountList); } return response; } /** * Get the average time duration of completed processes * * @return list with the completed processes and the average time duration taken for each process */ @GET @Path("/process-instances/duration/average") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder getAvgTimeDurationForCompletedProcesses() { ResponseHolder response = new ResponseHolder(); List<Object> list = new ArrayList<>(); HistoryService historyService = BPMNOSGIService.getHistoryService(); RepositoryService repositoryService = BPMNOSGIService.getRepositoryService(); List<ProcessDefinition> deployedProcessList = repositoryService. createProcessDefinitionQuery().processDefinitionTenantId(getTenantIdStr()).list(); for (ProcessDefinition instance : deployedProcessList) { ProcessInstanceAverageInfo bpmnProcessInstance = new ProcessInstanceAverageInfo(); bpmnProcessInstance.setProcessDefinitionId(instance.getId()); double totalTime = 0; double averageTime; String processDefinitionID = instance.getId(); HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService. createHistoricProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()). processDefinitionId(processDefinitionID).finished(); long instanceCount = historicProcessInstanceQuery.count(); if (instanceCount != 0) { List<HistoricProcessInstance> instanceList = historicProcessInstanceQuery.list(); for (HistoricProcessInstance completedProcess : instanceList) { double timeDurationOfTask = completedProcess.getDurationInMillis(); double timeInMins = timeDurationOfTask / (1000 * 60); totalTime += timeInMins; } averageTime = totalTime / instanceCount; bpmnProcessInstance.setAverageTimeForCompletion(averageTime); list.add(bpmnProcessInstance); } } response.setData(list); return response; } /** * Average task duration for completed processes * * @param pId processDefintionId of the process selected to view the average time duration for each task * @return list of completed tasks with the average time duration for the selected process */ @GET @Path("/task-instances/duration/avarage/{pid}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder avgTaskTimeDurationForCompletedProcesses(@PathParam("pid") String pId) { RepositoryService repositoryService = BPMNOSGIService.getRepositoryService(); HistoryService historyService = BPMNOSGIService.getHistoryService(); long processCount = repositoryService.createProcessDefinitionQuery(). processDefinitionTenantId(getTenantIdStr()).processDefinitionId(pId).count(); if (processCount == 0) { throw new ActivitiObjectNotFoundException("Count not find a matching process with PID '" + pId + "'."); } ResponseHolder response = new ResponseHolder(); List<Object> taskListForProcess = new ArrayList<>(); HashMap<String, Long> map = new HashMap<>(); //Get the number of completed/finished process instance for each process definition HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService .createHistoricProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()) .processDefinitionId(pId).finished(); //Get the count of the complete process instances long noOfHistoricInstances = historicProcessInstanceQuery.count(); //If the deployed process does not have any completed process instances --> Ignore if (noOfHistoricInstances == 0) { response.setData(taskListForProcess); } //If the deployed process has completed process instances --> then else { TaskInstanceAverageInfo tInstance; //Get the list of completed tasks/activities in the completed process instance by passing the //process definition id of the process List<HistoricTaskInstance> taskList = BPMNOSGIService.getHistoryService(). createHistoricTaskInstanceQuery().taskTenantId(getTenantIdStr()).processDefinitionId(pId) .processFinished().list(); //Iterate through each completed task/activity and get the task name and duration for (HistoricTaskInstance taskInstance : taskList) { //Get the task name String taskKey = taskInstance.getTaskDefinitionKey(); //Get the time duration taken for the task to be completed long taskDuration = taskInstance.getDurationInMillis(); if (map.containsKey(taskKey)) { long tt = map.get(taskKey); map.put(taskKey, taskDuration + tt); } else { map.put(taskKey, taskDuration); } //Iterating Task List finished } Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next().toString(); double value = map.get(key) / noOfHistoricInstances; tInstance = new TaskInstanceAverageInfo(); tInstance.setTaskDefinitionKey(key); tInstance.setAverageTimeForCompletion(value); taskListForProcess.add(tInstance); } response.setData(taskListForProcess); } return response; } /** * Task variation over time i.e. tasks started and completed over the months * * @return array with the no. of tasks started and completed over the months */ @GET @Path("/task-instances/count/variation") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder taskVariationOverTime() { ResponseHolder response = new ResponseHolder(); List list = new ArrayList(); SimpleDateFormat ft = new SimpleDateFormat("M"); ProcessInstanceStatInfo[] taskStatPerMonths = new ProcessInstanceStatInfo[12]; for (int i = 0; i < taskStatPerMonths.length; i++) { taskStatPerMonths[i] = new ProcessInstanceStatInfo(MONTHS[i], 0, 0); } // Get completed tasks List<HistoricTaskInstance> taskList = BPMNOSGIService.getHistoryService().createHistoricTaskInstanceQuery(). taskTenantId(getTenantIdStr()).finished().list(); for (HistoricTaskInstance instance : taskList) { int startTime = Integer.parseInt(ft.format(instance.getCreateTime())); int endTime = Integer.parseInt(ft.format(instance.getEndTime())); taskStatPerMonths[startTime - 1].setInstancesStarted(taskStatPerMonths[startTime - 1].getInstancesStarted() + 1); taskStatPerMonths[endTime - 1].setInstancesCompleted(taskStatPerMonths[endTime - 1].getInstancesCompleted() + 1); } // Get active/started tasks List<Task> taskActive = BPMNOSGIService.getTaskService().createTaskQuery().taskTenantId(getTenantIdStr()).active().list(); for (Task instance : taskActive) { int startTime = Integer.parseInt(ft.format(instance.getCreateTime())); taskStatPerMonths[startTime - 1].setInstancesStarted(taskStatPerMonths[startTime - 1].getInstancesStarted() + 1); } // Get suspended tasks List<Task> taskSuspended = BPMNOSGIService.getTaskService().createTaskQuery().taskTenantId(getTenantIdStr()).suspended() .list(); for (Task instance : taskSuspended) { int startTime = Integer.parseInt(ft.format(instance.getCreateTime())); taskStatPerMonths[startTime - 1].setInstancesStarted(taskStatPerMonths[startTime - 1].getInstancesStarted() + 1); } Collections.addAll(list, taskStatPerMonths); response.setData(list); return response; } /** * Process variation over time i.e. tasks started and completed over the months * * @return array with the no. of processes started and completed over the months */ @GET @Path("/process-instances/count/variation") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder processVariationOverTime() { ResponseHolder response = new ResponseHolder(); List list = new ArrayList(); SimpleDateFormat ft = new SimpleDateFormat("M"); ProcessInstanceStatInfo[] processStatPerMonths = new ProcessInstanceStatInfo[12]; for (int i = 0; i < processStatPerMonths.length; i++) { processStatPerMonths[i] = new ProcessInstanceStatInfo(MONTHS[i], 0, 0); } //Get completed process instances List<HistoricProcessInstance> completedProcesses = BPMNOSGIService.getHistoryService(). createHistoricProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()).finished().list(); for (HistoricProcessInstance instance : completedProcesses) { int startTime = Integer.parseInt(ft.format(instance.getStartTime())); int endTime = Integer.parseInt(ft.format(instance.getEndTime())); processStatPerMonths[startTime - 1].setInstancesStarted(processStatPerMonths[startTime - 1].getInstancesStarted() + 1); processStatPerMonths[endTime - 1].setInstancesCompleted(processStatPerMonths[endTime - 1].getInstancesCompleted() + 1); } // Get active process instances List<HistoricProcessInstance> activeProcesses = BPMNOSGIService.getHistoryService(). createHistoricProcessInstanceQuery().processInstanceTenantId(getTenantIdStr()).unfinished().list(); for (HistoricProcessInstance instance : activeProcesses) { int startTime = Integer.parseInt(ft.format(instance.getStartTime())); processStatPerMonths[startTime - 1].setInstancesStarted(processStatPerMonths[startTime - 1].getInstancesStarted() + 1); } Collections.addAll(list, processStatPerMonths); response.setData(list); return response; } /** * Get all deployed processes * * @return list with the processDefinitions of all deployed processes */ @GET @Path("/processes/") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder getAllProcesses() { //Get a list of the deployed processes RepositoryService repositoryService = BPMNOSGIService.getRepositoryService(); List<ProcessDefinition> processDefinitionList = repositoryService. createProcessDefinitionQuery().processDefinitionTenantId(getTenantIdStr()).list(); List<Object> listOfProcesses = new ArrayList<>(); ResponseHolder response = new ResponseHolder(); for (ProcessDefinition processDefinition : processDefinitionList) { listOfProcesses.add(processDefinition.getId()); } response.setData(listOfProcesses); return response; } /** * Return the no. of processes deployed * * @return list with the processDefinitions of all deployed processes */ @GET @Path("/processes/count") @Produces(MediaType.APPLICATION_JSON) public long getProcessCount() { //Get a list of the deployed processes return BPMNOSGIService.getRepositoryService(). createProcessDefinitionQuery().processDefinitionTenantId(getTenantIdStr()).count(); } /** * Return all the tasks/activities in a process * @param pId process instance id * @return all the tasks/activities in a process */ @GET @Path("/task-instances/{pId}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder getTasks(@PathParam("pId") String pId) { ResponseHolder response = new ResponseHolder(); List<Object> list = new ArrayList(); RepositoryService repositoryService = BPMNOSGIService.getRepositoryService(); ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService). getDeployedProcessDefinition(pId); if (processDefinition != null) { for (ActivityImpl activity : processDefinition.getActivities()) { TaskInfo taskInfo = new TaskInfo(); String taskDefKey = activity.getId(); String type = (String) activity.getProperty("type"); String taskName = (String) activity.getProperty("name"); taskInfo.setTaskDefinitionKey(taskDefKey); taskInfo.setType(type); taskInfo.setName(taskName); list.add(taskInfo); } } response.setData(list); return response; } /** * Task variation of user over time i.e. tasks started and completed by the user -- User Performance * * @param assignee taskAssignee/User selected to view the user performance of task completion over time * @return array with the tasks started and completed of the selected user */ @GET @Path("/user-performance/variation/{assignee}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public ResponseHolder taskVariationOverTime(@PathParam("assignee") String assignee) throws UserStoreException { if (!validateCurrentUser(assignee)) { throw new ActivitiObjectNotFoundException("User with user id " + assignee + "not defined in the system" ); } ResponseHolder response = new ResponseHolder(); List list = new ArrayList(); SimpleDateFormat ft = new SimpleDateFormat("M"); ProcessInstanceStatInfo[] taskStatPerMonths = new ProcessInstanceStatInfo[12]; for (int i = 0; i < taskStatPerMonths.length; i++) { taskStatPerMonths[i] = new ProcessInstanceStatInfo(MONTHS[i], 0, 0); } // Get completed tasks List<HistoricTaskInstance> taskList = BPMNOSGIService.getHistoryService() .createHistoricTaskInstanceQuery().taskTenantId(getTenantIdStr()).taskAssignee(assignee).finished().list(); for (HistoricTaskInstance instance : taskList) { int startTime = Integer.parseInt(ft.format(instance.getCreateTime())); int endTime = Integer.parseInt(ft.format(instance.getEndTime())); taskStatPerMonths[startTime - 1].setInstancesStarted(taskStatPerMonths[startTime - 1].getInstancesStarted() + 1); taskStatPerMonths[endTime - 1].setInstancesCompleted(taskStatPerMonths[endTime - 1].getInstancesCompleted() + 1); } // Get active/started tasks List<Task> taskActive = BPMNOSGIService.getTaskService(). createTaskQuery().taskTenantId(getTenantIdStr()).taskAssignee(assignee).active().list(); for (Task instance : taskActive) { int startTime = Integer.parseInt(ft.format(instance.getCreateTime())); taskStatPerMonths[startTime - 1].setInstancesStarted(taskStatPerMonths[startTime - 1].getInstancesStarted() + 1); } // Get suspended tasks List<Task> taskSuspended = BPMNOSGIService.getTaskService().createTaskQuery(). taskTenantId(getTenantIdStr()).taskAssignee(assignee).suspended().list(); for (Task instance : taskSuspended) { int startTime = Integer.parseInt(ft.format(instance.getCreateTime())); taskStatPerMonths[startTime - 1].setInstancesStarted(taskStatPerMonths[startTime - 1].getInstancesStarted() + 1); } for (int i = 0; i < taskStatPerMonths.length; i++) { list.add(taskStatPerMonths[i]); } response.setData(list); return response; } private int getTenantId() { return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); } private String getTenantIdStr() { return String.valueOf(getTenantId()); } private String getTenantDomain() { return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true); } private ProcessInstanceStatusCountInfo createStatusInfo(String status, long instanceCount) { return new ProcessInstanceStatusCountInfo(status, instanceCount); } private boolean validateCurrentUser(String user) throws UserStoreException { UserStoreManager userStoreManager = BPMNOSGIService.getUserRealm().getUserStoreManager(); return userStoreManager.isExistingUser(user); } }