/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.activiti.engine.impl.bpmn.behavior;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.DynamicBpmnConstants;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.bpmn.helper.SkipExpressionUtil;
import org.activiti.engine.impl.calendar.BusinessCalendar;
import org.activiti.engine.impl.calendar.DueDateBusinessCalendar;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.task.TaskDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* activity implementation for the user task.
*
* @author Joram Barrez
*/
public class UserTaskActivityBehavior extends TaskActivityBehavior {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(UserTaskActivityBehavior.class);
protected String userTaskId;
protected TaskDefinition taskDefinition;
public UserTaskActivityBehavior(String userTaskId, TaskDefinition taskDefinition) {
this.userTaskId = userTaskId;
this.taskDefinition = taskDefinition;
}
public void execute(ActivityExecution execution) throws Exception {
TaskEntity task = TaskEntity.createAndInsert(execution);
task.setExecution(execution);
Expression activeNameExpression = null;
Expression activeDescriptionExpression = null;
Expression activeDueDateExpression = null;
Expression activePriorityExpression = null;
Expression activeCategoryExpression = null;
Expression activeFormKeyExpression = null;
Expression activeSkipExpression = null;
Expression activeAssigneeExpression = null;
Expression activeOwnerExpression = null;
Set<Expression> activeCandidateUserExpressions = null;
Set<Expression> activeCandidateGroupExpressions = null;
if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {
ObjectNode taskElementProperties = Context.getBpmnOverrideElementProperties(userTaskId, execution.getProcessDefinitionId());
activeNameExpression = getActiveValue(taskDefinition.getNameExpression(), DynamicBpmnConstants.USER_TASK_NAME, taskElementProperties);
taskDefinition.setNameExpression(activeNameExpression);
activeDescriptionExpression = getActiveValue(taskDefinition.getDescriptionExpression(), DynamicBpmnConstants.USER_TASK_DESCRIPTION, taskElementProperties);
taskDefinition.setDescriptionExpression(activeDescriptionExpression);
activeDueDateExpression = getActiveValue(taskDefinition.getDueDateExpression(), DynamicBpmnConstants.USER_TASK_DUEDATE, taskElementProperties);
taskDefinition.setDueDateExpression(activeDueDateExpression);
activePriorityExpression = getActiveValue(taskDefinition.getPriorityExpression(), DynamicBpmnConstants.USER_TASK_PRIORITY, taskElementProperties);
taskDefinition.setPriorityExpression(activePriorityExpression);
activeCategoryExpression = getActiveValue(taskDefinition.getCategoryExpression(), DynamicBpmnConstants.USER_TASK_CATEGORY, taskElementProperties);
taskDefinition.setCategoryExpression(activeCategoryExpression);
activeFormKeyExpression = getActiveValue(taskDefinition.getFormKeyExpression(), DynamicBpmnConstants.USER_TASK_FORM_KEY, taskElementProperties);
taskDefinition.setFormKeyExpression(activeFormKeyExpression);
activeSkipExpression = getActiveValue(taskDefinition.getSkipExpression(), DynamicBpmnConstants.TASK_SKIP_EXPRESSION, taskElementProperties);
taskDefinition.setSkipExpression(activeSkipExpression);
activeAssigneeExpression = getActiveValue(taskDefinition.getAssigneeExpression(), DynamicBpmnConstants.USER_TASK_ASSIGNEE, taskElementProperties);
taskDefinition.setAssigneeExpression(activeAssigneeExpression);
activeOwnerExpression = getActiveValue(taskDefinition.getOwnerExpression(), DynamicBpmnConstants.USER_TASK_OWNER, taskElementProperties);
taskDefinition.setOwnerExpression(activeOwnerExpression);
activeCandidateUserExpressions = getActiveValueSet(taskDefinition.getCandidateUserIdExpressions(), DynamicBpmnConstants.USER_TASK_CANDIDATE_USERS, taskElementProperties);
taskDefinition.setCandidateUserIdExpressions(activeCandidateUserExpressions);
activeCandidateGroupExpressions = getActiveValueSet(taskDefinition.getCandidateGroupIdExpressions(), DynamicBpmnConstants.USER_TASK_CANDIDATE_GROUPS, taskElementProperties);
taskDefinition.setCandidateGroupIdExpressions(activeCandidateGroupExpressions);
} else {
activeNameExpression = taskDefinition.getNameExpression();
activeDescriptionExpression = taskDefinition.getDescriptionExpression();
activeDueDateExpression = taskDefinition.getDueDateExpression();
activePriorityExpression = taskDefinition.getPriorityExpression();
activeCategoryExpression = taskDefinition.getCategoryExpression();
activeFormKeyExpression = taskDefinition.getFormKeyExpression();
activeSkipExpression = taskDefinition.getSkipExpression();
activeAssigneeExpression = taskDefinition.getAssigneeExpression();
activeOwnerExpression = taskDefinition.getOwnerExpression();
activeCandidateUserExpressions = taskDefinition.getCandidateUserIdExpressions();
activeCandidateGroupExpressions = taskDefinition.getCandidateGroupIdExpressions();
}
task.setTaskDefinition(taskDefinition);
if (activeNameExpression != null) {
String name = null;
try {
name = (String) activeNameExpression.getValue(execution);
} catch (ActivitiException e) {
name = activeNameExpression.getExpressionText();
LOGGER.warn("property not found in task name expression " + e.getMessage());
}
task.setName(name);
}
if (activeDescriptionExpression != null) {
String description = null;
try {
description = (String) activeDescriptionExpression.getValue(execution);
} catch (ActivitiException e) {
description = activeDescriptionExpression.getExpressionText();
LOGGER.warn("property not found in task description expression " + e.getMessage());
}
task.setDescription(description);
}
if (activeDueDateExpression != null) {
Object dueDate = activeDueDateExpression.getValue(execution);
if (dueDate != null) {
if (dueDate instanceof Date) {
task.setDueDate((Date) dueDate);
} else if (dueDate instanceof String) {
BusinessCalendar businessCalendar = Context
.getProcessEngineConfiguration()
.getBusinessCalendarManager()
.getBusinessCalendar(taskDefinition.getBusinessCalendarNameExpression().getValue(execution).toString());
task.setDueDate(businessCalendar.resolveDuedate((String) dueDate));
} else {
throw new ActivitiIllegalArgumentException("Due date expression does not resolve to a Date or Date string: " +
activeDueDateExpression.getExpressionText());
}
}
}
if (activePriorityExpression != null) {
final Object priority = activePriorityExpression.getValue(execution);
if (priority != null) {
if (priority instanceof String) {
try {
task.setPriority(Integer.valueOf((String) priority));
} catch (NumberFormatException e) {
throw new ActivitiIllegalArgumentException("Priority does not resolve to a number: " + priority, e);
}
} else if (priority instanceof Number) {
task.setPriority(((Number) priority).intValue());
} else {
throw new ActivitiIllegalArgumentException("Priority expression does not resolve to a number: " +
activePriorityExpression.getExpressionText());
}
}
}
if (activeCategoryExpression != null) {
final Object category = activeCategoryExpression.getValue(execution);
if (category != null) {
if (category instanceof String) {
task.setCategory((String) category);
} else {
throw new ActivitiIllegalArgumentException("Category expression does not resolve to a string: " +
activeCategoryExpression.getExpressionText());
}
}
}
if (activeFormKeyExpression != null) {
final Object formKey = activeFormKeyExpression.getValue(execution);
if (formKey != null) {
if (formKey instanceof String) {
task.setFormKey((String) formKey);
} else {
throw new ActivitiIllegalArgumentException("FormKey expression does not resolve to a string: " +
activeFormKeyExpression.getExpressionText());
}
}
}
boolean skipUserTask = SkipExpressionUtil.isSkipExpressionEnabled(execution, activeSkipExpression) &&
SkipExpressionUtil.shouldSkipFlowElement(execution, activeSkipExpression);
if (!skipUserTask) {
handleAssignments(activeAssigneeExpression, activeOwnerExpression, activeCandidateUserExpressions,
activeCandidateGroupExpressions, task, execution);
}
task.fireEvent(TaskListener.EVENTNAME_CREATE);
// All properties set, now firing 'create' events
if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TASK_CREATED, task));
}
if (skipUserTask) {
task.complete(null, false);
}
}
public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {
if (!((ExecutionEntity) execution).getTasks().isEmpty())
throw new ActivitiException("UserTask should not be signalled before complete");
leave(execution);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void handleAssignments(Expression assigneeExpression, Expression ownerExpression, Set<Expression> candidateUserExpressions,
Set<Expression> candidateGroupExpressions, TaskEntity task, ActivityExecution execution) {
if (assigneeExpression != null) {
Object assigneeExpressionValue = assigneeExpression.getValue(execution);
String assigneeValue = null;
if (assigneeExpressionValue != null) {
assigneeValue = assigneeExpressionValue.toString();
}
task.setAssignee(assigneeValue, true, false);
}
if (ownerExpression != null) {
Object ownerExpressionValue = ownerExpression.getValue(execution);
String ownerValue = null;
if (ownerExpressionValue != null) {
ownerValue = ownerExpressionValue.toString();
}
task.setOwner(ownerValue);
}
if (candidateGroupExpressions != null && !candidateGroupExpressions.isEmpty()) {
for (Expression groupIdExpr : candidateGroupExpressions) {
Object value = groupIdExpr.getValue(execution);
if (value instanceof String) {
List<String> candidates = extractCandidates((String) value);
task.addCandidateGroups(candidates);
} else if (value instanceof Collection) {
task.addCandidateGroups((Collection) value);
} else {
throw new ActivitiIllegalArgumentException("Expression did not resolve to a string or collection of strings");
}
}
}
if (candidateUserExpressions != null && !candidateUserExpressions.isEmpty()) {
for (Expression userIdExpr : candidateUserExpressions) {
Object value = userIdExpr.getValue(execution);
if (value instanceof String) {
List<String> candiates = extractCandidates((String) value);
task.addCandidateUsers(candiates);
} else if (value instanceof Collection) {
task.addCandidateUsers((Collection) value);
} else {
throw new ActivitiException("Expression did not resolve to a string or collection of strings");
}
}
}
if (!taskDefinition.getCustomUserIdentityLinkExpressions().isEmpty()) {
Map<String, Set<Expression>> identityLinks = taskDefinition.getCustomUserIdentityLinkExpressions();
for (String identityLinkType : identityLinks.keySet()) {
for (Expression idExpression : identityLinks.get(identityLinkType) ) {
Object value = idExpression.getValue(execution);
if (value instanceof String) {
List<String> userIds = extractCandidates((String) value);
for (String userId : userIds) {
task.addUserIdentityLink(userId, identityLinkType);
}
} else if (value instanceof Collection) {
Iterator userIdSet = ((Collection) value).iterator();
while (userIdSet.hasNext()) {
task.addUserIdentityLink((String)userIdSet.next(), identityLinkType);
}
} else {
throw new ActivitiException("Expression did not resolve to a string or collection of strings");
}
}
}
}
if (!taskDefinition.getCustomGroupIdentityLinkExpressions().isEmpty()) {
Map<String, Set<Expression>> identityLinks = taskDefinition.getCustomGroupIdentityLinkExpressions();
for (String identityLinkType : identityLinks.keySet()) {
for (Expression idExpression : identityLinks.get(identityLinkType) ) {
Object value = idExpression.getValue(execution);
if (value instanceof String) {
List<String> groupIds = extractCandidates((String) value);
for (String groupId : groupIds) {
task.addGroupIdentityLink(groupId, identityLinkType);
}
} else if (value instanceof Collection) {
Iterator groupIdSet = ((Collection) value).iterator();
while (groupIdSet.hasNext()) {
task.addGroupIdentityLink((String)groupIdSet.next(), identityLinkType);
}
} else {
throw new ActivitiException("Expression did not resolve to a string or collection of strings");
}
}
}
}
}
/**
* Extract a candidate list from a string.
*
* @param str
* @return
*/
protected List<String> extractCandidates(String str) {
return Arrays.asList(str.split("[\\s]*,[\\s]*"));
}
protected Expression getActiveValue(Expression originalValue, String propertyName, ObjectNode taskElementProperties) {
Expression activeValue = originalValue;
if (taskElementProperties != null) {
JsonNode overrideValueNode = taskElementProperties.get(propertyName);
if (overrideValueNode != null) {
if (overrideValueNode.isNull()) {
activeValue = null;
} else {
activeValue = Context.getProcessEngineConfiguration().getExpressionManager().createExpression(overrideValueNode.asText());
}
}
}
return activeValue;
}
protected Set<Expression> getActiveValueSet(Set<Expression> originalValues, String propertyName, ObjectNode taskElementProperties) {
Set<Expression> activeValues = originalValues;
if (taskElementProperties != null) {
JsonNode overrideValuesNode = taskElementProperties.get(propertyName);
if (overrideValuesNode != null) {
if (overrideValuesNode.isNull() || overrideValuesNode.isArray() == false || overrideValuesNode.size() == 0) {
activeValues = null;
} else {
ExpressionManager expressionManager = Context.getProcessEngineConfiguration().getExpressionManager();
activeValues = new HashSet<Expression>();
for (JsonNode valueNode : overrideValuesNode) {
activeValues.add(expressionManager.createExpression(valueNode.asText()));
}
}
}
}
return activeValues;
}
// getters and setters //////////////////////////////////////////////////////
public TaskDefinition getTaskDefinition() {
return taskDefinition;
}
}