Package org.aperteworkflow.ext.activiti

Source Code of org.aperteworkflow.ext.activiti.ActivitiBpmSession

package org.aperteworkflow.ext.activiti;

import org.activiti.engine.*;
import org.activiti.engine.history.*;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.AbstractQuery;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.db.DbSqlSession;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.runtime.ExecutionImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.*;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.ibatis.session.Configuration;
import org.aperteworkflow.bpm.graph.GraphElement;
import org.aperteworkflow.bpm.graph.StateNode;
import org.aperteworkflow.bpm.graph.TransitionArc;
import org.aperteworkflow.bpm.graph.TransitionArcPoint;
import org.aperteworkflow.ext.activiti.mybatis.TaskQueryImplEnhanced;
import org.hibernate.Query;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
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.*;
import pl.net.bluesoft.rnd.processtool.model.ProcessInstance;
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.util.lang.Mapcar;
import pl.net.bluesoft.util.lang.Predicate;
import pl.net.bluesoft.util.lang.Strings;
import pl.net.bluesoft.util.lang.Transformer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static pl.net.bluesoft.util.lang.FormatUtil.nvl;
import static pl.net.bluesoft.util.lang.Lang.keyFilter;
import static pl.net.bluesoft.util.lang.StringUtil.hasText;

/**
* @author tlipski@bluesoft.net.pl
*/
public class ActivitiBpmSession extends AbstractProcessToolSession {

    private static final Logger LOGGER = Logger.getLogger(ActivitiBpmSession.class.getName());
    public static final String BPMNDI_NAMESPACE = "http://www.omg.org/spec/BPMN/20100524/DI";
    public static final String BPMN_NS = "http://www.omg.org/spec/BPMN/20100524/MODEL";
    public static final String OMG_DC_URI = "http://www.omg.org/spec/DD/20100524/DC";

    /**
     * @param user
     * @param roleNames
     */
    public ActivitiBpmSession(UserData user, Collection<String> roleNames) {
        super(user, roleNames, ProcessToolContext.Util.getThreadProcessToolContext().getRegistry());
        IdentityService is = getProcessEngine().getIdentityService();
        User bpmUser = is.createUserQuery().userId(user.getLogin()).singleResult();
        if (bpmUser == null) {
            bpmUser = is.newUser(user.getLogin());
            bpmUser.setEmail(user.getEmail());
            bpmUser.setFirstName(user.getRealName());
            is.saveUser(bpmUser);
        }
    }

    public String getProcessState(ProcessInstance pi, ProcessToolContext ctx) {
        Task newTask = findProcessTask(pi, ctx);
        return newTask != null ? newTask.getName() : null;
    }

    @Override
    public void saveProcessInstance(ProcessInstance processInstance, ProcessToolContext ctx) {
        RuntimeService es = getProcessEngine().getRuntimeService();
        for (ProcessInstanceAttribute pia : processInstance.getProcessAttributes()) {
            if (pia instanceof BpmVariable) {
                BpmVariable bpmVar = (BpmVariable) pia;
                if (hasText(bpmVar.getBpmVariableName())) {
                    es.setVariable(processInstance.getInternalId(),
                            bpmVar.getBpmVariableName(),
                            bpmVar.getBpmVariableValue());
                }
            }
        }

        super.saveProcessInstance(processInstance, ctx);
    }

    @Override
    public BpmTask assignTaskFromQueue(ProcessQueue q, ProcessToolContext processToolContextFromThread) {
        return assignTaskFromQueue(q, null, processToolContextFromThread);
    }

    @Override
    public void assignTaskToUser(ProcessToolContext ctx, String taskId, String userLogin) {
        ProcessEngine processEngine = getProcessEngine();
        processEngine.getTaskService().setAssignee(taskId, userLogin);
    }

    @Override
     public BpmTask getTaskData(String taskId, ProcessToolContext ctx) {
       Task task = getProcessEngine().getTaskService().createTaskQuery().taskId(taskId).singleResult();
       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) {
       List<Task> tasks = getProcessEngine().getTaskService().createTaskQuery()
           //.notSuspended()
                .taskName(taskName)
           .executionId(taskExecutionId)
           .taskAssignee(user.getLogin())
           .listPage(0, 1);
       if (tasks.isEmpty()) {
         log.warning("Task " + taskExecutionId + " not found");
         return null;
       }
       List<BpmTask> bpmTasks = findProcessInstancesForTasks(tasks, ctx);
       return bpmTasks;
     }

    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().getRuntimeService().createExecutionQuery().executionId(task.getExecutionId()).singleResult();
                   return exec.getProcessInstanceId();
               }
           });
       final Map<String, ProcessInstance> instances = ctx.getProcessInstanceDAO().getProcessInstanceByInternalIdMap(tasksByProcessId.keySet());
       final List<BpmTask> result = new ArrayList<BpmTask>();
       for (final String processId : tasksByProcessId.keySet()) {
         List<Task> processTasks = tasksByProcessId.get(processId);
         result.addAll(new Mapcar<Task, BpmTask>(processTasks) {
           @Override
           public BpmTask lambda(Task task) {
             ProcessInstance pi = instances.get(processId);
             if (pi == null) {
               log.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;
     }

    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.getName());
        t.setInternalTaskId(task.getId());
        t.setExecutionId(task.getExecutionId());
        t.setCreateDate(task.getCreateTime());
        t.setFinished(false);
        return t;
    }

     @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().getTaskService().createTaskQuery()
//           .notSuspended()
           .taskName(task.getTaskName())
           .executionId(task.getExecutionId())
           .taskAssignee(user.getLogin())
           .listPage(0, 1);
       if (tasks.isEmpty()) {
         log.warning("Task " + task.getExecutionId() + " not found");
         bpmTask.setFinished(true);
       }
       return bpmTask;
     }

    @Override
    public BpmTask getPastOrActualTask(ProcessInstanceLog log, ProcessToolContext ctx) {
        final UserData user = log.getUser();
        final ProcessInstance pi = log.getProcessInstance();
        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());
        }

        HistoryService hs = getProcessEngine().getHistoryService();

        HistoricTaskInstanceQuery q = hs.createHistoricTaskInstanceQuery()
                .taskAssignee(user.getLogin())
                .processInstanceId(pi.getInternalId());
        if (!taskNames.isEmpty()) //TODO what if there is > 1 task. Activiti API does not support such operation (e.g. in clause in SQL)?
            q = q.taskName(taskNames.iterator().next());

        q = q.orderByHistoricTaskInstanceEndTime().asc();

        List<HistoricTaskInstance> tasks = q.list();
        List<HistoricTaskInstance> pastTasks = new ArrayList<HistoricTaskInstance>();
        for (HistoricTaskInstance task : tasks) {
            if (task.getEndTime() != null && task.getEndTime().compareTo(minDate.getTime()) > 0) {
                pastTasks.add(task);
            }
        }
        if (!pastTasks.isEmpty()) {
            return collectHistoryActivity(pastTasks.get(0), pi, user);
        }
        else {
            List<BpmTask> actualTasks = findProcessTasks(pi, user.getLogin(), taskNames, ctx);
            if (!actualTasks.isEmpty()) {
                return actualTasks.get(0);
            }
        }
        return null;
    }

    private MutableBpmTask collectHistoryActivity(HistoricTaskInstance task, ProcessInstance pi, UserData user) {
        MutableBpmTask t = new MutableBpmTask();
        t.setProcessInstance(pi);
        t.setAssignee(user.getLogin());
        t.setOwner(user);
        t.setTaskName(task.getName());
        t.setInternalTaskId(null);
        t.setExecutionId(task.getExecutionId());
        t.setCreateDate(task.getStartTime());
        t.setFinishDate(task.getEndTime());
        t.setFinished(task.getEndTime() != null);
        return t;
    }



    @Override
    public BpmTask getPastEndTask(ProcessInstanceLog log, ProcessToolContext ctx) {
        final ProcessInstance pi = log.getProcessInstance();
       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;
    }

    private String findEndActivityName(ProcessInstance pi, ProcessToolContext ctx) {
        List<HistoricProcessInstance> history = getProcessEngine().getHistoryService().createHistoricProcessInstanceQuery()
                .processInstanceId(pi.getInternalId())
                .list();
        if (history != null && !history.isEmpty()) {
            String endActivityName = history.get(0).getEndActivityId();
            if (Strings.hasText(endActivityName)) {
                return endActivityName;
            }
        }
        return null;
    }

    @Override
    public List<BpmTask> findUserTasks(ProcessInstance processInstance, ProcessToolContext ctx) {
        List<Task> tasks = getProcessEngine().getTaskService().createTaskQuery()
                .processInstanceId(processInstance.getInternalId())
                .taskAssignee(user.getLogin())
                .listPage(0, 1000);
        return collectTasks(tasks, processInstance, ctx);
    }

    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();
    }

    @Override
    public List<BpmTask> findUserTasks(Integer offset, Integer limit, ProcessToolContext ctx) {
        List<Task> tasks = getProcessEngine().getTaskService().createTaskQuery()
                .taskAssignee(user.getLogin())
                .listPage(offset, limit);
        return findProcessInstancesForTasks(tasks, ctx);
    }

    @Override
    public List<BpmTask> findProcessTasks(ProcessInstance pi, ProcessToolContext ctx) {
        List<Task> tasks = getProcessEngine().getTaskService().createTaskQuery()
                .processInstanceId(pi.getInternalId())
                .listPage(0, 1000);
        return collectTasks(tasks, pi, ctx);
    }

    public List<String> getOutgoingTransitionNames(String internalId, ProcessToolContext ctx) {
        ProcessEngine engine = getProcessEngine();
        org.activiti.engine.runtime.ProcessInstance pi =
                engine.getRuntimeService().createProcessInstanceQuery().processInstanceId(internalId).singleResult();
        ExecutionImpl execution = (ExecutionImpl) pi;
        List<String> transitionNames = new ArrayList<String>();
        for (PvmTransition transition : execution.getActivity().getOutgoingTransitions()) {
            transitionNames.add(transition.getId());
        }
        return transitionNames;
    }

    @Override
    public List<String> getOutgoingTransitionDestinationNames(String internalId, ProcessToolContext ctx) {
        ProcessEngine engine = getProcessEngine();
        org.activiti.engine.runtime.ProcessInstance pi =
                engine.getRuntimeService().createProcessInstanceQuery().processInstanceId(internalId).singleResult();
        ExecutionImpl execution = (ExecutionImpl) pi;
        List<String> transitionNames = new ArrayList<String>();
        for (PvmTransition transition : execution.getActivity().getOutgoingTransitions()) {
            transitionNames.add(transition.getDestination().getId());
        }
        return transitionNames;
    }

    protected ProcessEngine getProcessEngine() {
        ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext();
        if (ctx instanceof ActivitiContextImpl) {
            ProcessEngine engine = ((ActivitiContextImpl) ctx).getProcessEngine();
            if (user != null && user.getLogin() != null)
                engine.getIdentityService().setAuthenticatedUserId(user.getLogin());
            return engine;
        } else {
            throw new IllegalArgumentException(ctx + " not an instance of " + ActivitiContextImpl.class.getName());
        }
    }

    public Collection<ProcessInstance> getUserProcesses(int offset, int limit, ProcessToolContext ctx) {
        List<Task> taskList = getProcessEngine().getTaskService().createTaskQuery()
                .taskAssignee(user.getLogin())
                .orderByExecutionId()
                .desc()
                .listPage(offset, limit);

        List<String> ids = keyFilter("processInstanceId", taskList);
        final Map<String, ProcessInstance> instances = ctx.getProcessInstanceDAO().getProcessInstanceByInternalIdMap(ids);
        return new Mapcar<Task, ProcessInstance>(taskList) {

            @Override
            public ProcessInstance lambda(Task task) {
                ProcessInstance pi = instances.get(task.getProcessInstanceId());
                if (pi == null) {
                    log.warning("process " + task.getProcessInstanceId() + " not found");
                    return null;
                }
                pi.setState(task.getName());
                pi.setTaskId(task.getId());
                return pi;
            }
        }.go();
    }

    @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();
        }

        TaskService ts = getProcessEngine().getTaskService();
        for (ProcessQueue q : configs) {
            q.setProcessCount(ts.createTaskQuery().taskCandidateGroup(q.getName()).taskUnnassigned().count());
        }
        return configs;
    }

    @Override
    public BpmTask assignTaskFromQueue(final ProcessQueue pq, BpmTask pi, ProcessToolContext ctx) {

        Collection<ProcessQueue> configs = getUserQueuesFromConfig(ctx);
        final List<String> names = keyFilter("name", configs);
        final String taskId = pi != null ? pi.getInternalTaskId() : null;
        if (!names.contains(pq.getName())) throw new ProcessToolSecurityException("queue.no.rights", pq.getName());
        TaskService ts = getProcessEngine().getTaskService();

        Task task;
        if (taskId == null) {
            task = ts.createTaskQuery()
                    .taskCandidateGroup(pq.getName())
                    .taskUnnassigned()
                    .orderByExecutionId()
                    .desc()
                    .singleResult();
        } else {
            task = ts.createTaskQuery().taskId(taskId).taskCandidateGroup(pq.getName())
                                .taskUnnassigned().singleResult();
        }
        if (task == null) {
            return null;
        }
        ts.setAssignee(task.getId(), user.getLogin());

        ProcessInstance pi2 = ctx.getProcessInstanceDAO().getProcessInstanceByInternalId(task.getProcessInstanceId());
        if (pi2 == null) {
            if (pi == null)
                return assignTaskFromQueue(pq, ctx);
            else
                return null;
        }
        if (!user.getLogin().equals(ts.createTaskQuery().taskId(task.getId()).singleResult().getAssignee())) {
            if (pi == null)
                return assignTaskFromQueue(pq, ctx);
            else
                return null;
        }
        pi2.setTaskId(task.getId());
        pi2.setState(task.getName());

        ProcessInstanceLog log = new ProcessInstanceLog();
        log.setLogType(ProcessInstanceLog.LOG_TYPE_CLAIM_PROCESS);
        log.setState(ctx.getProcessDefinitionDAO().getProcessStateConfiguration(pi));
        log.setEntryDate(Calendar.getInstance());
        log.setEventI18NKey("process.log.process-assigned");
        log.setLogValue(pq.getName());
        log.setUser(ctx.getUserDataDAO().findOrCreateUser(user));
        log.setAdditionalInfo(pq.getDescription());
        pi2.addProcessLog(log);

        fillProcessAssignmentData(pi2, ctx);
        ctx.getProcessInstanceDAO().saveProcessInstance(pi2);

        eventBusManager.publish(new BpmEvent(BpmEvent.Type.ASSIGN_TASK,
                pi2, user));

        ctx.getEventBusManager().publish(new BpmEvent(BpmEvent.Type.ASSIGN_TASK,
                pi2,
                user));

        return getTaskData(task.getId(), ctx);
    }

    private void fillProcessAssignmentData(final ProcessInstance pi, ProcessToolContext ctx) {
        Set<String> assignees = new HashSet<String>();
        Set<String> queues = new HashSet<String>();
        TaskService taskService = getProcessEngine().getTaskService();
        for (Task t : findProcessTasks(pi, ctx, false)) {
            if (t.getAssignee() != null) {
                assignees.add(t.getAssignee());
            } else { //some optimization could be possible
                for (IdentityLink participation : taskService.getIdentityLinksForTask(t.getId())) {
                    if ("candidate".equals(participation.getType())) {
                        queues.add(participation.getGroupId());
                    }
                }
            }
        }
        pi.setAssignees(assignees.toArray(new String[assignees.size()]));
        pi.setTaskQueues(queues.toArray(new String[queues.size()]));
    }

    @Override
    public boolean isProcessOwnedByUser(final ProcessInstance processInstance, ProcessToolContext ctx) {
        List<Task> taskList = findUserTask(processInstance, ctx);
        return !taskList.isEmpty();

    }

    private List<Task> findUserTask(final ProcessInstance processInstance, ProcessToolContext ctx) {
        return getProcessEngine().getTaskService().createTaskQuery().processInstanceId(processInstance.getInternalId())
                .taskAssignee(user.getLogin()).list();
    }

    @Override
    public List<BpmTask> findProcessTasks(ProcessInstance pi, final String userLogin, ProcessToolContext ctx) {
        return findProcessTasks(pi, userLogin, null, ctx);
    }

    @Override
    public List<BpmTask> findProcessTasks(ProcessInstance pi,
                                          String userLogin,
                                          Set<String> taskNames,
                                          ProcessToolContext ctx) {

        TaskQuery q = getProcessEngine().getTaskService().createTaskQuery().processInstanceId(pi.getInternalId());
        if (userLogin != null)
            q = q.taskAssignee(userLogin);
        if (taskNames != null && !taskNames.isEmpty())  //TODO what if more than 1 task name is supplied
            q = q.taskName(taskNames.iterator().next());
        List<Task> tasks = q.listPage(0, 1000);

       return collectTasks(tasks, pi, ctx);


    }

  @Override
  public List<BpmTask> findFilteredTasks(ProcessInstanceFilter filter, ProcessToolContext ctx) {
    return findProcessTasksHelper(filter, ctx, null);
  }

  @Override
  public List<BpmTask> findFilteredTasks(ProcessInstanceFilter filter, ProcessToolContext ctx, int resultOffset, int maxResults) {
    return findProcessTasksHelper(filter, ctx, new Page(resultOffset, maxResults));
  }

  private List<BpmTask> findProcessTasksHelper(ProcessInstanceFilter filter, ProcessToolContext ctx, final Page page) {
    final TaskQueryImplEnhanced q = new TaskQueryImplEnhanced();
    for (UserData u : filter.getOwners()) {
      q.addOwner(u.getLogin());
    }
    for (UserData u : filter.getCreators()) {
      q.addCreator(u.getLogin());
    }
    // TODO
//        for (UserData u : filter.getNotOwners()) {
//            q.addNotOwner(u.getLogin());
//        }
    for (String qn : filter.getQueues()) {
      q.addGroup(qn);
    }

    ActivitiContextFactoryImpl.CustomStandaloneProcessEngineConfiguration processEngineConfiguration = getProcessEngineConfiguration();
    CommandExecutor commandExecutorTxRequired = processEngineConfiguration.getCommandExecutorTxRequired();
    List<Task> tasks = commandExecutorTxRequired.execute(new Command<List<Task>>() {
      @Override
      public List<Task> execute(CommandContext commandContext) {
        if (page != null) {
          return commandContext.getDbSqlSession().selectList("selectTaskByQueryCriteria_Enhanced", q, page);
        }
        else {
          return commandContext.getDbSqlSession().selectList("selectTaskByQueryCriteria_Enhanced", q);
        }
      }
    });
    return findProcessInstancesForTasks(tasks, ctx);//TODO count total
  }

    private ActivitiContextFactoryImpl.CustomStandaloneProcessEngineConfiguration getProcessEngineConfiguration() {
        ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext();
        if (ctx instanceof ActivitiContextImpl) {
            return ((ActivitiContextImpl) ctx).getCustomStandaloneProcessEngineConfiguration();
        } else {
            throw new IllegalArgumentException(ctx + " not an instance of " + ActivitiContextImpl.class.getName());
        }
    }

    @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) {

        HistoryService hs = getProcessEngine().getHistoryService();

        HistoricTaskInstanceQuery q = hs.createHistoricTaskInstanceQuery()
                .taskAssignee(user.getLogin())
                .processInstanceId(pi.getInternalId());

        q = q.orderByHistoricTaskInstanceEndTime().asc();

        List<HistoricTaskInstance> tasks = q.list();
        List<HistoricTaskInstance> pastTasks = new ArrayList<HistoricTaskInstance>();
        for (HistoricTaskInstance task : tasks) {
            if (task.getEndTime() != null && task.getEndTime().compareTo(minDate.getTime()) > 0) {
                pastTasks.add(task);
            }
        }
        if (pastTasks.isEmpty()) return null;
        MutableBpmTask task = collectHistoryActivity(pastTasks.get(0), pi, user);
        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++;
            }
            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) {
    return findFilteredTasks(filter, ctx).size(); // TOOD
  }

  @Override
    public Collection<BpmTask> getAllTasks(ProcessToolContext ctx) {
        return findProcessInstancesForTasks(
                getProcessEngine().getTaskService().createTaskQuery().orderByTaskId().desc().listPage(0, 1000),
                ctx);
    }

    private List<Task> findProcessTasks(final ProcessInstance processInstance, ProcessToolContext ctx,
                                        final boolean mustHaveAssignee) {
        List<Task> list = getProcessEngine().getTaskService().createTaskQuery()
                .processInstanceId(processInstance.getInternalId()).list();
        if (!mustHaveAssignee)
            return list;

        List<Task> res = new ArrayList<Task>();
        for (Task t : list) {
            if (t.getAssignee() != null) res.add(t);
        }

        return res;
    }

    private Task findProcessTask(final ProcessInstance processInstance, ProcessToolContext ctx) {
        return getProcessEngine().getTaskService().createTaskQuery()
                        .processInstanceId(processInstance.getInternalId()).singleResult();
    }

    public BpmTask performAction(ProcessStateAction action,
                                 BpmTask task,
                                 ProcessToolContext ctx) {
        List<Task> tasks = findUserTask(task.getProcessInstance(), ctx);

        if (tasks.isEmpty()) {
            throw new IllegalArgumentException("process.not.owner");
        }

        return performAction(action, ctx, task);
    }

    public BpmTask performAction(ProcessStateAction action,
                                         ProcessToolContext ctx,
                                         BpmTask bpmTask) {
        return performAction(action, bpmTask.getProcessInstance(),ctx,
                getProcessEngine().getTaskService().createTaskQuery()
                        .taskId(bpmTask.getInternalTaskId()).singleResult());
    }


    private BpmTask performAction(ProcessStateAction action, ProcessInstance processInstance,
                                          ProcessToolContext ctx, Task task) {
        processInstance = getProcessData(processInstance.getInternalId(), ctx);

        addActionLogEntry(action, processInstance, ctx);
        ctx.getProcessInstanceDAO().saveProcessInstance(processInstance);
        ProcessEngine processEngine = getProcessEngine();

        Map<String,Object> variables = new HashMap<String, Object>();
        variables.put("ACTION",action.getBpmName());
        processEngine.getTaskService().complete(task.getId(), variables);

        updateSubprocess(processInstance, ctx);

        String s = getProcessState(processInstance, ctx);
        fillProcessAssignmentData(processInstance, ctx);
        processInstance.setState(s);
        if (s == null && processInstance.getRunning() && !isProcessRunning(processInstance.getInternalId(), ctx)) {
            processInstance.setRunning(false);
        }
        ctx.getProcessInstanceDAO().saveProcessInstance(processInstance);
        publishEvents(processInstance, processInstance.getRunning() ? BpmEvent.Type.SIGNAL_PROCESS : BpmEvent.Type.END_PROCESS);

    return collectTask(findProcessTask(processInstance, ctx), processInstance, ctx);
    }

    public void updateSubprocess(final ProcessInstance parentPi, ProcessToolContext ctx) {
        RuntimeService service = getProcessEngine().getRuntimeService();
        ProcessInstanceQuery subprocessQuery = service.createProcessInstanceQuery().superProcessInstanceId(parentPi.getInternalId());
        org.activiti.engine.runtime.ProcessInstance subprocess = subprocessQuery.singleResult();
        if(subprocess != null){
            String processDefinitionId = subprocess.getProcessDefinitionId().replaceFirst(":\\d+:\\d+$", "");
        ProcessDefinitionConfig config = ctx.getProcessDefinitionDAO().getActiveConfigurationByKey(processDefinitionId);
            pl.net.bluesoft.rnd.processtool.model.ProcessInstance subPi = createProcessInstance(config, null, ctx, null, null, "parent_process", subprocess.getId());
            subPi.setParent(parentPi);
            ctx.getProcessInstanceDAO().saveProcessInstance(subPi);
          }
    }

    private void publishEvents(ProcessInstance processInstance, BpmEvent.Type signalProcess) {
        eventBusManager.publish(new BpmEvent(signalProcess,
                processInstance,
                user));
        ProcessToolContext.Util.getThreadProcessToolContext().getEventBusManager().publish(new BpmEvent(signalProcess,
                processInstance,
                user));
    }

    private void addActionLogEntry(ProcessStateAction action, ProcessInstance processInstance, ProcessToolContext ctx) {
        //TODO
//        ProcessStateConfiguration state = ctx.getProcessDefinitionDAO().getProcessStateConfiguration(processInstance);

        ProcessInstanceLog log = new ProcessInstanceLog();
        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(ctx.getUserDataDAO().findOrCreateUser(user));
        log.setUserSubstitute(substitutingUser != null ? ctx.getUserDataDAO().findOrCreateUser(substitutingUser) : null);
        processInstance.addProcessLog(log);
    }

    @Override
    public boolean isProcessRunning(String internalId, ProcessToolContext ctx) {

        if (internalId == null) return false;

        RuntimeService service = getProcessEngine().getRuntimeService();
        org.activiti.engine.runtime.ProcessInstance processInstance = service.createProcessInstanceQuery().processInstanceId(internalId).singleResult();
        return processInstance != null && !processInstance.isEnded();

    }

    protected ProcessInstance startProcessInstance(ProcessDefinitionConfig config,
                                                   String externalKey,
                                                   ProcessToolContext ctx,
                                                   ProcessInstance pi) {
        ProcessEngine processEngine = getProcessEngine();
        final RuntimeService execService = processEngine.getRuntimeService();
        Map vars = new HashMap();
        vars.put("processInstanceId", String.valueOf(pi.getId()));
        vars.put("initiator", user.getLogin());
        for (ProcessInstanceAttribute pia : pi.getProcessAttributes()) {
            if (pia instanceof BpmVariable) {
                BpmVariable bpmVar = (BpmVariable) pia;
                if (hasText(bpmVar.getBpmVariableName())) {
                    vars.put(bpmVar.getBpmVariableName(), bpmVar.getBpmVariableValue());
                }
            }
        }

        org.activiti.engine.runtime.ProcessInstance instance = execService
                .startProcessInstanceByKey(config.getBpmDefinitionKey(),
                                            externalKey,
                                            vars);
        pi.setInternalId(instance.getId());
        fillProcessAssignmentData(pi, ctx);
        return pi;
    }

    @Override
    public ProcessToolBpmSession createSession(UserData user, Collection<String> roleNames, ProcessToolContext ctx) {
        ActivitiBpmSession session = new ActivitiBpmSession(user, roleNames);
        session.substitutingUser = this.user;
        return session;
    }

    @Override
    public void adminCancelProcessInstance(ProcessInstance pi) {
        log.severe("User: " + user.getLogin() + " attempting to cancel process: " + pi.getInternalId());
        ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext();
        pi = getProcessData(pi.getInternalId(), ctx);
        ProcessEngine processEngine = getProcessEngine();
        processEngine.getRuntimeService().deleteProcessInstance(pi.getInternalId(), "admin-cancelled");
        fillProcessAssignmentData(pi, ctx);
        pi.setRunning(false);
        pi.setState(null);
        ctx.getProcessInstanceDAO().saveProcessInstance(pi);
        log.severe("User: " + user.getLogin() + " has cancelled process: " + pi.getInternalId());

    }

    @Override
    public void adminReassignProcessTask(ProcessInstance pi, BpmTask bpmTask, String userLogin) {
        log.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();
        TaskService ts = processEngine.getTaskService();
        Task task = ts.createTaskQuery().taskId(bpmTask.getInternalTaskId()).singleResult();
        if (nvl(userLogin, "").equals(nvl(task.getAssignee(), ""))) {
            log.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.setAssignee(bpmTask.getInternalTaskId(), userLogin);
        fillProcessAssignmentData(pi, ctx);
        log.info("Process.running:" + pi.getRunning());
        ctx.getProcessInstanceDAO().saveProcessInstance(pi);
        log.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) {
        log.severe("User: " + user.getLogin() + " attempting to complete task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " to outcome: " + action);
        performAction(action, ProcessToolContext.Util.getThreadProcessToolContext(), bpmTask);
        log.severe("User: " + user.getLogin() + " has completed task " + bpmTask.getInternalTaskId() + " for process: " + pi.getInternalId() + " to outcome: " + action);

    }

    public List<String> getAvailableLogins(final String filter) {
        IdentityService is = getProcessEngine().getIdentityService();
        User user1 = is.createUserQuery().userId(filter).singleResult();

        List<User> users = is.createUserQuery()
                .userEmailLike("%" + filter + "%")
                .orderByUserId().asc()
                .listPage(0, 100);
        List<String> res = new ArrayList<String>();
        for (User u : users) {
            res.add(u.getId());
        }
        Collections.sort(res);
        if (user1 != null) {
            res.add(0, user1.getId());
        }
        return res;

    }

    @Override
    public List<GraphElement> getProcessHistory(final ProcessInstance pi) {
        ProcessEngine processEngine = getProcessEngine();
        HistoryService service = processEngine.getHistoryService();
        HistoricActivityInstanceQuery activityInstanceQuery = service.createHistoricActivityInstanceQuery().processInstanceId(pi.getInternalId());
        List<HistoricActivityInstance> list = activityInstanceQuery.list();

        Collections.sort(list, new Comparator<HistoricActivityInstance>() {
            @Override
            public int compare(HistoricActivityInstance o1, HistoricActivityInstance o2) {
                return nvl(new Date(),o1.getStartTime()).compareTo(nvl(new Date(),o2.getStartTime()));
            }
        });

        Map<String, GraphElement> processGraphElements = parseProcessDefinition(pi);

        ArrayList<GraphElement> res = new ArrayList<GraphElement>();
        HistoricActivityInstance prev = null;
        for (HistoricActivityInstance activity : list) {
            LOGGER.fine("Handling: " + activity.getActivityName());
            String activityName = activity.getActivityName();
            if (prev != null) {
                TransitionArc ta = (TransitionArc) processGraphElements.get("__AWF__" + prev.getActivityName() + "_" + activityName);
                if (ta == null) { //look for default!
                    ta = (TransitionArc) processGraphElements.get("__AWF__default_transition_" + prev.getActivityName());
                }
                if (ta != null) {
                    res.add(ta.cloneNode());

                }
            }
            prev = activity;

            //activiti notes first event quite well
//            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);
            if (sn == null) continue;
            sn = sn.cloneNode();
            sn.setUnfinished(activity.getEndTime() == null);
//            sn.setLabel(activityName + ": " + activity.getDurationInMillis() + "ms");
            res.add(sn);
            //look for transition
        }

        HistoricProcessInstanceQuery historyProcessInstanceQuery = getProcessEngine().getHistoryService()
                .createHistoricProcessInstanceQuery().processInstanceId(pi.getInternalId());
        HistoricProcessInstance historyProcessInstance = historyProcessInstanceQuery.singleResult();
        if (historyProcessInstance != null && historyProcessInstance.getEndActivityId() != null) {
            StateNode sn = (StateNode) processGraphElements.get(historyProcessInstance.getEndActivityId());
            if (sn != null) {
                if (prev != null) {
                    TransitionArc ta = (TransitionArc) processGraphElements.get("__AWF__" + prev.getActivityName() + "_" + sn.getLabel());
                    if (ta == null) { //look for default!
                        ta = (TransitionArc) processGraphElements.get("__AWF__default_transition_" + prev.getActivityName());
                    }
                    if (ta != null) {
                        res.add(ta.cloneNode());

                    }
                }
                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
            dbf.setNamespaceAware(true);
            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();

            NodeList diagrams = documentElement.getElementsByTagNameNS(BPMNDI_NAMESPACE,
                    "BPMNDiagram");
            if (diagrams.getLength() == 0) {
                log.severe("No diagram data for process definition for instance " + pi.getInternalId());
                return res;
            }
            Element diagramElement = (Element) diagrams.item(0);
            NodeList planes = diagramElement.getElementsByTagNameNS(BPMNDI_NAMESPACE, "BPMNPlane");
            if (planes.getLength() == 0) {
                log.severe("No plane data for process definition for instance " + pi.getInternalId());
                return res;
            }
            Element planeElement = (Element) planes.item(0);

            Map<String, StateNode> nodeById = getElementCoordinatesMap(planeElement);
            Map<String, TransitionArc> arcById = getArcCoordinatesMap(planeElement);

            NodeList processElements = documentElement.getElementsByTagName("process");
            if (processElements.getLength() == 0) {
                log.severe("No process data for process definition for instance " + pi.getInternalId());
            }
            Element processElement = (Element) processElements.item(0);

            String[] nodeTypes = new String[]{"startEvent", "userTask", "exclusiveGateway", "serviceTask", "endEvent"};
            for (String nodeType : nodeTypes) {
                NodeList nodes = processElement.getElementsByTagName(nodeType);
                for (int i = 0; i < nodes.getLength(); i++) {
                    Element node = (Element) nodes.item(i);
                    try {
                        StateNode sn = nodeById.get(node.getAttribute("id"));
                        if (sn == null) {
                            continue;
                        }
                        String name = node.getAttribute("name");
                        sn.setLabel(name);
                        res.put(name, sn);
                        res.put(sn.getId(), sn);
                        if ("startEvent".equals(nodeType)) {
                            res.put("__AWF__start_node", sn);
                        }
                        LOGGER.fine("Found node" + name);
                    } catch (Exception e) {
                        LOGGER.log(Level.SEVERE, e.getMessage(), e);
                    }
                }
            }
            /*
<sequenceFlow id="sid-1D101C48-E36A-4A42-8E95-5C83E9A4CE62" name="Continue" sourceRef="sid-7BAAA2C8-B1B8-44C7-8C4F-D3968FBAD1B9" targetRef="sid-9EB37DC6-A17D-4F6B-AD72-64481593034E">
  <conditionExpression id="sid-754c93dd-7b08-4c0b-b858-848069d98e4d" xsi:type="tFormalExpression">${ACTION=='Continue'}</conditionExpression>
</sequenceFlow>
             */
            nodeTypes = new String[]{"sequenceFlow"};
            for (String nodeType : nodeTypes) {
                NodeList nodes = processElement.getElementsByTagName(nodeType);
                for (int i = 0; i < nodes.getLength(); i++) {
                    Element node = (Element) nodes.item(i);
                    try {
                        TransitionArc arc = arcById.get(node.getAttribute("id"));
                        if (arc == null) {
                            continue;
                        }
                        String name = node.getAttribute("name");
                        arc.setName(name);
                        res.put(name, arc);
                        StateNode startNode = nodeById.get(node.getAttribute("sourceRef"));
                        StateNode endNode = nodeById.get(node.getAttribute("targetRef"));
                        if (startNode == null || endNode == null) {
                            continue;
                        }
                                                        //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;

                        arc.getPath().add(0, new TransitionArcPoint(startX, startY));
                        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);

                        res.put("__AWF__" + startNode.getLabel() + "_" + endNode.getLabel(),
                                arc);
                        res.put("__AWF__default_transition_" + startNode.getLabel(),
                                arc);
                        LOGGER.fine("Found node" + name);
                    } catch (Exception e) {
                        LOGGER.log(Level.SEVERE, e.getMessage(), e);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return res;
    }

    private Map<String, StateNode> getElementCoordinatesMap(Element planeElement) {
        Map<String, StateNode> nodeById = new HashMap<String, StateNode>();
        NodeList nodes = planeElement.getElementsByTagNameNS(BPMNDI_NAMESPACE, "BPMNShape");
        for (int i = 0; i < nodes.getLength(); i++) {
            Element node = (Element) nodes.item(i);
            try {
                /*
                <bpmndi:BPMNShape bpmnElement="sid-0085EC77-5E5A-41F2-AB86-1323CDAA63B9" id="sid-0085EC77-5E5A-41F2-AB86-1323CDAA63B9_gui">
                            <omgdc:Bounds height="30.0" width="30.0" x="21.0" y="89.0"/>
                         </bpmndi:BPMNShape>
                 */
                StateNode sn = new StateNode();
                sn.setId(node.getAttribute("bpmnElement"));
                NodeList boundsList = node.getElementsByTagNameNS(OMG_DC_URI, "Bounds");
                if (boundsList.getLength() == 0) {
                    continue;
                }
                Element boundsEl = (Element) boundsList.item(0);

                int x = new Double(boundsEl.getAttribute("x")).intValue();
                int y = new Double(boundsEl.getAttribute("y")).intValue();
                int w = new Double(boundsEl.getAttribute("width")).intValue();
                int h = new Double(boundsEl.getAttribute("height")).intValue();
                sn.setX(x);
                sn.setY(y);
                sn.setWidth(w);
                sn.setHeight(h);
                nodeById.put(sn.getId(), sn);
                LOGGER.fine("Found node" + sn.getId() + ": " + x + "," + y + "," + w + "," + h);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            }
        }
        return nodeById;
    }

    private Map<String, TransitionArc> getArcCoordinatesMap(Element planeElement) {
        Map<String, TransitionArc> arcById = new HashMap<String, TransitionArc>();
        NodeList nodes = planeElement.getElementsByTagNameNS(BPMNDI_NAMESPACE, "BPMNEdge");
        for (int i = 0; i < nodes.getLength(); i++) {
            Element node = (Element) nodes.item(i);
            try {
                /*
                 <bpmndi:BPMNEdge bpmnElement="sid-581B2A50-275F-4790-BA45-FC2728E3E8F7" id="sid-581B2A50-275F-4790-BA45-FC2728E3E8F7_gui">
                    <omgdi:waypoint x="552.0" y="198.0"/>
                    <omgdi:waypoint x="552.3181702913334" y="60.0"/>
                    <omgdi:waypoint x="274.0" y="60.0"/>
                 </bpmndi:BPMNEdge>
                 */
                TransitionArc ta = new TransitionArc();
                ta.setId(node.getAttribute("bpmnElement"));
                NodeList waypointList = node.getElementsByTagNameNS("http://www.omg.org/spec/DD/20100524/DI", "waypoint");
                if (waypointList.getLength() == 0) {
                    continue;
                }
                for (int j = 1; j < waypointList.getLength()-1; j++) {//skip first and last docker - we have to calculate them manually
                    Element waypointEl = (Element) waypointList.item(j);
                    ta.addPoint(new Double(waypointEl.getAttribute("x")).intValue(),
                            new Double(waypointEl.getAttribute("y")).intValue());
                }
                arcById.put(ta.getId(), ta);
                LOGGER.fine("Found arc" + ta);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            }
        }
        return arcById;
    }

    @Override
    public byte[] getProcessLatestDefinition(String definitionKey, String processName) {
        String resourceName = processName + ".bpmn20.xml";
        return fetchLatestProcessResource(definitionKey, resourceName);
    }

    @Override
    public byte[] getProcessMapImage(ProcessInstance pi) {
        String resourceName = pi.getDefinition().getProcessName() + ".png";
        return fetchProcessResource(pi, resourceName);
    }

    @Override
    public byte[] getProcessDefinition(ProcessInstance pi) {
        String resourceName = pi.getDefinition().getProcessName() + ".bpmn20.xml";
        return fetchProcessResource(pi, resourceName);
    }

    private byte[] fetchLatestProcessResource(String definitionKey, String resourceName) {
        RepositoryService service = getProcessEngine()
                .getRepositoryService();
        List<ProcessDefinition> latestList = service.createProcessDefinitionQuery()
                .processDefinitionKey(definitionKey).
                        orderByDeploymentId().desc().listPage(0, 1);
        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();
        RepositoryService service = processEngine.getRepositoryService();

        RuntimeService RuntimeService = processEngine.getRuntimeService();
        org.activiti.engine.runtime.ProcessInstance processInstanceById =
                RuntimeService.createProcessInstanceQuery().processInstanceId(pi.getInternalId()).singleResult();
        String processDefinitionId;
        if (processInstanceById == null) { //look in history service
            HistoricProcessInstanceQuery historyProcessInstanceQuery = processEngine.getHistoryService()
                    .createHistoricProcessInstanceQuery().processInstanceId(pi.getInternalId());
            HistoricProcessInstance historyProcessInstance = historyProcessInstanceQuery.singleResult();
            processDefinitionId = historyProcessInstance.getProcessDefinitionId();
        } else {
            processDefinitionId = processInstanceById.getProcessDefinitionId();
        }
        List<ProcessDefinition> latestList = service.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).orderByDeploymentId().desc().listPage(0, 1);
        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().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()
                .getRepositoryService();
        DeploymentBuilder deployment = service.createDeployment();
//        deployment.name(processName);
        deployment.addInputStream(processName + ".bpmn20.xml", definitionStream);
        if (processMapImageStream != null)
            deployment.addInputStream(processName + ".png", processMapImageStream);
        Deployment deploy = deployment.deploy();
        return deploy.getId();
    }

  @Override
  public Collection<BpmTask> getProcessTaskInQueues(ProcessToolContext ctx, ProcessInstance processInstance) {
    List<Task> tasks = getProcessEngine()
        .getTaskService()
        .createTaskQuery()
        .processInstanceId(processInstance.getInternalId()).list();
    return findProcessInstancesForTasks(tasks, ctx);
  }

  @Override
  public List<BpmTask> getQueueTasks(ProcessToolContext ctx, String queueName) {
    List<Task> tasks = getProcessEngine()
        .getTaskService()
        .createTaskQuery()
        .taskCandidateGroup(queueName)
        .taskUnnassigned()
        .list();
    return findProcessInstancesForTasks(tasks, ctx);
  }

  @Override
  public List<BpmTask> findProcessTasksWithUser(ProcessInstance pi,
      ProcessToolContext ctx) {
    return findProcessTasks(pi, user.getLogin(), ctx);
  }
}
TOP

Related Classes of org.aperteworkflow.ext.activiti.ActivitiBpmSession

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.