[jbpm-commits] JBoss JBPM SVN: r3316 - in jbpm3/trunk/modules/core/src: main/java/org/jbpm/db and 3 other directories.
do-not-reply at jboss.org
do-not-reply at jboss.org
Wed Dec 10 07:58:43 EST 2008
Author: camunda
Date: 2008-12-10 07:58:42 -0500 (Wed, 10 Dec 2008)
New Revision: 3316
Added:
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractProcessInstanceBaseCommand.java
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractTokenBaseCommand.java
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ResumeProcessInstanceCommand.java
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/SuspendProcessInstanceCommand.java
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/UnlockTokenCommand.java
jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/
jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ChangeProcessInstanceVersionCommandTest.java
jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ProcessInstanceCommandTest.java
jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/TokenCommandTest.java
Modified:
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/BatchSignalCommand.java
jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ChangeProcessInstanceVersionCommand.java
jbpm3/trunk/modules/core/src/main/java/org/jbpm/db/JobSession.java
jbpm3/trunk/modules/core/src/main/resources/org/jbpm/db/hibernate.queries.hbm.xml
Log:
JBPM-1905:
- Added Suspend/ResumeProcessInstanceCommand & UnlockTokenCommand (including test cases)
- Introduced Command base class for ProcessInstance / Token related Commands
- Improved / Refactored ChangeProcessInstanceVersionCommand, added TestCase
Added: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractProcessInstanceBaseCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractProcessInstanceBaseCommand.java (rev 0)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractProcessInstanceBaseCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,180 @@
+package org.jbpm.command;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Query;
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+
+/**
+ * Abstract base class for all commands working on {@link org.jbpm.graph.exe.ProcessInstance}s.
+ *
+ * The {@link ProcessInstance} can either be specified by id or multiple ids.
+ *
+ * The alternative is to specify a {@link ProcessDefinition} name and version.
+ * In this case <b>all</b> found {@link ProcessInstance}s are processed.
+ * If no version is specified, <b>all</b> versions are taken into account.
+ *
+ * if onlyRunning is set to false (default is true) already ended {@link ProcessInstance}s
+ * are processed too.
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public abstract class AbstractProcessInstanceBaseCommand implements Command
+{
+ protected Log log = LogFactory.getLog(this.getClass());
+
+ private long[] processInstanceIds = null;
+ private String processName = null;
+ private int processVersion = 0;
+ private boolean onlyRunning = true;
+
+ private boolean operateOnSingleObject;
+
+ private transient JbpmContext jbpmContext;
+
+ public AbstractProcessInstanceBaseCommand()
+ {
+ super();
+ }
+
+ protected JbpmContext getJbpmContext() {
+ return jbpmContext;
+ }
+
+ public Object execute(JbpmContext jbpmContext) throws Exception
+ {
+ this.jbpmContext = jbpmContext;
+ try {
+ log.debug("executing " + this);
+
+ // batch tokens
+ if (processInstanceIds != null && processInstanceIds.length > 0)
+ {
+ for (int i = 0; i < processInstanceIds.length; i++)
+ {
+ ProcessInstance pi = jbpmContext.loadProcessInstanceForUpdate(processInstanceIds[i]);
+ execute(pi);
+ }
+ }
+
+ // search for ProcessInstances according to parameters
+ if (processName != null)
+ {
+ operateOnSingleObject = false;
+
+ GetProcessInstancesCommand cmd = new GetProcessInstancesCommand();
+ cmd.setProcessName(processName);
+ cmd.setOnlyRunning(onlyRunning);
+ if (processVersion>0)
+ cmd.setVersion(String.valueOf(processVersion));
+
+ // Query query = null;
+ // if (processVersion>0) {
+ // query = jbpmContext.getSession().getNamedQuery("GraphSession.findProcessDefinitionByNameAndVersion");
+ // query.setInteger("version", processVersion);
+ // }
+ // else {
+ // query = jbpmContext.getSession().getNamedQuery("GraphSession.findTokensForProcessInNode");
+ // }
+ // query.setString("name", processName);
+ List processInstanceList = (List)cmd.execute(jbpmContext);
+
+ Iterator iter = processInstanceList.iterator();
+ while (iter.hasNext())
+ {
+ ProcessInstance pi = (ProcessInstance)iter.next();
+ execute(pi);
+ }
+ }
+
+ return null;
+ }
+ finally {
+ this.jbpmContext = null;
+ }
+ }
+
+ public abstract void execute(ProcessInstance processInstance);
+
+ public AbstractProcessInstanceBaseCommand setProcessInstanceIds(long[] processInstanceIds)
+ {
+ this.operateOnSingleObject = false;
+ this.processInstanceIds = processInstanceIds;
+ return this;
+ }
+
+ public AbstractProcessInstanceBaseCommand setProcessInstanceId(long processInstanceId)
+ {
+ this.operateOnSingleObject = true;
+ this.processInstanceIds = new long[1];
+ this.processInstanceIds[0] = processInstanceId;
+ return this;
+ }
+
+ public String toString() {
+ if (processName!=null) {
+ return this.getClass().getName()
+ + " [tokenIds=" + Arrays.toString(processInstanceIds)
+ + ";processName=" + processName
+ + ";processVersion=" + (processVersion>0 ? processVersion : "NA")
+ + getAdditionalToStringInformation()
+ + "]";
+ }
+ else {
+ return this.getClass().getName()
+ + " [tokenIds=" + Arrays.toString(processInstanceIds)
+ + ";operateOnSingleObject=" + operateOnSingleObject
+ + getAdditionalToStringInformation()
+ + "]";
+ }
+ }
+
+ public String getAdditionalToStringInformation() {
+ return "";
+ }
+
+ public String getProcessName()
+ {
+ return processName;
+ }
+
+ public AbstractProcessInstanceBaseCommand setProcessName(String processName)
+ {
+ this.processName = processName;
+ return this;
+ }
+
+ public int getProcessVersion()
+ {
+ return processVersion;
+ }
+
+ public AbstractProcessInstanceBaseCommand setProcessVersion(int processVersion)
+ {
+ this.processVersion = processVersion;
+ return this;
+ }
+
+ public long[] getProcessInstanceIds()
+ {
+ return processInstanceIds;
+ }
+
+ public boolean isOnlyRunning()
+ {
+ return onlyRunning;
+ }
+
+ public AbstractProcessInstanceBaseCommand setOnlyRunning(boolean onlyRunning)
+ {
+ this.onlyRunning = onlyRunning;
+ return this;
+ }
+
+}
\ No newline at end of file
Added: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractTokenBaseCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractTokenBaseCommand.java (rev 0)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/AbstractTokenBaseCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,181 @@
+package org.jbpm.command;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Query;
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.Token;
+
+/**
+ * Abstract base class for commands working on Tokens.
+ *
+ * The {@link Token} can either be specified by id or multiple ids.
+ *
+ * The alternative is to specify a {@link ProcessDefinition} name, a
+ * required node name and version.
+ * In this case <b>all</b> found {@link Token}s are processed.
+ * If no version is specified, <b>all</b> versions are taken into account.
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public abstract class AbstractTokenBaseCommand implements Command
+{
+ protected Log log = LogFactory.getLog(this.getClass());
+
+ private long[] tokenIds = null;
+ private String processName = null;
+ private String stateName = null;
+ private int processVersion = 0;
+
+ private boolean operateOnSingleObject;
+
+ private transient JbpmContext jbpmContext;
+
+ public AbstractTokenBaseCommand()
+ {
+ super();
+ }
+
+ protected JbpmContext getJbpmContext() {
+ return jbpmContext;
+ }
+
+ public Object execute(JbpmContext jbpmContext) throws Exception
+ {
+ this.jbpmContext = jbpmContext;
+ try {
+ ArrayList result = new ArrayList();
+ log.debug("executing " + this);
+
+ // batch tokens
+ if (tokenIds != null && tokenIds.length > 0)
+ {
+ for (int i = 0; i < tokenIds.length; i++)
+ {
+ Token token = jbpmContext.loadTokenForUpdate(tokenIds[i]);
+ result.add(
+ execute(token));
+ }
+ }
+
+ // search for tokens in process/state
+ if (processName != null && stateName != null)
+ {
+ this.operateOnSingleObject = false;
+
+ Query query = null;
+ if (processVersion>0) {
+ query = jbpmContext.getSession().getNamedQuery("GraphSession.findTokensForProcessVersionInNode");
+ query.setInteger("processDefinitionVersion", processVersion);
+ }
+ else {
+ query = jbpmContext.getSession().getNamedQuery("GraphSession.findTokensForProcessInNode");
+ }
+ query.setString("processDefinitionName", processName);
+ query.setString("nodeName", stateName);
+
+ Iterator iter = query.list().iterator();
+ while (iter.hasNext())
+ {
+ Token token = (Token)iter.next();
+ result.add(
+ execute(token));
+ }
+ }
+
+ if (operateOnSingleObject) {
+ if (result.size()<1)
+ return null;
+ else
+ return result.get(0);
+ }
+ else {
+ return result;
+ }
+ }
+ finally {
+ this.jbpmContext = null;
+ }
+ }
+
+ public abstract Object execute(Token token);
+
+ public AbstractTokenBaseCommand setTokenIds(long[] tokenIds)
+ {
+ this.operateOnSingleObject = false;
+ this.tokenIds = tokenIds;
+ return this;
+ }
+
+ public AbstractTokenBaseCommand setTokenId(long tokenId)
+ {
+ this.operateOnSingleObject = true;
+ this.tokenIds = new long[1];
+ this.tokenIds[0] = tokenId;
+ return this;
+ }
+
+ public String toString() {
+ if (processName!=null && stateName!=null) {
+ return this.getClass().getName()
+ + " [tokenIds=" + Arrays.toString(tokenIds)
+ + ";processName=" + processName
+ + ";processVersion=" + (processVersion>0 ? processVersion : "NA")
+ + ";stateName=" + stateName
+ + getAdditionalToStringInformation()
+ + "]";
+ }
+ else {
+ return this.getClass().getName()
+ + " [tokenIds=" + Arrays.toString(tokenIds)
+ + ";operateOnSingleObject=" + operateOnSingleObject
+ + getAdditionalToStringInformation()
+ + "]";
+ }
+ }
+
+ public String getAdditionalToStringInformation() {
+ return "";
+ }
+
+ public String getProcessName()
+ {
+ return processName;
+ }
+
+ public void setProcessName(String processName)
+ {
+ this.processName = processName;
+ }
+
+ public int getProcessVersion()
+ {
+ return processVersion;
+ }
+
+ public void setProcessVersion(int processVersion)
+ {
+ this.processVersion = processVersion;
+ }
+
+ public String getStateName()
+ {
+ return stateName;
+ }
+
+ public void setStateName(String stateName)
+ {
+ this.stateName = stateName;
+ }
+
+ public long[] getTokenIds()
+ {
+ return tokenIds;
+ }
+
+}
\ No newline at end of file
Modified: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/BatchSignalCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/BatchSignalCommand.java 2008-12-10 12:51:17 UTC (rev 3315)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/BatchSignalCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -1,154 +1,62 @@
package org.jbpm.command;
import java.util.Date;
-import java.util.Iterator;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.hibernate.Query;
import org.jbpm.JbpmContext;
import org.jbpm.graph.exe.Token;
/**
- * a bunch of processes is signalled with this command. you can specify the tokens either <li>by a array of token ids
- * <li>or by processName, processVersion (optional, without all versions), stateName
+ * a bunch of processes is signaled with this command. you can specify the
+ * tokens either
+ * <li>by a array of token ids
+ * <li>or by processName, processVersion (optional, without all versions),
+ * stateName
*
- * transitionName speicifies the transition to take (if null, the default transition is taken).
+ * transitionName specifies the transition to take (if null, the default
+ * transition is taken).
*
- * This command can be for example useful, if you have a lot of processes to check some information a not jBPM task has
- * altered before (maybe in batch too).
- *
- * CURRENTLY EXPERIMENTAL!
- *
- * @author Bernd Rucker (bernd.ruecker at camunda.com)
+ * @author Bernd Ruecker (bernd.ruecker at camunda.com)
*/
-public class BatchSignalCommand implements Command
-{
+public class BatchSignalCommand extends AbstractTokenBaseCommand {
- private static final long serialVersionUID = -4330623193546102772L;
+ private static final long serialVersionUID = -4330623193546102772L;
- private static Log log = LogFactory.getLog(BatchSignalCommand.class);
+ /**
+ * if set, only tokens which are started after this date are signaled
+ * (interesting to implement some timeout for example)
+ */
+ private Date inStateAtLeastSince = null;
- private long[] tokenIds = null;
+ private String transitionName = null;
- private String processName = null;
+ public Object execute(Token token) {
+ if (inStateAtLeastSince == null
+ || token.getNodeEnter().before(inStateAtLeastSince)) {
- private String stateName = null;
+ log.debug("signal token " + token);
+ if (transitionName == null) {
+ token.signal();
+ } else {
+ token.signal(transitionName);
+ }
+ }
+ return token;
+ }
- /**
- * if set, only tokens which are started after this date are signaled (interessting to implement some timeout for
- * example)
- */
- private Date inStateAtLeastSince = null;
+ public String getTransitionName() {
+ return transitionName;
+ }
- private long processVersion = 0;
+ public void setTransitionName(String transitionName) {
+ this.transitionName = transitionName;
+ }
- private String transitionName = null;
+ public Date getInStateAtLeastSince() {
+ return inStateAtLeastSince;
+ }
- public Object execute(JbpmContext jbpmContext) throws Exception
- {
- log.debug("executing " + this);
+ public void setInStateAtLeastSince(Date inStateAtLeastSince) {
+ this.inStateAtLeastSince = inStateAtLeastSince;
+ }
- // batch tokens
- if (tokenIds != null && tokenIds.length > 0)
- {
- for (int i = 0; i < tokenIds.length; i++)
- {
- Token token = jbpmContext.loadTokenForUpdate(tokenIds[i]);
- signalToken(token);
- }
- }
-
- // search for tokens in process/state
- if (processName != null && stateName != null)
- {
- Query query = jbpmContext.getSession().getNamedQuery("GraphSession.findTokensForProcessInNode");
- query.setString("processDefinitionName", processName);
- query.setString("nodeName", stateName);
-
- Iterator iter = query.list().iterator();
- while (iter.hasNext())
- {
- Token t = (Token)iter.next();
- if (inStateAtLeastSince == null || t.getNodeEnter().before(inStateAtLeastSince))
- signalToken(t);
- }
- }
-
- return null;
- }
-
- private void signalToken(Token token)
- {
- log.debug("signal token " + token);
- if (transitionName == null)
- {
- token.signal();
- }
- else
- {
- token.signal(transitionName);
- }
- }
-
- public String getProcessName()
- {
- return processName;
- }
-
- public void setProcessName(String processName)
- {
- this.processName = processName;
- }
-
- public long getProcessVersion()
- {
- return processVersion;
- }
-
- public void setProcessVersion(long processVersion)
- {
- this.processVersion = processVersion;
- }
-
- public String getStateName()
- {
- return stateName;
- }
-
- public void setStateName(String stateName)
- {
- this.stateName = stateName;
- }
-
- public long[] getTokenIds()
- {
- return tokenIds;
- }
-
- public void setTokenIds(long[] tokenIds)
- {
- this.tokenIds = tokenIds;
- }
-
- public String getTransitionName()
- {
- return transitionName;
- }
-
- public void setTransitionName(String transitionName)
- {
- this.transitionName = transitionName;
- }
-
- public Date getInStateAtLeastSince()
- {
- return inStateAtLeastSince;
- }
-
- public void setInStateAtLeastSince(Date inStateAtLeastSince)
- {
- this.inStateAtLeastSince = inStateAtLeastSince;
- }
-
}
Modified: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ChangeProcessInstanceVersionCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ChangeProcessInstanceVersionCommand.java 2008-12-10 12:51:17 UTC (rev 3315)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ChangeProcessInstanceVersionCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -8,66 +8,71 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
-import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
-import org.jbpm.db.JbpmSchema;
+import org.jbpm.graph.def.GraphElement;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
+import org.jbpm.graph.node.ProcessState;
+import org.jbpm.graph.node.TaskNode;
+import org.jbpm.job.Job;
+import org.jbpm.job.Timer;
+import org.jbpm.logging.log.ProcessLog;
import org.jbpm.taskmgmt.def.Task;
import org.jbpm.taskmgmt.exe.TaskInstance;
/**
- * <b>THIS COMMAND IS NOT YET STABLE, BUT FEEL FREE TO TEST :-)</b>
+ * <b>THIS COMMAND IS NOT YET STABLE, BUT FEEL FREE TO TEST :-)</b><br>
*
- * change the version of a running process instance. This works only, if the current node is also available in the new
- * version of the process definition (identified by name, so the name has to be exactly the same). One problem with this
- * approach ist also, that if a task with the same name is moved to another node (but this is a rare case)
+ * Status update: Still not complete, but refactored and added simple test cases:
+ * {@link ChangeProcessInstanceVersionCommandTest}.<br>
*
+ * Change the version of a running process instance.
*
- * make trouble, if there are 2 tokens in the process, because only one actual node is used...
+ * This works only, if the current node is also available in the new
+ * version of the process definition or a name mapping has to be provided.<br>
*
- * Possible workaround: use process id instead of node id.
+ * <b>Currently known limitations:</b>
+ * <ul>
+ * <li> {@link Task}s cannot move "into" another node. If an active
+ * {@link TaskInstance} exists, the {@link Task} definition must
+ * exist in the {@link TaskNode} with the same (or mapped) name.
+ * Otherwise the right node cannot be found easily because it may be
+ * ambiguous.
+ * <li> Sub processes aren't yet tested. Since the {@link ProcessState} is
+ * a {@link Node} like any other, it should work anyway.
+ * <li> Can have <b>negative impact on referential integrity</b>! Because
+ * one {@link ProcessInstance} can have {@link ProcessLog}s point to
+ * old {@link ProcessDefinition}s. Hence, delete a {@link ProcessDefinition}
+ * may not work and throw an Exception (Integrity constraint violation)
+ * </ul>
*
- * TODO: new hibernate query for that? Proposal Fluffi "select distinct task " + "from " + Task.class.getName() +
- * " task " + "where task.name = :taskName " + " and task.processDefinition.id = :processDefinitionId ";
- *
* @author Bernd Ruecker (bernd.ruecker at camunda.com)
*/
-public class ChangeProcessInstanceVersionCommand implements Command
+public class ChangeProcessInstanceVersionCommand extends AbstractProcessInstanceBaseCommand
{
private static final long serialVersionUID = 2277080393930008224L;
/**
- * process id of process to update. If set, this special process is updated
- */
- private long processId = -1;
-
- /**
- * if set, all running processes of the process with this name are updated
- */
- private String processName;
-
- /**
* new version of process, if <=0, the latest process definition is used
*/
private int newVersion = -1;
- private static final Log log = LogFactory.getLog(JbpmSchema.class);
+ private static final Log log = LogFactory.getLog(ChangeProcessInstanceVersionCommand.class);
- private transient JbpmContext jbpmContext = null;
-
/**
- * the map configures for every node-name in the old process definition (as key) which node-name to use in the new
+ * the map configures for every node-name in the old process definition
+ * (as key) which node-name to use in the new
* process definition.
*
- * if a node is not mentioned in this Map, old node name = new node name is applied
+ * if a node is not mentioned in this Map,
+ * old node name = new node name is applied
*/
- private Map nameMapping = new HashMap();
+ private Map<String, String> nodeNameMapping = new HashMap<String, String>();
- private transient ProcessDefinition newDef;
+ private Map<String, String> taskNameMapping = new HashMap<String, String>();
public ChangeProcessInstanceVersionCommand()
{
@@ -75,133 +80,165 @@
public ChangeProcessInstanceVersionCommand(long processId, int newVersion)
{
- this.processId = processId;
+ super();
+ super.setProcessInstanceId(processId);
this.newVersion = newVersion;
}
-
- private ProcessDefinition getNewDef(String processName)
- {
- if (newDef == null)
- {
- if (newVersion <= 0)
- newDef = jbpmContext.getGraphSession().findLatestProcessDefinition(processName);
- else
- newDef = jbpmContext.getGraphSession().findProcessDefinition(processName, newVersion);
- }
- return newDef;
+
+ public String getAdditionalToStringInformation() {
+ return ";newVersion=" + newVersion;
}
- /**
- * @return always null
- * @see org.jbpm.command.Command#execute(org.jbpm.JbpmContext)
- */
- public Object execute(JbpmContext jbpmContext) throws Exception
+ private ProcessDefinition loadNewProcessDefinition(String processName)
{
- this.jbpmContext = jbpmContext;
- if (processId > -1)
- {
- ProcessInstance pi = jbpmContext.getGraphSession().loadProcessInstance(processId);
- changeProcessVersion(pi);
- }
- if (processName != null && processName.length() > 0)
- {
- changeAllProcessInstances(processName);
- }
- return null;
+ if (newVersion <= 0)
+ return getJbpmContext().getGraphSession().findLatestProcessDefinition(processName);
+ else
+ return getJbpmContext().getGraphSession().findProcessDefinition(processName, newVersion);
}
- private void changeProcessVersion(ProcessInstance pi)
+ public void execute(ProcessInstance pi)
{
- changeTokenVersion(jbpmContext, pi.getRootToken());
-
ProcessDefinition oldDef = pi.getProcessDefinition();
- ProcessDefinition newDef = getNewDef(oldDef.getName());
+ ProcessDefinition newDef = loadNewProcessDefinition(oldDef.getName());
- log.debug("changes process id " + pi.getId() + " from version " + pi.getProcessDefinition().getVersion() + " to new version " + newDef.getVersion());
-
+ log.debug("Start changing process id " + pi.getId() + " from version " + pi.getProcessDefinition().getVersion() + " to new version " + newDef.getVersion());
pi.setProcessDefinition(newDef);
+ changeTokenVersion(pi.getRootToken());
+
log.debug("process id " + pi.getId() + " changed to version " + pi.getProcessDefinition().getVersion());
}
+
+ private ProcessDefinition getNewProcessDefinition(Token t) {
+ return t.getProcessInstance().getProcessDefinition();
+ }
- private void changeAllProcessInstances(String processName) throws Exception
- {
- log.debug("changing version all processes '" + processName + "'");
+ private void changeTokenVersion(Token token)
+ {
+ ProcessDefinition newDef = getNewProcessDefinition(token);
+ log.debug("change token id " + token.getId() + " to new version " + newDef.getVersion());
- GetProcessInstancesCommand cmd = new GetProcessInstancesCommand();
- cmd.setProcessName(processName);
- cmd.setOnlyRunning(true);
-
- List instances = (List)cmd.execute(jbpmContext);
- for (Iterator iter = instances.iterator(); iter.hasNext();)
- {
- ProcessInstance pi = (ProcessInstance)iter.next();
- changeProcessVersion(pi);
- }
- }
-
- private void changeTokenVersion(JbpmContext jbpmContext, Token token)
- {
+ // change node reference on token (current node)
Node oldNode = token.getNode();
+ Node newNode = findReplacementNode(newDef, oldNode);
+ token.setNode(newNode);
- ProcessDefinition oldDef = token.getProcessInstance().getProcessDefinition();
- ProcessDefinition newDef = getNewDef(oldDef.getName());
+ // Change timers too!
+ adjustTimersForToken(token);
- Node newNode = newDef.findNode(getNewNodeName(oldNode));
+ // change tasks
+ adjustTaskInstancesForToken(token);
- if (newNode == null)
- {
- throw new JbpmException("node with name '" + getNewNodeName(oldNode) + "' not found in new process definition");
+ // change children recursively
+ if (token.getChildren()!=null) {
+ Iterator<Token> tokenIter = token.getChildren().values().iterator();
+ while (tokenIter.hasNext())
+ {
+ changeTokenVersion(tokenIter.next());
+ }
}
+ }
- log.debug("change token id " + token.getId() + " from version " + oldDef.getVersion() + " to new version " + newDef.getVersion());
-
- token.setNode(newNode);
-
- // TODO: Change timers too!
-
- // change tasks
- Iterator iter = getTasksForToken(token).iterator();
+ private void adjustTaskInstancesForToken(Token token)
+ {
+ ProcessDefinition newDef = getNewProcessDefinition(token);
+ Iterator<TaskInstance> iter = getTasksForToken(token).iterator();
while (iter.hasNext())
{
- TaskInstance ti = (TaskInstance)iter.next();
+ TaskInstance ti = iter.next();
+ // find new task
Task oldTask = ti.getTask();
- // find new task
- Query q = jbpmContext.getSession().getNamedQuery("TaskMgmtSession.findTaskForNode");
- q.setString("taskName", oldTask.getName());
- q.setLong("taskNodeId", newNode.getId());
- // TODO: q.setLong("processDefinitionId", newDef.getId());
+ Node oldNode = oldTask.getTaskNode();
+
+ Task newTask = findReplacementTask(newDef, oldNode, oldTask);
+ ti.setTask(newTask);
+ log.debug("change dependent task-instance with id " + oldTask.getId());
+ }
+ }
- Task newTask = (Task)q.uniqueResult();
-
- if (newTask == null)
+ private void adjustTimersForToken(Token token)
+ {
+ ProcessDefinition newDef = getNewProcessDefinition(token);
+ List<Job> jobs = getJbpmContext().getJobSession().findJobsByToken(token);
+ for (Job job : jobs)
+ {
+ if (job instanceof Timer)
{
- throw new JbpmException("node '" + newNode.getName() + "' has no Task configured! Check the new process definition");
+ // check all timers if connected to a GraphElement
+ Timer timer = (Timer)job;
+ if (timer.getGraphElement()!=null) {
+
+ // and change the reference (take name mappings into account!)
+ if (timer.getGraphElement() instanceof Task) {
+ // change to new task definition
+ Task oldTask = (Task)timer.getGraphElement();
+ TaskNode oldNode = oldTask.getTaskNode();
+ timer.setGraphElement(
+ findReplacementTask(newDef, oldNode, oldTask));
+ }
+ else {
+ // change to new node
+ GraphElement oldNode = timer.getGraphElement();
+ // TODO: What with other GraphElements?
+ timer.setGraphElement(
+ findReplacementNode(newDef, oldNode));
+ }
+ }
}
-
- ti.setTask(newTask);
- log.debug("change dependent task-instance with id " + oldTask.getId());
}
+ }
+
+ private Node findReplacementNode(ProcessDefinition newDef, GraphElement oldNode)
+ {
+ String name = getReplacementNodeName( oldNode );
+ Node newNode = newDef.findNode(name);
+ if (newNode == null)
+ {
+ throw new JbpmException("node with name '" + name + "' not found in new process definition");
+ }
+ return newNode;
+ }
- // change childs recursive
- Iterator childIter = token.getChildren().values().iterator();
- while (childIter.hasNext())
+ private Task findReplacementTask(ProcessDefinition newDef, Node oldNode, Task oldTask)
+ {
+ String replacementTaskName = getReplacementTaskName( oldTask );
+ Node newTaskNode = findReplacementNode(newDef, oldNode);
+
+ Query q = getJbpmContext().getSession().getNamedQuery("TaskMgmtSession.findTaskForNode");
+ q.setString("taskName", replacementTaskName);
+ q.setLong("taskNodeId", newTaskNode.getId());
+
+ Task newTask = (Task)q.uniqueResult();
+ if (newTask == null)
{
- changeTokenVersion(jbpmContext, (Token)childIter.next());
+ throw new JbpmException("Task '" + replacementTaskName + "' for node '" + newTaskNode.getName() + "' not found in new process definition");
}
+ return newTask;
}
/**
* @param oldNode
* @return the name of the new node (given in the map or return default value, which is the old node name)
*/
- private String getNewNodeName(Node oldNode)
+ private String getReplacementNodeName(GraphElement oldNode)
+ {
+ String oldName = (oldNode instanceof Node ? ((Node)oldNode).getFullyQualifiedName() : oldNode.getName());
+ if (nodeNameMapping.containsKey(oldName))
+ {
+ return (String)nodeNameMapping.get(oldName);
+ }
+ // return new node name = old node name as default
+ return oldName;
+ }
+
+ private String getReplacementTaskName(Task oldTask)
{
- String oldName = oldNode.getFullyQualifiedName();
- if (nameMapping.containsKey(oldName))
+ String oldName = oldTask.getName();
+ if (taskNameMapping.containsKey(oldName))
{
- return (String)nameMapping.get(oldName);
+ return (String)taskNameMapping.get(oldName);
}
// return new node name = old node name as default
return oldName;
@@ -214,23 +251,25 @@
*/
private List getTasksForToken(Token token)
{
- Query query = jbpmContext.getSession().getNamedQuery("TaskMgmtSession.findTaskInstancesByTokenId");
+ Query query = getJbpmContext().getSession().getNamedQuery("TaskMgmtSession.findTaskInstancesByTokenId");
query.setLong("tokenId", token.getId());
return query.list();
}
- public Map getNameMapping()
+ public Map getNodeNameMapping()
{
- return nameMapping;
+ return nodeNameMapping;
}
-
- public void setNameMapping(Map nameMapping)
+
+ public ChangeProcessInstanceVersionCommand setNodeNameMapping(Map<String, String> nameMapping)
{
- if (nameMapping == null)
- this.nameMapping = new HashMap();
- else
- this.nameMapping = nameMapping;
+ if (nameMapping == null) {
+ this.nodeNameMapping = new HashMap<String, String>();
+ } else {
+ this.nodeNameMapping = nameMapping;
+ }
+ return this;
}
public int getNewVersion()
@@ -238,29 +277,60 @@
return newVersion;
}
- public void setNewVersion(int newVersion)
+ public ChangeProcessInstanceVersionCommand setNewVersion(int newVersion)
{
this.newVersion = newVersion;
+ return this;
}
+
+ public Map getTaskNameMapping()
+ {
+ return taskNameMapping;
+ }
+ public ChangeProcessInstanceVersionCommand setTaskNameMapping(Map<String, String> nameMapping)
+ {
+ if (nameMapping == null) {
+ this.taskNameMapping = new HashMap<String, String>();
+ } else {
+ this.taskNameMapping = nameMapping;
+ }
+ return this;
+ }
+
+ /**
+ * @deprecated use getProcessInstanceId instead
+ */
public long getProcessId()
{
- return processId;
+ if (getProcessInstanceIds()!=null && getProcessInstanceIds().length>0)
+ return getProcessInstanceIds()[0];
+ else
+ return 0;
}
+ /**
+ * @deprecated use setProcessInstanceId instead
+ */
public void setProcessId(long processId)
{
- this.processId = processId;
+ super.setProcessInstanceId(processId);
}
- public String getProcessName()
+ /**
+ * @deprecated use getNodeNameMapping instead
+ */
+ public Map getNameMapping()
{
- return processName;
+ return getNodeNameMapping();
}
- public void setProcessName(String processName)
- {
- this.processName = processName;
+ /**
+ * @deprecated use setNodeNameMapping instead
+ */
+ public ChangeProcessInstanceVersionCommand setNameMapping(Map nameMapping) {
+ return setNodeNameMapping(nameMapping);
}
+
}
Added: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ResumeProcessInstanceCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ResumeProcessInstanceCommand.java (rev 0)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/ResumeProcessInstanceCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,19 @@
+package org.jbpm.command;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.exe.ProcessInstance;
+
+/**
+ * Resume the specified {@link ProcessInstance}(s). See {@link AbstractProcessInstanceBaseCommand}
+ * to check possibilities to specify {@link ProcessInstance}(s).
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public class ResumeProcessInstanceCommand extends AbstractProcessInstanceBaseCommand {
+
+ @Override
+ public void execute(ProcessInstance processInstance)
+ {
+ processInstance.resume();
+ }
+}
Added: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/SuspendProcessInstanceCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/SuspendProcessInstanceCommand.java (rev 0)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/SuspendProcessInstanceCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,24 @@
+package org.jbpm.command;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+
+/**
+ * Suspend the specified {@link ProcessInstance}(s). See {@link AbstractProcessInstanceBaseCommand}
+ * to check possibilities to specify {@link ProcessInstance}(s).
+ *
+ * With filter to all {@link ProcessDefinition}s this can be used
+ * like an emergency shutdown for {@link ProcessDefinition}s.
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public class SuspendProcessInstanceCommand extends AbstractProcessInstanceBaseCommand {
+
+ @Override
+ public void execute(ProcessInstance processInstance)
+ {
+ processInstance.suspend();
+ }
+
+}
Added: jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/UnlockTokenCommand.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/UnlockTokenCommand.java (rev 0)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/command/UnlockTokenCommand.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,48 @@
+package org.jbpm.command;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.exe.Token;
+
+/**
+ * Unlocks the given token. Either the correct lockOwner has
+ * to be provided (otherwise an exception is thrown) or
+ * NO lockOwner is provided at all, then the lock is
+ * removed "with force".
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public class UnlockTokenCommand extends AbstractTokenBaseCommand {
+
+ private static final long serialVersionUID = 1L;
+
+ private String lockOwner;
+
+ public UnlockTokenCommand() {
+ }
+
+ public Object execute(Token token)
+ {
+ if (lockOwner!=null) {
+ token.unlock(lockOwner);
+ }
+ else {
+ // requires newer jbpm version, see https://jira.jboss.org/jira/browse/JBPM-1888
+ token.foreUnlock();
+ }
+ return token;
+ }
+
+ public String getAdditionalToStringInformation() {
+ return (lockOwner!=null ? ";lockOwner=" + lockOwner : "");
+ }
+
+ public String getLockOwner() {
+ return lockOwner;
+ }
+
+ public UnlockTokenCommand setLockOwner(String lockOwner) {
+ this.lockOwner = lockOwner;
+ return this;
+ }
+
+}
Modified: jbpm3/trunk/modules/core/src/main/java/org/jbpm/db/JobSession.java
===================================================================
--- jbpm3/trunk/modules/core/src/main/java/org/jbpm/db/JobSession.java 2008-12-10 12:51:17 UTC (rev 3315)
+++ jbpm3/trunk/modules/core/src/main/java/org/jbpm/db/JobSession.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -76,6 +76,20 @@
}
return jobs;
}
+
+ /**
+ * find all jobs
+ */
+ public List<Job> findJobsByToken(Token token) {
+ try {
+ Query query = session.getNamedQuery("JobSession.findJobsByToken");
+ query.setParameter("token", token);
+ List<Job> jobs = query.list();
+ return jobs;
+ } catch (Exception e) {
+ throw new JbpmException("couldn't find jobs for token '"+token+"'", e);
+ }
+ }
public Job getFirstDueJob(String lockOwner, Collection jobIdsToIgnore) {
Job job = null;
Modified: jbpm3/trunk/modules/core/src/main/resources/org/jbpm/db/hibernate.queries.hbm.xml
===================================================================
--- jbpm3/trunk/modules/core/src/main/resources/org/jbpm/db/hibernate.queries.hbm.xml 2008-12-10 12:51:17 UTC (rev 3315)
+++ jbpm3/trunk/modules/core/src/main/resources/org/jbpm/db/hibernate.queries.hbm.xml 2008-12-10 12:58:42 UTC (rev 3316)
@@ -62,7 +62,7 @@
select pd
from org.jbpm.graph.def.ProcessDefinition as pd
where pd.name = :name
- order by pd.version desC
+ order by pd.version desc
]]>
</query>
@@ -141,6 +141,16 @@
and token.node.name = :nodeName
]]>
</query>
+
+ <query name="GraphSession.findTokensForProcessVersionInNode">
+ <![CDATA[
+ select token
+ from org.jbpm.graph.exe.Token token
+ where token.processInstance.processDefinition.name = :processDefinitionName
+ and token.processInstance.processDefinition.version = :processDefinitionVersion
+ and token.node.name = :nodeName
+ ]]>
+ </query>
<query name="GraphSession.findProcessInstanceByKey">
<![CDATA[
@@ -329,6 +339,14 @@
and job.lockOwner is null
]]>
</query>
+
+ <query name="JobSession.findJobsByToken">
+ <![CDATA[
+ select job
+ from org.jbpm.job.Job as job
+ where job.token = :token
+ ]]>
+ </query>
<!-- related to Tasks -->
<!-- ########################### -->
Added: jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ChangeProcessInstanceVersionCommandTest.java
===================================================================
--- jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ChangeProcessInstanceVersionCommandTest.java (rev 0)
+++ jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ChangeProcessInstanceVersionCommandTest.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,643 @@
+package org.jbpm.command;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.jbpm.JbpmException;
+import org.jbpm.db.AbstractDbTestCase;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.graph.exe.Token;
+import org.jbpm.job.Job;
+import org.jbpm.job.Timer;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Tests for {@link ChangeProcessInstanceVersionCommand}
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public class ChangeProcessInstanceVersionCommandTest extends AbstractDbTestCase
+{
+
+ private ProcessDefinition pd1;
+ private ProcessDefinition pd2;
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ if (pd1!=null && pd2!=null) {
+ newTransaction();
+ // IMPORTANT: The ProcessDefinitions have to be deleted in one transaction,
+ // in the right order (new definition with ProcessInstance first) or the
+ // ProcessInstance has to be deleted first independently.
+
+ // This is because Logs of the ProcessInstance point to both ProcessDefinitions
+ // (the old and the new one) but only with the new ProcessDefinition the
+ // ProcessInstance is deleted
+
+ // exceptions look like this: could not delete: [org.jbpm.graph.def.Transition#9]
+ // Integrity constraint violation FK_LOG_TRANSITION table: JBPM_LOG in statement [/* delete org.jbpm.graph.def.Transition */ delete from JBPM_TRANSITION where ID_=?]
+ // or hibernate batch failed
+
+ // IMPORTANT: Keep this order of deletions! Otherwise if there is
+ // more than one ProcessInstance for the ProcessDefinition a HibernateSeassion.flush
+ // is called when queriing the seconf ProcessInstance after deleting the first
+ // one which may fire a Integrity constraint violation (same problem as described
+ // above), in this case I got
+ // could not delete: [org.jbpm.taskmgmt.def.TaskMgmtDefinition#2]
+ // Integrity constraint violation FK_TASKMGTINST_TMD table: JBPM_MODULEINSTANCE in statement [/* delete org.jbpm.taskmgmt.def.TaskMgmtDefinition */ delete from JBPM_MODULEDEFINITION where ID_=?]
+ jbpmContext.getGraphSession().deleteProcessDefinition(pd2.getId());
+ jbpmContext.getGraphSession().deleteProcessDefinition(pd1.getId());
+ } else if (pd1!=null) {
+ newTransaction();
+ jbpmContext.getGraphSession().deleteProcessDefinition(pd1.getId());
+ } else if (pd2!=null) {
+ newTransaction();
+ jbpmContext.getGraphSession().deleteProcessDefinition(pd2.getId());
+ }
+
+ pd1=null;
+ pd2=null;
+ super.tearDown();
+ }
+
+ /**
+ * test easy version migration (no fork or other stuff)
+ * but with name mapping (different state name in new process
+ * definition)
+ */
+ public void testNameMapping() throws Exception
+ {
+ String xmlVersion1 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition name='state1' to='state1' />"//
+ + " <transition name='state2' to='state2' />"//
+ + " </start-state>" //
+ + " <state name='state1'>" //
+ + " <transition name='end1' to='end' />" //
+ + " </state>" //
+ + " <state name='state2'>" //
+ + " <transition name='end2' to='end' />" //
+ + " </state>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ pd1 = ProcessDefinition.parseXmlString(xmlVersion1);
+ jbpmContext.deployProcessDefinition(pd1);
+ pd1 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // start 2 instances
+ ProcessInstance pi1 = jbpmContext.newProcessInstance("TestChangeVersion");
+ pi1.signal("state1");
+ ProcessInstance pi2 = jbpmContext.newProcessInstance("TestChangeVersion");
+ pi2.signal("state2");
+
+ String xmlVersion2 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition name='state1' to='state1' />"//
+ + " <transition name='state2' to='state2b'/>"//
+ + " </start-state>" //
+ + " <state name='state1'>" //
+ + " <transition name='end1' to='end' />" //
+ + " </state>" //
+ + " <state name='state2b'>" //
+ + " <transition name='end2b' to='end' />" //
+ + " </state>" //
+ + " <end-state name='end' />" //
+ + "</process-definition>";
+
+ pd2 = ProcessDefinition.parseXmlString(xmlVersion2);
+ jbpmContext.deployProcessDefinition(pd2);
+ pd2 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // now change all process instances to most current version
+ try
+ {
+ new ChangeProcessInstanceVersionCommand().setProcessName("TestChangeVersion").execute(jbpmContext);
+ fail("Exception expected, saying that state2 is missing in new version");
+ }
+ catch (JbpmException ex)
+ {
+ assertEquals("node with name 'state2' not found in new process definition", ex.getMessage());
+ }
+
+ HashMap nameMap = new HashMap();
+ nameMap.put("state2", "state2b");
+ // now supply a mapping for the missing node
+ new ChangeProcessInstanceVersionCommand().setNodeNameMapping(nameMap).setProcessName("TestChangeVersion").execute(jbpmContext);
+
+ newTransaction();
+ pi1 = graphSession.loadProcessInstance(pi1.getId());
+ pi2 = graphSession.loadProcessInstance(pi2.getId());
+
+ assertEquals("state1", pi1.getRootToken().getNode().getName());
+ assertEquals(pd2.getNode("state1").getId(), pi1.getRootToken().getNode().getId());
+
+ assertEquals("state2b", pi2.getRootToken().getNode().getName());
+ assertEquals(pd2.getNode("state2b").getId(), pi2.getRootToken().getNode().getId());
+
+ pi1.getRootToken().signal("end1");
+ pi2.getRootToken().signal("end2b");
+
+ newTransaction();
+ pi1 = graphSession.loadProcessInstance(pi1.getId());
+ pi2 = graphSession.loadProcessInstance(pi2.getId());
+
+ assertEquals(pd2.getNode("end").getId(), pi1.getRootToken().getNode().getId());
+ assertTrue(pi1.hasEnded());
+ assertEquals(pd2.getNode("end").getId(), pi2.getRootToken().getNode().getId());
+ assertTrue(pi2.hasEnded());
+ }
+
+ /**
+ * check that update of nodes work correctly if a fork was involved
+ * and multiple child tokens exist
+ */
+ public void testSubTokensInFork() throws Exception
+ {
+ String xmlVersion1 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='fork' />" //
+ + " </start-state>" //
+ + " <fork name='fork'>" //
+ + " <transition name='path1' to='path1' />" //
+ + " <transition name='path2' to='path2' />" //
+ + " </fork>" //
+ + " <state name='path1'>" //
+ + " <transition to='join' />" //
+ + " </state>" //
+ + " <state name='path2'>" //
+ + " <transition to='join' />" //
+ + " </state>" //
+ + " <join name='join'>" //
+ + " <transition to='end' />" //
+ + " </join>" //
+ + " <end-state name='end' />" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion1));
+ pd1 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // start instance
+ ProcessInstance pi1 = jbpmContext.newProcessInstance("TestChangeVersion");
+ pi1.signal();
+ Token t1 = pi1.getRootToken().getChild("path1");
+ Token t2 = pi1.getRootToken().getChild("path2");
+
+ String xmlVersion2 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='fork' />" //
+ + " </start-state>" //
+ + " <fork name='fork'>" //
+ + " <transition name='path1' to='path1' />" //
+ + " <transition name='path2' to='path2b' />" //
+ + " </fork>" //
+ + " <state name='path1'>" //
+ + " <transition to='join' />" //
+ + " </state>" //
+ + " <state name='path2b'>" //
+ + " <transition name='2b' to='join' />" //
+ + " </state>" //
+ + " <join name='join'>" //
+ + " <transition to='end' />" //
+ + " </join>" //
+ + " <end-state name='end' />" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion2));
+ pd2 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // now change all process instances to most current version
+ try
+ {
+ new ChangeProcessInstanceVersionCommand().setProcessInstanceId(pi1.getId()).execute(jbpmContext);
+ fail("Exception expected, saying that phase2 is missing in new version");
+ }
+ catch (JbpmException ex)
+ {
+ assertEquals("node with name 'path2' not found in new process definition", ex.getMessage());
+ }
+
+ HashMap nameMap = new HashMap();
+ nameMap.put("path2", "path2b");
+ // now supply a mapping for the missing node
+ new ChangeProcessInstanceVersionCommand().setNodeNameMapping(nameMap).setProcessInstanceId(pi1.getId()).execute(jbpmContext);
+
+ newTransaction();
+
+ t1 = graphSession.getToken(t1.getId());
+ t2 = graphSession.getToken(t2.getId());
+
+ assertEquals(pd2.getNode("path1").getId(), t1.getNode().getId());
+ assertEquals(pd2.getNode("path2b").getId(), t2.getNode().getId());
+
+ t1.signal();
+ t2.signal("2b");
+
+ pi1 = graphSession.getProcessInstance(pi1.getId());
+
+ assertEquals(pd2.getNode("end").getId(), pi1.getRootToken().getNode().getId());
+ assertTrue(pi1.hasEnded());
+ }
+
+ public void testTaskInFork() throws Exception
+ {
+ String xmlVersion1 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='fork' />" //
+ + " </start-state>" //
+ + " <fork name='fork'>" //
+ + " <transition name='path1' to='task1' />" //
+ + " <transition name='path2' to='task2' />" //
+ + " </fork>" //
+ + " <task-node name='task1'>" //
+ + " <task name='theTask1' />" //
+ + " <transition to='join' />" //
+ + " </task-node>" //
+ + " <task-node name='task2'>" //
+ + " <task name='theTask2' />" //
+ + " <transition to='join' />" //
+ + " </task-node>" //
+ + " <join name='join'>" //
+ + " <transition to='end' />" //
+ + " </join>" //
+ + " <end-state name='end' />" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion1));
+ pd1 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // start instance
+ ProcessInstance pi1 = jbpmContext.newProcessInstance("TestChangeVersion");
+ pi1.signal();
+ Token t1 = pi1.getRootToken().getChild("path1");
+ Token t2 = pi1.getRootToken().getChild("path2");
+
+ String xmlVersion2 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='fork' />" //
+ + " </start-state>" //
+ + " <fork name='fork'>" //
+ + " <transition name='path1' to='task1' />" //
+ + " <transition name='path2' to='task2' />" //
+ + " </fork>" //
+ + " <task-node name='task1b'>" //
+ + " <task name='theTask1b' />" //
+ + " <transition to='join' />" //
+ + " </task-node>" //
+ + " <task-node name='task2b'>" //
+ + " <task name='theTask2b' />" //
+ + " <transition to='join' />" //
+ + " </task-node>" //
+ + " <join name='join'>" //
+ + " <transition to='end' />" //
+ + " </join>" //
+ + " <end-state name='end' />" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion2));
+ pd2 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ HashMap<String, String> nodeNameMap = new HashMap<String, String>();
+ nodeNameMap.put("task1", "task1b");
+ nodeNameMap.put("task2", "task2b");
+ HashMap<String, String> taskNameMap = new HashMap<String, String>();
+ taskNameMap.put("theTask1", "theTask1b");
+ taskNameMap.put("theTask2", "theTask2b");
+
+ // now supply a mapping for the missing node
+ new ChangeProcessInstanceVersionCommand()
+ .setNodeNameMapping(nodeNameMap)
+ .setTaskNameMapping(taskNameMap)
+ .setProcessInstanceId(pi1.getId())
+ .execute(jbpmContext);
+
+ newTransaction();
+
+ t1 = jbpmContext.loadTokenForUpdate(t1.getId());
+ assertEquals(pd2.getNode("task1b").getId(), t1.getNode().getId());
+
+ Iterator<TaskInstance> taskInstanceIter = t1.getProcessInstance().getTaskMgmtInstance().getTaskInstances().iterator();
+ TaskInstance ti1 = taskInstanceIter.next();
+ if ("theTask2b".equals(ti1.getTask().getName())) {
+ // this was the wrong one
+ ti1 = taskInstanceIter.next();
+ }
+ assertEquals("theTask1b", ti1.getTask().getName());
+ assertEquals(pd2.getTaskMgmtDefinition().getTask("theTask1b").getId(), ti1.getTask().getId());
+
+ ti1.end();
+
+ ///////
+ newTransaction();
+
+ t2 = graphSession.getToken(t2.getId());
+ assertEquals(pd2.getNode("task2b").getId(), t2.getNode().getId());
+
+ taskInstanceIter = t2.getProcessInstance().getTaskMgmtInstance().getTaskInstances().iterator();
+ TaskInstance ti2 = taskInstanceIter.next();
+ if ("theTask1b".equals(ti2.getTask().getName())) {
+ // this was the wrong one
+ ti2 = taskInstanceIter.next();
+ }
+ assertEquals("theTask2b", ti2.getTask().getName());
+ assertEquals(pd2.getTaskMgmtDefinition().getTask("theTask2b").getId(), ti2.getTask().getId());
+
+ ti2.end();
+
+ newTransaction();
+ pi1 = graphSession.loadProcessInstance(pi1.getId());
+
+ assertEquals(pd2.getNode("end").getId(), pi1.getRootToken().getNode().getId());
+ assertTrue(pi1.hasEnded());
+ closeJbpmContext();
+ }
+
+ public void testSubProcesses()
+ {
+
+ }
+
+ /**
+ * check, that TaskInstances work (TaskInstance reference to Task has
+ * to be adjusted as will)
+ */
+ public void testTaskInstances() throws Exception
+ {
+ String xmlVersion1 = //
+ "<process-definition name='testTaskInstances'>" //
+ + " <start-state name='start'>" //
+ + " <transition name='path1' to='task1' />"//
+ + " <transition name='path2' to='task2' />"//
+ + " </start-state>" //
+ + " <task-node name='task1'>" //
+ + " <task name='theTask1'/>" //
+ + " <transition name='end1' to='end' />" //
+ + " </task-node>" //
+ + " <task-node name='task2'>" //
+ + " <task name='theTask2'/>" //
+ + " <transition name='end2' to='end' />" //
+ + " </task-node>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ pd1 = ProcessDefinition.parseXmlString(xmlVersion1);
+ jbpmContext.deployProcessDefinition(pd1);
+ pd1 = jbpmContext.getGraphSession().findLatestProcessDefinition("testTaskInstances");
+
+ // start 2 instances
+ ProcessInstance pi1 = jbpmContext.newProcessInstance("testTaskInstances");
+ pi1.signal("path1");
+ ProcessInstance pi2 = jbpmContext.newProcessInstance("testTaskInstances");
+ pi2.signal("path2");
+
+ String xmlVersion2 = //
+ "<process-definition name='testTaskInstances'>" //
+ + " <start-state name='start'>" //
+ + " <transition name='path1' to='task1' />"//
+ + " <transition name='path2' to='task2b' />"//
+ + " </start-state>" //
+ + " <task-node name='task1'>" //
+ + " <task name='theTask1'/>" //
+ + " <transition name='end1' to='end' />" //
+ + " </task-node>" //
+ + " <task-node name='task2b'>" //
+ + " <task name='theTask2b'/>" //
+ + " <transition name='end2b' to='end' />" //
+ + " </task-node>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ pd2 = ProcessDefinition.parseXmlString(xmlVersion2);
+ jbpmContext.deployProcessDefinition(pd2);
+ pd2 = jbpmContext.getGraphSession().findLatestProcessDefinition("testTaskInstances");
+
+ // process instance 1 can me updated, state names haven't changed in this path:
+ new ChangeProcessInstanceVersionCommand()
+ .setProcessInstanceId(pi1.getId())
+ .execute(jbpmContext);
+
+ HashMap nameMap = new HashMap();
+ nameMap.put("task2", "task2b");
+
+ // now change all process instances to most current version
+ try
+ {
+ new ChangeProcessInstanceVersionCommand()
+ .setNodeNameMapping(nameMap)
+ .setTaskNameMapping(nameMap)
+ .setProcessName("testTaskInstances")
+ .execute(jbpmContext);
+ // fail because task2 is not mapped
+ fail("Exception expected, saying that theTask2b is missing in new version");
+ }
+ catch (JbpmException ex)
+ {
+ assertEquals("Task 'theTask2' for node 'task2b' not found in new process definition", ex.getMessage());
+ }
+
+ // now supply a mapping for the missing task
+ nameMap.put("theTask2", "theTask2b");
+ new ChangeProcessInstanceVersionCommand()
+ .setNodeNameMapping(nameMap)
+ .setTaskNameMapping(nameMap)
+ .setProcessName("testTaskInstances")
+ .execute(jbpmContext);
+
+ newTransaction();
+
+ pi1 = graphSession.loadProcessInstance(pi1.getId());
+ pi2 = graphSession.loadProcessInstance(pi2.getId());
+
+ assertEquals(pd2.getNode("task1").getId(), pi1.getRootToken().getNode().getId());
+ assertEquals(pd2.getNode("task2b").getId(), pi2.getRootToken().getNode().getId());
+
+ TaskInstance ti1 = pi1.getTaskMgmtInstance().getTaskInstances().iterator().next();
+ TaskInstance ti2 = pi2.getTaskMgmtInstance().getTaskInstances().iterator().next();
+
+ assertEquals(pd2.getTaskMgmtDefinition().getTask("theTask1").getId(), ti1.getTask().getId());
+ assertEquals(pd2.getTaskMgmtDefinition().getTask("theTask2b").getId(), ti2.getTask().getId());
+
+ ti1.end("end1");
+ ti2.end("end2b");
+
+ assertEquals(pd2.getNode("end").getId(), pi1.getRootToken().getNode().getId());
+ assertTrue(pi1.hasEnded());
+ assertEquals(pd2.getNode("end").getId(), pi2.getRootToken().getNode().getId());
+ assertTrue(pi2.hasEnded());
+ }
+
+ /**
+ * test if changing process version works correctly
+ * if a timer is included in the process definition.
+ *
+ * Important: The timer itself IS NOT changed, so
+ * e.g. used leaving transitions must be still existent
+ */
+ public void testTimerInState() throws Exception
+ {
+ String xmlVersion1 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='timer1' />"//
+ + " </start-state>" //
+ + " <state name='timer1'>" //
+ + " <timer name='timer1' duedate='5 seconds' transition='end' />" //
+ + " <transition name='end' to='end' />" //
+ + " </state>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion1));
+ pd1 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // start instance
+ ProcessInstance pi1 = jbpmContext.newProcessInstance("TestChangeVersion");
+ pi1.signal();
+ Timer timer = (Timer) session.createQuery("from org.jbpm.job.Timer").uniqueResult();
+
+ // check timer
+ assertNotNull("Timer is null", timer);
+ assertEquals("timer1", timer.getName());
+ assertEquals(pd1.getNode("timer1").getId(), timer.getGraphElement().getId());
+
+ String xmlVersion2 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='timer2' />"//
+ + " </start-state>" //
+ + " <state name='timer2'>" //
+ + " <timer name='timer1' duedate='5 seconds' transition='end1' />" //
+ + " <transition name='end' to='end' />" //
+ + " </state>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion2));
+ pd2 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // change version
+ HashMap nameMap = new HashMap();
+ nameMap.put("timer1", "timer2");
+ new ChangeProcessInstanceVersionCommand()
+ .setNodeNameMapping(nameMap)
+ .setProcessInstanceId(pi1.getId())
+ .execute(jbpmContext);
+
+ // load changed stuff
+ newTransaction();
+ pi1 = graphSession.loadProcessInstance(pi1.getId());
+ timer = (Timer) session.createQuery("from org.jbpm.job.Timer").uniqueResult();
+
+ // and check again
+ assertEquals(pd2.getNode("timer2").getId(), pi1.getRootToken().getNode().getId());
+ assertEquals("timer1", timer.getName());
+ assertEquals(pd2.getNode("timer2").getId(), timer.getGraphElement().getId());
+
+ timer.execute(jbpmContext);
+
+ assertEquals(pd2.getNode("end").getId(), pi1.getRootToken().getNode().getId());
+ assertTrue(pi1.hasEnded());
+ }
+
+ public void testTimerInTask() throws Exception
+ {
+ String xmlVersion1 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='timer1' />"//
+ + " </start-state>" //
+ + " <task-node name='timer1'>" //
+ + " <task name='myTask'>" //
+ + " <timer name='timer1' duedate='5 seconds' transition='end' />" //
+ + " </task>" //
+ + " <transition name='end' to='end' />" //
+ + " </task-node>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion1));
+ pd1 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // start instance
+ ProcessInstance pi1 = jbpmContext.newProcessInstance("TestChangeVersion");
+ pi1.signal();
+// jbpmContext.getJobSession().deleteJobsForProcessInstance(processInstance);
+// NOT UNIQUE?!
+ Timer timer = (Timer) session.createQuery("from org.jbpm.job.Timer").uniqueResult();
+
+ // check timer
+ assertNotNull("Timer is null", timer);
+ assertEquals("timer1", timer.getName());
+ assertEquals(pd1.getTaskMgmtDefinition().getTask("myTask").getId(), timer.getGraphElement().getId());
+ TaskInstance ti1 = pi1.getTaskMgmtInstance().getTaskInstances().iterator().next();
+ assertEquals(pd1.getTaskMgmtDefinition().getTask("myTask").getId(), ti1.getTask().getId());
+
+ String xmlVersion2 = //
+ "<process-definition name='TestChangeVersion'>" //
+ + " <start-state name='start'>" //
+ + " <transition to='timer2' />"//
+ + " </start-state>" //
+ + " <task-node name='timer2'>" //
+ + " <task name='myTask2'>" //
+ + " <timer name='timer1' duedate='5 seconds' transition='end' />" //
+ + " </task>" //
+ + " <transition name='end' to='end' />" //
+ + " </task-node>" //
+ + " <end-state name='end'/>" //
+ + "</process-definition>";
+
+ jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlString(xmlVersion2));
+ pd2 = jbpmContext.getGraphSession().findLatestProcessDefinition("TestChangeVersion");
+
+ // change version
+ HashMap nameMap = new HashMap();
+ nameMap.put("timer1", "timer2");
+ nameMap.put("myTask", "myTask2");
+ new ChangeProcessInstanceVersionCommand()
+ .setNodeNameMapping(nameMap)
+ .setTaskNameMapping(nameMap)
+ .setProcessInstanceId(pi1.getId())
+ .execute(jbpmContext);
+
+ // load changed stuff
+ newTransaction();
+ pi1 = graphSession.loadProcessInstance(pi1.getId());
+ timer = (Timer) session.createQuery("from org.jbpm.job.Timer").uniqueResult();
+
+ // and check again
+ assertEquals(pd2.getNode("timer2").getId(), pi1.getRootToken().getNode().getId());
+ assertEquals("timer1", timer.getName());
+ assertEquals(pd2.getTaskMgmtDefinition().getTask("myTask2").getId(), timer.getGraphElement().getId());
+ ti1 = pi1.getTaskMgmtInstance().getTaskInstances().iterator().next();
+ assertEquals(pd2.getTaskMgmtDefinition().getTask("myTask2").getId(), ti1.getTask().getId());
+
+ // and go on
+ timer.execute(jbpmContext);
+
+ assertEquals(pd2.getNode("end").getId(), pi1.getRootToken().getNode().getId());
+ assertTrue(pi1.hasEnded());
+ }
+
+ public void testTimerInProcessDefinition() throws Exception
+ {
+ // TODO
+ }
+
+ /**
+ * Asynchronous continuation is not affected by changing the version,
+ * because a {@link Job} only holds {@link ProcessInstance}.id, {@link Token}.id
+ * or {@link TaskInstance}.id. None of them are changed while version changes.
+ */
+ public void notestAsync()
+ {
+ }
+
+}
Added: jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ProcessInstanceCommandTest.java
===================================================================
--- jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ProcessInstanceCommandTest.java (rev 0)
+++ jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/ProcessInstanceCommandTest.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,168 @@
+package org.jbpm.command;
+
+import java.util.Iterator;
+
+import org.jbpm.JbpmException;
+import org.jbpm.db.AbstractDbTestCase;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.graph.exe.Token;
+
+/**
+ * Tests for {@link Command}s working on {@link ProcessInstance}
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public class ProcessInstanceCommandTest extends AbstractDbTestCase
+{
+ private ProcessDefinition processDefinition;
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ if (processDefinition!=null) {
+ jbpmContext.getGraphSession().deleteProcessDefinition(processDefinition.getId());
+ }
+ processDefinition=null;
+
+ super.tearDown();
+ }
+
+ public void testEmpty() {}
+
+ public void testCancelProcessInstanceCommand() throws Exception {
+ String xml = //
+ "<?xml version='1.0' encoding='UTF-8'?>" //
+ +"<process-definition name='TestException'>" //
+ +" <start-state name='start'>" //
+ +" <transition to='fork' />" //
+ +" </start-state>" //
+ +" <fork name='fork'>" //
+ +" <transition name='path1' to='path1' />" //
+ +" <transition name='path2' to='path2' />" //
+ +" </fork>" //
+ +" <state name='path1'>" //
+ +" <transition to='join' />" //
+ +" </state>" //
+ +" <state name='path2'>" //
+ +" <transition to='join' />" //
+ +" </state>" //
+ +" <join name='join'>" //
+ +" <transition to='end' />" //
+ +" </join>" //
+ +" <end-state name='end' />" //
+ +"</process-definition>";
+
+ processDefinition = ProcessDefinition.parseXmlString(xml);
+ jbpmContext.deployProcessDefinition(processDefinition);
+ ProcessInstance processInstance = processDefinition.createProcessInstance();
+ processInstance.getRootToken().signal();
+ processInstance = saveAndReload(processInstance);
+
+ assertFalse(processInstance.getRootToken().hasEnded());
+ assertEquals("fork", processInstance.getRootToken().getNode().getName());
+ for (Iterator iterator = processInstance.getRootToken().getChildren().values().iterator(); iterator.hasNext();)
+ {
+ Token childToken = (Token)iterator.next();
+ assertFalse(childToken.hasEnded());
+ }
+
+ // execute CancelProcessInstanceCommand
+ new CancelProcessInstanceCommand(processInstance.getId()).execute(jbpmContext);
+
+ // and verify it is canceled
+ assertTrue(processInstance.getRootToken().hasEnded());
+ assertEquals("fork", processInstance.getRootToken().getNode().getName());
+ for (Iterator iterator = processInstance.getRootToken().getChildren().values().iterator(); iterator.hasNext();)
+ {
+ Token childToken = (Token)iterator.next();
+ assertTrue(childToken.hasEnded());
+ }
+ }
+
+ public void testSuspendResumeProcessInstanceCommand() throws Exception {
+ String xml = //
+ "<?xml version='1.0' encoding='UTF-8'?>" //
+ +"<process-definition name='TestException'>" //
+ +" <start-state name='start'>" //
+ +" <transition to='fork' />" //
+ +" </start-state>" //
+ +" <fork name='fork'>" //
+ +" <transition name='path1' to='path1' />" //
+ +" <transition name='path2' to='path2' />" //
+ +" </fork>" //
+ +" <state name='path1'>" //
+ +" <transition to='join' />" //
+ +" </state>" //
+ +" <state name='path2'>" //
+ +" <transition to='join' />" //
+ +" </state>" //
+ +" <join name='join'>" //
+ +" <transition to='end' />" //
+ +" </join>" //
+ +" <end-state name='end' />" //
+ +"</process-definition>";
+
+ processDefinition = ProcessDefinition.parseXmlString(xml);
+ jbpmContext.deployProcessDefinition(processDefinition);
+ ProcessInstance processInstance = processDefinition.createProcessInstance();
+ processInstance.getRootToken().signal();
+ processInstance = saveAndReload(processInstance);
+
+ assertFalse(processInstance.isSuspended());
+ assertFalse(processInstance.getRootToken().isSuspended());
+ assertEquals("fork", processInstance.getRootToken().getNode().getName());
+ for (Iterator iterator = processInstance.getRootToken().getChildren().values().iterator(); iterator.hasNext();)
+ {
+ Token childToken = (Token)iterator.next();
+ assertFalse(childToken.isSuspended());
+ }
+
+ // execute SuspendProcessInstanceCommand
+ new SuspendProcessInstanceCommand().setProcessInstanceId(processInstance.getId()).execute(jbpmContext);
+
+ // and verify
+ assertTrue(processInstance.isSuspended());
+ assertTrue(processInstance.getRootToken().isSuspended());
+ assertEquals("fork", processInstance.getRootToken().getNode().getName());
+ for (Iterator iterator = processInstance.getRootToken().getChildren().values().iterator(); iterator.hasNext();)
+ {
+ Token childToken = (Token)iterator.next();
+ assertTrue(childToken.isSuspended());
+
+ try {
+ childToken.signal();
+ fail("signal should not be accepted on suspended token");
+ }
+ catch (Exception ex) {
+ assertEquals(JbpmException.class, ex.getClass());
+ // can't signal token 'path1' (5): it is suspended
+ assertTrue("exception should be, that token is suspended", ex.getMessage().indexOf("it is suspended")>0);
+ }
+ }
+
+ // execute ResumeProcessInstanceCommand
+ new ResumeProcessInstanceCommand().setProcessInstanceId(processInstance.getId()).execute(jbpmContext);
+
+ // and verify
+ assertFalse(processInstance.isSuspended());
+ assertFalse(processInstance.getRootToken().isSuspended());
+ for (Iterator iterator = processInstance.getRootToken().getChildren().values().iterator(); iterator.hasNext();)
+ {
+ Token childToken = (Token)iterator.next();
+ assertFalse(childToken.isSuspended());
+ childToken.signal();
+ }
+
+ assertEquals("end", processInstance.getRootToken().getNode().getName());
+ assertTrue(processInstance.hasEnded());
+
+ // check db state
+ processInstance = saveAndReload(processInstance);
+
+ assertEquals("end", processInstance.getRootToken().getNode().getName());
+ assertTrue(processInstance.hasEnded());
+ }
+
+
+}
Added: jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/TokenCommandTest.java
===================================================================
--- jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/TokenCommandTest.java (rev 0)
+++ jbpm3/trunk/modules/core/src/test/java/org/jbpm/command/TokenCommandTest.java 2008-12-10 12:58:42 UTC (rev 3316)
@@ -0,0 +1,78 @@
+package org.jbpm.command;
+
+import org.jbpm.JbpmException;
+import org.jbpm.db.AbstractDbTestCase;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.graph.exe.Token;
+
+/**
+ * Tests for {@link Command}s working on {@link Token}
+ *
+ * @author bernd.ruecker at camunda.com
+ */
+public class TokenCommandTest extends AbstractDbTestCase
+{
+
+ public void testUnlockTokenCommand() throws Exception {
+ String xml = //
+ "<process-definition name='TestException'>" //
+ +" <start-state name='start'>" //
+ +" <transition to='wait' />" //
+ +" </start-state>" //
+ +" <state name='wait'>" //
+ +" <transition to='end' />" //
+ +" </state>" //
+ +" <end-state name='end' />" //
+ +"</process-definition>";
+
+ ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(xml);
+ jbpmContext.deployProcessDefinition(processDefinition);
+ try {
+ ProcessInstance processInstance = jbpmContext.newProcessInstanceForUpdate("TestException");//processDefinition.createProcessInstance();
+ processInstance.getRootToken().signal();
+ processInstance.getRootToken().lock("TEST-OWNER");
+ long tokenId = processInstance.getRootToken().getId();
+
+ processInstance = saveAndReload(processInstance);
+ try {
+ processInstance.getRootToken().signal();
+ fail("TOKEN IS LOCKED exception expected");
+ }
+ catch (JbpmException ex) {
+ // org.jbpm.JbpmException: this token is locked by TEST-OWNER
+ assertEquals("this token is locked by TEST-OWNER", ex.getMessage());
+ }
+
+ // unlocking without owner is a force unlock -> works
+ new UnlockTokenCommand().setTokenId(tokenId).execute(jbpmContext);
+// Token token = jbpmContext.loadTokenForUpdate(processInstance.getRootToken().getId());
+// token.foreUnlock();
+
+ // unlock with same owner
+ processInstance = saveAndReload(processInstance);
+ processInstance.getRootToken().lock("TEST-OWNER");
+ processInstance = saveAndReload(processInstance);
+ new UnlockTokenCommand().setLockOwner("TEST-OWNER").setTokenId(tokenId).execute(jbpmContext);
+
+ // unlocking with wrong owner fails
+ processInstance = saveAndReload(processInstance);
+ processInstance.getRootToken().lock("TEST-OWNER");
+ processInstance = saveAndReload(processInstance);
+ try {
+ new UnlockTokenCommand().setLockOwner("OTHER-OWNER").setTokenId(tokenId).execute(jbpmContext);
+ fail("'OTHER-OWNER' can't unlock token exception expected");
+ }
+ catch (JbpmException ex) {
+ // org.jbpm.JbpmException: 'OTHER-OWNER' can't unlock token '1' because it was already locked by 'TEST-OWNER'
+ assertTrue("Wrong exception, wasn't 'OTHER-OWNER' can't unlock token", ex.getMessage().indexOf("'OTHER-OWNER' can't unlock token")>=0);
+ }
+ }
+ finally {
+ newTransaction();
+ jbpmContext.getGraphSession().deleteProcessDefinition(processDefinition.getId());
+ }
+ }
+
+
+}
More information about the jbpm-commits
mailing list