/*
* 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.taskmgmt.exe;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.conselldemallorca.helium.jbpm3.integracio.Jbpm3HeliumBridge;
import net.conselldemallorca.helium.v3.core.api.dto.ReassignacioDto;
import net.conselldemallorca.helium.v3.core.api.exception.NoTrobatException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.JbpmException;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.context.exe.VariableContainer;
import org.jbpm.context.exe.VariableInstance;
import org.jbpm.graph.def.Event;
import org.jbpm.graph.def.Identifiable;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.Comment;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.security.SecurityHelper;
import org.jbpm.taskmgmt.def.Swimlane;
import org.jbpm.taskmgmt.def.Task;
import org.jbpm.taskmgmt.def.TaskController;
import org.jbpm.taskmgmt.log.TaskAssignLog;
import org.jbpm.taskmgmt.log.TaskEndLog;
import org.jbpm.util.Clock;
import org.jbpm.util.EqualsUtil;
/**
* is one task instance that can be assigned to an actor (read: put in someones
* task list) and that can trigger the coninuation of execution of the token
* upon completion.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class TaskInstance extends VariableContainer implements Identifiable,
Assignable {
private static final long serialVersionUID = 1L;
long id = 0;
int version = 0;
protected String name = null;
protected String description = null;
protected String actorId = null;
protected Date create = null;
protected Date start = null;
protected Date end = null;
protected Date dueDate = null;
protected int priority = Task.PRIORITY_NORMAL;
protected boolean isCancelled = false;
protected boolean isSuspended = false;
protected boolean isOpen = true;
protected boolean isSignalling = true;
protected boolean isBlocking = false;
protected Task task = null;
protected Token token = null;
protected SwimlaneInstance swimlaneInstance = null;
protected TaskMgmtInstance taskMgmtInstance = null;
protected ProcessInstance processInstance = null;
protected Set<PooledActor> pooledActors = null;
protected List comments = null;
protected String previousActorId = null; // not persisted. just extra
// information for listeners of
// the assign-event
//atributs per a finalització de tasques en segon pla
protected Date marcadaFinalitzar;
protected Date iniciFinalitzacio;
protected String errorFinalitzacio;
protected String selectedOutcome;
//
//flag titol actualitzat
protected boolean titolActualitzat = false;
//
public TaskInstance() {
}
public TaskInstance(String taskName) {
this.name = taskName;
}
public TaskInstance(String taskName, String actorId) {
this.name = taskName;
this.actorId = actorId;
}
public void setTask(Task task) {
this.name = task.getName();
this.description = task.getDescription();
this.task = task;
this.isBlocking = task.isBlocking();
this.priority = task.getPriority();
this.isSignalling = task.isSignalling();
}
void submitVariables() {
TaskController taskController = (task != null ? task
.getTaskController() : null);
// if there is a task controller,
if (taskController != null) {
// the task controller is responsible for copying variables back
// into the process
taskController.submitParameters(this);
// if there is no task controller
} else if ((token != null) && (token.getProcessInstance() != null)) {
// the default behaviour is that all task-local variables are
// flushed to the process
if (variableInstances != null) {
ContextInstance contextInstance = token.getProcessInstance()
.getContextInstance();
Iterator iter = variableInstances.values().iterator();
while (iter.hasNext()) {
VariableInstance variableInstance = (VariableInstance) iter
.next();
log.debug("flushing variable '"
+ variableInstance.getName() + "' from task '"
+ name + "' to process variables");
// This might be optimized, but this was the simplest way to
// make a clone of the variable instance.
contextInstance.setVariable(variableInstance.getName(),
variableInstance.getValue(), token);
}
}
}
}
void initializeVariables() {
TaskController taskController = (task != null ? task
.getTaskController() : null);
if (taskController != null) {
taskController.initializeVariables(this);
}
}
public void create() {
create(null);
}
public void create(ExecutionContext executionContext) {
if (create != null) {
throw new IllegalStateException("task instance '" + id
+ "' was already created");
}
create = Clock.getCurrentTime();
// if this task instance is associated with a task...
if ((task != null) && (executionContext != null)) {
// the TASK_CREATE event is fired
executionContext.setTaskInstance(this);
executionContext.setTask(task);
task.fireEvent(Event.EVENTTYPE_TASK_CREATE, executionContext);
Jbpm3HeliumBridge.getInstanceService().createDadesTasca(id);
}
// WARNING: The events create and assign are fired in the right order,
// but
// the logs are still not ordered properly.
// See also: TaskMgmtInstance.createTaskInstance
}
public void assign(ExecutionContext executionContext) {
TaskMgmtInstance taskMgmtInstance = executionContext
.getTaskMgmtInstance();
Swimlane swimlane = task.getSwimlane();
// if this task is in a swimlane
if (swimlane != null) {
// if this is a task assignment for a start-state
if (isStartTaskInstance()) {
// initialize the swimlane
swimlaneInstance = new SwimlaneInstance(swimlane);
taskMgmtInstance.addSwimlaneInstance(swimlaneInstance);
// with the current authenticated actor
swimlaneInstance.setActorId(SecurityHelper
.getAuthenticatedActorId());
} else {
// lazy initialize the swimlane...
// get the swimlane instance (if there is any)
swimlaneInstance = taskMgmtInstance
.getInitializedSwimlaneInstance(executionContext,
swimlane);
// copy the swimlaneInstance assignment into the taskInstance
// assignment
copySwimlaneInstanceAssignment(swimlaneInstance);
}
} else { // this task is not in a swimlane
taskMgmtInstance.performAssignment(task.getAssignmentDelegation(),
task.getActorIdExpression(), task
.getPooledActorsExpression(), this,
executionContext);
}
updatePooledActorsReferences(swimlaneInstance);
}
public boolean isStartTaskInstance() {
boolean isStartTaskInstance = false;
if ((taskMgmtInstance != null)
&& (taskMgmtInstance.getTaskMgmtDefinition() != null)) {
isStartTaskInstance = ((task != null) && (task
.equals(taskMgmtInstance.getTaskMgmtDefinition()
.getStartTask())));
}
return isStartTaskInstance;
}
void updatePooledActorsReferences(SwimlaneInstance swimlaneInstance) {
if (pooledActors != null) {
Iterator iter = pooledActors.iterator();
while (iter.hasNext()) {
PooledActor pooledActor = (PooledActor) iter.next();
pooledActor.setSwimlaneInstance(swimlaneInstance);
pooledActor.addTaskInstance(this);
}
}
}
/**
* copies the assignment (that includes both the swimlaneActorId and the set
* of pooledActors) of the given swimlane into this taskInstance.
*/
public void copySwimlaneInstanceAssignment(SwimlaneInstance swimlaneInstance) {
setSwimlaneInstance(swimlaneInstance);
setActorId(swimlaneInstance.getActorId());
setPooledActors(swimlaneInstance.getPooledActors());
}
/**
* gets the pool of actors for this task instance. If this task has a
* simlaneInstance and no pooled actors, the pooled actors of the swimlane
* instance are returned.
*/
public Set<PooledActor> getPooledActors() {
if ((swimlaneInstance != null)
&& ((pooledActors == null) || (pooledActors.isEmpty()))) {
return swimlaneInstance.getPooledActors();
}
return pooledActors;
}
/**
* (re)assign this task to the given actor. If this task is related to a
* swimlane instance, that swimlane instance will be updated as well.
*/
public void setActorId(String actorId) {
setActorId(actorId, true, false);
}
/**
* (re)assign this task to the given actor. If this task is related to a
* swimlane instance, that swimlane instance will be updated as well.
*/
public void setActorId(String actorId, boolean overwriteSwimlane) {
setActorId(actorId, overwriteSwimlane, false);
}
/**
* (re)assign this task to the given actor.
*
* @param actorId
* is reference to the person that is assigned to this task.
* @param overwriteSwimlane
* specifies if the related swimlane should be overwritten with
* the given swimlaneActorId.
* @param ignorarReassignacio
* indica si hem d'ignorar la reassignació entre usuaris
*/
public void setActorId(String actorId, boolean overwriteSwimlane, boolean ignorarReassignacio) {
// do the actual assignment
this.previousActorId = this.actorId;
this.actorId = actorId;
String actor = actorId;
if (!ignorarReassignacio) {
ReassignacioDto reassignacio = Jbpm3HeliumBridge.getInstanceService().findReassignacioActivaPerUsuariOrigen(actor);
if (reassignacio != null) {
actor = reassignacio.getUsuariDesti();
this.actorId = actor;
}
}
if ((swimlaneInstance != null) && (overwriteSwimlane)) {
log.debug("assigning task '" + name + "' to '" + actor + "'");
swimlaneInstance.setActorId(actor);
}
// fire the event
if ((task != null) && (token != null)) {
ExecutionContext executionContext = new ExecutionContext(token);
executionContext.setTask(task);
executionContext.setTaskInstance(this);
// WARNING: The events create and assign are fired in the right
// order, but
// the logs are still not ordered properly.
// See also: TaskMgmtInstance.createTaskInstance
task.fireEvent(Event.EVENTTYPE_TASK_ASSIGN, executionContext);
}
// add the log
if (token != null) {
// log this assignment
token.addLog(new TaskAssignLog(this, previousActorId, actor));
}
}
/** takes a set of String's as the actorIds */
public void setPooledActors(String[] actorIds) {
this.pooledActors = PooledActor.createPool(actorIds, null, this);
}
/**
* can optionally be used to indicate that the actor is starting to work on
* this task instance.
*/
public void start() {
if (start != null) {
throw new IllegalStateException("task instance '" + id
+ "' is already started");
}
start = Clock.getCurrentTime();
if ((task != null) && (token != null)) {
ExecutionContext executionContext = new ExecutionContext(token);
executionContext.setTask(task);
executionContext.setTaskInstance(this);
task.fireEvent(Event.EVENTTYPE_TASK_START, executionContext);
}
}
/**
* convenience method that combines a {@link #setActorId(String)} and a
* {@link #start()}.
*/
public void start(String actorId) {
start(actorId, true, false);
}
/**
* convenience method that combines a {@link #setActorId(String,boolean)}
* and a {@link #start()}.
*/
public void start(String actorId, boolean overwriteSwimlane, boolean ignorarReassignacio) {
setActorId(actorId, overwriteSwimlane, ignorarReassignacio);
start();
}
/**
* overwrite start date
*/
public void setStart(Date date) {
start = null;
}
private void markAsCancelled() {
this.isCancelled = true;
this.isOpen = false;
}
/**
* cancels this task. This task intance will be marked as cancelled and as
* ended. But cancellation doesn't influence singalling and continuation of
* process execution.
*/
public void cancel() {
markAsCancelled();
end();
}
/**
* cancels this task, takes the specified transition. This task intance will
* be marked as cancelled and as ended. But cancellation doesn't influence
* singalling and continuation of process execution.
*/
public void cancel(Transition transition) {
markAsCancelled();
end(transition);
}
/**
* cancels this task, takes the specified transition. This task intance will
* be marked as cancelled and as ended. But cancellation doesn't influence
* singalling and continuation of process execution.
*/
public void cancel(String transitionName) {
markAsCancelled();
end(transitionName);
}
/**
* marks this task as done. If this task is related to a task node this
* might trigger a signal on the token.
*
* @see #end(Transition)
*/
public void end() {
end((Transition) null);
}
/**
* marks this task as done and specifies the name of a transition leaving
* the task-node for the case that the completion of this task instances
* triggers a signal on the token. If this task leads to a signal on the
* token, the given transition name will be used in the signal. If this task
* completion does not trigger execution to move on, the transitionName is
* ignored.
*/
public void end(String transitionName) {
Transition leavingTransition = null;
if (task != null) {
Node node = task.getTaskNode();
if (node == null) {
node = (Node) task.getParent();
}
if (node != null) {
leavingTransition = node.getLeavingTransition(transitionName);
}
}
if (leavingTransition == null) {
throw new JbpmException(
"task node does not have leaving transition '"
+ transitionName + "'");
}
end(leavingTransition);
}
/**
* marks this task as done and specifies a transition leaving the task-node
* for the case that the completion of this task instances triggers a signal
* on the token. If this task leads to a signal on the token, the given
* transition name will be used in the signal. If this task completion does
* not trigger execution to move on, the transition is ignored.
*/
public void end(Transition transition) {
if (this.end != null) {
throw new IllegalStateException("task instance '" + id
+ "' is already ended");
}
if (this.isSuspended) {
throw new JbpmException("task instance '" + id + "' is suspended");
}
// mark the end of this task instance
this.end = Clock.getCurrentTime();
this.isOpen = false;
// fire the task instance end event
if ((task != null) && (token != null)) {
ExecutionContext executionContext = new ExecutionContext(token);
executionContext.setTask(task);
executionContext.setTaskInstance(this);
task.fireEvent(Event.EVENTTYPE_TASK_END, executionContext);
}
// log this assignment
if (token != null) {
token.addLog(new TaskEndLog(this));
}
// submit the variables
submitVariables();
// verify if the end of this task triggers continuation of execution
if (isSignalling) {
this.isSignalling = false;
if (this.isStartTaskInstance() // ending start tasks always leads to
// a signal
|| ((task != null) && (token != null)
&& (task.getTaskNode() != null) && (task
.getTaskNode().completionTriggersSignal(this)))) {
if (transition == null) {
log.debug("completion of task '" + task.getName()
+ "' results in taking the default transition");
token.signal();
} else {
log.debug("completion of task '" + task.getName()
+ "' results in taking transition '" + transition
+ "'");
token.signal(transition);
}
}
}
try {
Jbpm3HeliumBridge.getInstanceService().alertaEsborrarAmbTaskInstanceId(this.getId());
} catch (NoTrobatException ex) {
log.error("No s'ha trobat la taskInstance (id=" + this.getId() + ")", ex);
}
}
public boolean hasEnded() {
return (end != null);
}
/**
* suspends a process execution.
*/
public void suspend() {
if (!isOpen) {
throw new JbpmException(
"a task that is not open cannot be suspended: "
+ toString());
}
isSuspended = true;
}
/**
* resumes a process execution.
*/
public void resume() {
if (!isOpen) {
throw new JbpmException(
"a task that is not open cannot be resumed: " + toString());
}
isSuspended = false;
}
// comments
// /////////////////////////////////////////////////////////////////
public void addComment(String message) {
addComment(new Comment(message));
}
public void addComment(Comment comment) {
if (comment != null) {
if (comments == null)
comments = new ArrayList();
comments.add(comment);
comment.setTaskInstance(this);
if (token != null) {
comment.setToken(token);
token.addComment(comment);
}
}
}
public List getComments() {
return comments;
}
// task form
// ////////////////////////////////////////////////////////////////
public boolean isLast() {
return ((token != null) && (taskMgmtInstance != null) && (!taskMgmtInstance
.hasUnfinishedTasks(token)));
}
/**
* is the list of transitions that can be used in the end method and it is
* null in case this is not the last task instance.
*/
public List getAvailableTransitions() {
List transitions = null;
if ((!isLast()) && (token != null)) {
transitions = new ArrayList(token.getAvailableTransitions());
}
return transitions;
}
// equals
// ///////////////////////////////////////////////////////////////////
// hack to support comparing hibernate proxies against the real objects
// since this always falls back to ==, we don't need to overwrite the
// hashcode
public boolean equals(Object o) {
return EqualsUtil.equals(this, o);
}
public String toString() {
return "TaskInstance"
+ (name != null ? "(" + name + ")" : "@"
+ Integer.toHexString(hashCode()));
}
// private
// //////////////////////////////////////////////////////////////////
/** takes a set of {@link PooledActor}s */
public void setPooledActors(Set pooledActors) {
if (pooledActors != null) {
this.pooledActors = new HashSet(pooledActors);
Iterator iter = pooledActors.iterator();
while (iter.hasNext()) {
PooledActor pooledActor = (PooledActor) iter.next();
pooledActor.addTaskInstance(this);
}
} else {
this.pooledActors = null;
}
}
// protected
// ////////////////////////////////////////////////////////////////
protected VariableContainer getParentVariableContainer() {
ContextInstance contextInstance = getContextInstance();
return (contextInstance != null ? contextInstance
.getOrCreateTokenVariableMap(token) : null);
}
// getters and setters
// //////////////////////////////////////////////////////
public String getActorId() {
return actorId;
}
public Date getDueDate() {
return dueDate;
}
public void setDueDate(Date dueDate) {
this.dueDate = dueDate;
}
public Date getEnd() {
return end;
}
public void setEnd(Date end) {
this.end = end;
}
public void setCreate(Date create) {
this.create = create;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Date getStart() {
return start;
}
public TaskMgmtInstance getTaskMgmtInstance() {
return taskMgmtInstance;
}
public void setTaskMgmtInstance(TaskMgmtInstance taskMgmtInstance) {
this.taskMgmtInstance = taskMgmtInstance;
}
public Token getToken() {
return token;
}
public void setToken(Token token) {
this.token = token;
}
public void setSignalling(boolean isSignalling) {
this.isSignalling = isSignalling;
}
public boolean isSignalling() {
return isSignalling;
}
public boolean isCancelled() {
return isCancelled;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isBlocking() {
return isBlocking;
}
public void setBlocking(boolean isBlocking) {
this.isBlocking = isBlocking;
}
public Date getCreate() {
return create;
}
public Task getTask() {
return task;
}
public SwimlaneInstance getSwimlaneInstance() {
return swimlaneInstance;
}
public void setSwimlaneInstance(SwimlaneInstance swimlaneInstance) {
this.swimlaneInstance = swimlaneInstance;
}
public String getPreviousActorId() {
return previousActorId;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public boolean isOpen() {
return isOpen;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isSuspended() {
return isSuspended;
}
public ProcessInstance getProcessInstance() {
return processInstance;
}
public void setProcessInstance(ProcessInstance processInstance) {
this.processInstance = processInstance;
}
public Date getMarcadaFinalitzar() {
return marcadaFinalitzar;
}
public void setMarcadaFinalitzar(Date marcadaFinalitzar) {
this.marcadaFinalitzar = marcadaFinalitzar;
}
public Date getIniciFinalitzacio() {
return iniciFinalitzacio;
}
public void setIniciFinalitzacio(Date iniciFinalitzacio) {
this.iniciFinalitzacio = iniciFinalitzacio;
}
public String getErrorFinalitzacio() {
return errorFinalitzacio;
}
public void setErrorFinalitzacio(String errorFinalitzacio) {
this.errorFinalitzacio = errorFinalitzacio;
}
public String getSelectedOutcome() {
return selectedOutcome;
}
public void setSelectedOutcome(String selectedOutcome) {
this.selectedOutcome = selectedOutcome;
}
private static final Log log = LogFactory.getLog(TaskInstance.class);
}