/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.jbpm.services.task.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.drools.core.util.MVELSafeHelper;
import org.jbpm.services.task.commands.TaskCommand;
import org.jbpm.services.task.commands.TaskContext;
import org.jbpm.services.task.events.TaskEventSupport;
import org.jbpm.services.task.internals.lifecycle.LifeCycleManager;
import org.jbpm.services.task.utils.ClassUtil;
import org.jbpm.services.task.utils.ContentMarshallerHelper;
import org.kie.api.command.Command;
import org.kie.api.runtime.Environment;
import org.kie.api.task.model.Content;
import org.kie.api.task.model.I18NText;
import org.kie.api.task.model.OrganizationalEntity;
import org.kie.api.task.model.Status;
import org.kie.api.task.model.Task;
import org.kie.api.task.model.TaskSummary;
import org.kie.api.task.model.User;
import org.kie.internal.task.api.ContentMarshallerContext;
import org.kie.internal.task.api.TaskInstanceService;
import org.kie.internal.task.api.TaskModelProvider;
import org.kie.internal.task.api.TaskPersistenceContext;
import org.kie.internal.task.api.model.ContentData;
import org.kie.internal.task.api.model.FaultData;
import org.kie.internal.task.api.model.InternalContent;
import org.kie.internal.task.api.model.InternalI18NText;
import org.kie.internal.task.api.model.InternalPeopleAssignments;
import org.kie.internal.task.api.model.InternalTask;
import org.kie.internal.task.api.model.InternalTaskData;
import org.kie.internal.task.api.model.Operation;
import org.kie.internal.task.api.model.SubTasksStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*/
public class TaskInstanceServiceImpl implements TaskInstanceService {
private static final Logger logger = LoggerFactory.getLogger(TaskInstanceServiceImpl.class);
protected static final Pattern PARAMETER_MATCHER = Pattern.compile("\\$\\{([\\S&&[^\\}]]+)\\}", Pattern.DOTALL);
private LifeCycleManager lifeCycleManager;
private org.kie.internal.task.api.TaskContext context;
private TaskPersistenceContext persistenceContext;
private TaskEventSupport taskEventSupport;
private Environment environment;
public TaskInstanceServiceImpl() {
}
public TaskInstanceServiceImpl(org.kie.internal.task.api.TaskContext context, TaskPersistenceContext persistenceContext,
LifeCycleManager lifeCycleManager, TaskEventSupport taskEventSupport,
Environment environment) {
this.context = context;
this.persistenceContext = persistenceContext;
this.lifeCycleManager = lifeCycleManager;
this.taskEventSupport = taskEventSupport;
this.environment = environment;
}
public void setLifeCycleManager(LifeCycleManager lifeCycleManager) {
this.lifeCycleManager = lifeCycleManager;
}
public void setTaskEventSupport(TaskEventSupport taskEventSupport) {
this.taskEventSupport = taskEventSupport;
}
public void setPersistenceContext(TaskPersistenceContext persistenceContext) {
this.persistenceContext = persistenceContext;
}
public long addTask(Task task, Map<String, Object> params) {
taskEventSupport.fireBeforeTaskAdded(task, context);
persistenceContext.persistTask(task);
resolveTaskDetailsForTaskProperties(task);
if (params != null) {
resolveTaskDetails(params, task);
ContentData contentData = ContentMarshallerHelper.marshal(task, params, TaskContentRegistry.get().getMarshallerContext(task).getEnvironment());
Content content = TaskModelProvider.getFactory().newContent();
((InternalContent) content).setContent(contentData.getContent());
persistenceContext.persistContent(content);
persistenceContext.setDocumentToTask(content, contentData, task);
taskEventSupport.fireAfterTaskInputVariablesChanged(task, context, params);
}
taskEventSupport.fireAfterTaskAdded(task, context);
return task.getId();
}
public long addTask(Task task, ContentData contentData) {
taskEventSupport.fireBeforeTaskAdded(task, context);
persistenceContext.persistTask(task);
resolveTaskDetailsForTaskProperties(task);
if (contentData != null) {
Content content = TaskModelProvider.getFactory().newContent();
((InternalContent) content).setContent(contentData.getContent());
persistenceContext.persistContent(content);
persistenceContext.setDocumentToTask(content, contentData, task);
}
taskEventSupport.fireAfterTaskAdded(task, context);
return task.getId();
}
public void activate(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Activate, taskId, userId, null, null, toGroups(null));
}
public void claim(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Claim, taskId, userId, null, null, toGroups(null));
}
public void claim(long taskId, String userId, List<String> groupIds) {
lifeCycleManager.taskOperation(Operation.Claim, taskId, userId, null, null, groupIds);
}
public void claimNextAvailable(String userId) {
List<Status> status = new ArrayList<Status>();
status.add(Status.Ready);
List<TaskSummary> queryTasks = persistenceContext.queryWithParametersInTransaction("TasksAssignedAsPotentialOwnerByStatus",
persistenceContext.addParametersToMap("userId", userId, "status", status),
ClassUtil.<List<TaskSummary>>castClass(List.class));;
if (queryTasks.size() > 0) {
lifeCycleManager.taskOperation(Operation.Claim, queryTasks.get(0).getId(), userId, null, null, toGroups(null));
} else {
logger.info("No task available to assign for user {}", userId);
}
}
public void claimNextAvailable(String userId, List<String> groupIds) {
List<Status> status = new ArrayList<Status>();
status.add(Status.Ready);
List<TaskSummary> queryTasks = persistenceContext.queryWithParametersInTransaction("TasksAssignedAsPotentialOwnerByStatusByGroup",
persistenceContext.addParametersToMap("userId", userId, "status", status, "groupIds", groupIds),
ClassUtil.<List<TaskSummary>>castClass(List.class));;
if (queryTasks.size() > 0) {
lifeCycleManager.taskOperation(Operation.Claim, queryTasks.get(0).getId(), userId, null, null, groupIds);
} else {
logger.info("No task available to assign for user {} and groups {}", userId, groupIds);
}
}
public void complete(long taskId, String userId, Map<String, Object> data) {
lifeCycleManager.taskOperation(Operation.Complete, taskId, userId, null, data, toGroups(null));
}
public void delegate(long taskId, String userId, String targetUserId) {
lifeCycleManager.taskOperation(Operation.Delegate, taskId, userId, targetUserId, null, toGroups(null));
}
public void deleteFault(long taskId, String userId) {
Task task = persistenceContext.findTask(taskId);
long contentId = task.getTaskData().getFaultContentId();
Content content = persistenceContext.findContent(contentId);
FaultData data = TaskModelProvider.getFactory().newFaultData();
persistenceContext.removeContent(content);
persistenceContext.setFaultToTask(null, data, task);
}
public void deleteOutput(long taskId, String userId) {
Task task = persistenceContext.findTask(taskId);
long contentId = task.getTaskData().getOutputContentId();
Content content = persistenceContext.findContent(contentId);
ContentData data = TaskModelProvider.getFactory().newContentData();
persistenceContext.removeContent(content);
persistenceContext.setOutputToTask(null, data, task);
taskEventSupport.fireAfterTaskOutputVariablesChanged(task, context, null);
}
public void exit(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Exit, taskId, userId, null, null, toGroups(null));
}
public void fail(long taskId, String userId, Map<String, Object> faultData) {
lifeCycleManager.taskOperation(Operation.Fail, taskId, userId, null, faultData, toGroups(null));
}
public void forward(long taskId, String userId, String targetEntityId) {
lifeCycleManager.taskOperation(Operation.Forward, taskId, userId, targetEntityId, null, toGroups(null));
}
public void release(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Release, taskId, userId, null, null, toGroups(null));
}
public void remove(long taskId, String userId) {
Task task = persistenceContext.findTask(taskId);
User user = persistenceContext.findUser(userId);
if (((InternalPeopleAssignments)task.getPeopleAssignments()).getRecipients().contains(user)) {
((InternalPeopleAssignments)task.getPeopleAssignments()).getRecipients().remove(user);
} else {
throw new RuntimeException("Couldn't remove user " + userId + " since it isn't a notification recipient");
}
}
public void resume(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Resume, taskId, userId, null, null, toGroups(null));
}
public void setFault(long taskId, String userId, FaultData fault) {
Task task = persistenceContext.findTask(taskId);
Content content = TaskModelProvider.getFactory().newContent();
((InternalContent) content).setContent(fault.getContent());
persistenceContext.persistContent(content);
persistenceContext.setFaultToTask(content, fault, task);
}
public void setOutput(long taskId, String userId, Object outputContentData) {
Task task = persistenceContext.findTask(taskId);
ContentData contentData = ContentMarshallerHelper.marshal(task, outputContentData, environment);
Content content = TaskModelProvider.getFactory().newContent();
((InternalContent) content).setContent(contentData.getContent());
persistenceContext.persistContent(content);
persistenceContext.setOutputToTask(content, contentData, task);
}
public void setPriority(long taskId, int priority) {
Task task = persistenceContext.findTask(taskId);
taskEventSupport.fireBeforeTaskUpdated(task, context);
((InternalTask) task).setPriority(priority);
taskEventSupport.fireAfterTaskUpdated(task, context);
}
public void setTaskNames(long taskId, List<I18NText> inputTaskNames) {
Task task = persistenceContext.findTask(taskId);
taskEventSupport.fireBeforeTaskUpdated(task, context);
List<I18NText> taskNames = new ArrayList<I18NText>(inputTaskNames.size());
for( I18NText inputText : inputTaskNames ) {
I18NText text = TaskModelProvider.getFactory().newI18NText();
((InternalI18NText) text).setLanguage(inputText.getLanguage());
((InternalI18NText) text).setText(inputText.getText());
taskNames.add(text);
}
((InternalTask) task).setNames(taskNames);
((InternalTask) task).setName(taskNames.get(0).getText());
taskEventSupport.fireAfterTaskUpdated(task, context);
}
public void skip(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Skip, taskId, userId, null, null, toGroups(null));
}
public void start(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Start, taskId, userId, null, null, toGroups(null));
}
public void stop(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Stop, taskId, userId, null, null, toGroups(null));
}
public void suspend(long taskId, String userId) {
lifeCycleManager.taskOperation(Operation.Suspend, taskId, userId, null, null, toGroups(null));
}
public void nominate(long taskId, String userId, List<OrganizationalEntity> potentialOwners) {
lifeCycleManager.taskOperation(Operation.Nominate, taskId, userId, null, null, toGroups(null),
potentialOwners.toArray(new OrganizationalEntity[potentialOwners.size()]));
}
public void setSubTaskStrategy(long taskId, SubTasksStrategy strategy) {
Task task = persistenceContext.findTask(taskId);
((InternalTask) task).setSubTaskStrategy(strategy);
}
public void setExpirationDate(long taskId, Date date) {
Task task = persistenceContext.findTask(taskId);
taskEventSupport.fireBeforeTaskUpdated(task, context);
((InternalTaskData) task.getTaskData()).setExpirationTime(date);
taskEventSupport.fireAfterTaskUpdated(task, context);
}
public void setDescriptions(long taskId, List<I18NText> inputDescriptions) {
Task task = persistenceContext.findTask(taskId);
taskEventSupport.fireBeforeTaskUpdated(task, context);
List<I18NText> descriptions = new ArrayList<I18NText>(inputDescriptions.size());
for( I18NText inputText : inputDescriptions ) {
I18NText text = TaskModelProvider.getFactory().newI18NText();
((InternalI18NText) text).setLanguage(inputText.getLanguage());
((InternalI18NText) text).setText(inputText.getText());
descriptions.add(text);
}
((InternalTask) task).setDescriptions(descriptions);
((InternalTask) task).setDescription(descriptions.get(0).getText());
taskEventSupport.fireAfterTaskUpdated(task, context);
}
public void setSkipable(long taskId, boolean skipable) {
Task task = persistenceContext.findTask(taskId);
((InternalTaskData) task.getTaskData()).setSkipable(skipable);
}
public int getPriority(long taskId) {
Task task = persistenceContext.findTask(taskId);
return task.getPriority();
}
public Date getExpirationDate(long taskId) {
Task task = persistenceContext.findTask(taskId);
return task.getTaskData().getExpirationTime();
}
public List<I18NText> getDescriptions(long taskId) {
Task task = persistenceContext.findTask(taskId);
return (List<I18NText>) task.getDescriptions();
}
public boolean isSkipable(long taskId) {
Task task = persistenceContext.findTask(taskId);
return task.getTaskData().isSkipable();
}
public SubTasksStrategy getSubTaskStrategy(long taskId) {
Task task = persistenceContext.findTask(taskId);
return ((InternalTask) task).getSubTaskStrategy();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T execute(Command<T> command) {
return (T) ((TaskCommand) command).execute( new TaskContext() );
}
@Override
public void setName(long taskId, String name) {
Task task = persistenceContext.findTask(taskId);
((InternalTask) task).setName(name);
}
@Override
public void setDescription(long taskId, String description) {
Task task = persistenceContext.findTask(taskId);
((InternalTask) task).setDescription(description);
}
@Override
public void setSubject(long taskId, String subject) {
Task task = persistenceContext.findTask(taskId);
((InternalTask) task).setSubject(subject);
}
@Override
public long addOutputContentFromUser( long taskId, String userId, Map<String, Object> params ) {
// check permissions
this.lifeCycleManager.taskOperation(Operation.Modify, taskId, userId, null, null, toGroups(null));
return new TaskContentServiceImpl(context, this.persistenceContext, taskEventSupport).addOutputContent(taskId, params);
}
@Override
public Content getContentByIdForUser( long contentId, String userId ) {
long taskId = persistenceContext.findTaskIdByContentId(contentId);
// check permissions
this.lifeCycleManager.taskOperation(Operation.View, taskId, userId, null, null, toGroups(null));
return this.persistenceContext.findContent(contentId);
}
@Override
public Map<String, Object> getContentMapForUser( Long taskId, String userId ) {
// check permissions
this.lifeCycleManager.taskOperation(Operation.View, taskId, userId, null, null, toGroups(null));
Task task = this.persistenceContext.findTask(taskId);
if( task.getTaskData() != null && task.getTaskData().getOutputContentId() != null ) {
Content content = this.persistenceContext.findContent(task.getTaskData().getOutputContentId());
ContentMarshallerContext mContext = TaskContentRegistry.get().getMarshallerContext(task);
Object outputContent = ContentMarshallerHelper.unmarshall(content.getContent(), mContext.getEnvironment(), mContext.getClassloader());
if( outputContent instanceof Map ) {
return (Map<String, Object>) outputContent;
} else {
throw new IllegalStateException("Output content for task " + taskId + " is not a Map<String, Object>!");
}
}
return null;
}
@SuppressWarnings("unchecked")
protected List<String> toGroups(List<String> groups) {
if (groups == null) {
return (List<String>) context.get("local:groups");
}
return groups;
}
protected Map<String, Object> resolveTaskDetails(Map<String, Object> parameters, Task task) {
for (Map.Entry<String, Object> entry: parameters.entrySet()) {
if (entry.getValue() != null && entry.getValue() instanceof String) {
String s = (String) entry.getValue();
Map<String, String> replacements = new HashMap<String, String>();
Matcher matcher = PARAMETER_MATCHER.matcher(s);
while (matcher.find()) {
String paramName = matcher.group(1);
if (replacements.get(paramName) == null) {
try {
Object variableValue = MVELSafeHelper.getEvaluator().eval(paramName, new TaskResolverFactory(task));
String variableValueString = variableValue == null ? "" : variableValue.toString();
replacements.put(paramName, variableValueString);
} catch (Throwable t) {
logger.error("Continuing without setting parameter.");
}
}
}
for (Map.Entry<String, String> replacement: replacements.entrySet()) {
s = s.replace("${" + replacement.getKey() + "}", replacement.getValue());
}
parameters.put(entry.getKey(), s);
}
}
return parameters;
}
protected void resolveTaskDetailsForTaskProperties(Task task) {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("name", task.getName());
parameters.put("description", task.getDescription());
parameters.put("subject", task.getSubject());
parameters.put("formName", ((InternalTask)task).getFormName());
Map<String, Object> replacements = resolveTaskDetails(parameters, task);
((InternalTask)task).setName((String) replacements.get("name"));
((InternalTask)task).setDescription((String) replacements.get("description"));
((InternalTask)task).setSubject((String) replacements.get("subject"));
((InternalTask)task).setFormName((String) replacements.get("formName"));
}
}