Index: modules/api/src/main/java/org/jbpm/api/activity/TimedActivityBehaviour.java
===================================================================
--- modules/api/src/main/java/org/jbpm/api/activity/TimedActivityBehaviour.java	(revision 0)
+++ modules/api/src/main/java/org/jbpm/api/activity/TimedActivityBehaviour.java	(revision 0)
@@ -0,0 +1,58 @@
+/*
+ * 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 TimedActivityBehaviour extends ActivityBehaviour {
+
+  /**
+   * handles a timer expiration.
+   * <p>
+   * 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.
+   * </p>
+   * <p>
+   * The timeout method implements how the activity will react upon expiration. For
+   * example, the outgoing transition could be taken that corresponds with the given signalName.
+   * </p>
+   * @param execution the {@link Execution} for which the timer expiration is notified.
+   * 
+   * @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;
+
+}

Property changes on: modules/api/src/main/java/org/jbpm/api/activity/TimedActivityBehaviour.java
___________________________________________________________________
Added: svn:eol-style
   + native
Added: svn:keywords
   + Id Revision

Index: modules/api/src/main/java/org/jbpm/api/history/HistoryTask.java
===================================================================
--- modules/api/src/main/java/org/jbpm/api/history/HistoryTask.java	(revision 6297)
+++ 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_EXPIRED = "expired";
 
   /** the unique id for this task that is used as a reference in the service methods */
   String getId();
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 6297)
+++ modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/TaskActivity.java	(working copy)
@@ -26,6 +26,8 @@
 
 import org.jbpm.api.JbpmException;
 import org.jbpm.api.activity.ActivityExecution;
+import org.jbpm.api.activity.TimedActivityBehaviour;
+import org.jbpm.api.history.HistoryTask;
 import org.jbpm.api.model.Transition;
 import org.jbpm.pvm.internal.cal.Duration;
 import org.jbpm.pvm.internal.el.Expression;
@@ -34,7 +36,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 +47,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 TimedActivityBehaviour {
 
   private static final long serialVersionUID = 1L;
   
@@ -113,6 +116,14 @@
   }
 
   public void signal(ExecutionImpl execution, String signalName, Map<String, ?> 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<String, ?> parameters, boolean timeout) throws Exception {
     ActivityImpl activity = execution.getActivity();
     
     if (parameters!=null) {
@@ -169,6 +180,12 @@
         }
       }
       
+      if (task != null && !task.isCompleted()) {
+        // task should be cancelled since it is not completed yet !!!          
+        task.cancel(transition.getName(), timeout ? HistoryTask.STATE_EXPIRED
+          : HistoryTask.STATE_CANCELLED);
+      }
+      
       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,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.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;
+
+/**
+ * 
+ * @author Maciej Swiderski
+ *
+ */
+public class TaskCancel extends ActivityEnd {
+
+  private static final long serialVersionUID = 1L;
+
+  protected TaskImpl task;
+  protected String outcome;
+  protected String state;
+  
+  public TaskCancel(TaskImpl taskImpl, String outcome, String state) {
+    this.task = taskImpl;
+    this.outcome = outcome;
+    this.state = state;
+  }
+
+  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(state);
+
+    session.update(historyTaskInstance);
+     
+  }
+
+  protected Class<? extends HistoryActivityInstanceImpl> getHistoryActivityInstanceClass() {
+    return HistoryTaskInstanceImpl.class;
+  }
+  
+  public TaskImpl getTask() {
+    return task;
+  }
+
+}

Property changes on: modules/pvm/src/main/java/org/jbpm/pvm/internal/history/events/TaskCancel.java
___________________________________________________________________
Added: svn:eol-style
   + native
Added: svn:keywords
   + Id Revision

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 6297)
+++ 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<Boolean> 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) {
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 6297)
+++ modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ExecutionImpl.java	(working copy)
@@ -409,13 +409,21 @@
   }
 
   public void signal(String signal, Map<String, ?> parameters) {
+    signal(signal, parameters, false);
+  }
+  
+  public void signal(String signal, boolean timeout) {
+    signal(signal, null, timeout);
+  }
+  
+  private void signal(String signal, Map<String, ?> 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<String, ?> parameters, Execution execution) {
     ((ExecutionImpl)execution).signal(signalName, parameters);
   }
+  
+  public void timeout(String signalName) {
+    signal(signalName, (Map<String,?>)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/model/op/Signal.java
===================================================================
--- modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/Signal.java	(revision 6297)
+++ 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.TimedActivityBehaviour;
 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<String, ?> parameters;
+  boolean timeout;
 
   public Signal(String signalName, Map<String, ?> parameters) {
     this.signalName = signalName;
     this.parameters = parameters;
   }
+  
+  public Signal(String signalName, Map<String, ?> 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 TimedActivityBehaviour) {
+        
+        ((TimedActivityBehaviour) 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/task/TaskImpl.java
===================================================================
--- modules/pvm/src/main/java/org/jbpm/pvm/internal/task/TaskImpl.java	(revision 6297)
+++ modules/pvm/src/main/java/org/jbpm/pvm/internal/task/TaskImpl.java	(working copy)
@@ -37,6 +37,7 @@
 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.model.ExecutionImpl;
@@ -49,6 +50,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 {
 
@@ -148,7 +152,7 @@
       allRoles = new HashSet<ParticipationImpl>(participations);
     }
     if (swimlane != null) {
-      allRoles.addAll((Set) swimlane.getParticipations());
+      allRoles.addAll(swimlane.getParticipations());
     }
     return allRoles;
   }
@@ -210,6 +214,19 @@
   public void delete(String reason) {
     historyTaskDelete(reason);
   }
+  
+  public void cancel(String outcome, String state) {
+    if (outcome == null || outcome.equals("")) {
+      outcome = TaskConstants.NO_TASK_OUTCOME_SPECIFIED;
+    }
+    
+    historyTaskCancel(outcome, state);
+    
+    DbSession dbSession = EnvironmentImpl.getFromCurrent(DbSession.class, false);
+    if (dbSession!=null){
+      dbSession.delete(this);
+    }
+  }  
 
   // state ////////////////////////////////////////////////////////////////////
 
@@ -310,6 +327,12 @@
       HistoryEvent.fire(new TaskComplete(outcome), execution);
     }
   }
+  
+  public void historyTaskCancel(String outcome, String state) {
+    if (execution != null) {
+      HistoryEvent.fire(new TaskCancel(this, outcome, state), execution);
+    }
+  }
 
   public void signalExecution(String signalName) {
     if (execution != null) {
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 6297)
+++ 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<Task> 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<HistoryTask> history = historyService.createHistoryTaskQuery().taskId(tasks.get(0).getId()).list();
+
+    assertEquals(1, history.size());
+    assertEquals(HistoryTask.STATE_EXPIRED, history.get(0).getState());
   }
+  
+  public void testTaskClosedBySignal() {
+    deployJpdlXmlString(
+      "<process name='TaskTimer'>" +
+      "  <start name='start'>" +
+      "    <transition to='do work' />" +
+      "  </start>" +
+      "  <task name='do work' assignee='johndoe'>" +
+      "    <transition name='done' to='go home' />" +
+      "    <transition name='lunch' to='go to cafeteria'>" +
+      "      <timer duedate='10 seconds' />" +
+      "    </transition>" +
+      "  </task>" +
+      "  <state name='go home' />" +
+      "  <state name='go to cafeteria' />" +
+      "</process>");
+    
+    ProcessInstance processInstance = executionService.startProcessInstanceByKey("TaskTimer");
+    
+    List<Task> 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<HistoryTask> history = historyService.createHistoryTaskQuery().taskId(tasks.get(0).getId()).list();
 
+    assertEquals(1, history.size());
+    assertEquals(HistoryTask.STATE_CANCELLED, history.get(0).getState());
+  }
 }
