/** Send Token Signal */
  public static Token sendTokenSignal(long tokenId, String transitionName)
      throws WorkflowException {
    log.debug("sendTokenSignal({}, {})", new Object[] {tokenId, transitionName});
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    Token vo = new Token();

    try {
      org.jbpm.graph.exe.Token t = jbpmContext.getToken(tokenId);

      if (transitionName != null && !transitionName.equals("")) {
        t.signal(transitionName);
      } else {
        t.signal();
      }

      jbpmContext.getSession().flush();
      vo = WorkflowUtils.copy(t);

      // Avoid recursion
      vo.setProcessInstance(WorkflowUtils.copy(t.getProcessInstance()));
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("sendTokenSignal: {}", vo);
    return vo;
  }
  /** Find Pooled Task Instances */
  @SuppressWarnings("rawtypes")
  public static List<TaskInstance> findPooledTaskInstances(String user) throws WorkflowException {
    log.debug("findPooledTaskInstances({})", user);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    ArrayList<TaskInstance> al = new ArrayList<TaskInstance>();

    try {
      TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();

      for (Iterator it = taskMgmtSession.findPooledTaskInstances(user).iterator(); it.hasNext(); ) {
        org.jbpm.taskmgmt.exe.TaskInstance ti = (org.jbpm.taskmgmt.exe.TaskInstance) it.next();
        al.add(WorkflowUtils.copy(ti));
      }

      // Sort
      Collections.sort(al);
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("findPooledTaskInstances: {}", al);
    return al;
  }
  /** Find Task Instances */
  @SuppressWarnings("rawtypes")
  public static List<TaskInstance> findTaskInstances(long processInstanceId)
      throws WorkflowException {
    log.debug("findTaskInstances({})", processInstanceId);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    ArrayList<TaskInstance> al = new ArrayList<TaskInstance>();

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();
      org.jbpm.graph.exe.ProcessInstance pi = graphSession.getProcessInstance(processInstanceId);
      TaskMgmtInstance taskMgmtInstance = pi.getTaskMgmtInstance();

      if (taskMgmtInstance.getTaskInstances() != null) {
        for (Iterator it = taskMgmtInstance.getTaskInstances().iterator(); it.hasNext(); ) {
          org.jbpm.taskmgmt.exe.TaskInstance ti = (org.jbpm.taskmgmt.exe.TaskInstance) it.next();
          al.add(WorkflowUtils.copy(ti));
        }
      }

      // Sort
      Collections.sort(al);
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("findTaskInstances: {}", al);
    return al;
  }
  /** Find All Process Definition Versions */
  @SuppressWarnings("rawtypes")
  public static List<ProcessDefinition> findAllProcessDefinitionVersions(String name)
      throws WorkflowException {
    log.debug("findAllProcessDefinitionVersions({})", name);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    List<ProcessDefinition> al = new ArrayList<ProcessDefinition>();

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();

      for (Iterator it = graphSession.findAllProcessDefinitionVersions(name).iterator();
          it.hasNext(); ) {
        org.jbpm.graph.def.ProcessDefinition procDef =
            (org.jbpm.graph.def.ProcessDefinition) it.next();
        al.add(WorkflowUtils.copy(procDef));
      }
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("findAllProcessDefinitionVersions: {}", al);
    return al;
  }
  @SuppressWarnings("rawtypes")
  public static ProcessDefinition findLastProcessDefinition(String name) throws WorkflowException {
    log.debug("findLastProcessDefinition()");
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    ProcessDefinition pd = new ProcessDefinition();

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();

      for (Iterator it = graphSession.findLatestProcessDefinitions().iterator(); it.hasNext(); ) {
        org.jbpm.graph.def.ProcessDefinition procDef =
            (org.jbpm.graph.def.ProcessDefinition) it.next();

        if (procDef.getName().equals(name)) {
          pd = WorkflowUtils.copy(procDef);
        }
      }
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("findLastProcessDefinition: {}", pd);
    return pd;
  }
  /** Find Process Instance */
  @SuppressWarnings("rawtypes")
  public static List<ProcessInstance> findProcessInstances(long processDefinitionId)
      throws WorkflowException {
    log.debug("findProcessInstances({})", processDefinitionId);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    List<ProcessInstance> al = new ArrayList<ProcessInstance>();

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();

      for (Iterator it = graphSession.findProcessInstances(processDefinitionId).iterator();
          it.hasNext(); ) {
        org.jbpm.graph.exe.ProcessInstance procInst =
            (org.jbpm.graph.exe.ProcessInstance) it.next();
        al.add(WorkflowUtils.copy(procInst));
      }
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("findProcessInstances: {}", al);
    return al;
  }
  /** Send Process Instance Signal */
  public static ProcessInstance sendProcessInstanceSignal(
      long processInstanceId, String transitionName) throws WorkflowException {
    log.debug(
        "sendProcessInstanceSignal({}, {})", new Object[] {processInstanceId, transitionName});
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    ProcessInstance vo = new ProcessInstance();

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();
      org.jbpm.graph.exe.ProcessInstance pi = graphSession.getProcessInstance(processInstanceId);
      org.jbpm.graph.exe.Token t = pi.getRootToken();

      if (transitionName != null && !transitionName.equals("")) {
        t.signal(transitionName);
      } else {
        t.signal();
      }

      jbpmContext.getSession().flush();
      vo = WorkflowUtils.copy(pi);
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("sendProcessInstanceSignal: {}", vo);
    return vo;
  }
  /** Get Token */
  public static Token getToken(long tokenId) throws WorkflowException {
    log.debug("getToken({})", tokenId);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    Token vo = new Token();

    try {
      org.jbpm.graph.exe.Token t = jbpmContext.getToken(tokenId);
      vo = WorkflowUtils.copy(t);

      // Avoid recursion
      vo.setProcessInstance(WorkflowUtils.copy(t.getProcessInstance()));
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("getToken: " + vo);
    return vo;
  }
  /** Start Process Definition */
  public static ProcessInstance runProcessDefinition(
      String user, long processDefinitionId, String uuid, List<FormElement> variables)
      throws WorkflowException {
    log.debug(
        "runProcessDefinition({}, {}, {}, {})",
        new Object[] {user, processDefinitionId, uuid, variables});
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    ProcessInstance vo = new ProcessInstance();

    if (Config.SYSTEM_READONLY) {
      throw new WorkflowException("System is in read-only mode");
    }

    try {
      jbpmContext.setActorId(user);
      GraphSession graphSession = jbpmContext.getGraphSession();
      Map<String, Object> hm = new HashMap<String, Object>();
      hm.put(Config.WORKFLOW_PROCESS_INSTANCE_VARIABLE_UUID, uuid);

      for (FormElement fe : variables) {
        hm.put(fe.getName(), fe);
      }

      org.jbpm.graph.def.ProcessDefinition pd =
          graphSession.getProcessDefinition(processDefinitionId);
      org.jbpm.graph.exe.ProcessInstance pi = pd.createProcessInstance(hm);

      if (pi != null) {
        org.jbpm.taskmgmt.exe.TaskMgmtInstance tmi = pi.getTaskMgmtInstance();

        // http://community.jboss.org/thread/115182
        if (tmi.getTaskMgmtDefinition().getStartTask() != null) {
          org.jbpm.taskmgmt.exe.TaskInstance ti = tmi.createStartTaskInstance();

          if (Config.WORKFLOW_START_TASK_AUTO_RUN) {
            ti.start();
            ti.end();
          }
        } else {
          pi.getRootToken().signal();
        }

        jbpmContext.save(pi);
        vo = WorkflowUtils.copy(pi);
      }
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("runProcessDefinition: {}", vo);
    return vo;
  }
  /** Get Task Instance */
  public static TaskInstance getTaskInstance(long taskInstanceId) throws WorkflowException {
    log.debug("getTaskInstance({})", taskInstanceId);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    TaskInstance vo = new TaskInstance();

    try {
      TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();
      org.jbpm.taskmgmt.exe.TaskInstance ti = taskMgmtSession.getTaskInstance(taskInstanceId);
      vo = WorkflowUtils.copy(ti);
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("getTaskInstance: {}", vo);
    return vo;
  }
  /** Get Process Instance */
  public static ProcessInstance getProcessInstance(long processInstanceId)
      throws WorkflowException {
    log.debug("getProcessInstance({})", processInstanceId);
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    ProcessInstance vo = new ProcessInstance();

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();
      org.jbpm.graph.exe.ProcessInstance pi = graphSession.getProcessInstance(processInstanceId);
      vo = WorkflowUtils.copy(pi);
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("getProcessInstance: {}", vo);
    return vo;
  }
  /** Get Process Definition Image */
  public static byte[] getProcessDefinitionImage(long processDefinitionId, String node)
      throws WorkflowException {
    log.debug("getProcessDefinitionImage({}, {})", new Object[] {processDefinitionId, node});
    JbpmContext jbpmContext = JBPMUtils.getConfig().createJbpmContext();
    byte[] image = null;

    try {
      GraphSession graphSession = jbpmContext.getGraphSession();
      org.jbpm.graph.def.ProcessDefinition pd =
          graphSession.getProcessDefinition(processDefinitionId);
      FileDefinition fileDef = pd.getFileDefinition();

      WorkflowUtils.DiagramInfo dInfo =
          WorkflowUtils.getDiagramInfo(fileDef.getInputStream("gpd.xml"));
      WorkflowUtils.DiagramNodeInfo dNodeInfo = dInfo.getNodeMap().get(node);
      BufferedImage img = ImageIO.read(fileDef.getInputStream("processimage.jpg"));

      // Obtain all nodes Y and X
      List<Integer> ordenadas = new ArrayList<Integer>();
      List<Integer> abcisas = new ArrayList<Integer>();

      for (WorkflowUtils.DiagramNodeInfo nodeInfo : dInfo.getNodeMap().values()) {
        ordenadas.add(nodeInfo.getY());
        abcisas.add(nodeInfo.getX());
      }

      // Calculate minimal Y
      Collections.sort(ordenadas);
      int fixOrd = ordenadas.get(0) < 0 ? ordenadas.get(0) : 0;

      // Calculate minimal X
      Collections.sort(abcisas);
      int fixAbs = abcisas.get(0) < 0 ? abcisas.get(0) : 0;

      if (dNodeInfo != null) {
        // Select node
        log.debug("DiagramNodeInfo: {}", dNodeInfo);
        Graphics g = img.getGraphics();
        Graphics2D g2d = (Graphics2D) g;
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25F));
        g2d.setColor(Color.blue);
        g2d.fillRect(
            dNodeInfo.getX() - fixAbs,
            dNodeInfo.getY() - fixOrd,
            dNodeInfo.getWidth(),
            dNodeInfo.getHeight());
        g.dispose();
      }

      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ImageIO.write(img, "jpg", baos);
      image = baos.toByteArray();
      baos.flush();
      baos.close();
    } catch (JbpmException e) {
      throw new WorkflowException(e.getMessage(), e);
    } catch (IOException e) {
      throw new WorkflowException(e.getMessage(), e);
    } finally {
      jbpmContext.close();
    }

    log.debug("getProcessDefinitionImage: {}", image);
    return image;
  }