/* * Copyright (c) 2010-2013 Evolveum * * 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 com.evolveum.midpoint.notifications.impl; import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.events.*; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.wf.api.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.xml.datatype.Duration; import java.util.List; /** * Listener that accepts events generated by workflow module. These events are related to processes and work items. * * TODO what about tasks? Should the task (wfTask) be passed to the notification module? * * @author mederly */ @Component public class WorkflowListener implements ProcessListener, WorkItemListener { private static final Trace LOGGER = TraceManager.getTrace(WorkflowListener.class); //private static final String DOT_CLASS = WorkflowListener.class.getName() + "."; @Autowired private NotificationManager notificationManager; @Autowired private NotificationFunctionsImpl functions; @Autowired private LightweightIdentifierGenerator identifierGenerator; // WorkflowManager is not required, because e.g. within model-test and model-intest we have no workflows. // However, during normal operation, it is expected to be available. @Autowired(required = false) private WorkflowManager workflowManager; @PostConstruct public void init() { if (workflowManager != null) { workflowManager.registerProcessListener(this); workflowManager.registerWorkItemListener(this); } else { LOGGER.warn("WorkflowManager not present, notifications for workflows will not be enabled."); } } //region Process-level notifications @Override public void onProcessInstanceStart(Task wfTask, OperationResult result) { WorkflowProcessEvent event = new WorkflowProcessEvent(identifierGenerator, ChangeType.ADD, wfTask); initializeWorkflowEvent(event, wfTask); processEvent(event, result); } @Override public void onProcessInstanceEnd(Task wfTask, OperationResult result) { WorkflowProcessEvent event = new WorkflowProcessEvent(identifierGenerator, ChangeType.DELETE, wfTask); initializeWorkflowEvent(event, wfTask); processEvent(event, result); } //endregion //region WorkItem-level notifications @Override public void onWorkItemCreation(ObjectReferenceType assignee, @NotNull WorkItemType workItem, Task wfTask, OperationResult result) { WorkItemEvent event = new WorkItemLifecycleEvent(identifierGenerator, ChangeType.ADD, workItem, SimpleObjectRefImpl.create(functions, assignee), null, null, null, wfTask.getWorkflowContext()); initializeWorkflowEvent(event, wfTask); processEvent(event, result); } @Override public void onWorkItemDeletion(ObjectReferenceType assignee, @NotNull WorkItemType workItem, WorkItemOperationInfo operationInfo, WorkItemOperationSourceInfo sourceInfo, Task wfTask, OperationResult result) { WorkItemEvent event = new WorkItemLifecycleEvent(identifierGenerator, ChangeType.DELETE, workItem, SimpleObjectRefImpl.create(functions, assignee), getInitiator(sourceInfo), operationInfo, sourceInfo, wfTask.getWorkflowContext()); initializeWorkflowEvent(event, wfTask); processEvent(event, result); } @Override public void onWorkItemCustomEvent(ObjectReferenceType assignee, @NotNull WorkItemType workItem, @NotNull WorkItemNotificationActionType notificationAction, WorkItemEventCauseInformationType cause, Task wfTask, OperationResult result) { WorkItemEvent event = new WorkItemCustomEvent(identifierGenerator, ChangeType.ADD, workItem, SimpleObjectRefImpl.create(functions, assignee), new WorkItemOperationSourceInfo(null, cause, notificationAction), wfTask.getWorkflowContext(), notificationAction.getHandler()); initializeWorkflowEvent(event, wfTask); processEvent(event, result); } @Override public void onWorkItemAllocationChangeCurrentActors(@NotNull WorkItemType workItem, @NotNull WorkItemAllocationChangeOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, Duration timeBefore, Task task, OperationResult result) { checkOids(operationInfo.getCurrentActors()); for (ObjectReferenceType currentActor : operationInfo.getCurrentActors()) { onWorkItemAllocationModifyDelete(currentActor, workItem, operationInfo, sourceInfo, timeBefore, task, result); } } @Override public void onWorkItemAllocationChangeNewActors(@NotNull WorkItemType workItem, @NotNull WorkItemAllocationChangeOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, Task task, OperationResult result) { Validate.notNull(operationInfo.getNewActors()); checkOids(operationInfo.getCurrentActors()); checkOids(operationInfo.getNewActors()); for (ObjectReferenceType newActor : operationInfo.getNewActors()) { onWorkItemAllocationAdd(newActor, workItem, operationInfo, sourceInfo, task, result); } } private void checkOids(List<ObjectReferenceType> refs) { refs.forEach(r -> Validate.notNull(r.getOid(), "No OID in actor object reference " + r)); } private void onWorkItemAllocationAdd(ObjectReferenceType newActor, @NotNull WorkItemType workItem, @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, Task task, OperationResult result) { WorkItemAllocationEvent event = new WorkItemAllocationEvent(identifierGenerator, ChangeType.ADD, workItem, SimpleObjectRefImpl.create(functions, newActor), getInitiator(sourceInfo), operationInfo, sourceInfo, task.getWorkflowContext(), null); initializeWorkflowEvent(event, task); processEvent(event, result); } private SimpleObjectRef getInitiator(WorkItemOperationSourceInfo sourceInfo) { return sourceInfo != null ? SimpleObjectRefImpl.create(functions, sourceInfo.getInitiatorRef()) : null; } private void onWorkItemAllocationModifyDelete(ObjectReferenceType currentActor, @NotNull WorkItemType workItem, @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, Duration timeBefore, Task task, OperationResult result) { WorkItemAllocationEvent event = new WorkItemAllocationEvent(identifierGenerator, timeBefore != null ? ChangeType.MODIFY : ChangeType.DELETE, workItem, SimpleObjectRefImpl.create(functions, currentActor), getInitiator(sourceInfo), operationInfo, sourceInfo, task.getWorkflowContext(), timeBefore); initializeWorkflowEvent(event, task); processEvent(event, result); } //endregion private void processEvent(WorkflowEvent event, OperationResult result) { try { notificationManager.processEvent(event); } catch (RuntimeException e) { result.recordFatalError("An unexpected exception occurred when preparing and sending notifications: " + e.getMessage(), e); LoggingUtils.logUnexpectedException(LOGGER, "An unexpected exception occurred when preparing and sending notifications: " + e.getMessage(), e); } // todo work correctly with operationResult (in whole notification module) if (result.isUnknown()) { result.computeStatus(); } result.recordSuccessIfUnknown(); } private void initializeWorkflowEvent(WorkflowEvent event, Task wfTask) { WfContextType wfc = wfTask.getWorkflowContext(); event.setRequester(SimpleObjectRefImpl.create(functions, wfc.getRequesterRef())); event.setRequestee(SimpleObjectRefImpl.create(functions, wfc.getObjectRef())); // TODO what if requestee is yet to be created? } }