### Eclipse Workspace Patch 1.0 #P jbpm4 Index: modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/TaskActivity.java =================================================================== --- modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/TaskActivity.java (revision 6211) +++ modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/TaskActivity.java (working copy) @@ -26,6 +26,7 @@ import org.jbpm.api.JbpmException; import org.jbpm.api.activity.ActivityExecution; +import org.jbpm.api.activity.TimeoutActivityBehaviour; import org.jbpm.api.model.Transition; import org.jbpm.pvm.internal.cal.Duration; import org.jbpm.pvm.internal.el.Expression; @@ -34,7 +35,6 @@ import org.jbpm.pvm.internal.history.events.TaskActivityStart; import org.jbpm.pvm.internal.model.ActivityImpl; import org.jbpm.pvm.internal.model.ExecutionImpl; -import org.jbpm.pvm.internal.script.ScriptManager; import org.jbpm.pvm.internal.session.DbSession; import org.jbpm.pvm.internal.task.ParticipationImpl; import org.jbpm.pvm.internal.task.SwimlaneDefinitionImpl; @@ -46,8 +46,10 @@ /** * @author Tom Baeyens * @author Alejandro Guizar + * @author Ronald van Kuijk + * @author Maciej Swiderski */ -public class TaskActivity extends JpdlExternalActivity { +public class TaskActivity extends JpdlExternalActivity implements TimeoutActivityBehaviour { private static final long serialVersionUID = 1L; @@ -113,6 +115,14 @@ } public void signal(ExecutionImpl execution, String signalName, Map parameters) throws Exception { + signal((ExecutionImpl)execution, signalName, parameters, false); + } + + public void timeout(ActivityExecution execution, String signalName) throws Exception { + signal((ExecutionImpl)execution, signalName, null, true); + } + + private void signal(ExecutionImpl execution, String signalName, Map parameters, boolean timeout) throws Exception { ActivityImpl activity = execution.getActivity(); if (parameters!=null) { @@ -169,6 +179,17 @@ } } + if (task != null && !task.isCompleted()) { + if (timeout) { + task.timeout(transition.getName()); + } else { + // task should be cancelled since it is not completed yet !!! + + task.cancel(transition.getName()); + } + + } + if (transition!=null) { execution.take(transition); } Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskCancel.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskCancel.java (revision 0) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskCancel.java (revision 0) @@ -0,0 +1,52 @@ +package org.jbpm.pvm.internal.history.events; + +import org.hibernate.Session; +import org.jbpm.api.history.HistoryTask; +import org.jbpm.pvm.internal.env.EnvironmentImpl; +import org.jbpm.pvm.internal.history.model.HistoryActivityInstanceImpl; +import org.jbpm.pvm.internal.history.model.HistoryTaskImpl; +import org.jbpm.pvm.internal.history.model.HistoryTaskInstanceImpl; +import org.jbpm.pvm.internal.task.TaskImpl; +import org.jbpm.pvm.internal.util.Clock; + + +public class TaskCancel extends ActivityEnd { + + private static final long serialVersionUID = 1L; + + protected TaskImpl task; + protected String outcome; + + public TaskCancel(TaskImpl taskImpl, String outcome) { + this.task = taskImpl; + this.outcome = outcome; + } + + protected void updateHistoryActivityInstance(HistoryActivityInstanceImpl historyActivityInstance) { + super.updateHistoryActivityInstance(historyActivityInstance); + + Session session = EnvironmentImpl.getFromCurrent(Session.class); + Long historyActivityInstanceDbId = execution.getHistoryActivityInstanceDbid(); + HistoryTaskInstanceImpl historyTaskInstance = (HistoryTaskInstanceImpl) + session.load(HistoryTaskInstanceImpl.class, historyActivityInstanceDbId); + historyTaskInstance.setEndTime(Clock.getTime()); + historyTaskInstance.setTransitionName(outcome); + + HistoryTaskImpl historyTask = historyTaskInstance.getHistoryTask(); + historyTask.setOutcome(outcome); + historyTask.setEndTime(Clock.getTime()); + historyTask.setState(HistoryTask.STATE_CANCELLED); + + session.update(historyTaskInstance); + + } + + protected Class getHistoryActivityInstanceClass() { + return HistoryTaskInstanceImpl.class; + } + + public TaskImpl getTask() { + return task; + } + +} Index: modules/api/src/main/java/org/jbpm/api/activity/TimeoutActivityBehaviour.java =================================================================== --- modules/api/src/main/java/org/jbpm/api/activity/TimeoutActivityBehaviour.java (revision 0) +++ modules/api/src/main/java/org/jbpm/api/activity/TimeoutActivityBehaviour.java (revision 0) @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.api.activity; + +import org.jbpm.api.Execution; +import org.jbpm.api.ExecutionService; + + +/** extends {@link ActivityBehaviour} for handling external triggers after a wait state. + * + * @author Tom Baeyens + * @author Ronald van Kuijk + */ +public interface TimeoutActivityBehaviour extends ActivityBehaviour { + + /** handles an external signal. + * + *

An timeout that comes into an execution + * through one of the {@link ExecutionService#signalExecutionById(String)} methods. + * It will be delegated to the activity in which the execution is positioned when it + * receives the external trigger. + *

+ * + *

The timeout method implements how the activity will react on the timeout. + * For example, the outgoing transition could be taken that corresponds with the + * given signalName. + *

+ * + * @param execution the {@link Execution} for which the timeout is given + * + * @param signalName is an abstract text that can be associated with a signal. this + * corresponds to e.g. a method name in a java class interface. The implementation + * can decide e.g. to use the signal to identify the outgoing transition. + * + * @throws Exception to indicate any kind of failure. Note that exceptions are + * considered non recoverable. After an Exception, the execution should not be + * used any more and if this is during a transaction, the transaction should be + * rolled back. */ + void timeout(ActivityExecution execution, String signalName) throws Exception; + +} Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/Signal.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/Signal.java (revision 6211) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/Signal.java (working copy) @@ -25,6 +25,7 @@ import org.jbpm.api.JbpmException; import org.jbpm.api.activity.ExternalActivityBehaviour; +import org.jbpm.api.activity.TimeoutActivityBehaviour; import org.jbpm.internal.log.Log; import org.jbpm.pvm.internal.job.MessageImpl; import org.jbpm.pvm.internal.model.ExecutionImpl; @@ -33,6 +34,7 @@ /** * @author Tom Baeyens + * @author Maciej Swiderski */ public class Signal extends AtomicOperation { @@ -42,11 +44,18 @@ String signalName; Map parameters; + boolean timeout; public Signal(String signalName, Map parameters) { this.signalName = signalName; this.parameters = parameters; } + + public Signal(String signalName, Map parameters, boolean timeout) { + this.signalName = signalName; + this.parameters = parameters; + this.timeout = timeout; + } public boolean isAsync(ExecutionImpl execution) { return false; @@ -65,8 +74,14 @@ try { execution.setPropagation(Propagation.UNSPECIFIED); - externalActivityBehaviour.signal(execution, signalName, parameters); - + // check if time out way of signaling should be used + if (timeout && externalActivityBehaviour instanceof TimeoutActivityBehaviour) { + + ((TimeoutActivityBehaviour) externalActivityBehaviour).timeout(execution, signalName); + } else { + + externalActivityBehaviour.signal(execution, signalName, parameters); + } } catch (RuntimeException e) { throw e; Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ExecutionImpl.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ExecutionImpl.java (revision 6211) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ExecutionImpl.java (working copy) @@ -409,13 +409,21 @@ } public void signal(String signal, Map parameters) { + signal(signal, parameters, false); + } + + public void signal(String signal, boolean timeout) { + signal(signal, null, timeout); + } + + private void signal(String signal, Map parameters, boolean timeout) { checkActive(); if (getProcessDefinition().isSuspended()) { throw new JbpmException("process definition "+getProcessDefinition().getId()+" is suspended"); } propagation = Propagation.EXPLICIT; if (getActivity()!=null) { - performAtomicOperation(new Signal(signal, parameters)); + performAtomicOperation(new Signal(signal, parameters, timeout)); } else if (transition!=null) { performAtomicOperation(AtomicOperation.TRANSITION_START_ACTIVITY); } else { @@ -438,6 +446,10 @@ public void signal(String signalName, Map parameters, Execution execution) { ((ExecutionImpl)execution).signal(signalName, parameters); } + + public void timeout(String signalName) { + signal(signalName, (Map)null, true); + } // execution method : take //////////////////////////////////////////////// @@ -793,6 +805,7 @@ // JBPM-2758 // TODO Find out why processdefinition is null in at this time.... + // Below code might be a workaround if (processDefinition == null) { processDefinition = getProcessDefinition(); } Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/task/TaskImpl.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/task/TaskImpl.java (revision 6211) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/task/TaskImpl.java (working copy) @@ -37,8 +37,10 @@ import org.jbpm.pvm.internal.client.ClientExecution; import org.jbpm.pvm.internal.env.EnvironmentImpl; import org.jbpm.pvm.internal.history.HistoryEvent; +import org.jbpm.pvm.internal.history.events.TaskCancel; import org.jbpm.pvm.internal.history.events.TaskComplete; import org.jbpm.pvm.internal.history.events.TaskDelete; +import org.jbpm.pvm.internal.history.events.TaskTimeout; import org.jbpm.pvm.internal.model.ExecutionImpl; import org.jbpm.pvm.internal.model.ProcessDefinitionImpl; import org.jbpm.pvm.internal.model.ScopeInstanceImpl; @@ -49,6 +51,9 @@ /** * is one task instance that can be assigned to an actor (read: put in someone's task list) and that * can trigger the continuation of execution of the token upon completion. + * + * @author Tom Baeyens + * @author Ronald van Kuijk */ public class TaskImpl extends ScopeInstanceImpl implements Serializable, OpenTask, Assignable { @@ -210,6 +215,34 @@ public void delete(String reason) { historyTaskDelete(reason); } + + public void timeout(String outcome) { + + if (outcome == null || outcome.equals("")) { + outcome = TaskConstants.NO_TASK_OUTCOME_SPECIFIED; + } + + historyTaskTimeout(outcome); + + DbSession dbSession = EnvironmentImpl.getFromCurrent(DbSession.class, false); + if (dbSession!=null){ + dbSession.delete(this); + } + } + + public void cancel(String outcome) { + + if (outcome == null || outcome.equals("")) { + outcome = TaskConstants.NO_TASK_OUTCOME_SPECIFIED; + } + + historyTaskCancel(outcome); + + DbSession dbSession = EnvironmentImpl.getFromCurrent(DbSession.class, false); + if (dbSession!=null){ + dbSession.delete(this); + } + } // state //////////////////////////////////////////////////////////////////// @@ -310,6 +343,18 @@ HistoryEvent.fire(new TaskComplete(outcome), execution); } } + + public void historyTaskTimeout(String outcome) { + if (execution != null) { + HistoryEvent.fire(new TaskTimeout(this, outcome), execution); + } + } + + public void historyTaskCancel(String outcome) { + if (execution != null) { + HistoryEvent.fire(new TaskCancel(this, outcome), execution); + } + } public void signalExecution(String signalName) { if (execution != null) { Index: modules/api/src/main/java/org/jbpm/api/history/HistoryTask.java =================================================================== --- modules/api/src/main/java/org/jbpm/api/history/HistoryTask.java (revision 6211) +++ modules/api/src/main/java/org/jbpm/api/history/HistoryTask.java (working copy) @@ -37,6 +37,8 @@ public interface HistoryTask { String STATE_COMPLETED = "completed"; + String STATE_CANCELLED = "cancelled"; + String STATE_TIMEDOUT = "timedout"; /** the unique id for this task that is used as a reference in the service methods */ String getId(); Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskTimeout.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskTimeout.java (revision 0) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskTimeout.java (revision 0) @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.pvm.internal.history.events; + +import org.hibernate.Session; +import org.jbpm.api.JbpmException; +import org.jbpm.api.history.HistoryTask; +import org.jbpm.pvm.internal.env.EnvironmentImpl; +import org.jbpm.pvm.internal.history.model.HistoryActivityInstanceImpl; +import org.jbpm.pvm.internal.history.model.HistoryTaskImpl; +import org.jbpm.pvm.internal.history.model.HistoryTaskInstanceImpl; +import org.jbpm.pvm.internal.session.DbSession; +import org.jbpm.pvm.internal.task.TaskImpl; +import org.jbpm.pvm.internal.util.Clock; + + +/** + * @author Tom Baeyens + * @author Ronald van Kuijk + */ +public class TaskTimeout extends ActivityEnd { + + private static final long serialVersionUID = 1L; + + protected TaskImpl task; + protected String outcome; + + public TaskTimeout(TaskImpl task, String outcome) { + this.task = task; + this.outcome = outcome; + } + + protected void updateHistoryActivityInstance(HistoryActivityInstanceImpl historyActivityInstance) { + super.updateHistoryActivityInstance(historyActivityInstance); + + Session session = EnvironmentImpl.getFromCurrent(Session.class); + Long historyActivityInstanceDbId = execution.getHistoryActivityInstanceDbid(); + HistoryTaskInstanceImpl historyTaskInstance = (HistoryTaskInstanceImpl) + session.load(HistoryTaskInstanceImpl.class, historyActivityInstanceDbId); + historyTaskInstance.setEndTime(Clock.getTime()); + historyTaskInstance.setTransitionName(outcome); + + HistoryTaskImpl historyTask = historyTaskInstance.getHistoryTask(); + historyTask.setOutcome(outcome); + historyTask.setEndTime(Clock.getTime()); + historyTask.setState(HistoryTask.STATE_TIMEDOUT); + + session.update(historyTaskInstance); + + } + + protected Class getHistoryActivityInstanceClass() { + return HistoryTaskInstanceImpl.class; + } + + public TaskImpl getTask() { + return task; + } + +} Index: modules/test-db/src/test/java/org/jbpm/test/timer/TaskTimerTaskTest.java =================================================================== --- modules/test-db/src/test/java/org/jbpm/test/timer/TaskTimerTaskTest.java (revision 6211) +++ modules/test-db/src/test/java/org/jbpm/test/timer/TaskTimerTaskTest.java (working copy) @@ -26,7 +26,9 @@ import java.util.List; +import org.jbpm.api.Execution; import org.jbpm.api.ProcessInstance; +import org.jbpm.api.history.HistoryTask; import org.jbpm.api.job.Job; import org.jbpm.api.task.Task; import org.jbpm.test.JbpmTestCase; @@ -34,6 +36,8 @@ /** * @author Joram Barrez + * @author Ronald van Kuijk + * @author Maciej Swiderski */ public class TaskTimerTaskTest extends JbpmTestCase { @@ -55,10 +59,9 @@ ProcessInstance processInstance = executionService.startProcessInstanceByKey("TaskTimer"); - assertEquals(1, taskService.createTaskQuery() - .assignee("johndoe") - .list() - .size() ); + List tasks = taskService.createTaskQuery().assignee("johndoe").list(); + + assertEquals(1, tasks.size()); Job timer = managementService.createJobQuery() .processInstanceId(processInstance.getId()) @@ -71,12 +74,53 @@ assertActivityActive(processInstance.getId(), "go to cafeteria"); -// TODO JBPM-2537 -// assertEquals(0, taskService.createTaskQuery() -// .assignee("johndoe") -// .list() -// .size() ); + assertEquals(0, taskService.createTaskQuery() + .assignee("johndoe") + .list() + .size() ); + + List history = historyService.createHistoryTaskQuery().taskId(tasks.get(0).getId()).list(); + + assertEquals(1, history.size()); + assertEquals(HistoryTask.STATE_TIMEDOUT, history.get(0).getState()); } + + public void testTaskClosedBySignal() { + deployJpdlXmlString( + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""); + + ProcessInstance processInstance = executionService.startProcessInstanceByKey("TaskTimer"); + + List tasks = taskService.createTaskQuery().assignee("johndoe").list(); + + assertEquals(1, tasks.size()); + + Execution exec = processInstance.findActiveExecutionIn("do work"); + + processInstance = executionService.signalExecutionById(exec.getId(), "done"); + + assertActivityActive(processInstance.getId(), "go home"); + assertEquals(0, taskService.createTaskQuery() + .assignee("johndoe") + .list() + .size() ); + + List history = historyService.createHistoryTaskQuery().taskId(tasks.get(0).getId()).list(); + assertEquals(1, history.size()); + assertEquals(HistoryTask.STATE_CANCELLED, history.get(0).getState()); + } } Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/job/TimerImpl.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/job/TimerImpl.java (revision 6211) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/job/TimerImpl.java (working copy) @@ -46,6 +46,7 @@ * @author Pascal Verdage * @author Alejandro Guizar * @author Ronald Van Kuijk + * @author Maciej Swiderski */ public class TimerImpl extends JobImpl implements Timer { @@ -84,7 +85,10 @@ if (signalName!=null) { if (log.isDebugEnabled()) log.debug("feeding timer signal "+signalName+" into "+execution); - execution.signal(signalName); + + // since this is timeout based execution it should make it as + // activity that is going to be signaled as timed out must implement TimeoutActivityBehaviour + execution.signal(signalName, true); } if (eventName!=null) {