package pl.net.bluesoft.rnd.pt.ext.jbpm; import static pl.net.bluesoft.util.lang.FormatUtil.nvl; import static pl.net.bluesoft.util.lang.Lang.keyFilter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.aperteworkflow.bpm.graph.GraphElement; import org.aperteworkflow.bpm.graph.StateNode; import org.aperteworkflow.bpm.graph.TransitionArc; import org.aperteworkflow.ui.view.ViewEvent; import org.hibernate.Query; import org.hibernate.SQLQuery; import org.jbpm.api.Execution; import org.jbpm.api.ExecutionService; import org.jbpm.api.HistoryService; import org.jbpm.api.IdentityService; import org.jbpm.api.NewDeployment; import org.jbpm.api.ProcessDefinition; import org.jbpm.api.ProcessEngine; import org.jbpm.api.RepositoryService; import org.jbpm.api.TaskService; import org.jbpm.api.cmd.Command; import org.jbpm.api.cmd.Environment; import org.jbpm.api.history.HistoryActivityInstance; import org.jbpm.api.history.HistoryActivityInstanceQuery; import org.jbpm.api.history.HistoryProcessInstance; import org.jbpm.api.history.HistoryProcessInstanceQuery; import org.jbpm.api.identity.User; import org.jbpm.api.model.Activity; import org.jbpm.api.model.Transition; import org.jbpm.api.task.Participation; import org.jbpm.api.task.Task; import org.jbpm.pvm.internal.history.model.HistoryActivityInstanceImpl; import org.jbpm.pvm.internal.history.model.HistoryProcessInstanceImpl; import org.jbpm.pvm.internal.history.model.HistoryTaskImpl; import org.jbpm.pvm.internal.history.model.HistoryTaskInstanceImpl; import org.jbpm.pvm.internal.identity.impl.UserImpl; import org.jbpm.pvm.internal.model.ExecutionImpl; import org.jbpm.pvm.internal.query.AbstractQuery; import org.jbpm.pvm.internal.task.ParticipationImpl; import org.jbpm.pvm.internal.task.TaskImpl; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.sun.org.apache.bcel.internal.generic.ISTORE; import pl.net.bluesoft.rnd.processtool.ProcessToolContext; import pl.net.bluesoft.rnd.processtool.bpm.BpmEvent; import pl.net.bluesoft.rnd.processtool.bpm.ProcessToolBpmSession; import pl.net.bluesoft.rnd.processtool.bpm.exception.ProcessToolSecurityException; import pl.net.bluesoft.rnd.processtool.bpm.impl.AbstractProcessToolSession; import pl.net.bluesoft.rnd.processtool.hibernate.ResultsPageWrapper; import pl.net.bluesoft.rnd.processtool.model.BpmTask; import pl.net.bluesoft.rnd.processtool.model.ProcessInstance; import pl.net.bluesoft.rnd.processtool.model.ProcessInstanceFilter; import pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog; import pl.net.bluesoft.rnd.processtool.model.ProcessStatus; import pl.net.bluesoft.rnd.processtool.model.QueueType; import pl.net.bluesoft.rnd.processtool.model.UserData; import pl.net.bluesoft.rnd.processtool.model.config.ProcessDefinitionConfig; import pl.net.bluesoft.rnd.processtool.model.config.ProcessStateAction; import pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration; import pl.net.bluesoft.rnd.processtool.model.nonpersistent.MutableBpmTask; import pl.net.bluesoft.rnd.processtool.model.nonpersistent.ProcessQueue; import pl.net.bluesoft.rnd.pt.ext.jbpm.query.BpmTaskFilterQuery; import pl.net.bluesoft.util.lang.Lang; import pl.net.bluesoft.util.lang.Mapcar; import pl.net.bluesoft.util.lang.Predicate; import pl.net.bluesoft.util.lang.Strings; import pl.net.bluesoft.util.lang.Transformer; /** * jBPM session implementation * * @author tlipski@bluesoft.net.pl * @author amichalak@bluesoft.net.pl */ public class ProcessToolJbpmSession extends AbstractProcessToolSession { private static final String AUTO_SKIP_TASK_NAME_PREFIX = "AUTO_SKIP"; private static final String AUTO_SKIP_ACTION_NAME = "AUTO_SKIP"; private static final String JOIN_NODE_NAME = "join"; private static final String FORK_NODE_NAME = "fork"; protected Logger log = Logger.getLogger(ProcessToolJbpmSession.class.getName()); private static final Integer DEFAULT_OFFSET_VALUE = 0; private static final Integer DEFAULT_LIMIT_VALUE = 1000; protected Logger loger = Logger.getLogger(ProcessToolJbpmSession.class.getName()); public ProcessToolJbpmSession(UserData user, Collection<String> roleNames, ProcessToolContext ctx) { super(user, roleNames, ctx.getRegistry()); if (user != null) { IdentityService is = getProcessEngine(ctx).getIdentityService(); User jbpmUser = is.findUserById(user.getLogin()); if (jbpmUser == null) { is.createUser(user.getLogin(), user.getRealName(), user.getEmail()); } getProcessEngine(ctx).setAuthenticatedUserId(user.getLogin()); } } @Override public Collection<BpmTask> getAllTasks(ProcessToolContext ctx) { Command<List<Task>> cmd = new Command<List<Task>>() { @Override public List<Task> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { } @Override protected void applyParameters(Query query) { } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select task "); hql.append("from "); hql.append(TaskImpl.class.getName()); hql.append(" as task "); hql.append("order by task.id DESC"); return hql.toString(); } }; return (List<Task>) q.execute(environment); } }; List<Task> tasks = getProcessEngine(ctx).execute(cmd); return findProcessInstancesForTasks(tasks, ctx); } public Collection<BpmTask> getProcessTaskInQueues(ProcessToolContext ctx, final ProcessInstance processInstance) { Command<List<Task>> cmd = new Command<List<Task>>() { @Override public List<Task> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { } @Override protected void applyParameters(Query query) { query.setString("executionId", processInstance.getInternalId()); } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select task ") .append("from ") .append(TaskImpl.class.getName()).append(" as task, ") .append(ParticipationImpl.class.getName()).append(" as participation ") .append(" where participation.task = task ") .append(" and task.executionId = :executionId "); return hql.toString(); } }; return (List<Task>) q.execute(environment); } }; List<Task> tasks = getProcessEngine(ctx).execute(cmd); return findProcessInstancesForTasks(tasks, ctx); } @Override public BpmTask getPastEndTask(ProcessInstanceLog log, ProcessToolContext ctx) { final ProcessInstance pi = log.getOwnProcessInstance(); String endTaskName = findEndActivityName(pi, ctx); if (Strings.hasText(endTaskName)) { MutableBpmTask t = new MutableBpmTask(); t.setProcessInstance(pi); t.setAssignee(user.getLogin()); t.setOwner(user); t.setTaskName(endTaskName); t.setFinished(true); return t; } return null; } @Override public BpmTask getPastOrActualTask(final ProcessInstanceLog log, ProcessToolContext ctx) { final UserData user = log.getUser(); final ProcessInstance pi = log.getOwnProcessInstance(); final Calendar minDate = log.getEntryDate(); final Set<String> taskNames = new HashSet<String>(); if (log.getState() != null && Strings.hasText(log.getState().getName())) { taskNames.add(log.getState().getName()); } Command<List<HistoryActivityInstance>> cmd = new Command<List<HistoryActivityInstance>>() { @Override public List<HistoryActivityInstance> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); query.setMaxResults(1); } @Override protected void applyParameters(Query query) { query.setString("userIds", user.getLogin()); query.setString("internalIds", log.getExecutionId()); query.setDate("minDate", minDate.getTime()); if (!taskNames.isEmpty()) { query.setParameterList("taskName", taskNames); } } @Override public String hql() { StringBuilder hql = new StringBuilder() .append("select act ") .append("from ") .append(HistoryTaskInstanceImpl.class.getName()).append(" as act, ") .append(HistoryProcessInstanceImpl.class.getName()).append(" as proc, ") .append(HistoryTaskImpl.class.getName()).append(" as task ") .append(" where act.historyProcessInstance = proc ") .append(" and act.historyTask = task ") .append(" and act.endTime is not null "); if (!taskNames.isEmpty()) { hql.append(" and act.activityName in (:taskName) "); } hql.append(" and task.assignee = :userIds ") .append(" and task.endTime > :minDate ") .append(" and act.executionId = :internalIds ") .append(" order by act.endTime asc "); return hql.toString(); } }; return (List<HistoryActivityInstance>) q.execute(environment); } }; List<HistoryActivityInstance> pastTasks = getProcessEngine(ctx).execute(cmd); if (!pastTasks.isEmpty()) { return collectHistoryActivity(pastTasks.get(0), pi, user, ctx); } else { List<BpmTask> actualTasks = findProcessTasks(pi, user.getLogin(), taskNames, ctx); if (!actualTasks.isEmpty()) { return actualTasks.get(0); } } return null; } @Override public List<BpmTask> findRecentTasks(Calendar minDate, Integer offset, Integer limit, ProcessToolContext ctx) { List<BpmTask> recentTasks = new ArrayList<BpmTask>(); UserData user = getUser(ctx); ResultsPageWrapper<ProcessInstance> recentInstances = ctx.getProcessInstanceDAO().getRecentProcesses(user, minDate, offset, limit); Collection<ProcessInstance> instances = recentInstances.getResults(); for (ProcessInstance pi : instances) { List<BpmTask> tasks = findProcessTasks(pi, user.getLogin(), ctx); if (tasks.isEmpty()) { BpmTask task = getMostRecentProcessHistoryTask(pi, user, minDate, ctx); if (task != null) { recentTasks.add(task); } } else { recentTasks.addAll(tasks); } } return recentTasks; } private BpmTask getMostRecentProcessHistoryTask(final ProcessInstance pi, final UserData user, final Calendar minDate, ProcessToolContext ctx) { Command<List<HistoryActivityInstance>> cmd = new Command<List<HistoryActivityInstance>>() { @Override public List<HistoryActivityInstance> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); query.setMaxResults(1); } @Override protected void applyParameters(Query query) { query.setString("userIds", user.getLogin()); query.setString("internalIds", pi.getInternalId()); query.setDate("minDate", minDate.getTime()); } @Override public String hql() { StringBuilder hql = new StringBuilder() .append("select act ") .append("from ") .append(HistoryTaskInstanceImpl.class.getName()).append(" as act, ") .append(HistoryProcessInstanceImpl.class.getName()).append(" as proc, ") .append(HistoryTaskImpl.class.getName()).append(" as task ") .append(" where act.historyProcessInstance = proc ") .append(" and act.historyTask = task ") .append(" and act.endTime is not null ") .append(" and task.assignee = :userIds ") .append(" and task.endTime > :minDate ") .append(" and proc.processInstanceId = :internalIds ") .append(" order by act.endTime desc "); return hql.toString(); } }; return (List<HistoryActivityInstance>) q.execute(environment); } }; List<HistoryActivityInstance> tasks = getProcessEngine(ctx).execute(cmd); if (tasks.isEmpty()) { return null; } MutableBpmTask task = collectHistoryActivity(tasks.get(0), pi, user, ctx); String endTaskName = findEndActivityName(pi, ctx); if (Strings.hasText(endTaskName)) { task.setTaskName(endTaskName); } return task; } @Override public Integer getRecentTasksCount(Calendar minDate, ProcessToolContext ctx) { int count = 0; UserData user = getUser(ctx); Collection<ProcessInstance> instances = ctx.getProcessInstanceDAO().getUserProcessesAfterDate(user, minDate); for (ProcessInstance pi : instances) { List<BpmTask> tasks = findProcessTasks(pi, user.getLogin(), ctx); if (tasks.isEmpty() && getMostRecentProcessHistoryTask(pi, user, minDate, ctx) != null) { count += 1; } else { count += tasks.size(); } } return count; } @Override public int getTasksCount(ProcessToolContext ctx, String userLogin, QueueType... queueTypes) { return ctx.getUserProcessQueueDAO().getQueueLength(userLogin, queueTypes); } @Override public int getTasksCount(ProcessToolContext ctx, String userLogin, Collection<QueueType> queueTypes) { return ctx.getUserProcessQueueDAO().getQueueLength(userLogin, queueTypes); } @Override public int getFilteredTasksCount(ProcessInstanceFilter filter, ProcessToolContext ctx) { /* Initialize query */ BpmTaskFilterQuery taskFilterQuery = new BpmTaskFilterQuery(ctx); /* Queues filter do not have owner */ if(filter.getFilterOwner() != null) taskFilterQuery.addUserLoginCondition(filter.getFilterOwner().getLogin()); if(!filter.getQueueTypes().isEmpty()) taskFilterQuery.addQueueTypeCondition(filter.getQueueTypes()); /* Add external conditions for process instance filter */ addExternalConditions(taskFilterQuery, filter); /* Get results count. No entities are loaded to memory using this */ return taskFilterQuery.getBpmTaskCount(); } public List<BpmTask> findFilteredTasks(ProcessInstanceFilter filter, ProcessToolContext ctx) { return findFilteredTasks(filter, ctx, 0, 0); } public List<BpmTask> findFilteredTasks(ProcessInstanceFilter filter, ProcessToolContext ctx, int offset, int maxResults) { /* Initialize query */ BpmTaskFilterQuery taskFilterQuery = new BpmTaskFilterQuery(ctx); /* Queues filter do not have owner */ if(filter.getFilterOwner() != null) taskFilterQuery.addUserLoginCondition(filter.getFilterOwner().getLogin()); if(!filter.getQueueTypes().isEmpty()) taskFilterQuery.addQueueTypeCondition(filter.getQueueTypes()); /* Set limit for max results count */ taskFilterQuery.setMaxResultsLimit(maxResults); taskFilterQuery.setResultsOffset(offset); /* Add external conditions for process instance filter */ addExternalConditions(taskFilterQuery, filter); /* BpmTasks */ List<BpmTask> result = taskFilterQuery.getBpmTasks(); return result; } /** Add additional conditions to query from process instance filter */ private void addExternalConditions(BpmTaskFilterQuery taskFilterQuery,ProcessInstanceFilter filter) { /* Prepare data for owner lists and not owner list */ Collection<String> ownerNames = new HashSet<String>(); for(UserData userData: filter.getOwners()) ownerNames.add(userData.getLogin()); /* Add condition for task names if any exists */ if(!filter.getTaskNames().isEmpty()) taskFilterQuery.addTaskNamesCondtition(filter.getTaskNames()); /* Add conidtion for created before date */ if(filter.getCreatedBefore() != null) taskFilterQuery.addCreatedBeforeCondition(filter.getCreatedBefore()); /* Add conidtion for created after date */ if(filter.getCreatedAfter() != null) taskFilterQuery.addCreatedAfterCondition(filter.getCreatedAfter()); if(!filter.getQueues().isEmpty()) taskFilterQuery.addQueuesCondition(filter.getQueues()); /* Add condition for owner */ if(!ownerNames.isEmpty()) taskFilterQuery.addOwnerLoginsCondtition(ownerNames); } // /** Prepare hql query using given filter */ // private ProcessEngineQuery<HistoryTaskInstanceImpl> prepareTaskQuery(ProcessInstanceFilter filter) // { // /* Create process engine query */ // ProcessEngineQuery<HistoryTaskInstanceImpl> processEngineQuery = new ProcessEngineQuery<HistoryTaskInstanceImpl>(); // // Collection<String> ownerNames = new HashSet<String>(); // for(UserData userData: filter.getOwners()) // ownerNames.add(userData.getLogin()); // // // /* Prepare hql statement */ // StringBuilder hql = new StringBuilder(); // hql.append("SELECT act "); // hql.append("FROM "); // hql.append(HistoryTaskInstanceImpl.class.getName()).append(" as act "); // hql.append("left join fetch act.historyTask ").append(" as task "); // hql.append(", ").append(HistoryProcessInstanceImpl.class.getSimpleName()).append(" as proc "); // // if (!filter.getQueues().isEmpty()) { // hql.append(", "); // hql.append(ParticipationImpl.class.getName()); // hql.append(" AS participant "); // hql.append("WHERE participant.task=task "); // hql.append("AND participant.type = 'candidate' "); // hql.append("AND participant.groupId IN (:groupIds) "); // hql.append("AND task.assignee IS null "); // hql.append(" AND "); // } // else { // hql.append(" WHERE "); // } // hql.append(" act.historyProcessInstance = proc "); // hql.append(" and act.historyTask = task "); // // if(filter.getQueueTypes().contains(QueueType.OWN_FINISHED)) // hql.append(" and proc.state = 'ended' "); // else // hql.append(" and proc.state = 'active' "); // // StringBuffer hqltmp = new StringBuffer(); // hqltmp.append(" NOT EXISTS (SELECT 1 FROM ").append(HistoryTaskInstanceImpl.class.getName()).append(" as act1 WHERE act.historyProcessInstance = act1.historyProcessInstance AND act1.dbid > act.dbid) "); // // if (!filter.getTaskNames().isEmpty()) { // hql.append(" AND act.activityName IN (:taskNames) "); // } // if (!ownerNames.isEmpty()) { // hql.append(" AND task.assignee IN (:ownerIds) "); // } // // hql.append("order by task.id DESC"); // // /* Set prepared query */ // processEngineQuery.setQuery(hql.toString()); // // /* Add parameters for query */ // if (!filter.getQueues().isEmpty()) // processEngineQuery.addListParameter("groupIds", filter.getQueues()); // // if (!ownerNames.isEmpty()) // processEngineQuery.addListParameter("ownerIds", ownerNames); // // // if (!filter.getTaskNames().isEmpty()) // processEngineQuery.addListParameter("taskNames", filter.getTaskNames()); // // return processEngineQuery; // } protected ProcessEngine getProcessEngine(ProcessToolContext ctx) { if (ctx instanceof ProcessToolContextImpl) { ProcessEngine engine = ((ProcessToolContextImpl) ctx).getProcessEngine(); if (user != null && user.getLogin() != null) { engine.setAuthenticatedUserId(user.getLogin()); } return engine; } else { throw new IllegalArgumentException(ctx + " not an instance of " + ProcessToolContextImpl.class.getName()); } } @Override public Collection<ProcessQueue> getUserAvailableQueues(ProcessToolContext ctx) { Collection<ProcessQueue> configs = getUserQueuesFromConfig(ctx); final List<String> names = keyFilter("name", configs); if (names.isEmpty()) { return new ArrayList<ProcessQueue>(); } Command<List<Map>> cmd = new Command<List<Map>>() { @Override public List<Map> execute(Environment environment) throws Exception { return (List<Map>) new AbstractQuery() { @Override protected void applyParameters(Query query) { query.setParameterList("groupIds", names); } @Override public String hql() { return new StringBuilder() .append("select new map(participant.groupId as groupId, count(task) as taskCount) ") .append("from ") .append(TaskImpl.class.getName()) .append(" as task ") .append(", ") .append(ParticipationImpl.class.getName()) .append(" as participant ") .append("where participant.task=task ") .append("and participant.type = 'candidate' ") .append("and participant.groupId IN (:groupIds) ") .append("and task.assignee is null ") .append("group by participant.groupId") .toString(); } }.execute(environment); } }; List<Map> maps = getProcessEngine(ctx).execute(cmd); Map<String, Long> counts = new HashMap<String, Long>(); for (Map m : maps) { counts.put((String) m.get("groupId"), (Long) m.get("taskCount")); } for (ProcessQueue q : configs) { q.setProcessCount(nvl(counts.get(q.getName()), (long) 0)); } return configs; } @SuppressWarnings("unchecked") @Override public List<BpmTask> getQueueTasks(ProcessToolContext ctx, String queueName) { SQLQuery query = ctx.getHibernateSession().createSQLQuery( "select task.*, process.*, part.* from jbpm4_task task, pt_process_instance process, jbpm4_participation part " + "where process.internalid = task.execution_id_ and part.task_ = task.dbid_ " + "and part.groupid_ = '"+queueName+"'"); query.addEntity("task", TaskImpl.class); query.addEntity("process", ProcessInstance.class); query.addEntity("part", ParticipationImpl.class); /* Get query results */ List<Object[]> queueResults = query.list(); List<BpmTask> result = new ArrayList<BpmTask>(); BpmTaskFactory taskFactory = new BpmTaskFactory(ctx); /* Every row is one queue element with jbpm task as first column and process instance as second */ for(Object[] resultRow: queueResults) { TaskImpl taskInstance = (TaskImpl)resultRow[0]; ProcessInstance processInstance = (ProcessInstance)resultRow[1]; /* Map process and jbpm task to system's bpm task */ BpmTask task = taskFactory.create(taskInstance, processInstance); result.add(task); } return result; } @Override public BpmTask assignTaskFromQueue(ProcessQueue queue, ProcessToolContext ctx) { return assignTaskFromQueue(queue, null, ctx); } @Override public BpmTask assignTaskFromQueue(final ProcessQueue pq, BpmTask bpmTask, ProcessToolContext ctx) { Collection<ProcessQueue> configs = getUserQueuesFromConfig(ctx); final List<String> names = keyFilter("name", configs); if (!names.contains(pq.getName())) { throw new ProcessToolSecurityException("queue.no.rights", pq.getName()); } ProcessEngine processEngine = getProcessEngine(ctx); final String taskId = bpmTask != null ? bpmTask.getInternalTaskId() : null; Command<List<Task>> cmd = new Command<List<Task>>() { @Override public List<Task> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); query.setFetchSize(1); } @Override protected void applyParameters(Query query) { query.setParameterList("groupIds", java.util.Collections.singleton(pq.getName())); if (taskId != null) { query.setParameter("taskId", new Long(taskId)); } } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select task "); hql.append("from "); hql.append(TaskImpl.class.getName()); hql.append(" as task "); hql.append(", "); hql.append(ParticipationImpl.class.getName()); hql.append(" as participant "); hql.append("where participant.task=task "); hql.append("and participant.type = 'candidate' "); hql.append("and participant.groupId IN (:groupIds) "); hql.append("and task.assignee is null "); if (taskId != null) { hql.append("and task.id = :taskId"); } return hql.toString(); } }; return (List<Task>) q.execute(environment); } }; List<Task> taskList = processEngine.execute(cmd); if (taskList.isEmpty()) { loger.warning("No tasks found in queue: " + pq.getName()); return null; } Task task = taskList.get(0); Execution exec = processEngine.getExecutionService().findExecutionById(task.getExecutionId()); String internalId = exec.getProcessInstance().getId(); ProcessInstance pi = getProcessData(internalId, ctx); if (pi == null) { loger.warning("Process instance not found for instance id: " + internalId); return null; } Calendar snapshotDate = Calendar.getInstance(); processEngine.getTaskService().assignTask(task.getId(), user.getLogin()); task = processEngine.getTaskService().getTask(task.getId()); if (!user.getLogin().equals(task.getAssignee())) { loger.warning("Task: + " + bpmTask.getExecutionId() + " not assigned to requesting user: " + user.getLogin()); return null; } ProcessInstanceLog log = new ProcessInstanceLog(); log.setLogType(ProcessInstanceLog.LOG_TYPE_CLAIM_PROCESS); ctx.getProcessInstanceDAO().saveProcessInstance(pi); bpmTask = collectTask(task, pi, ctx); log.setState(ctx.getProcessDefinitionDAO().getProcessStateConfiguration(bpmTask)); log.setEntryDate(snapshotDate); log.setEventI18NKey("process.log.process-assigned"); log.setLogValue(pq.getName()); log.setUser(findOrCreateUser(user, ctx)); log.setAdditionalInfo(pq.getDescription()); log.setExecutionId(task.getExecutionId()); log.setOwnProcessInstance(pi); pi.getRootProcessInstance().addProcessLog(log); if (!ProcessStatus.RUNNING.equals(pi.getStatus())) { pi.setStatus(ProcessStatus.RUNNING); } /* Inform queue manager about task assigne */ ctx.getUserProcessQueueManager().onTaskAssigne(bpmTask); broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.ASSIGN_TASK, bpmTask, user)); broadcastEvent(ctx, new ViewEvent(ViewEvent.Type.ACTION_COMPLETE)); return bpmTask; } private List<BpmTask> collectTasks(List<Task> tasks, final ProcessInstance pi, final ProcessToolContext ctx) { return new Mapcar<Task, BpmTask>(tasks) { @Override public BpmTask lambda(Task x) { return collectTask(x, pi, ctx); } }.go(); } private MutableBpmTask collectHistoryActivity(HistoryActivityInstance task, ProcessInstance pi, UserData user, ProcessToolContext ctx) { MutableBpmTask t = new MutableBpmTask(); t.setProcessInstance(pi); t.setAssignee(user.getLogin()); t.setOwner(user); t.setTaskName(task.getActivityName()); t.setInternalTaskId(null); t.setExecutionId(task.getExecutionId()); t.setCreateDate(task.getStartTime()); t.setFinishDate(task.getEndTime()); t.setFinished(task.getEndTime() != null); return t; } /** Get the last step name of the process */ private String findEndActivityName(ProcessInstance pi, ProcessToolContext ctx) { /* Get all history activities for given process and order it by the end date */ List<HistoryActivityInstance> activities = getProcessEngine(ctx).getHistoryService().createHistoryActivityInstanceQuery() .processInstanceId(pi.getInternalId()) .orderDesc(HistoryActivityInstanceQuery.PROPERTY_ENDTIME) .list(); /* Looking for specific activity, becouse if there is a for-each statment in process, there * might be a problem with correct end task name */ if (activities != null && !activities.isEmpty()) { for(HistoryActivityInstance activity: activities) { if(activity instanceof HistoryActivityInstanceImpl) { HistoryActivityInstanceImpl activityImpl = (HistoryActivityInstanceImpl)activity; /* There are trasictions and xors, we are interested in only with tasks */ if(activityImpl.getType().equals("task")) return activityImpl.getActivityName(); } } } return null; } private MutableBpmTask collectTask(Task task, ProcessInstance pi, ProcessToolContext ctx) { MutableBpmTask t = new MutableBpmTask(); t.setProcessInstance(pi); t.setAssignee(task.getAssignee()); UserData ud = ctx.getUserDataDAO().loadUserByLogin(task.getAssignee()); if (ud == null) { ud = new UserData(); ud.setLogin(task.getAssignee()); } t.setOwner(ud); t.setTaskName(task.getActivityName()); t.setInternalTaskId(task.getId()); t.setExecutionId(task.getExecutionId()); t.setCreateDate(task.getCreateTime()); t.setFinished(false); return t; } @Override public BpmTask getTaskData(String taskId, ProcessToolContext ctx) { Task task = getProcessEngine(ctx).getTaskService().getTask(taskId); if (task == null) { return null; } List<BpmTask> tasks = findProcessInstancesForTasks(java.util.Collections.singletonList(task), ctx); return tasks.isEmpty() ? null : tasks.get(0); } @Override public List<BpmTask> getTaskData(String taskExecutionId, String taskName, ProcessToolContext ctx) { long start = System.currentTimeMillis(); List<Task> tasks = getProcessEngine(ctx).getTaskService().createTaskQuery() .notSuspended() .activityName(taskName) .executionId(taskExecutionId) .page(0, 1) .list(); if (tasks.isEmpty()) { loger.warning("Task " + taskExecutionId + " not found"); return null; } List<BpmTask> bpmTasks = findProcessInstancesForTasks(tasks, ctx); long duration = System.currentTimeMillis() - start; log.severe("getTaskData: " + duration); return bpmTasks; } @Override public BpmTask refreshTaskData(BpmTask task, ProcessToolContext ctx) { MutableBpmTask bpmTask = task instanceof MutableBpmTask ? (MutableBpmTask) task : new MutableBpmTask(task); bpmTask.setProcessInstance(getProcessData(task.getProcessInstance().getInternalId(), ctx)); List<Task> tasks = getProcessEngine(ctx).getTaskService().createTaskQuery() .notSuspended() .activityName(task.getTaskName()) .executionId(task.getExecutionId()) .assignee(user.getLogin()) .page(0, 1) .list(); if (tasks.isEmpty()) { loger.warning("Task " + task.getExecutionId() + " not found"); bpmTask.setFinished(true); } return bpmTask; } @Override public List<BpmTask> findProcessTasks(ProcessInstance pi, final String userLogin, final Set<String> taskNames, ProcessToolContext ctx) { final Map<String, Execution> executions = getActiveExecutions(pi.getInternalId(), ctx); if (executions.isEmpty()) { return new ArrayList<BpmTask>(); } Command<List<Task>> cmd = new Command<List<Task>>() { @Override public List<Task> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); } @Override protected void applyParameters(Query query) { query.setParameterList("executionId", executions.keySet()); if (userLogin != null) { query.setParameterList("userLogin", java.util.Collections.singleton(userLogin)); } if (taskNames != null && !taskNames.isEmpty()) { query.setParameterList("taskNames", taskNames); } } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select task "); hql.append("from "); hql.append(TaskImpl.class.getName()); hql.append(" as task "); hql.append("where executionId in (:executionId) "); hql.append("and task.assignee "); hql.append(userLogin != null ? " in (:userLogin) " : " is not null "); if (taskNames != null && !taskNames.isEmpty()) { hql.append("and task.name in (:taskNames) "); } return hql.toString(); } }; return (List<Task>) q.execute(environment); } }; List<Task> tasks = getProcessEngine(ctx).execute(cmd); return collectTasks(tasks, pi, ctx); } @Override public List<BpmTask> findProcessTasks(ProcessInstance pi, final String userLogin, ProcessToolContext ctx) { return findProcessTasks(pi, userLogin, null, ctx); } @Override public List<BpmTask> findProcessTasksWithUser(ProcessInstance pi, ProcessToolContext ctx) { long start = System.currentTimeMillis(); List<BpmTask> findProcessTasks; if(user!=null){ findProcessTasks = findProcessTasks(pi, user.getLogin(), ctx); } else{ findProcessTasks = findProcessTasks(pi, null, ctx); } long duration = System.currentTimeMillis() - start; log.severe("findProcessTasks: " + duration); return findProcessTasks; } public List<BpmTask> findProcessTasks(ProcessInstance pi, ProcessToolContext ctx) { long start = System.currentTimeMillis(); List<BpmTask> findProcessTasks; findProcessTasks = findProcessTasks(pi, null, ctx); long duration = System.currentTimeMillis() - start; log.severe("findProcessTasks: " + duration); return findProcessTasks; } @Override public boolean isProcessOwnedByUser(final ProcessInstance processInstance, ProcessToolContext ctx) { long start = System.currentTimeMillis(); final Map<String, Execution> executions = getActiveExecutions(processInstance.getInternalId(), ctx); if (executions.isEmpty()) { return false; } Command<List<Task>> cmd = new Command<List<Task>>() { @Override public List<Task> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); query.setMaxResults(1); } @Override protected void applyParameters(Query query) { query.setParameterList("executionId", executions.keySet()); query.setParameterList("assignee", java.util.Collections.singleton(user.getLogin())); } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select task "); hql.append("from "); hql.append(TaskImpl.class.getName()); hql.append(" as task "); hql.append("where executionId in (:executionId) "); hql.append("and task.assignee in (:assignee) "); return hql.toString(); } }; return (List<Task>) q.execute(environment); } }; List<Task> tasks = getProcessEngine(ctx).execute(cmd); long duration = System.currentTimeMillis() - start; log.severe("isProcessOwnedByUser: " + duration); return !tasks.isEmpty(); } @Override public List<BpmTask> findUserTasks(Integer offset, Integer limit, final ProcessToolContext ctx) { long start = System.currentTimeMillis(); if(limit== null){ limit=DEFAULT_LIMIT_VALUE; } if(offset== null){ offset=DEFAULT_OFFSET_VALUE; } List<Task> tasks = getProcessEngine(ctx).getTaskService().createTaskQuery() .notSuspended() .assignee(user.getLogin()) .page(offset, limit) .list(); List<BpmTask> processInstancesForTasks = findProcessInstancesForTasks(tasks, ctx); long duration = System.currentTimeMillis() - start; log.severe("findUserTasks: " + duration); return processInstancesForTasks; } private List<BpmTask> findProcessInstancesForTasks(List<Task> tasks, final ProcessToolContext ctx) { Map<String, List<Task>> tasksByProcessId = pl.net.bluesoft.util.lang.Collections.group(tasks, new Transformer<Task, String>() { @Override public String transform(Task task) { Execution exec = getProcessEngine(ctx).getExecutionService().findExecutionById(task.getExecutionId()); return exec.getProcessInstance().getId(); } }); final Map<String, ProcessInstance> instances = ctx.getProcessInstanceDAO().getProcessInstanceByInternalIdMap(tasksByProcessId.keySet()); final List<BpmTask> result = new ArrayList<BpmTask>(); for (Map.Entry<String, List<Task>> entry : tasksByProcessId.entrySet()) { final String processId = entry.getKey(); List<Task> processTasks = entry.getValue(); result.addAll(new Mapcar<Task, BpmTask>(processTasks) { @Override public BpmTask lambda(Task task) { ProcessInstance pi = instances.get(processId); if (pi == null) { loger.warning("process " + processId + " not found"); return null; } return collectTask(task, pi, ctx); } }.go()); } java.util.Collections.sort(result, new Comparator<BpmTask>() { @Override public int compare(BpmTask o1, BpmTask o2) { return o2.getCreateDate().compareTo(o1.getCreateDate()); } }); return result; } @Override public List<BpmTask> findUserTasks(final ProcessInstance processInstance, ProcessToolContext ctx) { final Map<String, Execution> executions = getActiveExecutions(processInstance.getInternalId(), ctx); if (executions.isEmpty()) { return new ArrayList<BpmTask>(); } Command<List<Task>> cmd = new Command<List<Task>>() { @Override public List<Task> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); } @Override protected void applyParameters(Query query) { query.setParameterList("executionId", executions.keySet()); query.setParameterList("assignee", java.util.Collections.singleton(user.getLogin())); } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select task "); hql.append("from "); hql.append(TaskImpl.class.getName()); hql.append(" as task "); hql.append("where executionId in (:executionId) "); hql.append("and task.assignee in (:assignee) "); return hql.toString(); } }; return (List<Task>) q.execute(environment); } }; List<Task> tasks = getProcessEngine(ctx).execute(cmd); return collectTasks(tasks, processInstance, ctx); } @Override public BpmTask performAction(ProcessStateAction action, BpmTask task, ProcessToolContext ctx) { BpmTask bpmTask = getTaskData(task.getInternalTaskId(), ctx); if (bpmTask == null || bpmTask.isFinished()) return bpmTask; ProcessInstance processInstance = bpmTask.getProcessInstance(); ProcessInstanceLog log = addActionLogEntry(action, task, ctx); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("ACTION", action.getBpmName()); processInstance.setSimpleAttribute("ACTION", action.getBpmName()); List<String> outgoingTransitionNames = getOutgoingTransitionNames(task.getInternalTaskId(), ctx); ProcessEngine processEngine = getProcessEngine(ctx); Task bpmTaskInstance = processEngine.getTaskService().getTask(task.getInternalTaskId()); String executionId = bpmTaskInstance.getExecutionId(); Set<String> taskIdsBeforeCompletion = new HashSet<String>(); pl.net.bluesoft.util.lang.Collections.collect(findProcessTasksWithUser(processInstance, ctx), new Transformer<BpmTask, String>() { @Override public String transform(BpmTask obj) { return obj.getInternalTaskId(); } }, taskIdsBeforeCompletion); if (outgoingTransitionNames.size() == 1) processEngine.getTaskService().completeTask(task.getInternalTaskId(), outgoingTransitionNames.get(0), vars); //BPMN2.0 style, decision is taken on the XOR gateway else processEngine.getTaskService().completeTask(task.getInternalTaskId(), action.getBpmName(), vars); broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.TASK_FINISHED, task, user)); /* Inform queue manager about task finish and process state change */ ctx.getUserProcessQueueManager().onTaskFinished(task); String processState = getProcessState(processInstance, ctx); /* Check if new subProcess is created */ boolean startsSubprocess = updateSubprocess(processInstance, executionId, ctx); fillProcessAssignmentData(processEngine, processInstance, ctx); processInstance.setState(processState); if (startsSubprocess == false && processState == null && processInstance.getRunning() && !isProcessRunning(processInstance.getInternalId(), ctx)) { processInstance.setRunning(false); } if (log.getUserSubstitute() == null) broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.SIGNAL_PROCESS, bpmTask, user)); else broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.SIGNAL_PROCESS, bpmTask, log.getUserSubstitute())); if (Strings.hasText(action.getAssignProcessStatus())) { String processStatus = action.getAssignProcessStatus(); ProcessStatus ps = processStatus.length() == 1 ? ProcessStatus.fromChar(processStatus.charAt(0)) : ProcessStatus.fromString(processStatus); processInstance.setStatus(ps); } else { boolean isProcessRunning = processInstance.isProcessRunning(); //boolean isProcessRunning = isProcessRunning(pi.getInternalId(), ctx); /* Process is not running and no new suprocesses are created, so process should * be finished by now */ if(!isProcessRunning && !startsSubprocess) { broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.END_PROCESS, bpmTask, user)); /* Inform queue manager about process ending. Only main process is stored */ if(!processInstance.isSubprocess()) ctx.getUserProcessQueueManager().onProcessFinished(processInstance, bpmTask); processInstance.setStatus(ProcessStatus.FINISHED); } /* Process is running or is halted, but new subprocess are created */ else if(!isProcessRunning && startsSubprocess) { broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.PROCESS_HALTED, bpmTask, user)); /* Inform queue manager about process halt */ ctx.getUserProcessQueueManager().onProcessHalted(processInstance, bpmTask); processInstance.setStatus(ProcessStatus.RUNNING); } else { processInstance.setStatus(ProcessStatus.RUNNING); } } ctx.getProcessInstanceDAO().saveProcessInstance(processInstance); BpmTask userTask = null; BpmTask autoSkipTask = null; /* Is process finished */ boolean isProcessFinished = processInstance.getStatus().equals(ProcessStatus.FINISHED); boolean isSubProcess = processInstance.getParent() != null; List<BpmTask> tasksAfterCompletion = null; if(startsSubprocess && processInstance.getChildren() != null) { for(ProcessInstance child : processInstance.getChildren()) { tasksAfterCompletion = findProcessTasks(child, ctx); if(tasksAfterCompletion != null && tasksAfterCompletion.size() > 0) break; } } if(tasksAfterCompletion == null || tasksAfterCompletion.size() == 0) { tasksAfterCompletion = findProcessTasks(processInstance, ctx); } if(processInstance.getParent() != null && (tasksAfterCompletion == null || tasksAfterCompletion.size() == 0)) { tasksAfterCompletion = findProcessTasks(processInstance.getParent(), ctx); } if(tasksAfterCompletion != null) { for (BpmTask createdTask : tasksAfterCompletion) { if (!taskIdsBeforeCompletion.contains(createdTask.getInternalTaskId())) { broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.ASSIGN_TASK, createdTask, user)); /* Inform queue manager about task assigne */ ctx.getUserProcessQueueManager().onTaskAssigne(createdTask); } if (Lang.equals(user.getId(), createdTask.getOwner().getId())) { userTask = createdTask; } if(createdTask.getTaskName().toLowerCase().startsWith(AUTO_SKIP_TASK_NAME_PREFIX.toLowerCase())) { autoSkipTask = createdTask; } } } if(autoSkipTask != null) { ProcessStateAction skipAction = new ProcessStateAction(); skipAction.setBpmName(AUTO_SKIP_ACTION_NAME); return performAction(skipAction, autoSkipTask, ctx); } /* Task assigned to queue */ if (userTask == null) { /* Process is finished, ask about parent process queues */ if(isProcessFinished && isSubProcess) processInstance = processInstance.getParent(); /* Get task assigned to queues */ Collection<BpmTask> queueTasks = getProcessTaskInQueues(ctx, processInstance); for(BpmTask queueTask: queueTasks) { MutableBpmTask mutableTask = new MutableBpmTask(queueTask); mutableTask.setAssignee(task.getAssignee()); mutableTask.setProcessInstance(processInstance); /* Inform queue manager about task assigne */ ctx.getUserProcessQueueManager().onQueueAssigne(mutableTask); } MutableBpmTask mutableTask = new MutableBpmTask(task); mutableTask.setFinished(true); mutableTask.setProcessInstance(processInstance); userTask = mutableTask; } broadcastEvent(ctx, new ViewEvent(ViewEvent.Type.ACTION_COMPLETE)); return userTask; } @Override public boolean isProcessRunning(String internalId, ProcessToolContext ctx) { long start = System.currentTimeMillis(); if (internalId == null) { return false; } ExecutionService service = getProcessEngine(ctx).getExecutionService(); org.jbpm.api.ProcessInstance processInstance = service.findProcessInstanceById(internalId); long duration = System.currentTimeMillis() - start; log.severe("isProcessRunning: " + duration); return processInstance != null && !processInstance.isEnded(); } @Override protected ProcessInstance startProcessInstance(ProcessDefinitionConfig config, String externalKey, ProcessToolContext ctx, ProcessInstance pi) { final ExecutionService execService = getProcessEngine(ctx).getExecutionService(); Map vars = new HashMap(); vars.put("processInstanceId", String.valueOf(pi.getId())); vars.put("initiator", user.getLogin()); org.jbpm.api.ProcessInstance instance = execService.startProcessInstanceByKey(config.getBpmDefinitionKey(), vars, externalKey); pi.setInternalId(instance.getId()); return pi; } @Override public ProcessToolBpmSession createSession(UserData user, Collection<String> roleNames, ProcessToolContext ctx) { ProcessToolJbpmSession session = new ProcessToolJbpmSession(user, roleNames, ctx); session.substitutingUser = this.user; session.substitutingUserEventBusManager = this.eventBusManager; return session; } @Override public void assignTaskToUser(ProcessToolContext ctx, String taskId, String userLogin) { long start = System.currentTimeMillis(); ProcessEngine processEngine = getProcessEngine(ctx); processEngine.getTaskService().assignTask(taskId, userLogin); BpmTask taskData = getTaskData(taskId, ctx); UserData newUser = new UserData(); newUser.setLogin(userLogin); UserData loadOrCreateUser = loadOrCreateUser(ctx,newUser); broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.ASSIGN_TASK, taskData, loadOrCreateUser)); broadcastEvent(ctx, new BpmEvent(BpmEvent.Type.ASSIGN_TASK, taskData, user)); ctx.getUserProcessQueueManager().onTaskAssigne(taskData); long duration = System.currentTimeMillis() - start; log.severe("assignTaskToUser: " + duration); } protected Map<String, Execution> getActiveExecutions(String internalId, ProcessToolContext ctx) { Execution exec = getProcessEngine(ctx).getExecutionService().findExecutionById(internalId); if (exec == null) { loger.warning("Unable to find execution by process internal id: " + internalId); return new HashMap<String, Execution>(); } Collection<Execution> executions = getActiveExecutions(exec, new Predicate<Execution>() { @Override public boolean apply(Execution exec) { return Execution.STATE_ACTIVE_ROOT.equals(exec.getState()) || Execution.STATE_ACTIVE_CONCURRENT.equals(exec.getState()); } }); return pl.net.bluesoft.util.lang.Collections.transform(executions, new Transformer<Execution, String>() { @Override public String transform(Execution obj) { return obj.getId(); } }); } private Collection<Execution> getActiveExecutions(Execution exec, Predicate<Execution> predicate) { Collection<Execution> result = new ArrayList<Execution>(); if (predicate.apply(exec)) { result.add(exec); } else { Collection<? extends Execution> executions = exec.getExecutions(); if (executions != null && !executions.isEmpty()) { for (Execution e : executions) { result.addAll(getActiveExecutions(e, predicate)); } } } return result; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public List<String> getOutgoingTransitionNames(String internalId, ProcessToolContext ctx) { ProcessEngine engine = getProcessEngine(ctx); return new ArrayList<String>(engine.getTaskService().getOutcomes(internalId)); } public boolean updateSubprocess(final ProcessInstance parentPi, String executionId, ProcessToolContext ctx) { ProcessEngine engine = getProcessEngine(ctx); ExecutionService executionService = engine.getExecutionService(); Execution jbpmPi = executionService.findExecutionById(executionId); if(jbpmPi != null){ Execution subprocess = jbpmPi.getSubProcessInstance(); if(subprocess != null){ ctx.getHibernateSession().refresh(subprocess); if (ctx.getProcessInstanceDAO().getProcessInstanceByInternalId(subprocess.getId()) == null) { String processDefinitionId = subprocess.getProcessDefinitionId().replaceFirst("-\\d+$", ""); ProcessDefinitionConfig config = ctx.getProcessDefinitionDAO().getActiveConfigurationByKey( processDefinitionId); /* Create new instance of parent process' subprocess */ ProcessInstance subProcessInstance = createSubprocessInstance(config, ctx, parentPi, "parent_process", subprocess.getId()); long subPiId = ctx.getProcessInstanceDAO().saveProcessInstance(subProcessInstance); executionService.createVariable(subprocess.getId(), "processInstanceId", String.valueOf(subPiId), false); return true; } } } return false; } public List<String> getOutgoingTransitionDestinationNames(String internalId, ProcessToolContext ctx) { long start = System.currentTimeMillis(); ProcessEngine engine = getProcessEngine(ctx); org.jbpm.api.ProcessInstance pi = engine.getExecutionService().findProcessInstanceById(internalId); final ExecutionImpl execution = (ExecutionImpl) pi.getProcessInstance(); final List<String> transitionNames = new ArrayList<String>(); engine.execute(new Command() { public Object execute(Environment env) { for (Transition transition : execution.getActivity().getOutgoingTransitions()) { transitionNames.add(transition.getDestination().getName()); } return null; } }); long duration = System.currentTimeMillis() - start; log.severe("getOutgoingTransitionDestinationNames: " + duration); return transitionNames; } private void fillProcessAssignmentData(final ProcessEngine processEngine, final ProcessInstance pi, ProcessToolContext ctx) { Set<String> assignees = new HashSet<String>(); Set<String> queues = new HashSet<String>(); TaskService taskService = processEngine.getTaskService(); List<BpmTask> processTasks = findProcessTasks(pi, null, null, ctx); for (BpmTask t : processTasks) { if (t.getAssignee() != null) { assignees.add(t.getAssignee()); } else { //some optimization could be possible for (Participation participation : taskService.getTaskParticipations(t.getInternalTaskId())) { if ("candidate".equals(participation.getType())) { queues.add(participation.getGroupId()); } } } } pi.setActiveTasks(processTasks.toArray(new BpmTask[processTasks.size()])); pi.setAssignees(assignees.toArray(new String[assignees.size()])); pi.setTaskQueues(queues.toArray(new String[queues.size()])); } private ProcessInstanceLog addActionLogEntry(ProcessStateAction action, BpmTask task, ProcessToolContext ctx) { ProcessStateConfiguration state = ctx.getProcessDefinitionDAO().getProcessStateConfiguration(task); ProcessInstanceLog log = new ProcessInstanceLog(); if(AUTO_SKIP_ACTION_NAME.toLowerCase().equals(action.getBpmName().toLowerCase())) return log; log.setLogType(ProcessInstanceLog.LOG_TYPE_PERFORM_ACTION); log.setState(state); log.setEntryDate(Calendar.getInstance()); log.setEventI18NKey("process.log.action-performed"); log.setLogValue(action.getBpmName()); log.setAdditionalInfo(nvl(action.getLabel(), action.getDescription(), action.getBpmName())); log.setUser(findOrCreateUser(user, ctx)); log.setUserSubstitute(getSubstitutingUser(ctx)); log.setExecutionId(task.getExecutionId()); log.setOwnProcessInstance(task.getProcessInstance()); task.getProcessInstance().getRootProcessInstance().addProcessLog(log); return log; } @Override public void adminCancelProcessInstance(ProcessInstance pi) { loger.severe("User: " + user.getLogin() + " attempting to cancel process: " + pi.getInternalId()); long start = System.currentTimeMillis(); ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext(); pi = getProcessData(pi.getInternalId(), ctx); ProcessEngine processEngine = getProcessEngine(ctx); processEngine.getExecutionService().endProcessInstance(pi.getInternalId(), "admin-cancelled"); fillProcessAssignmentData(processEngine, pi, ctx); pi.setRunning(false); pi.setState(null); ctx.getProcessInstanceDAO().saveProcessInstance(pi); long duration = System.currentTimeMillis() - start; log.severe("adminCancelProcessInstance: " + duration); loger.severe("User: " + user.getLogin() + " has cancelled process: " + pi.getInternalId()); } @Override public void adminReassignProcessTask(ProcessInstance pi, BpmTask bpmTask, String userLogin) { loger.severe("User: " + user.getLogin() + " attempting to reassign task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " to user: " + userLogin); ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext(); pi = getProcessData(pi.getInternalId(), ctx); ProcessEngine processEngine = getProcessEngine(ctx); TaskService ts = processEngine.getTaskService(); Task task = ts.getTask(bpmTask.getInternalTaskId()); if (nvl(userLogin,"").equals(nvl(task.getAssignee(),""))) { loger.severe("User: " + user.getLogin() + " has not reassigned task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " as the user is the same: " + userLogin); return; } //this call should also take care of swimlanes ts.assignTask(bpmTask.getInternalTaskId(), userLogin); fillProcessAssignmentData(processEngine, pi, ctx); loger.info("Process.running:" + pi.getRunning()); ctx.getProcessInstanceDAO().saveProcessInstance(pi); loger.severe("User: " + user.getLogin() + " has reassigned task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " to user: " + userLogin); } @Override public void adminCompleteTask(ProcessInstance pi, BpmTask bpmTask, ProcessStateAction action) { loger.severe("User: " + user.getLogin() + " attempting to complete task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " to outcome: " + action); performAction(action, bpmTask, ProcessToolContext.Util.getThreadProcessToolContext()); loger.severe("User: " + user.getLogin() + " has completed task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " to outcome: " + action); } public List<String> getAvailableLogins(final String filter) { Command<List<User>> cmd = new Command<List<User>>() { @Override public List<User> execute(Environment environment) throws Exception { AbstractQuery q = new AbstractQuery() { @Override protected void applyPage(Query query) { query.setFirstResult(0); query.setFetchSize(20); } @Override protected void applyParameters(Query query) { query.setParameter("filter", "%" + filter + "%"); } @Override public String hql() { StringBuilder hql = new StringBuilder(); hql.append("select user "); hql.append("from "); hql.append(UserImpl.class.getName()); hql.append(" as user "); hql.append("where id like :filter "); return hql.toString(); } }; return (List<User>) q.execute(environment); } }; List<User> users = getProcessEngine(ProcessToolContext.Util.getThreadProcessToolContext()).execute(cmd); List<String> res = new ArrayList<String>(); for (User u : users) { res.add(u.getId()); } java.util.Collections.sort(res); return res; } @Override public List<GraphElement> getProcessHistory(final ProcessInstance pi) { ProcessEngine processEngine = getProcessEngine(ProcessToolContext.Util.getThreadProcessToolContext()); HistoryService service = processEngine.getHistoryService(); HistoryActivityInstanceQuery createHistoryActivityInstanceQuery = service.createHistoryActivityInstanceQuery(); HistoryActivityInstanceQuery activityInstanceQuery = createHistoryActivityInstanceQuery.processInstanceId(pi.getInternalId()); List<HistoryActivityInstance> list = activityInstanceQuery.list(); Map<String,GraphElement> processGraphElements = parseProcessDefinition(pi); List<GraphElement> transitionaArcsList = new ArrayList<GraphElement>(); Collection<GraphElement> pge = processGraphElements.values() ; for (GraphElement ge : pge) { if( ge instanceof TransitionArc){ TransitionArc ta = (TransitionArc)ge; transitionaArcsList.add(ta); } } ArrayList<GraphElement> res = new ArrayList<GraphElement>(); for (HistoryActivityInstance hpi : list) { loger.fine("Handling: " + hpi.getActivityName()); if (hpi instanceof HistoryActivityInstanceImpl) { HistoryActivityInstanceImpl activity = (HistoryActivityInstanceImpl) hpi; String activityName = activity.getActivityName(); if (res.isEmpty()) { //initialize start node and its transition GraphElement startNode = processGraphElements.get("__AWF__start_node"); if (startNode != null) { res.add(startNode); } GraphElement firstTransition = processGraphElements.get("__AWF__start_transition_to_" + activityName); if (firstTransition != null) { res.add(firstTransition); } } StateNode sn = (StateNode) processGraphElements.get(activityName); ArrayList<StateNode> arrayList = new ArrayList<StateNode>(); Collection<GraphElement> values = processGraphElements.values(); for (GraphElement graphElement : values) { if(graphElement instanceof StateNode){ arrayList.add((StateNode) graphElement); } } if (sn == null) continue; sn = sn.cloneNode(); sn.setUnfinished(activity.getEndTime() == null); sn.setLabel(activityName + ": " + hpi.getDuration() + "ms"); res.add(sn); //look for transition TransitionArc ta = (TransitionArc) processGraphElements.get(activityName + "_" + activity.getTransitionName()); if (ta == null) { //look for default! ta = (TransitionArc) processGraphElements.get("__AWF__default_transition_" + activityName); } if (ta == null) { continue; } res.add(ta.cloneNode()); } else { loger.severe("Unsupported entry: " + hpi); } } addJoinAndForkElements(res,processGraphElements); HistoryProcessInstanceQuery historyProcessInstanceQuery = processEngine.getHistoryService() .createHistoryProcessInstanceQuery().processInstanceId(pi.getInternalId()); HistoryProcessInstance historyProcessInstance = historyProcessInstanceQuery.uniqueResult(); if (historyProcessInstance != null && historyProcessInstance.getEndActivityName() != null) { StateNode sn = (StateNode) processGraphElements.get(historyProcessInstance.getEndActivityName()); if (sn != null) { StateNode e = sn.cloneNode(); e.setUnfinished(true); res.add(e); } } return res; } private HashMap<String, GraphElement> parseProcessDefinition(ProcessInstance pi) { HashMap<String, GraphElement> res = new HashMap<String, GraphElement>(); byte[] processDefinition = getProcessDefinition(pi); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { //Using factory get an instance of document builder DocumentBuilder db = dbf.newDocumentBuilder(); //parse using builder to get DOM representation of the XML file Document dom = db.parse(new ByteArrayInputStream(processDefinition)); Element documentElement = dom.getDocumentElement(); String[] nodeTypes = new String[]{"start", "end", "java", "task", "decision","join","fork"}; for (String nodeType : nodeTypes) { NodeList nodes = documentElement.getElementsByTagName(nodeType); for (int i = 0; i < nodes.getLength(); i++) { Element node = (Element) nodes.item(i); try { StateNode sn = new StateNode(); String gval = node.getAttribute("g"); String[] vals = gval.split(",", 4); int x = Integer.parseInt(vals[0]); int y = Integer.parseInt(vals[1]); int w = Integer.parseInt(vals[2]); int h = Integer.parseInt(vals[3]); sn.setX(x); sn.setY(y); sn.setWidth(w); sn.setHeight(h); sn.setNodeType(nodeType); String name = node.getAttribute("name"); sn.setLabel(name); res.put(name, sn); if ("start".equals(nodeType)) { res.put("__AWF__start_node", sn); } loger.fine("Found node" + name + ": " + x + "," + y + "," + w + "," + h); } catch (Exception e) { loger.log(Level.SEVERE, e.getMessage(), e); } } } //once again - for transitions for (String nodeType : nodeTypes) { NodeList nodes = documentElement.getElementsByTagName(nodeType); for (int i = 0; i < nodes.getLength(); i++) { Element node = (Element) nodes.item(i); try { String startNodeName = node.getAttribute("name"); StateNode startNode = (StateNode) res.get(startNodeName); if (startNode == null) { loger.severe("Start node " + startNodeName + " has not been localized, skipping transition drawing too."); continue; } NodeList transitions = node.getElementsByTagName("transition"); for (int j=0; j < transitions.getLength(); j++) { Element transitionEl = (Element) transitions.item(j); String name = transitionEl.getAttribute("name"); String to = transitionEl.getAttribute("to") ; StateNode endNode = (StateNode) res.get(to); if (endNode == null) { loger.severe("End node " + to + " has not been localized for transition " + name + " of node " + startNodeName + ", skipping transition drawing."); continue; } String g = transitionEl.getAttribute("g"); if (g != null) { String[] dockersAndDistances = g.split(":"); String[] dockers = new String[0]; if (dockersAndDistances.length == 2) { dockers = dockersAndDistances[0].split(";");//what the other numbers mean - I have no idea... } //calculate line start node which is a center of the start node int startX = startNode.getX() + startNode.getWidth()/2; int startY = startNode.getY() + startNode.getHeight()/2; //and the same for end node int endX = endNode.getX() + endNode.getWidth()/2; int endY = endNode.getY() + endNode.getHeight()/2; TransitionArc arc = new TransitionArc(); arc.setName(name); arc.addPoint(startX, startY); for (String docker : dockers) { String[] split = docker.split(",",2); arc.addPoint(Integer.parseInt(split[0]), Integer.parseInt(split[1])); } arc.addPoint(endX, endY); double a;//remember about vertical line double b; endX = arc.getPath().get(1).getX(); endY = arc.getPath().get(1).getY(); if (startX - endX == 0) { //whoa - vertical line - simple case, but requires special approach if (endY > startNode.getY()+startNode.getHeight()) { //below startY = startNode.getY()+startNode.getHeight(); } else { startY = startNode.getY(); } } else { a = ((double)(startY-endY))/((double)(startX - endX)); b = (double)startY - (double)startX*a; for (int x = startX; x <= endX; x++) { int y = (int) Math.round(a*x+b); boolean inside = false; if (x >= startNode.getX() && x <= startNode.getX() + startNode.getWidth()) { if (y >= startNode.getY() && y <= startNode.getY() + startNode.getHeight()) { inside = true; } } if (!inside) { startX = x; startY = y; break; } } for (int x = startX; x > endX; x--) { int y = (int) Math.round(a*x+b); boolean inside = false; if (x >= startNode.getX() && x <= startNode.getX() + startNode.getWidth()) { if (y >= startNode.getY() && y <= startNode.getY() + startNode.getHeight()) { inside = true; } } if (!inside) { startX = x; startY = y; break; } } } arc.getPath().get(0).setX(startX); arc.getPath().get(0).setY(startY); endX = arc.getPath().get(arc.getPath().size()-1).getX(); endY = arc.getPath().get(arc.getPath().size()-1).getY(); startX = arc.getPath().get(arc.getPath().size()-2).getX(); startY = arc.getPath().get(arc.getPath().size()-2).getY(); if (startX - endX == 0) { //whoa - vertical line - simple case, but requires special approach if (startY > endNode.getY()+endNode.getHeight()) { //below endY = endNode.getY()+endNode.getHeight(); } else { endY = endNode.getY(); } } else { a = ((double)(startY-endY))/((double)(startX - endX));//remember about vertical line //startY = startX*a+b b = (double)startY - (double)startX*a; for (int x = endX; x <= startX; x++) { int y = (int) Math.round(a*x+b); boolean inside = false; if (x >= endNode.getX() && x <= endNode.getX() + endNode.getWidth()) { if (y >= endNode.getY() && y <= endNode.getY() + endNode.getHeight()) { inside = true; } } if (!inside) { endX = x; endY = y; break; } } for (int x = endX; x > startX; x--) { int y = (int) Math.round(a*x+b); boolean inside = false; if (x >= endNode.getX() && x <= endNode.getX() + endNode.getWidth()) { if (y >= endNode.getY() && y <= endNode.getY() + endNode.getHeight()) { inside = true; } } if (!inside) { endX = x; endY = y; break; } } } arc.getPath().get(arc.getPath().size()-1).setX(endX); arc.getPath().get(arc.getPath().size()-1).setY(endY); arc.setDestination(to); arc.setSource(startNodeName); res.put(startNodeName + "_" + name,arc); if ("start".equals(nodeType)) { res.put("__AWF__start_transition_to_" + to, arc); } if (transitions.getLength() == 1) { res.put("__AWF__default_transition_" + startNodeName, arc); } } else { loger.severe("No 'g' attribute for transition "+ name + " of node " + startNodeName + ", skipping transition drawing."); } } } catch (Exception e) { loger.log(Level.SEVERE, e.getMessage(), e); } } } } catch (Exception e) { throw new RuntimeException(e); } return res; } private void addJoinAndForkElements(ArrayList<GraphElement> historyGraph, Map<String, GraphElement> originalGraphMap) { Map<String, StateNode> joinList = new HashMap<String, StateNode>(); Map<String, StateNode> forkList = new HashMap<String, StateNode>(); Map<GraphElement, List<TransitionArc>> joinWithTransitions = new HashMap<GraphElement, List<TransitionArc>>(); Map<GraphElement, List<TransitionArc>> forkWithTransitions = new HashMap<GraphElement, List<TransitionArc>>(); List<TransitionArc> transitionArcList = new ArrayList<TransitionArc>(); Collection<GraphElement> originalGraph = originalGraphMap.values(); for (GraphElement originalGraphElement : originalGraph) { fillListWithAllPosibleTransitionArc(originalGraphElement, transitionArcList); fillListsWithAllPosibleJoinAndForkNodes(originalGraphElement, joinList, forkList); } if (joinList.size() > 0 || forkList.size() > 0) { for (TransitionArc transitionArc : transitionArcList) { relateNodesWithTransitionArcs(forkList, transitionArc, forkWithTransitions); relateNodesWithTransitionArcs(joinList, transitionArc, joinWithTransitions); } Set<GraphElement> forks = forkWithTransitions.keySet(); ArrayList<String> tasksNamesFromHistoryAsArray = getTasksNamesFromHistoryAsArray(historyGraph); for (GraphElement fork : forks) { List<TransitionArc> forkTransitions = forkWithTransitions .get(fork); if (isForkOutgoingsExistsInHistoryGraph(forkTransitions, fork, tasksNamesFromHistoryAsArray)) { historyGraph.addAll(forkTransitions); historyGraph.add(fork); } } Set<GraphElement> joins = joinWithTransitions.keySet(); for (GraphElement join : joins) { List<TransitionArc> list = joinWithTransitions.get(join); if (isJoinOutgoingWasMissingInHistoryGraph(list, tasksNamesFromHistoryAsArray, historyGraph)) { historyGraph.add(join); } } } } private void fillListWithAllPosibleTransitionArc( GraphElement originalGraphElement, List<TransitionArc> transitionArcList) { if (originalGraphElement instanceof TransitionArc) { TransitionArc transitionArc = (TransitionArc) originalGraphElement; transitionArcList.add(transitionArc); } } private void fillListsWithAllPosibleJoinAndForkNodes( GraphElement originalGraphElement, Map<String, StateNode> joinList, Map<String, StateNode> forkList) { if (originalGraphElement instanceof StateNode) { updateNodeList(joinList, JOIN_NODE_NAME, originalGraphElement); updateNodeList(forkList, FORK_NODE_NAME, originalGraphElement); } } private void updateNodeList(Map<String, StateNode> nodeList, String nodeType, GraphElement originalGraphElement) { StateNode stateNode = (StateNode) originalGraphElement; if (stateNode.getNodeType() != null && (stateNode.getNodeType().equals(nodeType))) { nodeList.put(stateNode.getLabel(), stateNode); } } private void relateNodesWithTransitionArcs( Map<String, StateNode> joinForkList, TransitionArc transitionArc, Map<GraphElement, List<TransitionArc>> joinOrForkWithTransitions) { if (joinForkList.containsKey(transitionArc.getSource())) { StateNode stateNode = joinForkList.get(transitionArc.getSource()); List<TransitionArc> listOfTransition = null; if (joinOrForkWithTransitions.containsKey(stateNode)) { listOfTransition = joinOrForkWithTransitions.get(stateNode); } else { listOfTransition = new ArrayList<TransitionArc>(); } listOfTransition.add(transitionArc); joinOrForkWithTransitions.put(stateNode, listOfTransition); } } private ArrayList<String> getTasksNamesFromHistoryAsArray( ArrayList<GraphElement> historyGraph) { ArrayList<String> newHistoryElement = new ArrayList<String>(); for (GraphElement historyElement : historyGraph) { if (historyElement instanceof StateNode) { StateNode historyNode = (StateNode) historyElement; String histloryNodeLabel = historyNode.getLabel(); String[] split = histloryNodeLabel.split(":"); String taskNameFromHistory = split[0]; newHistoryElement.add(taskNameFromHistory); } } return newHistoryElement; } private boolean isForkOutgoingsExistsInHistoryGraph( List<TransitionArc> list, GraphElement fork, ArrayList<String> tasksNamesFromHistoryAsArray) { int apperenceCounter = list.size(); for (TransitionArc transitionArc : list) { if (tasksNamesFromHistoryAsArray.contains(transitionArc .getDestination())) { apperenceCounter--; } } if (apperenceCounter == 0) { return true; } return false; } private boolean isJoinOutgoingWasMissingInHistoryGraph( List<TransitionArc> list, ArrayList<String> tasksNamesFromHistoryAsArray, ArrayList<GraphElement> historyGraph) { boolean addedTransition = false; for (TransitionArc transitionArc : list) { if (tasksNamesFromHistoryAsArray.contains(transitionArc .getDestination())) { historyGraph.add(transitionArc); addedTransition = true; } } if (addedTransition == true) { return true; } return false; } @Override public byte[] getProcessLatestDefinition(String definitionKey, String processName) { String resourceName = processName + ".jpdl.xml"; long start = System.currentTimeMillis(); byte[] fetchLatestProcessResource = fetchLatestProcessResource(definitionKey, resourceName); long duration = System.currentTimeMillis() - start; log.severe("getProcessLatestDefinition: " + duration); return fetchLatestProcessResource; } @Override public byte[] getProcessMapImage(ProcessInstance pi) { long start = System.currentTimeMillis(); String resourceName = pi.getDefinition().getProcessName() + ".png"; byte[] processResource = fetchProcessResource(pi, resourceName); long duration = System.currentTimeMillis() - start; log.severe("getProcessMapImage: " + duration); return processResource; } @Override public byte[] getProcessDefinition(ProcessInstance pi) { long start = System.currentTimeMillis(); String resourceName = pi.getDefinition().getProcessName() + ".jpdl.xml"; byte[] processResource = fetchProcessResource(pi, resourceName); long duration = System.currentTimeMillis() - start; log.severe("getProcessDefinition: " + duration); return processResource; } private byte[] fetchLatestProcessResource(String definitionKey, String resourceName) { RepositoryService service = getProcessEngine(ProcessToolContext.Util.getThreadProcessToolContext()) .getRepositoryService(); List<ProcessDefinition> latestList = service.createProcessDefinitionQuery() .processDefinitionKey(definitionKey).orderDesc("deployment.dbid").page(0, 1).list(); if (!latestList.isEmpty()) { String oldDeploymentId = latestList.get(0).getDeploymentId(); return getDeploymentResource(resourceName, oldDeploymentId); } return null; } private byte[] fetchProcessResource(ProcessInstance pi, String resourceName) { ProcessEngine processEngine = getProcessEngine(ProcessToolContext.Util.getThreadProcessToolContext()); RepositoryService service = processEngine.getRepositoryService(); ExecutionService executionService = processEngine.getExecutionService(); org.jbpm.api.ProcessInstance processInstanceById = executionService.findProcessInstanceById(pi.getInternalId()); String processDefinitionId; if (processInstanceById == null) { //look in history service HistoryProcessInstanceQuery historyProcessInstanceQuery = processEngine.getHistoryService() .createHistoryProcessInstanceQuery().processInstanceId(pi.getInternalId()); HistoryProcessInstance historyProcessInstance = historyProcessInstanceQuery.uniqueResult(); processDefinitionId = historyProcessInstance.getProcessDefinitionId(); } else { processDefinitionId = processInstanceById.getProcessDefinitionId(); } List<ProcessDefinition> latestList = service.createProcessDefinitionQuery() .processDefinitionId(processDefinitionId).orderDesc("deployment.dbid").page(0, 1).list(); if (!latestList.isEmpty()) { String oldDeploymentId = latestList.get(0).getDeploymentId(); return getDeploymentResource(resourceName, oldDeploymentId); } return null; } private byte[] getDeploymentResource(String resourceName, String oldDeploymentId) { RepositoryService service = getProcessEngine(ProcessToolContext.Util.getThreadProcessToolContext()).getRepositoryService();; try { InputStream oldStream = service.getResourceAsStream(oldDeploymentId, resourceName); try { if (oldStream != null) { ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); int c; while ((c = oldStream.read()) >= 0) { bos2.write(c); } return bos2.toByteArray(); } } finally { if (oldStream != null) { oldStream.close(); } } } catch (IOException e) { throw new RuntimeException(e); } return null; } public String deployProcessDefinition(String processName, InputStream definitionStream, InputStream processMapImageStream) { RepositoryService service = getProcessEngine(ProcessToolContext.Util.getThreadProcessToolContext()) .getRepositoryService(); NewDeployment deployment = service.createDeployment(); deployment.addResourceFromInputStream(processName + ".jpdl.xml", definitionStream); if (processMapImageStream != null) deployment.addResourceFromInputStream(processName + ".png", processMapImageStream); return deployment.deploy(); } public String getProcessState(ProcessInstance pi, ProcessToolContext ctx) { List<BpmTask> tasks = findProcessTasks(pi, ctx); for (BpmTask task : tasks) { return task.getTaskName(); } return null; } }