001: package org.jbpm.command;
002:
003: import java.util.HashMap;
004: import java.util.Iterator;
005: import java.util.List;
006: import java.util.Map;
007:
008: import org.apache.commons.logging.Log;
009: import org.apache.commons.logging.LogFactory;
010: import org.hibernate.Query;
011: import org.jbpm.JbpmContext;
012: import org.jbpm.JbpmException;
013: import org.jbpm.db.JbpmSchema;
014: import org.jbpm.graph.def.Node;
015: import org.jbpm.graph.def.ProcessDefinition;
016: import org.jbpm.graph.exe.ProcessInstance;
017: import org.jbpm.graph.exe.Token;
018: import org.jbpm.taskmgmt.def.Task;
019: import org.jbpm.taskmgmt.exe.TaskInstance;
020:
021: /**
022: * <b>THIS COMMAND IS NOT YET STABLE, BUT FEEL FREE TO TEST :-)</b>
023: *
024: * change the version of a running process instance. This works only, if the
025: * current node is also available in the new version of the process definition
026: * (identified by name, so the name has to be exactly the same). One problem
027: * with this approach ist also, that if a task with the same name is moved to
028: * another node (but this is a rare case)
029: *
030: *
031: * make trouble, if there are 2 tokens in the process, because only one actual
032: * node is used...
033: *
034: * Possible workaround: use process id instead of node id.
035: *
036: * TODO: new hibernate query for that? Proposal Fluffi "select distinct task " +
037: * "from " + Task.class.getName() + " task " + "where task.name = :taskName " + "
038: * and task.processDefinition.id = :processDefinitionId ";
039: *
040: * @author Bernd Ruecker (bernd.ruecker@camunda.com)
041: */
042: public class ChangeProcessInstanceVersionCommand implements Command {
043:
044: private static final long serialVersionUID = 2277080393930008224L;
045:
046: /**
047: * process id of process to update. If set, this special process is updated
048: */
049: private long processId = -1;
050:
051: /**
052: * if set, all running processes of the process with this name are updated
053: */
054: private String processName;
055:
056: /**
057: * new version of process, if <=0, the latest process definition is used
058: */
059: private int newVersion = -1;
060:
061: private static final Log log = LogFactory.getLog(JbpmSchema.class);
062:
063: private transient JbpmContext jbpmContext = null;
064:
065: /**
066: * the map configures for every node-name in the old process definition (as
067: * key) which node-name to use in the new process definition.
068: *
069: * if a node is not mentioned in this Map, old node name = new node name is
070: * applied
071: */
072: private Map nameMapping = new HashMap();
073:
074: private transient ProcessDefinition newDef;
075:
076: public ChangeProcessInstanceVersionCommand() {
077: }
078:
079: public ChangeProcessInstanceVersionCommand(long processId,
080: int newVersion) {
081: this .processId = processId;
082: this .newVersion = newVersion;
083: }
084:
085: private ProcessDefinition getNewDef(String processName) {
086: if (newDef == null) {
087: if (newVersion <= 0)
088: newDef = jbpmContext.getGraphSession()
089: .findLatestProcessDefinition(processName);
090: else
091: newDef = jbpmContext.getGraphSession()
092: .findProcessDefinition(processName, newVersion);
093: }
094: return newDef;
095: }
096:
097: /**
098: * @return always null
099: * @see org.jbpm.command.Command#execute(org.jbpm.JbpmContext)
100: */
101: public Object execute(JbpmContext jbpmContext) throws Exception {
102: this .jbpmContext = jbpmContext;
103: if (processId > -1) {
104: ProcessInstance pi = jbpmContext.getGraphSession()
105: .loadProcessInstance(processId);
106: changeProcessVersion(pi);
107: }
108: if (processName != null && processName.length() > 0) {
109: changeAllProcessInstances(processName);
110: }
111: return null;
112: }
113:
114: private void changeProcessVersion(ProcessInstance pi) {
115: changeTokenVersion(jbpmContext, pi.getRootToken());
116:
117: ProcessDefinition oldDef = pi.getProcessDefinition();
118: ProcessDefinition newDef = getNewDef(oldDef.getName());
119:
120: log.debug("changes process id " + pi.getId() + " from version "
121: + pi.getProcessDefinition().getVersion()
122: + " to new version " + newDef.getVersion());
123:
124: pi.setProcessDefinition(newDef);
125:
126: log.debug("process id " + pi.getId() + " changed to version "
127: + pi.getProcessDefinition().getVersion());
128: }
129:
130: private void changeAllProcessInstances(String processName)
131: throws Exception {
132: log.debug("changing version all processes '" + processName
133: + "'");
134:
135: GetProcessInstancesCommand cmd = new GetProcessInstancesCommand();
136: cmd.setProcessName(processName);
137: cmd.setOnlyRunning(true);
138:
139: List instances = (List) cmd.execute(jbpmContext);
140: for (Iterator iter = instances.iterator(); iter.hasNext();) {
141: ProcessInstance pi = (ProcessInstance) iter.next();
142: changeProcessVersion(pi);
143: }
144: }
145:
146: private void changeTokenVersion(JbpmContext jbpmContext, Token token) {
147: Node oldNode = token.getNode();
148:
149: ProcessDefinition oldDef = token.getProcessInstance()
150: .getProcessDefinition();
151: ProcessDefinition newDef = getNewDef(oldDef.getName());
152:
153: Node newNode = newDef.findNode(getNewNodeName(oldNode));
154:
155: if (newNode == null) {
156: throw new JbpmException("node with name '"
157: + getNewNodeName(oldNode)
158: + "' not found in new process definition");
159: }
160:
161: log.debug("change token id " + token.getId() + " from version "
162: + oldDef.getVersion() + " to new version "
163: + newDef.getVersion());
164:
165: token.setNode(newNode);
166:
167: // TODO: Change timers too!
168:
169: // change tasks
170: Iterator iter = getTasksForToken(token).iterator();
171: while (iter.hasNext()) {
172: TaskInstance ti = (TaskInstance) iter.next();
173:
174: Task oldTask = ti.getTask();
175: // find new task
176: Query q = jbpmContext.getSession().getNamedQuery(
177: "TaskMgmtSession.findTaskForNode");
178: q.setString("taskName", oldTask.getName());
179: q.setLong("taskNodeId", newNode.getId());
180: // TODO: q.setLong("processDefinitionId", newDef.getId());
181:
182: Task newTask = (Task) q.uniqueResult();
183:
184: if (newTask == null) {
185: throw new JbpmException(
186: "node '"
187: + newNode.getName()
188: + "' has no Task configured! Check the new process definition");
189: }
190:
191: ti.setTask(newTask);
192: log.debug("change dependent task-instance with id "
193: + oldTask.getId());
194: }
195:
196: // change childs recursive
197: Iterator childIter = token.getChildren().values().iterator();
198: while (childIter.hasNext()) {
199: changeTokenVersion(jbpmContext, (Token) childIter.next());
200: }
201: }
202:
203: /**
204: * @param oldNode
205: * @return the name of the new node (given in the map or return default
206: * value, which is the old node name)
207: */
208: private String getNewNodeName(Node oldNode) {
209: String oldName = oldNode.getFullyQualifiedName();
210: if (nameMapping.containsKey(oldName)) {
211: return (String) nameMapping.get(oldName);
212: }
213: // return new node name = old node name as default
214: return oldName;
215: }
216:
217: /**
218: * We may still have open tasks, even though their parent tokens have been
219: * ended. So we'll simply get all tasks from this process instance and
220: * cancel them if they are still active.
221: *
222: */
223: private List getTasksForToken(Token token) {
224: Query query = jbpmContext.getSession().getNamedQuery(
225: "TaskMgmtSession.findTaskInstancesByTokenId");
226: query.setLong("tokenId", token.getId());
227: return query.list();
228:
229: }
230:
231: public Map getNameMapping() {
232: return nameMapping;
233: }
234:
235: public void setNameMapping(Map nameMapping) {
236: if (nameMapping == null)
237: this .nameMapping = new HashMap();
238: else
239: this .nameMapping = nameMapping;
240: }
241:
242: public int getNewVersion() {
243: return newVersion;
244: }
245:
246: public void setNewVersion(int newVersion) {
247: this .newVersion = newVersion;
248: }
249:
250: public long getProcessId() {
251: return processId;
252: }
253:
254: public void setProcessId(long processId) {
255: this .processId = processId;
256: }
257:
258: public String getProcessName() {
259: return processName;
260: }
261:
262: public void setProcessName(String processName) {
263: this.processName = processName;
264: }
265:
266: }
|