/* * Copyright 2016 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. */ package org.kie.server.services.jbpm.admin; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import org.jbpm.services.api.DeploymentNotFoundException; import org.jbpm.services.api.TaskNotFoundException; import org.jbpm.services.api.admin.TaskNotification; import org.jbpm.services.api.admin.TaskReassignment; import org.jbpm.services.api.admin.UserTaskAdminService; import org.kie.api.task.model.Group; import org.kie.api.task.model.OrganizationalEntity; import org.kie.api.task.model.User; import org.kie.internal.runtime.error.ExecutionError; import org.kie.internal.task.api.TaskModelFactory; import org.kie.internal.task.api.TaskModelProvider; import org.kie.server.api.model.admin.EmailNotification; import org.kie.server.api.model.admin.ExecutionErrorInstance; import org.kie.server.api.model.admin.ExecutionErrorInstanceList; import org.kie.server.api.model.admin.OrgEntities; import org.kie.server.api.model.admin.TaskNotificationList; import org.kie.server.api.model.admin.TaskReassignmentList; import org.kie.server.services.api.KieServerRegistry; import org.kie.server.services.impl.KieContainerInstanceImpl; import org.kie.server.services.impl.marshal.MarshallerHelper; import org.kie.server.services.jbpm.locator.ByTaskIdContainerLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.stream.Collectors.toList; import static org.kie.server.services.jbpm.ConvertUtils.*; public class UserTaskAdminServiceBase { private static final Logger logger = LoggerFactory.getLogger(UserTaskAdminServiceBase.class); private UserTaskAdminService userTaskAdminService; private MarshallerHelper marshallerHelper; private KieServerRegistry context; private TaskModelFactory factory = TaskModelProvider.getFactory(); private Function<String, OrganizationalEntity> mapToUser = id -> factory.newUser(id); private Function<String, OrganizationalEntity> mapToGroup = id -> factory.newGroup(id); public UserTaskAdminServiceBase(UserTaskAdminService userTaskAdminService, KieServerRegistry context) { this.userTaskAdminService = userTaskAdminService; this.marshallerHelper = new MarshallerHelper(context); this.context = context; } public void addPotentialOwners(String containerId, Number taskId, boolean removeExisting, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to map of task inputs", payload); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(containerId, taskId, payload, marshallingType); userTaskAdminService.addPotentialOwners(taskId.longValue(), removeExisting, entities); logger.debug("Potential owners {} added to task {}", entities, taskId); } public void addExcludedOwners(String containerId, Number taskId, boolean removeExisting, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to map of task inputs", payload); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(containerId, taskId, payload, marshallingType); userTaskAdminService.addExcludedOwners(taskId.longValue(), removeExisting, entities); logger.debug("Excluded owners {} added to task {}", entities, taskId); } public void addBusinessAdmins(String containerId, Number taskId, boolean removeExisting, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to map of task inputs", payload); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(containerId, taskId, payload, marshallingType); userTaskAdminService.addBusinessAdmins(taskId.longValue(), removeExisting, entities); logger.debug("Business admins {} added to task {}", entities, taskId); } public void removePotentialOwners(String containerId, Number taskId, List<String> orgEntities, boolean isUser) { logger.debug("About to remove {} from task {} as potential owners", orgEntities, taskId); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(orgEntities, isUser); userTaskAdminService.removePotentialOwners(taskId.longValue(), entities); logger.debug("Potential owners {} removed task {}", entities, taskId); } public void removeExcludedOwners(String containerId, Number taskId, List<String> orgEntities, boolean isUser) { logger.debug("About to remove {} from task {} as excluded owners", orgEntities, taskId); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(orgEntities, isUser); userTaskAdminService.removeExcludedOwners(taskId.longValue(), entities); logger.debug("Excluded owners {} removed task {}", entities, taskId); } public void removeBusinessAdmins(String containerId, Number taskId, List<String> orgEntities, boolean isUser) { logger.debug("About to remove {} from task {} as business admins", orgEntities, taskId); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(orgEntities, isUser); userTaskAdminService.removeBusinessAdmins(taskId.longValue(), entities); logger.debug("Business admins {} removed task {}", entities, taskId); } public void addTaskInputs(String containerId, Number taskId, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to map of task inputs", payload); verifyContainerId(containerId, taskId); Map<String, Object> data = marshallerHelper.unmarshal(containerId, payload, marshallingType, Map.class, new ByTaskIdContainerLocator(taskId.longValue())); logger.debug("Task input data to be added to a task {} is {}", taskId, data); userTaskAdminService.addTaskInputs(taskId.longValue(), data); logger.debug("Task inputs {} added successfully to task {}", data, taskId); } public void removeTaskInputs(String containerId, Number taskId, List<String> inputNames) { logger.debug("About to remove task inputs {} from task {}", inputNames, taskId); verifyContainerId(containerId, taskId); userTaskAdminService.removeTaskInputs(taskId.longValue(), inputNames.toArray(new String[inputNames.size()])); logger.debug("Task inputs {} removed successfully from task {}", inputNames, taskId); } public void removeTaskOutputs(String containerId, Number taskId, List<String> outputNames) { logger.debug("About to remove task outputs {} from task {}", outputNames, taskId); verifyContainerId(containerId, taskId); userTaskAdminService.removeTaskOutputs(taskId.longValue(), outputNames.toArray(new String[outputNames.size()])); logger.debug("Task outputs {} removed successfully from task {}", outputNames, taskId); } public String reassignWhenNotStarted(String containerId, Number taskId, String timeExpression, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to list of org entities (users/groups)", payload); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(containerId, taskId, payload, marshallingType); Long id = userTaskAdminService.reassignWhenNotStarted(taskId.longValue(), timeExpression, entities); logger.debug("Reassignment (when not started) to {} successfully created for task {} to fire at {}", entities, taskId, timeExpression); String response = marshallerHelper.marshal(containerId, marshallingType, id, new ByTaskIdContainerLocator(taskId.longValue())); return response; } public String reassignWhenNotCompleted(String containerId, Number taskId, String timeExpression, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to list of org entities (users/groups)", payload); verifyContainerId(containerId, taskId); OrganizationalEntity[] entities = convert(containerId, taskId, payload, marshallingType); Long id = userTaskAdminService.reassignWhenNotCompleted(taskId.longValue(), timeExpression, entities); logger.debug("Reassignment (when not completed) to {} successfully created for task {} to fire at {}", entities, taskId, timeExpression); String response = marshallerHelper.marshal(containerId, marshallingType, id, new ByTaskIdContainerLocator(taskId.longValue())); return response; } public String notifyWhenNotStarted(String containerId, Number taskId, String timeExpression, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to EmailNotification (when not started) of task inputs", payload); verifyContainerId(containerId, taskId); EmailNotification emailNotification = marshallerHelper.unmarshal(containerId, payload, marshallingType, EmailNotification.class, new ByTaskIdContainerLocator(taskId.longValue())); logger.debug("Email notification to be added to a task {} is {}", taskId, emailNotification); org.kie.internal.task.api.model.EmailNotification email = buildEmail(emailNotification); Long id = userTaskAdminService.notifyWhenNotStarted(taskId.longValue(), timeExpression, email); logger.debug("Email notification (when not started) {} added successfully to task {} to be fired after {}", emailNotification, taskId, timeExpression); String response = marshallerHelper.marshal(containerId, marshallingType, id, new ByTaskIdContainerLocator(taskId.longValue())); return response; } public String notifyWhenNotCompleted(String containerId, Number taskId, String timeExpression, String payload, String marshallingType) { logger.debug("About to unmarshall payload '{}' to EmailNotification (when not completed) of task inputs", payload); verifyContainerId(containerId, taskId); EmailNotification emailNotification = marshallerHelper.unmarshal(containerId, payload, marshallingType, EmailNotification.class, new ByTaskIdContainerLocator(taskId.longValue())); logger.debug("Email notification to be added to a task {} is {}", taskId, emailNotification); org.kie.internal.task.api.model.EmailNotification email = buildEmail(emailNotification); Long id = userTaskAdminService.notifyWhenNotCompleted(taskId.longValue(), timeExpression, email); logger.debug("Email notification (when not completed) {} added successfully to task {} to be fired after {}", emailNotification, taskId, timeExpression); String response = marshallerHelper.marshal(containerId, marshallingType, id, new ByTaskIdContainerLocator(taskId.longValue())); return response; } public void cancelNotification(String containerId, Number taskId, Number notificationId) { logger.debug("About to cancel notification {} from task {}", notificationId, taskId); verifyContainerId(containerId, taskId); userTaskAdminService.cancelNotification(taskId.longValue(), notificationId.longValue()); logger.debug("Notification {} canceled successfully for task {}", notificationId, taskId); } public void cancelReassignment(String containerId, Number taskId, Number reassignmentId) { logger.debug("About to cancel reassignment {} from task {}", reassignmentId, taskId); verifyContainerId(containerId, taskId); userTaskAdminService.cancelReassignment(taskId.longValue(), reassignmentId.longValue()); logger.debug("Reassignment {} canceled successfully for task {}", reassignmentId, taskId); } public TaskReassignmentList getTaskReassignments(String containerId, Number taskId, boolean activeOnly) { Collection<TaskReassignment> reassignments = userTaskAdminService.getTaskReassignments(taskId.longValue(), activeOnly); List<org.kie.server.api.model.admin.TaskReassignment> converted = reassignments.stream().map(r -> org.kie.server.api.model.admin.TaskReassignment.builder().id(r.getId()).active(r.isActive()).name(r.getName()).reassignAt(r.getDate()).users(r.getPotentialOwners().stream().filter(oe -> oe instanceof User).map(oe -> oe.getId()).collect(toList())).groups(r.getPotentialOwners().stream().filter(oe -> oe instanceof Group).map(oe -> oe.getId()).collect(toList())).build()).collect(toList()); return new TaskReassignmentList(converted); } public TaskNotificationList getTaskNotifications(String containerId, Number taskId, boolean activeOnly) { Collection<TaskNotification> notifications = userTaskAdminService.getTaskNotifications(taskId.longValue(), activeOnly); List<org.kie.server.api.model.admin.TaskNotification> converted = notifications.stream().map(r -> org.kie.server.api.model.admin.TaskNotification.builder().id(r.getId()).active(r.isActive()).name(r.getName()).subject(r.getSubject()).content(r.getContent()).notifyAt(r.getDate()).users(r.getRecipients().stream().filter(oe -> oe instanceof User).map(oe -> oe.getId()).collect(toList())).groups(r.getRecipients().stream().filter(oe -> oe instanceof Group).map(oe -> oe.getId()).collect(toList())).build()).collect(toList()); return new TaskNotificationList(converted); } public ExecutionErrorInstanceList getExecutionErrorsByTaskId(String containerId, Number taskId, boolean includeAcknowledged, Integer page, Integer pageSize, String sort, boolean sortOrder) { logger.debug("About to get execution errors for task id {}", taskId); List<ExecutionError> errors = userTaskAdminService.getErrorsByTaskId(taskId.longValue(), includeAcknowledged, buildQueryContext(page, pageSize, sort, sortOrder)); logger.debug("Found errors {}", errors); ExecutionErrorInstanceList errorInstanceList = convertToErrorInstanceList(errors); return errorInstanceList; } public ExecutionErrorInstanceList getExecutionErrorsByTaskName(String containerId, String processId, String taskName, boolean includeAcknowledged, Integer page, Integer pageSize, String sort, boolean sortOrder) { logger.debug("About to get execution errors for task name {} in process {} and container {}", taskName, processId, containerId); List<ExecutionError> errors = null; if (containerId != null && !containerId.isEmpty()) { errors = userTaskAdminService.getErrorsByTaskName(containerId, processId, taskName, includeAcknowledged, buildQueryContext(page, pageSize, sort, sortOrder)); } else if (processId != null && !processId.isEmpty()) { errors = userTaskAdminService.getErrorsByTaskName(processId, taskName, includeAcknowledged, buildQueryContext(page, pageSize, sort, sortOrder)); } else if (taskName != null && !taskName.isEmpty()) { errors = userTaskAdminService.getErrorsByTaskName(taskName, includeAcknowledged, buildQueryContext(page, pageSize, sort, sortOrder)); } else { errors = userTaskAdminService.getErrors(includeAcknowledged, buildQueryContext(page, pageSize, sort, sortOrder)); } logger.debug("Found errors {}", errors); ExecutionErrorInstanceList errorInstanceList = convertToErrorInstanceList(errors); return errorInstanceList; } public ExecutionErrorInstance getError(String errorId) { logger.debug("About to get execution error for {}", errorId); ExecutionError error = userTaskAdminService.getError(errorId); logger.debug("Found error {} for error id {}", error, errorId); return convertToErrorInstance(error); } public void acknowledgeError(List<String> errorIds) { logger.debug("About to acknowledge execution error with id {}", errorIds); String[] errors = errorIds.toArray(new String[errorIds.size()]); userTaskAdminService.acknowledgeError(errors); logger.debug("Error {} successfully acknowledged", errorIds); } /* * Helper methods */ protected OrganizationalEntity[] convert(List<String> orgEntities, boolean isUser) { return orgEntities.stream().map(isUser ? mapToUser : mapToGroup).toArray(size -> new OrganizationalEntity[size]); } protected org.kie.internal.task.api.model.EmailNotification buildEmail(EmailNotification emailNotification) { List<OrganizationalEntity> recipients = new ArrayList<>(); if (emailNotification.getUsers() != null) { recipients.addAll(emailNotification.getUsers().stream().map(mapToUser).collect(toList())); } if (emailNotification.getGroups() != null) { recipients.addAll(emailNotification.getGroups().stream().map(mapToGroup).collect(toList())); } org.kie.internal.task.api.model.EmailNotification email = userTaskAdminService.buildEmailNotification(emailNotification.getSubject(), recipients, emailNotification.getBody(), emailNotification.getFrom(), emailNotification.getReplyTo()); return email; } protected OrganizationalEntity[] convert(String containerId, Number taskId, String payload, String marshallingType) { OrgEntities orgEntities = marshallerHelper.unmarshal(containerId, payload, marshallingType, OrgEntities.class, new ByTaskIdContainerLocator(taskId.longValue())); List<OrganizationalEntity> entities = new ArrayList<>(); if (orgEntities.getUsers() != null) { entities.addAll(orgEntities.getUsers().stream().map(mapToUser).collect(toList())); } if (orgEntities.getGroups() != null) { entities.addAll(orgEntities.getGroups().stream().map(mapToGroup).collect(toList())); } return entities.toArray(new OrganizationalEntity[entities.size()]); } /* * Verify the container with given id has been registered or that it is a valid alias */ private void verifyContainerId(String containerId, Number taskId) { String taskContainerId; try { taskContainerId = (new ByTaskIdContainerLocator(taskId.longValue())).locateContainer(containerId, null); } catch (IllegalArgumentException e) { throw new TaskNotFoundException(e.getMessage()); } KieContainerInstanceImpl taskContainer = context.getContainer(taskContainerId); List<KieContainerInstanceImpl> containersByAlias = context.getContainersForAlias(containerId); // The container id is either a non-existent one or is not a valid alias for the container id the task is associated with. Both scenarios should raise an exception. if (context.getContainer(containerId) == null && !containersByAlias.contains(taskContainer)) { throw new DeploymentNotFoundException("Task Id: " + taskId + " is not associated with the provided container id: " + containerId + " or its alias."); } } }