From aac7b859b75a21dc9486535125e388af62293190 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Fri, 13 Mar 2020 17:59:06 +0100 Subject: [PATCH] Align applicability checks with text generation Notifiers were applied to events based on criteria that were different from those used to generate textual representation of the events. This sometimes lead to events with empty list of modifications (e.g. in MID-5849). This commit aligns those two aspects. Also we have refactored some years-old code e.g. cleaned up Event class hierarchy. Quick applicability check is now carried out before nested event handlers are invoked, making their execution more intuitive and robust. (cherry picked from commit fce73aeafe03260680ae992a8417b31722e0e6dc) --- .../scripting/actions/NotifyExecutor.java | 19 +- .../model/intest/TestNotifications.java | 24 +- .../test/AbstractModelIntegrationTest.java | 15 +- model/notifications-api/pom.xml | 11 - .../api/NotificationFunctions.java | 27 +- .../api/events/AccessCertificationEvent.java | 115 ++---- .../api/events/CertCampaignEvent.java | 35 +- .../api/events/CertCampaignStageEvent.java | 33 +- .../api/events/CertReviewEvent.java | 86 +---- .../notifications/api/events/CustomEvent.java | 103 +----- .../notifications/api/events/Event.java | 159 +++++++-- .../notifications/api/events/ModelEvent.java | 208 ++--------- .../api/events/PolicyRuleEvent.java | 56 +-- .../api/events/ResourceObjectEvent.java | 174 ++------- .../api/events/SimpleObjectRef.java | 10 +- .../notifications/api/events/TaskEvent.java | 128 +------ .../api/events/WorkItemAllocationEvent.java | 46 +-- .../api/events/WorkItemCustomEvent.java | 48 +-- .../api/events/WorkItemEvent.java | 134 +------ .../api/events/WorkItemLifecycleEvent.java | 42 +-- .../api/events/WorkflowEvent.java | 136 +------ .../api/events/WorkflowProcessEvent.java | 44 +-- .../events/factory/CustomEventFactory.java | 31 ++ .../impl/AccountOperationListener.java | 47 +-- .../impl/CertificationListener.java | 66 ++-- .../impl/NotificationFunctionsImpl.java | 297 +--------------- .../notifications/impl/NotificationHook.java | 24 +- .../impl/NotificationManagerImpl.java | 24 +- .../impl/NotificationTaskListener.java | 6 +- .../impl/SimpleObjectRefImpl.java | 11 +- .../impl/WorkflowListenerImpl.java | 48 +-- .../events/AccessCertificationEventImpl.java | 89 +++++ .../impl/events/BaseEventImpl.java} | 219 +++++------- .../impl/events/CertCampaignEventImpl.java | 41 +++ .../events/CertCampaignStageEventImpl.java | 44 +++ .../impl/events/CertReviewEventImpl.java | 99 ++++++ .../impl/events/CustomEventImpl.java | 104 ++++++ .../impl/events/ModelEventImpl.java | 332 ++++++++++++++++++ .../impl/events/PolicyRuleEventImpl.java | 70 ++++ .../impl/events/ResourceObjectEventImpl.java | 247 +++++++++++++ .../impl/events/TaskEventImpl.java | 145 ++++++++ .../events/WorkItemAllocationEventImpl.java | 45 +++ .../impl/events/WorkItemCustomEventImpl.java | 48 +++ .../impl/events/WorkItemEventImpl.java | 141 ++++++++ .../events/WorkItemLifecycleEventImpl.java | 42 +++ .../impl/events/WorkflowEventImpl.java | 154 ++++++++ .../impl/events/WorkflowProcessEventImpl.java | 47 +++ .../{cert => factory}/CertEventFactory.java | 55 +-- .../factory/CustomEventFactoryImpl.java | 48 +++ .../impl/formatters/DeltaFormatter.java | 106 +++--- .../impl/formatters/TextFormatter.java | 83 ++++- .../impl/formatters/ValueFormatter.java | 117 ++---- .../impl/helpers/FocusTypeFilterHelper.java | 10 +- .../impl/helpers/KindIntentFilterHelper.java | 6 +- .../helpers/NotificationExpressionHelper.java | 6 +- .../notifiers/AbstractGeneralNotifier.java | 110 +++--- .../notifiers/AbstractPolicyRuleNotifier.java | 2 - .../notifiers/AccountActivationNotifier.java | 9 +- .../notifiers/AccountPasswordNotifier.java | 8 +- .../impl/notifiers/PasswordResetNotifier.java | 5 +- .../RegistrationConfirmationNotifier.java | 3 +- .../notifiers/SimpleCampaignNotifier.java | 2 +- .../SimpleCampaignStageNotifier.java | 2 +- .../notifiers/SimpleFocalObjectNotifier.java | 39 +- .../SimpleResourceObjectNotifier.java | 31 +- .../notifiers/SimpleWorkflowNotifier.java | 54 +-- .../impl/util/ApplicationContextHolder.java | 37 ++ .../notifications/impl/util/EventHelper.java | 42 +++ .../notifications/impl/TestTextFormatter.java | 6 +- .../task/api/LightweightIdentifier.java | 4 +- 70 files changed, 2644 insertions(+), 2215 deletions(-) create mode 100644 model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/factory/CustomEventFactory.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/AccessCertificationEventImpl.java rename model/{notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java => notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/BaseEventImpl.java} (67%) create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignStageEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertReviewEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CustomEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ModelEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/PolicyRuleEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ResourceObjectEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/TaskEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemAllocationEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemCustomEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemLifecycleEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowEventImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowProcessEventImpl.java rename model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/{cert => factory}/CertEventFactory.java (63%) create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CustomEventFactoryImpl.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/ApplicationContextHolder.java create mode 100644 model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/EventHelper.java diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java index 6c16af833af..56973eb8fe0 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java @@ -13,16 +13,14 @@ import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.events.CustomEvent; -import com.evolveum.midpoint.notifications.api.events.Event; +import com.evolveum.midpoint.notifications.api.events.factory.CustomEventFactory; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -34,13 +32,11 @@ @Component public class NotifyExecutor extends BaseActionExecutor { - @Autowired - private LightweightIdentifierGenerator lightweightIdentifierGenerator; - @Autowired(required = false) // During some tests this might be unavailable private NotificationManager notificationManager; - private static final Trace LOGGER = TraceManager.getTrace(NotifyExecutor.class); + @Autowired(required = false) + private CustomEventFactory customEventFactory; private static final String NAME = "notify"; private static final String PARAM_SUBTYPE = "subtype"; @@ -80,10 +76,13 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, if (notificationManager == null) { throw new IllegalStateException("Notification manager is unavailable"); } + if (customEventFactory == null) { + throw new IllegalStateException("Custom event factory is unavailable"); + } int eventCount = 0; if (forWholeInput) { - Event event = new CustomEvent(lightweightIdentifierGenerator, subtype, handler, input.getData(), operation, status, context.getChannel()); + CustomEvent event = customEventFactory.createEvent(subtype, handler, input.getData(), operation, status, context.getChannel()); notificationManager.processEvent(event, context.getTask(), globalResult); eventCount++; } else { @@ -91,7 +90,7 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, PrismValue value = item.getValue(); OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); context.checkTaskStop(); - Event event = new CustomEvent(lightweightIdentifierGenerator, subtype, handler, value, operation, status, context.getChannel()); + CustomEvent event = customEventFactory.createEvent(subtype, handler, value, operation, status, context.getChannel()); notificationManager.processEvent(event, context.getTask(), result); eventCount++; operationsHelper.trimAndCloneResult(result, globalResult, context); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestNotifications.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestNotifications.java index 688fde669de..4122cd4b6c7 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestNotifications.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestNotifications.java @@ -6,9 +6,9 @@ */ package com.evolveum.midpoint.model.intest; -import com.evolveum.midpoint.notifications.api.events.CustomEvent; import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.transports.Message; +import com.evolveum.midpoint.notifications.impl.events.CustomEventImpl; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.crypto.EncryptionException; @@ -138,7 +138,15 @@ public void test100ModifyUserAddAccount() throws Exception { // WHEN TestUtil.displayWhen(TEST_NAME); - modifyUserAddAccount(USER_JACK_OID, ACCOUNT_JACK_DUMMY_FILE, task, result); + ObjectDelta userDelta = createAddAccountDelta(USER_JACK_OID, ACCOUNT_JACK_DUMMY_FILE); + // This is to test for MID-5849. The applicability checking was not correct, so it passed even if there we no items + // to show in the notification. + userDelta.addModification( + deltaFor(UserType.class) + .item(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_METADATA, MetadataType.F_CREATE_CHANNEL) + .replace("dummy") + .asItemDelta()); + executeChanges(userDelta, null, task, result); // THEN TestUtil.displayThen(TEST_NAME); @@ -151,7 +159,7 @@ public void test100ModifyUserAddAccount() throws Exception { PrismObject userJack = modelService.getObject(UserType.class, USER_JACK_OID, null, task, result); assertUserJack(userJack); UserType userJackType = userJack.asObjectable(); - assertEquals("Unexpected number of accountRefs", 1, userJackType.getLinkRef().size()); + assertEquals("Unexpected number of linkRefs", 1, userJackType.getLinkRef().size()); ObjectReferenceType accountRefType = userJackType.getLinkRef().get(0); accountJackOid = accountRefType.getOid(); assertFalse("No accountRef oid", StringUtils.isBlank(accountJackOid)); @@ -548,7 +556,7 @@ public void test200SendSmsUsingGet() { // WHEN TestUtil.displayWhen(TEST_NAME); - Event event = new CustomEvent(lightweightIdentifierGenerator, "get", null, + Event event = new CustomEventImpl(lightweightIdentifierGenerator, "get", null, "hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null); notificationManager.processEvent(event, task, result); @@ -573,7 +581,7 @@ public void test210SendSmsUsingPost() { // WHEN TestUtil.displayWhen(TEST_NAME); - Event event = new CustomEvent(lightweightIdentifierGenerator, "post", null, + Event event = new CustomEventImpl(lightweightIdentifierGenerator, "post", null, "hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null); notificationManager.processEvent(event, task, result); @@ -606,7 +614,7 @@ public void test215SendSmsUsingGeneralPost() { // WHEN TestUtil.displayWhen(TEST_NAME); - Event event = new CustomEvent(lightweightIdentifierGenerator, "general-post", null, + Event event = new CustomEventImpl(lightweightIdentifierGenerator, "general-post", null, "hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null); notificationManager.processEvent(event, task, result); @@ -639,7 +647,7 @@ public void test220SendSmsViaProxy() { // WHEN TestUtil.displayWhen(TEST_NAME); - Event event = new CustomEvent(lightweightIdentifierGenerator, "get-via-proxy", null, + Event event = new CustomEventImpl(lightweightIdentifierGenerator, "get-via-proxy", null, "hello world via proxy", EventOperationType.ADD, EventStatusType.SUCCESS, null); notificationManager.processEvent(event, task, result); @@ -667,7 +675,7 @@ public void test300CheckVariables() { // WHEN TestUtil.displayWhen(TEST_NAME); - Event event = new CustomEvent(lightweightIdentifierGenerator, "check-variables", null, + Event event = new CustomEventImpl(lightweightIdentifierGenerator, "check-variables", null, "hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null); notificationManager.processEvent(event, task, result); diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index b16b5762e97..8ed49da9b6f 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -4960,17 +4960,22 @@ protected ModelExecuteOptions nullToEmpty(ModelExecuteOptions options) { } protected void modifyUserAddAccount(String userOid, File accountFile, Task task, OperationResult result) throws SchemaException, IOException, ObjectAlreadyExistsException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException { + //noinspection unchecked + Collection> deltas = singleton(createAddAccountDelta(userOid, accountFile)); + modelService.executeChanges(deltas, null, task, result); + } + + @NotNull + protected ObjectDelta createAddAccountDelta(String userOid, File accountFile) + throws SchemaException, IOException { PrismObject account = prismContext.parseObject(accountFile); - ObjectDelta userDelta = prismContext.deltaFactory().object().createEmptyModifyDelta(UserType.class, userOid - ); + ObjectDelta userDelta = prismContext.deltaFactory().object().createEmptyModifyDelta(UserType.class, userOid); PrismReferenceValue accountRefVal = itemFactory().createReferenceValue(); accountRefVal.setObject(account); ReferenceDelta accountDelta = prismContext.deltaFactory().reference().createModificationAdd(UserType.F_LINK_REF, getUserDefinition(), accountRefVal); userDelta.addModification(accountDelta); - Collection> deltas = (Collection)MiscUtil.createCollection(userDelta); - - modelService.executeChanges(deltas, null, task, result); + return userDelta; } protected void assertAuthorized(MidPointPrincipal principal, String action) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { diff --git a/model/notifications-api/pom.xml b/model/notifications-api/pom.xml index ee191278b1f..c3e5b14c768 100644 --- a/model/notifications-api/pom.xml +++ b/model/notifications-api/pom.xml @@ -43,11 +43,6 @@ model-api 4.0.3-SNAPSHOT - - com.evolveum.midpoint.model - workflow-api - 4.0.3-SNAPSHOT - com.evolveum.midpoint.provisioning provisioning-api @@ -58,12 +53,6 @@ task-api 4.0.3-SNAPSHOT - - - commons-lang - commons-lang - - org.jetbrains annotations-java5 diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java index 686a7fcf7fd..eec8bacde96 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java @@ -7,35 +7,10 @@ package com.evolveum.midpoint.notifications.api; -import com.evolveum.midpoint.notifications.api.events.Event; -import com.evolveum.midpoint.notifications.api.events.ModelEvent; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; - -import java.util.List; - /** * Useful functions to be used in notifications scripts (including velocity templates). - * - * @author mederly + * TODO Decide what to do with this interface. Many of its methods were moved to Event and TextFormatter. */ public interface NotificationFunctions { - String getShadowName(PrismObject shadow); - - String getPlaintextPasswordFromDelta(ObjectDelta delta); - - String getContentAsFormattedList(Event event, boolean showSynchronizationItems, boolean showAuxiliaryAttributes); - - List getSynchronizationPaths(); - - List getAuxiliaryPaths(); - - // TODO: polish this method - // TODO indicate somehow if password was erased from the focus - // We should (probably) return only a value if it has been (successfully) written to the focus. - String getFocusPasswordFromEvent(ModelEvent modelEvent); - } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/AccessCertificationEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/AccessCertificationEvent.java index 103bb1b39c0..62bd82a6f64 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/AccessCertificationEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/AccessCertificationEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,90 +7,47 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationStageDefinitionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; -import org.apache.commons.lang.Validate; -import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.*; +import org.jetbrains.annotations.NotNull; /** - * Any event that is related to access certification. - * - * @author mederly + * An event related to access certification. */ -public abstract class AccessCertificationEvent extends BaseEvent { - - // all these must not be null - protected AccessCertificationCampaignType campaign; - protected OperationResultStatus status; - protected EventOperationType operationType; - - - public AccessCertificationEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, AccessCertificationCampaignType campaign, EventOperationType opType) { - super(lightweightIdentifierGenerator); - Validate.notNull(campaign, "campaign"); - Validate.notNull(opType, "opType"); - this.campaign = campaign; - this.operationType = opType; - this.status = OperationResultStatus.SUCCESS; // TODO fix this temporary implementation - } - - public AccessCertificationCampaignType getCampaign() { - return campaign; - } - - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - return false; // not supported for this kind of events - } - - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - return false; - } - - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - return this.operationType.equals(eventOperationType); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return EventCategoryType.ACCESS_CERTIFICATION_EVENT.equals(eventCategoryType); - } - - public String getCampaignName() { - return campaign.getName().getOrig(); - } - - public OperationResultStatus getStatus() { - return status; - } - - public EventOperationType getOperationType() { - return operationType; - } - - public AccessCertificationStageDefinitionType getCurrentStageDefinition() { - if (campaign.getState() != IN_REVIEW_STAGE && campaign.getState() != REVIEW_STAGE_DONE) { - return null; - } - return CertCampaignTypeUtil.findStageDefinition(campaign, campaign.getStageNumber()); - } - - @Override - protected void debugDumpCommon(StringBuilder sb, int indent) { - super.debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelToStringLn(sb, "campaign", campaign, indent + 1); - DebugUtil.debugDumpWithLabelToStringLn(sb, "status", status, indent + 1); - DebugUtil.debugDumpWithLabelToStringLn(sb, "operationType", operationType, indent + 1); - } +public interface AccessCertificationEvent extends Event { + + /** + * Type of the operation: usually + * - ADD (opening campaign/stage) + * - DELETE (closing campaign/stage). + * + * In special cases there can be MODIFY e.g. meaning "stage deadline approaching". + */ + EventOperationType getOperationType(); + + /** + * Status of the operation. + * (Currently always SUCCESS.) + */ + OperationResultStatus getStatus(); + + /** + * Related certification campaign. + */ + @NotNull AccessCertificationCampaignType getCampaign(); + + /** + * Name of the related certification campaign. + */ + default String getCampaignName() { + return getCampaign().getName().getOrig(); + } + + /** + * Definition of the current stage. + */ + AccessCertificationStageDefinitionType getCurrentStageDefinition(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignEvent.java index 87c3c97a717..48802235471 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,36 +7,5 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; - -/** - * Event related to certification campaign. - * ADD = campaign started - * MODIFY = some state change was done (covered in detail via CertCampaignStageEvent) - * DELETE = campaign closed (_not_ deleted; that is not supported via this kind of events) - * - * @author mederly - */ -public class CertCampaignEvent extends AccessCertificationEvent { - - public CertCampaignEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, AccessCertificationCampaignType campaign, EventOperationType opType) { - super(lightweightIdentifierGenerator, campaign, opType); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return super.isCategoryType(eventCategoryType) || - EventCategoryType.CERT_CAMPAIGN_EVENT.equals(eventCategoryType); - } - - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - return sb.toString(); - } +public interface CertCampaignEvent extends AccessCertificationEvent { } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignStageEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignStageEvent.java index 1362b334731..4b9de83643e 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignStageEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertCampaignStageEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,37 +7,8 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; - /** - * Event related to certification campaign stage. - * ADD = stage opened (including remediation stage) - * MODIFY = stage deadline is approaching - * DELETE = stage closed * - * @author mederly */ -public class CertCampaignStageEvent extends AccessCertificationEvent { - - public CertCampaignStageEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, AccessCertificationCampaignType campaign, EventOperationType opType) { - super(lightweightIdentifierGenerator, campaign, opType); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return super.isCategoryType(eventCategoryType) || - EventCategoryType.CERT_CAMPAIGN_STAGE_EVENT.equals(eventCategoryType); - } - - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - return sb.toString(); - } - +public interface CertCampaignStageEvent extends AccessCertificationEvent { } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertReviewEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertReviewEvent.java index 0014cacafe1..ab2e66d0ad2 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertReviewEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CertReviewEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,81 +7,29 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.schema.util.WorkItemTypeUtil; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType; + +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.Collection; import java.util.List; - /** - * Event related to given certification case. - * ADD = case created (first stage) - * MODIFY = stage deadline is approaching - * DELETE = stage closed - * - * @author mederly + * Event for certification reviewers (reminding him/her of the need to do the work). */ -public class CertReviewEvent extends AccessCertificationEvent { - - private List cases; - private SimpleObjectRef actualReviewer; // Actual reviewer - the person which the work item is assigned to. I.e. _not_ his deputy. - // Must be set to non-null value just after instantiation (TODO do more cleanly) - - public CertReviewEvent(LightweightIdentifierGenerator idGenerator, List cases, AccessCertificationCampaignType campaign, EventOperationType opType) { - super(idGenerator, campaign, opType); - this.cases = cases; - } - - public SimpleObjectRef getActualReviewer() { - return actualReviewer; - } - - public void setActualReviewer(SimpleObjectRef actualReviewer) { - this.actualReviewer = actualReviewer; - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return super.isCategoryType(eventCategoryType) || - EventCategoryType.CERT_CASE_EVENT.equals(eventCategoryType); - } - - public Collection getCasesAwaitingResponseFromActualReviewer() { - List rv = new ArrayList<>(); - for (AccessCertificationCaseType aCase : cases) { - if (awaitsResponseFrom(aCase, actualReviewer.getOid(), campaign.getStageNumber())) { - rv.add(aCase); - } - } - return rv; - } +public interface CertReviewEvent extends AccessCertificationEvent { - private boolean awaitsResponseFrom(AccessCertificationCaseType aCase, String reviewerOid, int currentStageNumber) { - for (AccessCertificationWorkItemType workItem : aCase.getWorkItem()) { - if (workItem.getStageNumber() == currentStageNumber - && WorkItemTypeUtil.getOutcome(workItem) == null - && workItem.getCloseTimestamp() == null - && ObjectTypeUtil.containsOid(workItem.getAssigneeRef(), reviewerOid)) { - return true; - } - } - return false; - } + /** + * Actual reviewer - the person which the work item is assigned to. + * This is never his/her deputy. + */ + SimpleObjectRef getActualReviewer(); - public List getCases() { - return cases; - } + /** + * List of cases that await response from the actual reviewer. + */ + @NotNull + Collection getCasesAwaitingResponseFromActualReviewer(); - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelLn(sb, "cases", cases, indent + 1); - return sb.toString(); - } + List getCases(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CustomEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CustomEvent.java index ca70bea3b81..5f66e33770c 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CustomEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/CustomEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,100 +7,15 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * @author mederly - */ -public class CustomEvent extends BaseEvent { - - private static final Trace LOGGER = TraceManager.getTrace(CustomEvent.class); - - private final String subtype; - /** - * Any object, e.g. PrismObject, any Item, any PrismValue, any real value. It can be even null. - */ - private final Object object; - @NotNull private final EventStatusType status; - @NotNull private final EventOperationType operationType; - - public CustomEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, @Nullable String subtype, @Nullable EventHandlerType adHocHandler, - @Nullable Object object, @NotNull EventOperationType operationType, @NotNull EventStatusType status, String channel) { - super(lightweightIdentifierGenerator, adHocHandler); - this.subtype = subtype; - this.object = object; - this.status = status; - this.operationType = operationType; - setChannel(channel); - } - - @NotNull - public EventOperationType getOperationType() { - return operationType; - } - - @NotNull - public EventStatusType getStatus() { - return status; - } - - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - return status == eventStatusType; - } - - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - return this.operationType == eventOperationType; - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.CUSTOM_EVENT; - } - - @Nullable - public String getSubtype() { - return subtype; - } - - @Nullable - public Object getObject() { - return object; - } - - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - // TODO implement if needed - return false; - } - - @Override - public boolean isUserRelated() { - if (object instanceof UserType) { - return true; - } else if (object instanceof PrismObject) { - PrismObject prismObject = (PrismObject) object; - return prismObject.getCompileTimeClass() != null && UserType.class.isAssignableFrom(prismObject.getCompileTimeClass()); - } else { - return false; - } - } - - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabel(sb, "subtype", subtype, indent + 1); - return sb.toString(); - } - +public interface CustomEvent extends Event { + @NotNull EventOperationType getOperationType(); + @NotNull EventStatusType getStatus(); + @Nullable String getSubtype(); + @Nullable Object getObject(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java index b7e643088fc..f9f056c5ac1 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java @@ -8,69 +8,153 @@ package com.evolveum.midpoint.notifications.api.events; import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifier; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.ShortDumpable; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; + +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; + /** - * @author mederly + * Notification event that should be propagated, filtered, externalized (typically to ascii or html), and send out. */ -public interface Event extends DebugDumpable, ShortDumpable { +@SuppressWarnings("unused") // Event methods are often called from notification expressions. +public interface Event extends DebugDumpable, ShortDumpable, Serializable { - LightweightIdentifier getId(); + /** + * Randomly generated event ID. It is immutable. + */ + @NotNull LightweightIdentifier getId(); - boolean isStatusType(EventStatusType eventStatusType); - boolean isOperationType(EventOperationType eventOperationType); - boolean isCategoryType(EventCategoryType eventCategoryType); + /** + * Entity that requested the operation that resulted in the event being generated. + * May be null if unknown. + */ + SimpleObjectRef getRequester(); - boolean isAccountRelated(); + /** + * @return OID of the requester + */ + default String getRequesterOid() { + return getRequester() != null ? getRequester().getOid() : null; + } - boolean isUserRelated(); + /** + * Entity that is the object of this event or the "owner" of the object of this event. + * Typically it is the user that we'd like to send the notification to. + * + * For example: + * - for model notifications: the focal object + * - for resource object (account) notifications: resource object (account) owner + * - workflow notifications: the object of the approval case + * - certification notifications: campaign owner or reviewer (depending on the kind of event) + * + * May be null if unknown. + */ + SimpleObjectRef getRequestee(); - boolean isWorkItemRelated(); + /** + * @return resolved requestee object (or null) + */ + ObjectType getRequesteeObject(); - boolean isWorkflowProcessRelated(); + /** + * @return display name of the requestee (or null) + */ + PolyStringType getRequesteeDisplayName(); - boolean isWorkflowRelated(); + /** + * @return OID of the requestee + */ + default String getRequesteeOid() { + return getRequestee() != null ? getRequestee().getOid() : null; + } - boolean isPolicyRuleRelated(); + /** + * @return true if the status of the operation that caused this event corresponds to the specified one + */ + boolean isStatusType(EventStatusType eventStatus); - boolean isAdd(); + /** + * @return true if the type of the operation that caused this event corresponds to the specified one + */ + boolean isOperationType(EventOperationType eventOperation); - boolean isModify(); + /** + * @return true if the categoru of the event matches the specified one + */ + boolean isCategoryType(EventCategoryType eventCategory); - boolean isDelete(); + /** + * @return true if the object of the event is of UserType + * + * Currently applies only to ModelEvent and CustomEvent. + * TODO specify semantics of this method more precisely; see also MID-4598 + */ + boolean isUserRelated(); - boolean isSuccess(); + @Deprecated + default boolean isAccountRelated() { + return isCategoryType(EventCategoryType.RESOURCE_OBJECT_EVENT); + } - boolean isAlsoSuccess(); + default boolean isWorkItemRelated() { + return isCategoryType(EventCategoryType.WORK_ITEM_EVENT); + } - boolean isFailure(); + @Deprecated // We no longer talk about workflow processes. There are approval cases instead. + default boolean isWorkflowProcessRelated() { + return isCategoryType(EventCategoryType.WORKFLOW_PROCESS_EVENT); + } - boolean isOnlyFailure(); + @Deprecated // We no longer talk about workflows. There are approvals instead. + default boolean isWorkflowRelated() { + return isCategoryType(EventCategoryType.WORKFLOW_EVENT); + } - boolean isInProgress(); + default boolean isPolicyRuleRelated() { + return isCategoryType(EventCategoryType.POLICY_RULE_EVENT); + } - // requester + default boolean isCertCampaignStageRelated() { + return isCategoryType(EventCategoryType.CERT_CAMPAIGN_STAGE_EVENT); + } - SimpleObjectRef getRequester(); + default boolean isAdd() { + return isOperationType(EventOperationType.ADD); + } - String getRequesterOid(); + default boolean isModify() { + return isOperationType(EventOperationType.MODIFY); + } - void setRequester(SimpleObjectRef requester); + default boolean isDelete() { + return isOperationType(EventOperationType.DELETE); + } - // requestee + default boolean isSuccess() { + return isStatusType(EventStatusType.SUCCESS); + } - SimpleObjectRef getRequestee(); + default boolean isAlsoSuccess() { + return isStatusType(EventStatusType.ALSO_SUCCESS); + } - String getRequesteeOid(); + default boolean isFailure() { + return isStatusType(EventStatusType.FAILURE); + } - void setRequestee(SimpleObjectRef requestee); + default boolean isOnlyFailure() { + return isStatusType(EventStatusType.ONLY_FAILURE); + } - void createExpressionVariables(VariablesMap variables, OperationResult result); + default boolean isInProgress() { + return isStatusType(EventStatusType.IN_PROGRESS); + } /** * Checks if the event is related to an item with a given path. @@ -88,14 +172,21 @@ public interface Event extends DebugDumpable, ShortDumpable { * Paths are compared without taking ID segments into account. * * EXPERIMENTAL; does not always work (mainly for values being deleted) - * - * @param itemPath - * @return */ boolean isRelatedToItem(ItemPath itemPath); + /** + * @return channel that was used to initiate the operation that caused this event + */ String getChannel(); + /** + * @return textual representation of event status + * + * TODO consider what to do with this + */ + String getStatusAsText(); + /** * If needed, we can prescribe the handler that should process this event. It is recommended only for ad-hoc situations. * A better is to define handlers in system configuration. diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java index e330f55c349..f15f6c413d7 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -10,216 +10,60 @@ import com.evolveum.midpoint.model.api.context.ModelContext; import com.evolveum.midpoint.model.api.context.ModelElementContext; import com.evolveum.midpoint.model.api.context.ModelProjectionContext; -import com.evolveum.midpoint.prism.PrismContainerDefinition; -import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.delta.ObjectDeltaCollectionsUtil; -import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.schema.ObjectDeltaOperation; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; -import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; import javax.xml.namespace.QName; -import java.util.ArrayList; import java.util.Collection; import java.util.List; /** - * @author mederly + * Event about model operation (TODO) */ -public class ModelEvent extends BaseEvent { +public interface ModelEvent extends Event { - private static final Trace LOGGER = TraceManager.getTrace(ModelEvent.class); + @NotNull + ModelContext getModelContext(); - // we can expect that modelContext != null and focus context != null as well - private ModelContext modelContext; + @NotNull + ModelElementContext getFocusContext(); - public ModelEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, ModelContext modelContext) { - super(lightweightIdentifierGenerator); - this.modelContext = modelContext; - } - - public ModelContext getModelContext() { - return modelContext; - } - - public ModelElementContext getFocusContext() { - return modelContext.getFocusContext(); - } + Collection getProjectionContexts(); - public Collection getProjectionContexts() { - return modelContext.getProjectionContexts(); - } + List getFocusExecutedDeltas(); - public List getFocusExecutedDeltas() { - return getFocusContext().getExecutedDeltas(); - } - - public List getAllExecutedDeltas() { - List retval = new ArrayList<>(); - retval.addAll(getFocusContext().getExecutedDeltas()); - for (Object o : modelContext.getProjectionContexts()) { - ModelProjectionContext modelProjectionContext = (ModelProjectionContext) o; - retval.addAll(modelProjectionContext.getExecutedDeltas()); - } - return retval; - } - - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - boolean allSuccess = true, anySuccess = false, allFailure = true, anyFailure = false, anyInProgress = false; - for (ObjectDeltaOperation objectDeltaOperation : getAllExecutedDeltas()) { - if (objectDeltaOperation.getExecutionResult() != null) { - switch (objectDeltaOperation.getExecutionResult().getStatus()) { - case SUCCESS: anySuccess = true; allFailure = false; break; - case FATAL_ERROR: allSuccess = false; anyFailure = true; break; - case WARNING: anySuccess = true; allFailure = false; break; - case HANDLED_ERROR: anySuccess = true; allFailure = false; break; - case IN_PROGRESS: allSuccess = false; allFailure = false; anyInProgress = true; break; - case NOT_APPLICABLE: break; - case PARTIAL_ERROR: allSuccess = false; anyFailure = true; break; - case UNKNOWN: allSuccess = false; allFailure = false; break; - default: LOGGER.warn("Unknown execution result: " + objectDeltaOperation.getExecutionResult().getStatus()); - } - } else { - allSuccess = false; allFailure = false; anyInProgress = true; - } - } - - switch (eventStatusType) { - case ALSO_SUCCESS: return anySuccess; - case SUCCESS: return allSuccess; - case FAILURE: return anyFailure; - case ONLY_FAILURE: return allFailure; - case IN_PROGRESS: return anyInProgress; - default: throw new IllegalStateException("Invalid eventStatusType: " + eventStatusType); - } - } + List getAllExecutedDeltas(); // a bit of hack but ... - public ChangeType getChangeType() { - if (isOperationType(EventOperationType.ADD)) { - return ChangeType.ADD; - } else if (isOperationType(EventOperationType.DELETE)) { - return ChangeType.DELETE; - } else { - return ChangeType.MODIFY; - } - } - - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - - // we consider an operation to be 'add' when there is 'add' delta among deltas - // in a similar way with 'delete' - // - // alternatively, we could summarize deltas and then decide based on the type of summarized delta (would be a bit inefficient) - - for (Object o : getFocusExecutedDeltas()) { - ObjectDeltaOperation objectDeltaOperation = (ObjectDeltaOperation) o; - if (objectDeltaOperation.getObjectDelta().isAdd()) { - return eventOperationType == EventOperationType.ADD; - } else if (objectDeltaOperation.getObjectDelta().isDelete()) { - return eventOperationType == EventOperationType.DELETE; - } - } - return eventOperationType == EventOperationType.MODIFY; - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.MODEL_EVENT; - } + ChangeType getChangeType(); - public ObjectDelta getFocusPrimaryDelta() { - return getFocusContext() != null ? getFocusContext().getPrimaryDelta() : null; - } + ObjectDelta getFocusPrimaryDelta(); - public ObjectDelta getFocusSecondaryDelta() { - return getFocusContext() != null ? getFocusContext().getSecondaryDelta() : null; - } + ObjectDelta getFocusSecondaryDelta(); - public List> getFocusDeltas() { - List> retval = new ArrayList<>(); - Class c = modelContext.getFocusClass(); - if (c != null && AssignmentHolderType.class.isAssignableFrom(c)) { - for (Object o : getFocusExecutedDeltas()) { - ObjectDeltaOperation objectDeltaOperation = (ObjectDeltaOperation) o; - //noinspection unchecked - retval.add(objectDeltaOperation.getObjectDelta()); - } - } - return retval; - } + List> getFocusDeltas(); - public ObjectDelta getSummarizedFocusDeltas() throws SchemaException { - return ObjectDeltaCollectionsUtil.summarize(getFocusDeltas()); - } + ObjectDelta getSummarizedFocusDeltas() throws SchemaException; - public boolean hasFocusOfType(Class clazz) { - return getFocusContext() != null && clazz.isAssignableFrom(getFocusContext().getObjectTypeClass()); - } + boolean hasFocusOfType(Class clazz); - public boolean hasFocusOfType(QName focusType) { - PrismContext prismContext = getModelContext().getPrismContext(); - if (prismContext == null) { - throw new IllegalStateException("No prismContext in model context"); - } - PrismContainerDefinition pcd = prismContext.getSchemaRegistry().findContainerDefinitionByType(focusType); - if (pcd == null) { - LOGGER.warn("Couldn't find definition for type " + focusType); - return false; - } - Class expectedClass = pcd.getCompileTimeClass(); - if (expectedClass == null) { - LOGGER.warn("Couldn't find class for type " + focusType); - return false; - } - return hasFocusOfType(expectedClass); - } + boolean hasFocusOfType(QName focusType); - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - return containsItem(getFocusDeltas(), itemPath); - } + String getFocusTypeName(); - @Override - public boolean isUserRelated() { - return hasFocusOfType(UserType.class); + default boolean hasContentToShow() { + return hasContentToShow(false); } - public String getFocusTypeName() { - if (getFocusContext() == null || getFocusContext().getObjectTypeClass() == null) { - return null; - } - String simpleName = getFocusContext().getObjectTypeClass().getSimpleName(); - return StringUtils.substringBeforeLast(simpleName, "Type"); // should usually work ;) - } - - public String getContentAsFormattedList() { - return getContentAsFormattedList(false, false); - } - - public String getContentAsFormattedList(boolean showSynchronizationItems, boolean showAuxiliaryAttributes) { - return getNotificationFunctions().getContentAsFormattedList(this, showSynchronizationItems, showAuxiliaryAttributes); - } - - public String getFocusPassword() { - return getNotificationFunctions().getFocusPasswordFromEvent(this); - } + boolean hasContentToShow(boolean showAuxiliaryAttributes); - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelToString(sb, "modelContext", modelContext, indent + 1); - return sb.toString(); + default String getContentAsFormattedList() { + return getContentAsFormattedList(false); } + String getContentAsFormattedList(boolean showAuxiliaryAttributes); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/PolicyRuleEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/PolicyRuleEvent.java index 6115e458703..45a581826c0 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/PolicyRuleEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/PolicyRuleEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -8,62 +8,14 @@ package com.evolveum.midpoint.notifications.api.events; import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; import org.jetbrains.annotations.NotNull; /** * Any event that is triggered by the 'notify' policy rule action. - * - * @author mederly */ -public class PolicyRuleEvent extends BaseEvent { - - @NotNull private final EvaluatedPolicyRule policyRule; - - public PolicyRuleEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull EvaluatedPolicyRule policyRule) { - super(lightweightIdentifierGenerator); - this.policyRule = policyRule; - } - - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - return false; // not supported for this kind of events - } - - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - return eventStatusType == EventStatusType.SUCCESS || eventStatusType == EventStatusType.ALSO_SUCCESS; - } - - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - return eventOperationType == EventOperationType.ADD; - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.POLICY_RULE_EVENT; - } - - @NotNull - public EvaluatedPolicyRule getPolicyRule() { - return policyRule; - } +public interface PolicyRuleEvent extends Event { - public String getRuleName() { - return policyRule.getName(); - } + @NotNull EvaluatedPolicyRule getPolicyRule(); - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelToStringLn(sb, "policyRule", policyRule, indent + 1); - return sb.toString(); - } + String getRuleName(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ResourceObjectEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ResourceObjectEvent.java index e4c9015ddba..169e4ef6150 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ResourceObjectEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ResourceObjectEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -8,174 +8,58 @@ package com.evolveum.midpoint.notifications.api.events; import com.evolveum.midpoint.notifications.api.OperationStatus; -import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.provisioning.api.ResourceOperationDescription; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; - import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; -import org.apache.commons.lang.StringUtils; + +import org.jetbrains.annotations.NotNull; /** - * @author mederly + * Event about resource object (account) creation, modification, or deletion. + * It is emitted when midPoint carries out the respective action. + * Not when account is changed on resource (by some other way). */ -public class ResourceObjectEvent extends BaseEvent { - - private static final Trace LOGGER = TraceManager.getTrace(ResourceObjectEvent.class); - - private OperationStatus operationStatus; // status of the operation - - private ResourceOperationDescription accountOperationDescription; - - private ChangeType changeType; - - // the following two are currently unused - private boolean activationRequested; - private boolean deactivationRequested; - - public ResourceObjectEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator) { - super(lightweightIdentifierGenerator); - } - - public ResourceOperationDescription getAccountOperationDescription() { - return accountOperationDescription; - } - - public void setAccountOperationDescription(ResourceOperationDescription accountOperationDescription) { - this.accountOperationDescription = accountOperationDescription; - } - - public boolean isActivationRequested() { - return activationRequested; - } - - public void setActivationRequested(boolean activationRequested) { - this.activationRequested = activationRequested; - } - - public boolean isDeactivationRequested() { - return deactivationRequested; - } - - public void setDeactivationRequested(boolean deactivationRequested) { - this.deactivationRequested = deactivationRequested; - } - - public ChangeType getChangeType() { - return changeType; - } - - public void setChangeType(ChangeType changeType) { - this.changeType = changeType; - } +@SuppressWarnings("unused") +public interface ResourceObjectEvent extends Event { - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - return changeTypeMatchesOperationType(changeType, eventOperationType); - } + @NotNull + ResourceOperationDescription getOperationDescription(); - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.RESOURCE_OBJECT_EVENT; + @Deprecated + default ResourceOperationDescription getAccountOperationDescription() { + return getOperationDescription(); } - public boolean isShadowKind(ShadowKindType shadowKindType) { - ShadowKindType actualKind = accountOperationDescription.getCurrentShadow().asObjectable().getKind(); - if (actualKind != null) { - return actualKind.equals(shadowKindType); - } else { - return ShadowKindType.ACCOUNT.equals(shadowKindType); - } - } + @NotNull + ChangeType getChangeType(); - public ShadowType getShadow() { - PrismObject shadow = accountOperationDescription.getCurrentShadow(); - return shadow != null ? shadow.asObjectable() : null; - } + @NotNull + OperationStatus getOperationStatus(); - public boolean isShadowIntent(String intent) { - if (StringUtils.isNotEmpty(intent)) { - return intent.equals(accountOperationDescription.getCurrentShadow().asObjectable().getIntent()); - } else { - return StringUtils.isEmpty(accountOperationDescription.getCurrentShadow().asObjectable().getIntent()); - } - } - - public ObjectDelta getShadowDelta() { - return (ObjectDelta) accountOperationDescription.getObjectDelta(); - } - - public OperationStatus getOperationStatus() { - return operationStatus; - } + boolean isShadowKind(ShadowKindType shadowKindType); - public void setOperationStatus(OperationStatus operationStatus) { - this.operationStatus = operationStatus; - } + ShadowType getShadow(); - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - return operationStatus.matchesEventStatusType(eventStatusType); - } + boolean isShadowIntent(String intent); - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - return containsItem(getShadowDelta(), itemPath); - } + ObjectDelta getShadowDelta(); - @Override - public String toString() { - return "ResourceObjectEvent{" + - "base=" + super.toString() + - ", changeType=" + changeType + - ", operationStatus=" + operationStatus + - '}'; - } + String getShadowName(); - public String getShadowName() { - return getNotificationFunctions().getShadowName(getAccountOperationDescription().getCurrentShadow()); - } + PolyStringType getResourceName(); - public PolyStringType getResourceName() { - return getAccountOperationDescription().getResource().asObjectable().getName(); - } + String getResourceOid(); - public String getResourceOid() { - return getAccountOperationDescription().getResource().getOid(); - } + String getPlaintextPassword(); - public String getPlaintextPassword() { - ObjectDelta delta = getAccountOperationDescription().getObjectDelta(); - return delta != null ? getNotificationFunctions().getPlaintextPasswordFromDelta(delta) : null; - } + String getContentAsFormattedList(); - public String getContentAsFormattedList() { - return getContentAsFormattedList(false, false); - } + String getContentAsFormattedList(boolean showSynchronizationItems, boolean showAuxiliaryAttributes); - public String getContentAsFormattedList(boolean showSynchronizationItems, boolean showAuxiliaryAttributes) { - return getNotificationFunctions().getContentAsFormattedList(this, showSynchronizationItems, showAuxiliaryAttributes); - } + boolean hasContentToShow(); - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelToStringLn(sb, "operationStatus", operationStatus, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "accountOperationDescription", accountOperationDescription, indent + 1); - DebugUtil.debugDumpWithLabelToStringLn(sb, "changeType", changeType, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "activationRequested", activationRequested, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "deactivationRequested", deactivationRequested, indent + 1); - return sb.toString(); - } + boolean hasContentToShow(boolean watchSynchronizationAttributes, boolean watchAuxiliaryAttributes); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/SimpleObjectRef.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/SimpleObjectRef.java index e8ab5aca9ad..fdcb1fe4b3c 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/SimpleObjectRef.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/SimpleObjectRef.java @@ -12,13 +12,13 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** - * @author mederly + * */ public interface SimpleObjectRef extends DebugDumpable { - public String getOid(); - public void setOid(String oid); - public ObjectType getObjectType(); - public void setObjectType(ObjectType objectType); + String getOid(); + void setOid(String oid); + ObjectType getObjectType(); + void setObjectType(ObjectType objectType); ObjectType resolveObjectType(OperationResult result, boolean allowNotFound); ObjectType resolveObjectType(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/TaskEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/TaskEvent.java index 8d70fa6d89d..189c31619b7 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/TaskEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/TaskEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,135 +7,37 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskRunResult; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * @author mederly + * */ -public class TaskEvent extends BaseEvent { - - private static final Trace LOGGER = TraceManager.getTrace(TaskEvent.class); - - @NotNull private final Task task; - @Nullable private final TaskRunResult taskRunResult; // nullable only if operationType == ADD - @NotNull private final EventOperationType operationType; // only ADD or DELETE - - public TaskEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull Task task, @Nullable TaskRunResult runResult, - @NotNull EventOperationType operationType, String channel) { - super(lightweightIdentifierGenerator); - this.task = task; - this.taskRunResult = runResult; - this.operationType = operationType; - setChannel(channel); - } - - @NotNull - public Task getTask() { - return task; - } - - @Nullable - public TaskRunResult getTaskRunResult() { - return taskRunResult; - } - - @NotNull - public EventOperationType getOperationType() { - return operationType; - } - - public boolean isTemporaryError() { - return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.TEMPORARY_ERROR; - } - - public boolean isPermanentError() { - return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.PERMANENT_ERROR; - } - - public boolean isFinished() { - return taskRunResult != null && - (taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.FINISHED || - taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.FINISHED_HANDLER); - } +public interface TaskEvent extends Event { - public boolean isInterrupted() { - return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.INTERRUPTED; - } + @NotNull Task getTask(); - public boolean isRestartRequested() { - return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.RESTART_REQUESTED; - } + @Nullable TaskRunResult getTaskRunResult(); - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - if (eventStatusType == null) { - return false; - } - if (taskRunResult == null || taskRunResult.getOperationResult() == null) { - // TODO consider if we really want to return 'true' for both success and in_progress here - return eventStatusType == EventStatusType.SUCCESS || eventStatusType == EventStatusType.ALSO_SUCCESS || eventStatusType == EventStatusType.IN_PROGRESS; - } - OperationResult result = taskRunResult.getOperationResult(); - switch (eventStatusType) { - case SUCCESS: - case ALSO_SUCCESS: return result.isSuccess() || result.isHandledError() || result.isWarning(); - case IN_PROGRESS: return false; - case FAILURE: return result.isError(); - case ONLY_FAILURE: return result.isFatalError(); - default: throw new IllegalStateException("Invalid eventStatusType: " + eventStatusType); - } - } + @NotNull EventOperationType getOperationType(); - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - return this.operationType == eventOperationType; - } + boolean isTemporaryError(); - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.TASK_EVENT; - } + boolean isPermanentError(); - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - return false; - } + boolean isFinished(); - @Override - public boolean isUserRelated() { - return false; - } + boolean isInterrupted(); - public OperationResultStatus getOperationResultStatus() { - return taskRunResult != null && taskRunResult.getOperationResult() != null ? taskRunResult.getOperationResult().getStatus() : null; - } + boolean isRestartRequested(); - public String getMessage() { - return taskRunResult != null && taskRunResult.getOperationResult() != null ? taskRunResult.getOperationResult().getMessage() : null; - } + OperationResultStatus getOperationResultStatus(); - public long getProgress() { - return taskRunResult != null && taskRunResult.getProgress() != null ? taskRunResult.getProgress() : task.getProgress(); - } + String getMessage(); - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelToStringLn(sb, "task", task, indent + 1); - DebugUtil.debugDumpWithLabelToStringLn(sb, "taskRunResult", taskRunResult, indent + 1); - DebugUtil.debugDumpWithLabelToString(sb, "operationType", operationType, indent + 1); - return sb.toString(); - } + long getProgress(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemAllocationEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemAllocationEvent.java index 4fb981460e6..0a707ace834 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemAllocationEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemAllocationEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,48 +7,8 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.wf.api.WorkItemOperationInfo; -import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.xml.datatype.Duration; - /** - * @author mederly + * */ -public class WorkItemAllocationEvent extends WorkItemEvent { - - public WorkItemAllocationEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, - @NotNull ChangeType changeType, - @NotNull CaseWorkItemType workItem, @Nullable SimpleObjectRef assignee, @Nullable SimpleObjectRef initiator, - @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, - @Nullable ApprovalContextType approvalContext, @NotNull CaseType aCase, - @Nullable Duration timeBefore) { - super(lightweightIdentifierGenerator, changeType, workItem, assignee, initiator, operationInfo, sourceInfo, - approvalContext, aCase, null, timeBefore); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.WORK_ITEM_ALLOCATION_EVENT - || eventCategoryType == EventCategoryType.WORK_ITEM_EVENT - || eventCategoryType == EventCategoryType.WORKFLOW_EVENT; - } - - @Override - public void createExpressionVariables(VariablesMap variables, OperationResult result) { - super.createExpressionVariables(variables, result); - } - - @Override - public String toString() { - return "WorkItemAllocationEvent:" + super.toString(); - } - +public interface WorkItemAllocationEvent { } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemCustomEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemCustomEvent.java index 9f9a0d8ca80..b235437e18b 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemCustomEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemCustomEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,49 +7,11 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemNotificationActionType; /** - * @author mederly + * */ -public class WorkItemCustomEvent extends WorkItemEvent { - - public WorkItemCustomEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, - @NotNull ChangeType changeType, - @NotNull CaseWorkItemType workItem, - @Nullable SimpleObjectRef assignee, @Nullable WorkItemOperationSourceInfo sourceInfo, - @Nullable ApprovalContextType approvalContext, CaseType aCase, - @Nullable EventHandlerType handler) { - super(lightweightIdentifierGenerator, changeType, workItem, assignee, null, null, - sourceInfo, approvalContext, aCase, handler, null); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.WORK_ITEM_CUSTOM_EVENT - || eventCategoryType == EventCategoryType.WORK_ITEM_EVENT - || eventCategoryType == EventCategoryType.WORKFLOW_EVENT; - } - - @Override - public void createExpressionVariables(VariablesMap variables, OperationResult result) { - super.createExpressionVariables(variables, result); - } - - public WorkItemNotificationActionType getNotificationAction() { - return (WorkItemNotificationActionType) getSource(); - } - - @Override - public String toString() { - return "WorkItemCustomEvent:" + super.toString(); - } - +public interface WorkItemCustomEvent { + WorkItemNotificationActionType getNotificationAction(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java index 8666a2c3221..2573efa2e56 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,136 +7,8 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.schema.constants.ExpressionConstants; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.wf.api.WorkItemOperationInfo; -import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; -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 javax.xml.datatype.Duration; - /** - * @author mederly + * */ -public class WorkItemEvent extends WorkflowEvent { - - @NotNull protected final CaseWorkItemType workItem; - // (Currently) Each work item event is related to at most one assignee. So, if a work item has more assignees, - // more events will be generated. This might change in a future. - protected final SimpleObjectRef assignee; - /** - * User who "pressed the button". I.e. the one that really approved, rejected or delegated/escalated a work item. - * In case of automated actions (completion, delegation/escalation) this is not filled-in. - */ - protected final SimpleObjectRef initiator; - protected final WorkItemOperationInfo operationInfo; - protected final WorkItemOperationSourceInfo sourceInfo; - protected final Duration timeBefore; - - WorkItemEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ChangeType changeType, - @NotNull CaseWorkItemType workItem, - @Nullable SimpleObjectRef assignee, @Nullable SimpleObjectRef initiator, - @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, - @Nullable ApprovalContextType approvalContext, - @NotNull CaseType aCase, - @Nullable EventHandlerType handler, @Nullable Duration timeBefore) { - super(lightweightIdentifierGenerator, changeType, approvalContext, aCase, handler); - Validate.notNull(workItem); - this.workItem = workItem; - this.assignee = assignee; - this.initiator = initiator; - this.operationInfo = operationInfo; - this.sourceInfo = sourceInfo; - this.timeBefore = timeBefore; - } - - public String getWorkItemName() { - return PolyString.getOrig(workItem.getName()); // todo MID-5916 - } - - @NotNull - public CaseWorkItemType getWorkItem() { - return workItem; - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.WORK_ITEM_EVENT || eventCategoryType == EventCategoryType.WORKFLOW_EVENT; - } - - public SimpleObjectRef getAssignee() { - return assignee; - } - - public SimpleObjectRef getInitiator() { - return initiator; - } - - public WorkItemOperationKindType getOperationKind() { - return operationInfo != null ? operationInfo.getOperationKind() : null; - } - - public AbstractWorkItemActionType getSource() { - return sourceInfo != null ? sourceInfo.getSource() : null; - } - - public WorkItemEventCauseInformationType getCause() { - return sourceInfo != null ? sourceInfo.getCause() : null; - } - - public Duration getTimeBefore() { - return timeBefore; - } - - public WorkItemOperationInfo getOperationInfo() { - return operationInfo; - } - - public WorkItemOperationSourceInfo getSourceInfo() { - return sourceInfo; - } - - @Override - public void createExpressionVariables(VariablesMap variables, OperationResult result) { - super.createExpressionVariables(variables, result); - variables.put(ExpressionConstants.VAR_ASSIGNEE, resolveTypedObject(assignee, false, result)); - variables.put(ExpressionConstants.VAR_WORK_ITEM, workItem, CaseWorkItemType.class); - } - - public AbstractWorkItemOutputType getOutput() { - return workItem.getOutput(); - } - - @Override - public String getOutcome() { - AbstractWorkItemOutputType output = getOutput(); - return output != null ? output.getOutcome() : null; - } - - @Override - public String toString() { - return "WorkflowProcessEvent{" + - "workflowEvent=" + super.toString() + - ", workItemName=" + getWorkItemName() + - ", assignee=" + assignee + - '}'; - - } - - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelLn(sb, "workItemName", getWorkItemName(), indent + 1); - DebugUtil.debugDumpWithLabelToString(sb, "assignee", assignee, indent + 1); - return sb.toString(); - } +public interface WorkItemEvent { } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemLifecycleEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemLifecycleEvent.java index a3bf8b6318d..a5b117b5717 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemLifecycleEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemLifecycleEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,44 +7,8 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.wf.api.WorkItemOperationInfo; -import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - /** - * @author mederly + * */ -public class WorkItemLifecycleEvent extends WorkItemEvent { - - public WorkItemLifecycleEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ChangeType changeType, - @NotNull CaseWorkItemType workItem, - @Nullable SimpleObjectRef assignee, @Nullable SimpleObjectRef initiator, - @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, - @Nullable ApprovalContextType approvalContext, @NotNull CaseType aCase) { - super(lightweightIdentifierGenerator, changeType, workItem, assignee, initiator, - operationInfo, sourceInfo, approvalContext, aCase, null, null); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.WORK_ITEM_LIFECYCLE_EVENT - || eventCategoryType == EventCategoryType.WORK_ITEM_EVENT - || eventCategoryType == EventCategoryType.WORKFLOW_EVENT; - } - - @Override - public void createExpressionVariables(VariablesMap variables, OperationResult result) { - super.createExpressionVariables(variables, result); - } - - @Override - public String toString() { - return "WorkItemLifecycleEvent:" + super.toString(); - } +public interface WorkItemLifecycleEvent { } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEvent.java index 9a0cf87160a..e196411b223 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -9,137 +9,33 @@ import com.evolveum.midpoint.notifications.api.OperationStatus; import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.wf.util.ApprovalUtils; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ApprovalContextType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** - * @author mederly + * */ -abstract public class WorkflowEvent extends BaseEvent { - - @Nullable protected final ApprovalContextType approvalContext; - @NotNull private final ChangeType changeType; - @NotNull protected final CaseType aCase; - - WorkflowEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ChangeType changeType, - @Nullable ApprovalContextType approvalContext, @NotNull CaseType aCase, EventHandlerType handler) { - super(lightweightIdentifierGenerator, handler); - this.changeType = changeType; - this.approvalContext = approvalContext; - this.aCase = aCase; - } - - @NotNull - public CaseType getCase() { - return aCase; - } - - public String getProcessInstanceName() { - return aCase.getName().getOrig(); - } - - public OperationStatus getOperationStatus() { - return outcomeToStatus(changeType, getOutcome()); - } - - protected abstract String getOutcome(); - - @Override - public boolean isStatusType(EventStatusType eventStatusType) { - return getOperationStatus().matchesEventStatusType(eventStatusType); - } - - public ChangeType getChangeType() { - return changeType; - } - - @Override - public boolean isOperationType(EventOperationType eventOperationType) { - return changeTypeMatchesOperationType(changeType, eventOperationType); - } - - public boolean isApprovalCase() { - return ObjectTypeUtil.hasArchetype(aCase, SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value()); - } - - public boolean isManualResourceCase() { - return ObjectTypeUtil.hasArchetype(aCase, SystemObjectsType.ARCHETYPE_MANUAL_CASE.value()); - } - - public boolean isResultKnown() { - return !isInProgress(); // for now - } +public interface WorkflowEvent extends Event { + @NotNull CaseType getCase(); - public boolean isApproved() { - return isSuccess(); // for now - } + String getProcessInstanceName(); - public boolean isRejected() { - return isFailure(); // for now - } + OperationStatus getOperationStatus(); - private OperationStatus outcomeToStatus(ChangeType changeType, String outcome) { - if (changeType != ChangeType.DELETE) { - return OperationStatus.SUCCESS; - } else { - if (outcome == null) { - return OperationStatus.IN_PROGRESS; - } else if (QNameUtil.matchUri(outcome, SchemaConstants.MODEL_APPROVAL_OUTCOME_APPROVE)) { - return OperationStatus.SUCCESS; - } else if (QNameUtil.matchUri(outcome, SchemaConstants.MODEL_APPROVAL_OUTCOME_REJECT)) { - return OperationStatus.FAILURE; - } else { - return OperationStatus.OTHER; - } - } - } + ChangeType getChangeType(); - @Override - public boolean isRelatedToItem(ItemPath itemPath) { - return false; - } + boolean isApprovalCase(); - @NotNull - public ApprovalContextType getApprovalContext() { - return approvalContext; - } + boolean isManualResourceCase(); - @NotNull - public CaseType getWorkflowTask() { - return aCase; - } + boolean isResultKnown(); - @Override - public String toString() { - return "WorkflowEvent{" + - "event=" + super.toString() + - ", processInstanceName='" + getProcessInstanceName() + '\'' + - ", changeType=" + changeType + - ", outcome=" + getOutcome() + - '}'; - } + boolean isApproved(); - // This method is not used. It is here just for maven dependency plugin to detect the - // dependency on workflow-api - @SuppressWarnings("unused") - private void notUsed() { - ApprovalUtils.approvalBooleanValueFromUri(""); - } + boolean isRejected(); - @Override - protected void debugDumpCommon(StringBuilder sb, int indent) { - super.debugDumpCommon(sb, indent); - DebugUtil.debugDumpWithLabelLn(sb, "processInstanceName", getProcessInstanceName(), indent + 1); - DebugUtil.debugDumpWithLabelToStringLn(sb, "changeType", changeType, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "outcome", getOutcome(), indent + 1); - } + @NotNull ApprovalContextType getApprovalContext(); + @NotNull CaseType getWorkflowTask(); } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowProcessEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowProcessEvent.java index bdb43284009..a19f97dfcfe 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowProcessEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowProcessEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,45 +7,5 @@ package com.evolveum.midpoint.notifications.api.events; -import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; - -/** - * @author mederly - */ -public class WorkflowProcessEvent extends WorkflowEvent { - - public WorkflowProcessEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, ChangeType changeType, CaseType aCase) { - super(lightweightIdentifierGenerator, changeType, aCase.getApprovalContext(), aCase, null); - } - - @Override - public boolean isCategoryType(EventCategoryType eventCategoryType) { - return eventCategoryType == EventCategoryType.WORKFLOW_PROCESS_EVENT || eventCategoryType == EventCategoryType.WORKFLOW_EVENT; - } - - @Override - protected String getOutcome() { - return aCase.getOutcome(); - } - - @Override - public String toString() { - return "WorkflowProcessEvent{" + - "workflowEvent=" + super.toString() + - '}'; - - } - - @Override - public String debugDump(int indent) { - StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); - debugDumpCommon(sb, indent); - return sb.toString(); - } - +public interface WorkflowProcessEvent extends Event { } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/factory/CustomEventFactory.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/factory/CustomEventFactory.java new file mode 100644 index 00000000000..50394f628dc --- /dev/null +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/factory/CustomEventFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.api.events.factory; + +import com.evolveum.midpoint.model.api.PipelineItem; +import com.evolveum.midpoint.notifications.api.events.CustomEvent; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Factory for custom events. + */ +public interface CustomEventFactory { + + @NotNull CustomEvent createEvent(String subtype, EventHandlerType handler, PrismValue value, EventOperationType operation, + EventStatusType status, String channel); + + @NotNull CustomEvent createEvent(String subtype, EventHandlerType handler, List data, + EventOperationType operation, EventStatusType status, String channel); +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java index 4dc3b52bac1..a73cda7b8c0 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java @@ -9,7 +9,7 @@ import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.OperationStatus; -import com.evolveum.midpoint.notifications.api.events.ResourceObjectEvent; +import com.evolveum.midpoint.notifications.impl.events.ResourceObjectEventImpl; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.provisioning.api.ChangeNotificationDispatcher; import com.evolveum.midpoint.provisioning.api.ResourceOperationDescription; @@ -19,7 +19,6 @@ import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -34,7 +33,7 @@ import javax.annotation.PostConstruct; /** - * @author mederly + * Converts provisioning events into notification events. */ @Component public class AccountOperationListener implements ResourceOperationListener { @@ -43,28 +42,16 @@ public class AccountOperationListener implements ResourceOperationListener { private static final String DOT_CLASS = AccountOperationListener.class.getName() + "."; - @Autowired - private LightweightIdentifierGenerator lightweightIdentifierGenerator; - - @Autowired - private ChangeNotificationDispatcher provisioningNotificationDispatcher; - - @Autowired - private NotificationManager notificationManager; - - @Autowired - @Qualifier("cacheRepositoryService") - private transient RepositoryService cacheRepositoryService; - - @Autowired - private NotificationFunctionsImpl notificationsUtil; + @Autowired private LightweightIdentifierGenerator lightweightIdentifierGenerator; + @Autowired private ChangeNotificationDispatcher provisioningNotificationDispatcher; + @Autowired private NotificationManager notificationManager; + @Autowired @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; + @Autowired private NotificationFunctionsImpl notificationsUtil; @PostConstruct public void init() { provisioningNotificationDispatcher.registerNotificationListener(this); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Registered account operation notification listener."); - } + LOGGER.trace("Registered account operation notification listener."); } @Override @@ -145,20 +132,18 @@ private void executeNotifyAny(OperationStatus status, ResourceOperationDescripti return; } - ResourceObjectEvent request = createRequest(status, operationDescription, task, result); + ResourceObjectEventImpl request = createRequest(status, operationDescription, task, result); notificationManager.processEvent(request, task, result); } @NotNull - private ResourceObjectEvent createRequest(OperationStatus status, + private ResourceObjectEventImpl createRequest(OperationStatus status, ResourceOperationDescription operationDescription, Task task, OperationResult result) { - ResourceObjectEvent event = new ResourceObjectEvent(lightweightIdentifierGenerator); - event.setAccountOperationDescription(operationDescription); - event.setOperationStatus(status); - event.setChangeType(operationDescription.getObjectDelta().getChangeType()); // fortunately there's 1:1 mapping + ResourceObjectEventImpl event = new ResourceObjectEventImpl(lightweightIdentifierGenerator, + operationDescription, status); String accountOid = operationDescription.getObjectDelta().getOid(); @@ -182,14 +167,6 @@ private ResourceObjectEvent createRequest(OperationStatus status, return event; } -// private boolean isRequestApplicable(ResourceObjectEvent request, NotificationConfigurationEntryType entry) { -// -// ResourceOperationDescription opDescr = request.getAccountOperationDescription(); -// OperationStatus status = request.getOperationStatus(); -// ChangeType type = opDescr.getObjectDelta().getChangeType(); -// return typeMatches(type, entry.getSituation(), opDescr) && statusMatches(status, entry.getSituation()); -// } - private PrismObject findRequestee(String shadowOid, Task task, OperationResult result) { // This is (still) a temporary solution. We need to rework it eventually. if (task != null && task.getRequestee() != null) { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/CertificationListener.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/CertificationListener.java index 6dabf4e7384..0cb34dcb516 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/CertificationListener.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/CertificationListener.java @@ -9,15 +9,13 @@ import com.evolveum.midpoint.certification.api.AccessCertificationEventListener; import com.evolveum.midpoint.certification.api.CertificationManager; -import com.evolveum.midpoint.notifications.api.NotificationManager; -import com.evolveum.midpoint.notifications.api.events.AccessCertificationEvent; import com.evolveum.midpoint.notifications.api.events.CertCampaignEvent; import com.evolveum.midpoint.notifications.api.events.CertCampaignStageEvent; import com.evolveum.midpoint.notifications.api.events.CertReviewEvent; -import com.evolveum.midpoint.notifications.impl.events.cert.CertEventFactory; +import com.evolveum.midpoint.notifications.impl.events.factory.CertEventFactory; +import com.evolveum.midpoint.notifications.impl.util.EventHelper; import com.evolveum.midpoint.schema.result.OperationResult; 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.xml.ns._public.common.common_3.AccessCertificationCampaignType; @@ -30,28 +28,19 @@ import java.util.List; /** - * Listener that accepts events generated by certification module. - * - * @author mederly + * Converts events generated by certification module to notification events. */ @Component public class CertificationListener implements AccessCertificationEventListener { private static final Trace LOGGER = TraceManager.getTrace(CertificationListener.class); - //private static final String DOT_CLASS = WorkflowListener.class.getName() + "."; - - @Autowired - private NotificationManager notificationManager; - // CertificationManager is not required, because e.g. within model-test and model-intest we have no certifications. // However, during normal operation, it is expected to be available. - @Autowired(required = false) - private CertificationManager certificationManager; - - @Autowired - private CertEventFactory certEventFactory; + @Autowired(required = false) private CertificationManager certificationManager; + @Autowired private CertEventFactory certEventFactory; + @Autowired private EventHelper eventHelper; @PostConstruct public void init() { @@ -64,60 +53,45 @@ public void init() { @Override public void onCampaignStart(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignEvent event = certEventFactory.createOnCampaignStartEvent(campaign, task, result); - processEvent(event, task, result); + CertCampaignEvent event = certEventFactory.createOnCampaignStartEvent(campaign, task); + eventHelper.processEvent(event, task, result); } @Override public void onCampaignEnd(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignEvent event = certEventFactory.createOnCampaignEndEvent(campaign, task, result); - processEvent(event, task, result); + CertCampaignEvent event = certEventFactory.createOnCampaignEndEvent(campaign, task); + eventHelper.processEvent(event, task, result); } @Override public void onCampaignStageStart(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignStageEvent event = certEventFactory.createOnCampaignStageStartEvent(campaign, task, result); - processEvent(event, task, result); + CertCampaignStageEvent event = certEventFactory.createOnCampaignStageStartEvent(campaign, task); + eventHelper.processEvent(event, task, result); } @Override public void onCampaignStageDeadlineApproaching(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignStageEvent event = certEventFactory.createOnCampaignStageDeadlineApproachingEvent(campaign, task, result); - processEvent(event, task, result); + CertCampaignStageEvent event = certEventFactory.createOnCampaignStageDeadlineApproachingEvent(campaign, task); + eventHelper.processEvent(event, task, result); } @Override public void onCampaignStageEnd(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignStageEvent event = certEventFactory.createOnCampaignStageEndEvent(campaign, task, result); - processEvent(event, task, result); + CertCampaignStageEvent event = certEventFactory.createOnCampaignStageEndEvent(campaign, task); + eventHelper.processEvent(event, task, result); } @Override public void onReviewRequested(ObjectReferenceType reviewerOrDeputyRef, ObjectReferenceType actualReviewerRef, List cases, AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertReviewEvent event = certEventFactory.createReviewRequestedEvent(reviewerOrDeputyRef, actualReviewerRef, cases, campaign, task, result); - processEvent(event, task, result); + CertReviewEvent event = certEventFactory.createReviewRequestedEvent(reviewerOrDeputyRef, actualReviewerRef, cases, campaign, task); + eventHelper.processEvent(event, task, result); } @Override public void onReviewDeadlineApproaching(ObjectReferenceType reviewerOrDeputyRef, ObjectReferenceType actualReviewerRef, List cases, AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertReviewEvent event = certEventFactory.createReviewDeadlineApproachingEvent(reviewerOrDeputyRef, actualReviewerRef, cases, campaign, task, result); - processEvent(event, task, result); - } - - private void processEvent(AccessCertificationEvent event, Task task, OperationResult result) { - try { - notificationManager.processEvent(event, task, result); - } 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(); + CertReviewEvent event = certEventFactory.createReviewDeadlineApproachingEvent(reviewerOrDeputyRef, actualReviewerRef, cases, campaign, task); + eventHelper.processEvent(event, task, result); } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java index 781fe1ca1a4..63ffa9e8704 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java @@ -7,32 +7,18 @@ package com.evolveum.midpoint.notifications.impl; -import com.evolveum.midpoint.model.api.context.ModelContext; -import com.evolveum.midpoint.model.api.context.ModelElementContext; -import com.evolveum.midpoint.model.api.expr.MidpointFunctions; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.notifications.api.NotificationFunctions; -import com.evolveum.midpoint.notifications.api.OperationStatus; -import com.evolveum.midpoint.notifications.api.events.Event; -import com.evolveum.midpoint.notifications.api.events.ModelEvent; -import com.evolveum.midpoint.notifications.api.events.ResourceObjectEvent; import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; import com.evolveum.midpoint.notifications.impl.formatters.TextFormatter; -import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.crypto.EncryptionException; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.delta.ObjectDeltaCollectionsUtil; -import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.repo.api.RepositoryService; -import com.evolveum.midpoint.schema.DeltaConvertor; -import com.evolveum.midpoint.schema.processor.ResourceAttribute; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.schema.util.ShadowUtil; -import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; @@ -42,18 +28,8 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; -import org.jetbrains.annotations.Nullable; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import java.util.*; - -import static java.util.Collections.singletonList; - /** - * + * Various useful functions. TODO decide what to do with this class. */ @Component public class NotificationFunctionsImpl implements NotificationFunctions { @@ -61,29 +37,7 @@ public class NotificationFunctionsImpl implements NotificationFunctions { private static final Trace LOGGER = TraceManager.getTrace(NotificationFunctionsImpl.class); @Autowired @Qualifier("cacheRepositoryService") private RepositoryService cacheRepositoryService; - @Autowired private MidpointFunctions midpointFunctions; @Autowired protected TextFormatter textFormatter; - @Autowired private PrismContext prismContext; - - private static final List SYNCHRONIZATION_PATHS = Collections.unmodifiableList(Arrays.asList( - ShadowType.F_SYNCHRONIZATION_SITUATION, - ShadowType.F_SYNCHRONIZATION_SITUATION_DESCRIPTION, - ShadowType.F_SYNCHRONIZATION_TIMESTAMP, - ShadowType.F_FULL_SYNCHRONIZATION_TIMESTAMP)); - - private static final List AUXILIARY_PATHS = Collections.unmodifiableList(Arrays.asList( - ShadowType.F_METADATA, - ShadowType.F_ACTIVATION.append(ActivationType.F_VALIDITY_STATUS), // works for user activation as well - ShadowType.F_ACTIVATION.append(ActivationType.F_VALIDITY_CHANGE_TIMESTAMP), - ShadowType.F_ACTIVATION.append(ActivationType.F_EFFECTIVE_STATUS), - ShadowType.F_ACTIVATION.append(ActivationType.F_DISABLE_TIMESTAMP), - ShadowType.F_ACTIVATION.append(ActivationType.F_ARCHIVE_TIMESTAMP), - ShadowType.F_ACTIVATION.append(ActivationType.F_ENABLE_TIMESTAMP), - ShadowType.F_ITERATION, - ShadowType.F_ITERATION_TOKEN, - FocusType.F_LINK_REF, - ShadowType.F_TRIGGER)); - // beware, may return null if there's any problem getting sysconfig (e.g. during initial import) public static SystemConfigurationType getSystemConfiguration(RepositoryService repositoryService, OperationResult result) { @@ -110,19 +64,6 @@ public SystemConfigurationType getSystemConfiguration(OperationResult result) { return getSystemConfiguration(cacheRepositoryService, result); } - public static SecurityPolicyType getSecurityPolicyConfiguration(ObjectReferenceType securityPolicyRef, RepositoryService repositoryService, OperationResult result) { - try { - if (securityPolicyRef == null) { - return null; - } - return repositoryService.getObject(SecurityPolicyType.class, securityPolicyRef.getOid(), - null, result).asObjectable(); - } catch (ObjectNotFoundException|SchemaException e) { - LoggingUtils.logException(LOGGER, "Notification(s) couldn't be processed, because the security policy configuration couldn't be retrieved", e); - return null; - } - } - public static String getResourceNameFromRepo(RepositoryService repositoryService, String oid, OperationResult result) { try { PrismObject resource = repositoryService.getObject(ResourceType.class, oid, null, result); @@ -189,230 +130,4 @@ private ObjectType getObjectFromRepo(String oid, boolean allowNotFound, Operatio } return objectType; } - - public static boolean isAmongHiddenPaths(ItemPath path, List hiddenPaths) { - if (hiddenPaths == null) { - return false; - } - for (ItemPath hiddenPath : hiddenPaths) { - if (hiddenPath.isSubPathOrEquivalent(path)) { - return true; - } - } - return false; - } - - @Override - public String getShadowName(PrismObject shadow) { - if (shadow == null) { - return null; - } else if (shadow.asObjectable().getName() != null) { - return shadow.asObjectable().getName().getOrig(); - } else { - Collection> secondaryIdentifiers = ShadowUtil.getSecondaryIdentifiers(shadow); - LOGGER.trace("secondary identifiers: {}", secondaryIdentifiers); - // first phase = looking for "name" identifier - for (ResourceAttribute ra : secondaryIdentifiers) { - if (ra.getElementName() != null && ra.getElementName().getLocalPart().contains("name")) { - LOGGER.trace("Considering {} as a name", ra); - return String.valueOf(ra.getAnyRealValue()); - } - } - // second phase = returning any value ;) - if (!secondaryIdentifiers.isEmpty()) { - return String.valueOf(secondaryIdentifiers.iterator().next().getAnyRealValue()); - } else { - return null; - } - } - } - - // TODO move to some other class? - public void addRequesterAndChannelInformation(StringBuilder body, Event event, OperationResult result) { - if (event.getRequester() != null) { - body.append("Requester: "); - try { - ObjectType requester = event.getRequester().resolveObjectType(result, false); - if (requester instanceof UserType) { - UserType requesterUser = (UserType) requester; - body.append(requesterUser.getFullName()).append(" (").append(requester.getName()).append(")"); - } else { - body.append(ObjectTypeUtil.toShortString(requester)); - } - } catch (RuntimeException e) { - body.append("couldn't be determined: ").append(e.getMessage()); - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't determine requester for a notification", e); - } - body.append("\n"); - } - body.append("Channel: ").append(event.getChannel()).append("\n\n"); - } - - @Override - public String getPlaintextPasswordFromDelta(ObjectDelta delta) { - try { - return midpointFunctions.getPlaintextAccountPasswordFromDelta(delta); - } catch (EncryptionException e) { - LoggingUtils.logException(LOGGER, "Couldn't decrypt password from shadow delta: {}", e, delta.debugDump()); - return null; - } - } - - @Override - public List getSynchronizationPaths() { - return SYNCHRONIZATION_PATHS; - } - - @Override - public List getAuxiliaryPaths() { - return AUXILIARY_PATHS; - } - - public String getContentAsFormattedList(Event event, boolean showSynchronizationItems, boolean showAuxiliaryAttributes) { - List hiddenPaths = new ArrayList<>(); - if (!showSynchronizationItems) { - hiddenPaths.addAll(SYNCHRONIZATION_PATHS); - } - if (!showAuxiliaryAttributes) { - hiddenPaths.addAll(AUXILIARY_PATHS); - } - - if (event instanceof ResourceObjectEvent) { - final ResourceObjectEvent resourceObjectEvent = (ResourceObjectEvent) event; - final ObjectDelta shadowDelta = resourceObjectEvent.getShadowDelta(); - if (shadowDelta == null) { - return ""; - } - if (shadowDelta.isAdd()) { - return getResourceObjectAttributesAsFormattedList(shadowDelta.getObjectToAdd().asObjectable(), hiddenPaths, showAuxiliaryAttributes); - } else if (shadowDelta.isModify()) { - return getResourceObjectModifiedAttributesAsFormattedList(resourceObjectEvent, shadowDelta, hiddenPaths, showAuxiliaryAttributes); - } else { - return ""; - } - } else if (event instanceof ModelEvent) { - final ModelEvent modelEvent = (ModelEvent) event; - ModelContext modelContext = (ModelContext) modelEvent.getModelContext(); - ModelElementContext focusContext = modelContext.getFocusContext(); - ObjectDelta summarizedDelta; - try { - summarizedDelta = modelEvent.getSummarizedFocusDeltas(); - } catch (SchemaException e) { - LoggingUtils.logUnexpectedException(LOGGER, "Unable to determine the focus change; focus context = {}", e, focusContext.debugDump()); - return("(unable to determine the change because of schema exception: " + e.getMessage() + ")\n"); - } - if (summarizedDelta.isAdd()) { - return textFormatter.formatObject(summarizedDelta.getObjectToAdd(), hiddenPaths, showAuxiliaryAttributes); - } else if (summarizedDelta.isModify()) { - return textFormatter.formatObjectModificationDelta(summarizedDelta, hiddenPaths, showAuxiliaryAttributes, focusContext.getObjectOld(), - focusContext.getObjectNew()); - } else { - return ""; - } - } else { - return ""; - } - } - - private String getResourceObjectAttributesAsFormattedList(ShadowType shadowType, List hiddenAttributes, boolean showAuxiliaryAttributes) { - return textFormatter.formatAccountAttributes(shadowType, hiddenAttributes, false); - } - - private String getResourceObjectModifiedAttributesAsFormattedList(ResourceObjectEvent event, ObjectDelta shadowDelta, - List hiddenPaths, boolean showAuxiliaryAttributes) { - - StringBuilder rv = new StringBuilder(); - if (event.getOperationStatus() != OperationStatus.IN_PROGRESS) { - // todo we do not have objectOld + objectNew, only the current status - // it is used to explain modified containers with identifiers -- however, currently I don't know of use of such containers in shadows, which would be visible in notifications - rv.append(textFormatter.formatObjectModificationDelta(shadowDelta, hiddenPaths, showAuxiliaryAttributes, - event.getAccountOperationDescription().getCurrentShadow(), null)); - } else { - // special case - here the attributes are 'result', 'failedOperationType', 'objectChange', 'attemptNumber' - // we have to unwrap attributes that are to be modified from the objectChange item - Collection> changes = null; - if (shadowDelta.getModifications() != null) { - for (ItemDelta itemDelta : shadowDelta.getModifications()) { - // TODO: get list of changes from pendingOperations -// if (itemDelta.getPath().equivalent(ShadowType.F_OBJECT_CHANGE)) { -// changes = itemDelta.getValuesToAdd() != null && !itemDelta.getValuesToAdd().isEmpty() ? -// itemDelta.getValuesToAdd() : itemDelta.getValuesToReplace(); -// } - } - } - - if (changes != null && !changes.isEmpty()) { - try { - List> deltas = new ArrayList<>(changes.size()); - for (PrismPropertyValue change : changes) { - deltas.add((ObjectDelta) DeltaConvertor.createObjectDelta(change.getValue(), prismContext)); - } - ObjectDelta summarizedDelta = ObjectDeltaCollectionsUtil.summarize(deltas); - rv.append(textFormatter.formatObjectModificationDelta(summarizedDelta, hiddenPaths, showAuxiliaryAttributes, - event.getAccountOperationDescription().getCurrentShadow(), null)); - } catch (SchemaException e) { - LoggingUtils.logUnexpectedException(LOGGER, "Unable to determine the shadow change; operation = {}", e, event.getAccountOperationDescription().debugDump()); - rv.append("(unable to determine the change because of schema exception: ").append(e.getMessage()).append(")\n"); - } - } else { - rv.append("(unable to determine the change)\n"); - } - } - return rv.toString(); - } - - // TODO: polish this method - // We should (probably) return only a value if it has been (successfully) written to the focus. - @Override - public String getFocusPasswordFromEvent(ModelEvent modelEvent) { - if (modelEvent.getFocusDeltas().isEmpty()) { - LOGGER.trace("getFocusPasswordFromEvent: No user deltas in event"); - return null; - } - if (modelEvent.getFocusContext() == null || !modelEvent.getFocusContext().isOfType(FocusType.class)) { - LOGGER.trace("getFocusPasswordFromEvent: Not a FocusType context"); - return null; - } - //noinspection unchecked,rawtypes - String password = getPasswordFromDeltas((List) modelEvent.getFocusDeltas()); - if (password != null) { - LOGGER.trace("getFocusPasswordFromEvent: Found password in user executed delta(s)"); - return password; - } - // in executed deltas - - //noinspection unchecked - ObjectDelta focusPrimaryDelta = (ObjectDelta) modelEvent.getFocusPrimaryDelta(); - //noinspection unchecked - ObjectDelta focusSecondaryDelta = (ObjectDelta) modelEvent.getFocusSecondaryDelta(); - if (focusPrimaryDelta == null && focusSecondaryDelta == null) { - LOGGER.trace("getFocusPasswordFromEvent: No password in executed delta(s) and no primary/secondary deltas"); - return null; - } - if (focusPrimaryDelta != null) { - password = getPasswordFromDeltas(singletonList(focusPrimaryDelta)); - if (password != null) { - LOGGER.trace("getFocusPasswordFromEvent: Found password in user primary delta, continuing"); - return password; - } - } - if (focusSecondaryDelta != null) { - password = getPasswordFromDeltas(singletonList(focusSecondaryDelta)); - if (password != null) { - LOGGER.trace("getFocusPasswordFromEvent: Found password in user secondary delta(s)"); - return password; - } - } - LOGGER.trace("getFocusPasswordFromEvent: No password in executed delta(s) nor in primary/secondary deltas"); - return null; - } - - private String getPasswordFromDeltas(List> deltas) { - try { - return midpointFunctions.getPlaintextUserPasswordFromDeltas(deltas); - } catch (EncryptionException e) { - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't decrypt password from user deltas: {}", e, DebugUtil.debugDump(deltas)); - return null; - } - } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationHook.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationHook.java index d63d0996bcf..0e84c821a06 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationHook.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationHook.java @@ -18,9 +18,12 @@ import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import com.evolveum.midpoint.notifications.api.NotificationManager; -import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.notifications.api.events.PolicyRuleEvent; +import com.evolveum.midpoint.notifications.impl.events.BaseEventImpl; +import com.evolveum.midpoint.notifications.api.events.Event; +import com.evolveum.midpoint.notifications.impl.events.ModelEventImpl; +import com.evolveum.midpoint.notifications.impl.events.PolicyRuleEventImpl; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.DeltaSetTriple; import com.evolveum.midpoint.schema.constants.SchemaConstants; @@ -133,12 +136,13 @@ private void emitModelEvent(@NotNull ModelContext context, @NotNull Task task notificationManager.processEvent(event, task, result); } - private PrismObject getObject(@NotNull ModelContext context) { - PrismObject object = context.getFocusContext().getObjectNew(); - if (object == null) { - object = context.getFocusContext().getObjectOld(); + private PrismObject getObject(@NotNull ModelContext context) { + PrismObject object = context.getFocusContext().getObjectNew(); + if (object != null) { + return object; + } else { + return context.getFocusContext().getObjectOld(); } - return object; } @Override @@ -148,7 +152,7 @@ public void invokeOnException(@NotNull ModelContext context, @NotNull Throwable @NotNull private PolicyRuleEvent createRuleEvent(EvaluatedPolicyRule rule, ModelContext context, Task task) { - PolicyRuleEvent ruleEvent = new PolicyRuleEvent(lightweightIdentifierGenerator, rule); + PolicyRuleEventImpl ruleEvent = new PolicyRuleEventImpl(lightweightIdentifierGenerator, rule); setCommonEventProperties(getObject(context), task, context, ruleEvent); return ruleEvent; } @@ -156,7 +160,7 @@ private PolicyRuleEvent createRuleEvent(EvaluatedPolicyRule rule, ModelContext object, ModelContext modelContext, Task task) { - ModelEvent event = new ModelEvent(lightweightIdentifierGenerator, modelContext); + ModelEventImpl event = new ModelEventImpl(lightweightIdentifierGenerator, modelContext); setCommonEventProperties(object, task, modelContext, event); // TODO is this correct? it's not quite clear how we work with channel info in task / modelContext String channel = task.getChannel(); @@ -169,7 +173,7 @@ private ModelEvent createModelEvent(PrismObject object, ModelContext model private void setCommonEventProperties(PrismObject object, Task task, ModelContext modelContext, Event event) { if (task.getOwner() != null) { - event.setRequester(new SimpleObjectRefImpl(notificationsUtil, task.getOwner().asObjectable())); + ((BaseEventImpl) event).setRequester(new SimpleObjectRefImpl(notificationsUtil, task.getOwner().asObjectable())); } else { LOGGER.debug("No owner for task " + task + ", therefore no requester will be set for event " + event.getId()); } @@ -179,6 +183,6 @@ private void setCommonEventProperties(PrismObject object, Task task, ModelCon object = object.clone(); object.setOid(modelContext.getFocusContext().getOid()); } - event.setRequestee(new SimpleObjectRefImpl(notificationsUtil, (ObjectType) object.asObjectable())); + ((BaseEventImpl) event).setRequestee(new SimpleObjectRefImpl(notificationsUtil, (ObjectType) object.asObjectable())); } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationManagerImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationManagerImpl.java index ac702e307ee..6b248cfe0ce 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationManagerImpl.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationManagerImpl.java @@ -7,17 +7,18 @@ package com.evolveum.midpoint.notifications.impl; -import com.evolveum.midpoint.notifications.api.NotificationFunctions; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.notifications.api.NotificationManager; -import com.evolveum.midpoint.notifications.api.events.BaseEvent; import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.transports.Transport; -import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -25,15 +26,9 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.NotificationConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - /** - * @author mederly + * */ - @Component public class NotificationManagerImpl implements NotificationManager { @@ -44,8 +39,6 @@ public class NotificationManagerImpl implements NotificationManager { @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; - @Autowired private PrismContext prismContext; - @Autowired private NotificationFunctions notificationFunctions; @Autowired private TransportRegistry transportRegistry; @Autowired private EventHandlerRegistry eventHandlerRegistry; @@ -62,11 +55,6 @@ public void processEvent(@NotNull Event event, Task task, OperationResult parent .addArbitraryObjectAsParam("event", event) .build(); try { - if (event instanceof BaseEvent) { - ((BaseEvent) event).setNotificationFunctions(notificationFunctions); - ((BaseEvent) event).setPrismContext(prismContext); - } - LOGGER.trace("NotificationManager processing event:\n{}", event.debugDumpLazily(1)); if (event.getAdHocHandler() != null) { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationTaskListener.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationTaskListener.java index 404abef6588..38d748c0639 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationTaskListener.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationTaskListener.java @@ -8,7 +8,7 @@ package com.evolveum.midpoint.notifications.impl; import com.evolveum.midpoint.notifications.api.NotificationManager; -import com.evolveum.midpoint.notifications.api.events.TaskEvent; +import com.evolveum.midpoint.notifications.impl.events.TaskEventImpl; import com.evolveum.midpoint.task.api.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -22,8 +22,6 @@ * One of interfaces of the notifier to midPoint. * * Used to catch task-related events. - * - * @author mederly */ @Component public class NotificationTaskListener implements TaskListener { @@ -60,7 +58,7 @@ public void onTaskFinish(Task task, TaskRunResult runResult) { } private void createAndProcessEvent(Task task, TaskRunResult runResult, EventOperationType operationType) { - TaskEvent event = new TaskEvent(lightweightIdentifierGenerator, task, runResult, operationType, task.getChannel()); + TaskEventImpl event = new TaskEventImpl(lightweightIdentifierGenerator, task, runResult, operationType, task.getChannel()); if (task.getOwner() != null) { event.setRequester(new SimpleObjectRefImpl(notificationsUtil, task.getOwner().asObjectable())); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/SimpleObjectRefImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/SimpleObjectRefImpl.java index fe3011ed088..7c08c45abb4 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/SimpleObjectRefImpl.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/SimpleObjectRefImpl.java @@ -17,8 +17,6 @@ /** * TODO change to ObjectReferenceType - * - * @author mederly */ public class SimpleObjectRefImpl implements SimpleObjectRef { @@ -26,13 +24,13 @@ public class SimpleObjectRefImpl implements SimpleObjectRef { private ObjectType objectType; private NotificationFunctionsImpl functions; // used to resolve object refs - public SimpleObjectRefImpl(NotificationFunctionsImpl functions, ObjectType objectType) { + SimpleObjectRefImpl(NotificationFunctionsImpl functions, ObjectType objectType) { this.oid = objectType.getOid(); this.objectType = objectType; this.functions = functions; } - public SimpleObjectRefImpl(NotificationFunctionsImpl functions, PrismObject object) { + public SimpleObjectRefImpl(NotificationFunctionsImpl functions, PrismObject object) { this.oid = object.getOid(); this.objectType = (ObjectType) object.asObjectable(); this.functions = functions; @@ -47,11 +45,6 @@ public SimpleObjectRefImpl(NotificationFunctionsImpl functions, ObjectReferenceT this.functions = functions; } - public SimpleObjectRefImpl(NotificationFunctionsImpl functions, String oid) { - this.oid = oid; - this.functions = functions; - } - public String getOid() { return oid; } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListenerImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListenerImpl.java index 38abff29eab..ebc9ff4b795 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListenerImpl.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListenerImpl.java @@ -9,6 +9,8 @@ import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.events.*; +import com.evolveum.midpoint.notifications.impl.events.*; +import com.evolveum.midpoint.notifications.impl.util.EventHelper; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; @@ -45,6 +47,7 @@ public class WorkflowListenerImpl implements WorkflowListener { @Autowired private NotificationManager notificationManager; @Autowired private NotificationFunctionsImpl functions; @Autowired private LightweightIdentifierGenerator identifierGenerator; + @Autowired private EventHelper eventHelper; // 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. @@ -63,16 +66,16 @@ public void init() { //region Process-level notifications @Override public void onProcessInstanceStart(CaseType aCase, Task task, OperationResult result) { - WorkflowProcessEvent event = new WorkflowProcessEvent(identifierGenerator, ChangeType.ADD, aCase); + WorkflowProcessEventImpl event = new WorkflowProcessEventImpl(identifierGenerator, ChangeType.ADD, aCase); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } @Override public void onProcessInstanceEnd(CaseType aCase, Task task, OperationResult result) { - WorkflowProcessEvent event = new WorkflowProcessEvent(identifierGenerator, ChangeType.DELETE, aCase); + WorkflowProcessEventImpl event = new WorkflowProcessEventImpl(identifierGenerator, ChangeType.DELETE, aCase); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } //endregion @@ -80,34 +83,34 @@ public void onProcessInstanceEnd(CaseType aCase, Task task, OperationResult resu @Override public void onWorkItemCreation(ObjectReferenceType assignee, @NotNull CaseWorkItemType workItem, CaseType aCase, Task task, OperationResult result) { - WorkItemEvent event = new WorkItemLifecycleEvent(identifierGenerator, ChangeType.ADD, workItem, + WorkItemEventImpl event = new WorkItemLifecycleEventImpl(identifierGenerator, ChangeType.ADD, workItem, SimpleObjectRefImpl.create(functions, assignee), null, null, null, aCase.getApprovalContext(), aCase); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } @Override public void onWorkItemDeletion(ObjectReferenceType assignee, @NotNull CaseWorkItemType workItem, WorkItemOperationInfo operationInfo, WorkItemOperationSourceInfo sourceInfo, CaseType aCase, Task task, OperationResult result) { - WorkItemEvent event = new WorkItemLifecycleEvent(identifierGenerator, ChangeType.DELETE, workItem, + WorkItemEventImpl event = new WorkItemLifecycleEventImpl(identifierGenerator, ChangeType.DELETE, workItem, SimpleObjectRefImpl.create(functions, assignee), getInitiator(sourceInfo), operationInfo, sourceInfo, aCase.getApprovalContext(), aCase); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } @Override public void onWorkItemCustomEvent(ObjectReferenceType assignee, @NotNull CaseWorkItemType workItem, @NotNull WorkItemNotificationActionType notificationAction, WorkItemEventCauseInformationType cause, CaseType aCase, Task task, OperationResult result) { - WorkItemEvent event = new WorkItemCustomEvent(identifierGenerator, ChangeType.ADD, workItem, + WorkItemEventImpl event = new WorkItemCustomEventImpl(identifierGenerator, ChangeType.ADD, workItem, SimpleObjectRefImpl.create(functions, assignee), new WorkItemOperationSourceInfo(null, cause, notificationAction), aCase.getApprovalContext(), aCase, notificationAction.getHandler()); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } @Override @@ -143,12 +146,12 @@ private void checkOids(List refs) { private void onWorkItemAllocationAdd(ObjectReferenceType newActor, @NotNull CaseWorkItemType workItem, @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, CaseType aCase, Task task, OperationResult result) { - WorkItemAllocationEvent event = new WorkItemAllocationEvent(identifierGenerator, ChangeType.ADD, workItem, + WorkItemAllocationEventImpl event = new WorkItemAllocationEventImpl(identifierGenerator, ChangeType.ADD, workItem, SimpleObjectRefImpl.create(functions, newActor), getInitiator(sourceInfo), operationInfo, sourceInfo, aCase.getApprovalContext(), aCase, null); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } private SimpleObjectRef getInitiator(WorkItemOperationSourceInfo sourceInfo) { @@ -160,32 +163,17 @@ private void onWorkItemAllocationModifyDelete(ObjectReferenceType currentActor, @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, Duration timeBefore, CaseType aCase, Task task, OperationResult result) { - WorkItemAllocationEvent event = new WorkItemAllocationEvent(identifierGenerator, + WorkItemAllocationEventImpl event = new WorkItemAllocationEventImpl(identifierGenerator, timeBefore != null ? ChangeType.MODIFY : ChangeType.DELETE, workItem, SimpleObjectRefImpl.create(functions, currentActor), getInitiator(sourceInfo), operationInfo, sourceInfo, aCase.getApprovalContext(), aCase, timeBefore); initializeWorkflowEvent(event, aCase); - processEvent(event, task, result); + eventHelper.processEvent(event, task, result); } //endregion - private void processEvent(WorkflowEvent event, Task task, OperationResult result) { - try { - notificationManager.processEvent(event, task, result); - } 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, CaseType aCase) { + private void initializeWorkflowEvent(WorkflowEventImpl event, CaseType aCase) { event.setRequester(SimpleObjectRefImpl.create(functions, aCase.getRequestorRef())); event.setRequestee(SimpleObjectRefImpl.create(functions, aCase.getObjectRef())); // TODO what if requestee is yet to be created? diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/AccessCertificationEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/AccessCertificationEventImpl.java new file mode 100644 index 00000000000..a604dd7ac25 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/AccessCertificationEventImpl.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.AccessCertificationEvent; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.result.OperationResultStatus; +import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationStageDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; +import org.jetbrains.annotations.NotNull; + +import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.*; + +public abstract class AccessCertificationEventImpl extends BaseEventImpl implements AccessCertificationEvent { + + @NotNull final protected AccessCertificationCampaignType campaign; + @NotNull final protected OperationResultStatus status; + @NotNull final private EventOperationType operationType; + + AccessCertificationEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, + @NotNull AccessCertificationCampaignType campaign, @NotNull EventOperationType opType) { + super(lightweightIdentifierGenerator); + this.campaign = campaign; + this.operationType = opType; + this.status = OperationResultStatus.SUCCESS; // TODO fix this temporary implementation + } + + @Override + @NotNull + public AccessCertificationCampaignType getCampaign() { + return campaign; + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return false; // not supported for this kind of events + } + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + return false; + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + return this.operationType.equals(eventOperation); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return EventCategoryType.ACCESS_CERTIFICATION_EVENT.equals(eventCategory); + } + + @NotNull + public OperationResultStatus getStatus() { + return status; + } + + @NotNull + public EventOperationType getOperationType() { + return operationType; + } + + public AccessCertificationStageDefinitionType getCurrentStageDefinition() { + if (campaign.getState() != IN_REVIEW_STAGE && campaign.getState() != REVIEW_STAGE_DONE) { + return null; + } + return CertCampaignTypeUtil.findStageDefinition(campaign, campaign.getStageNumber()); + } + + @Override + protected void debugDumpCommon(StringBuilder sb, int indent) { + super.debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelToStringLn(sb, "campaign", campaign, indent + 1); + DebugUtil.debugDumpWithLabelToStringLn(sb, "status", status, indent + 1); + DebugUtil.debugDumpWithLabelToStringLn(sb, "operationType", operationType, indent + 1); + } +} diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/BaseEventImpl.java similarity index 67% rename from model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java rename to model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/BaseEventImpl.java index ce44e67c0b7..4fed540b4e4 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/BaseEventImpl.java @@ -1,13 +1,17 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.midpoint.notifications.api.events; +package com.evolveum.midpoint.notifications.impl.events; -import com.evolveum.midpoint.notifications.api.NotificationFunctions; +import com.evolveum.midpoint.model.api.expr.MidpointFunctions; +import com.evolveum.midpoint.notifications.api.events.Event; +import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; +import com.evolveum.midpoint.notifications.impl.formatters.TextFormatter; +import com.evolveum.midpoint.notifications.impl.util.ApplicationContextHolder; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ItemDelta; @@ -31,41 +35,41 @@ import java.util.List; /** - * @author mederly + * Base implementation of Event that contains the common functionality. */ -public abstract class BaseEvent implements Event, DebugDumpable, ShortDumpable { +public abstract class BaseEventImpl implements Event, DebugDumpable, ShortDumpable { - private LightweightIdentifier id; // randomly generated event ID - private SimpleObjectRef requester; // who requested this operation (null if unknown) + @NotNull private final LightweightIdentifier id; + + private SimpleObjectRef requester; + + private SimpleObjectRef requestee; /** - * If needed, we can prescribe the handler that should process this event. It is recommended only for ad-hoc situations. + * If needed, we can prescribe the handler that should process this event, in addition to the handlers + * defined by the system configuration. + * + * It is recommended only for specific (ad-hoc) situations. * A better is to define handlers in system configuration. */ - protected final EventHandlerType adHocHandler; + private final EventHandlerType adHocHandler; - private transient NotificationFunctions notificationFunctions; // needs not be set when creating an event ... it is set in NotificationManager + private transient MidpointFunctions midpointFunctions; + private transient TextFormatter textFormatter; private transient PrismContext prismContext; - // about who is this operation (null if unknown); - // - for model notifications, this is the focus, (usually a user but may be e.g. role or other kind of object) - // - for account notifications, this is the account owner, - // - for workflow notifications, this is the workflow process instance object - // - for certification notifications, this is the campaign owner or reviewer (depending on the kind of event) - - private SimpleObjectRef requestee; - private String channel; - public BaseEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator) { + BaseEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator) { this(lightweightIdentifierGenerator, null); } - public BaseEvent(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, EventHandlerType adHocHandler) { + BaseEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, EventHandlerType adHocHandler) { id = lightweightIdentifierGenerator.generate(); this.adHocHandler = adHocHandler; } + @NotNull public LightweightIdentifier getId() { return id; } @@ -79,115 +83,62 @@ public String toString() { '}'; } - abstract public boolean isStatusType(EventStatusType eventStatusType); - abstract public boolean isOperationType(EventOperationType eventOperationType); - abstract public boolean isCategoryType(EventCategoryType eventCategoryType); + abstract public boolean isStatusType(EventStatusType eventStatus); + abstract public boolean isOperationType(EventOperationType eventOperation); - public boolean isAccountRelated() { - return isCategoryType(EventCategoryType.RESOURCE_OBJECT_EVENT); + boolean changeTypeMatchesOperationType(ChangeType changeType, EventOperationType eventOperationType) { + switch (eventOperationType) { + case ADD: return changeType == ChangeType.ADD; + case MODIFY: return changeType == ChangeType.MODIFY; + case DELETE: return changeType == ChangeType.DELETE; + default: throw new IllegalStateException("Unexpected EventOperationType: " + eventOperationType); + } } + abstract public boolean isCategoryType(EventCategoryType eventCategory); + public boolean isUserRelated() { return false; // overriden in ModelEvent } - public boolean isWorkItemRelated() { - return isCategoryType(EventCategoryType.WORK_ITEM_EVENT); - } - - public boolean isWorkflowProcessRelated() { - return isCategoryType(EventCategoryType.WORKFLOW_PROCESS_EVENT); - } - - public boolean isWorkflowRelated() { - return isCategoryType(EventCategoryType.WORKFLOW_EVENT); - } - - @Override - public boolean isPolicyRuleRelated() { - return isCategoryType(EventCategoryType.POLICY_RULE_EVENT); - } - - public boolean isCertCampaignStageRelated() { - return isCategoryType(EventCategoryType.CERT_CAMPAIGN_STAGE_EVENT); - } - - public boolean isAdd() { - return isOperationType(EventOperationType.ADD); - } - - public boolean isModify() { - return isOperationType(EventOperationType.MODIFY); - } - - public boolean isDelete() { - return isOperationType(EventOperationType.DELETE); - } - - public boolean isSuccess() { - return isStatusType(EventStatusType.SUCCESS); - } - - public boolean isAlsoSuccess() { - return isStatusType(EventStatusType.ALSO_SUCCESS); - } - - public boolean isFailure() { - return isStatusType(EventStatusType.FAILURE); - } - - public boolean isOnlyFailure() { - return isStatusType(EventStatusType.ONLY_FAILURE); - } - - public boolean isInProgress() { - return isStatusType(EventStatusType.IN_PROGRESS); - } - - // requester - public SimpleObjectRef getRequester() { return requester; } - public String getRequesterOid() { - return requester.getOid(); - } - + // TODO make requester final and remove this method public void setRequester(SimpleObjectRef requester) { this.requester = requester; } - // requestee - public SimpleObjectRef getRequestee() { return requestee; } - public String getRequesteeOid() { - return requestee.getOid(); - } - + // TODO we need the operation result parent here @Nullable private ObjectType resolveObject(SimpleObjectRef ref) { if (ref == null) { return null; } - return ref.resolveObjectType(new OperationResult(BaseEvent.class + ".resolveObject"), true); + return ref.resolveObjectType(new OperationResult(BaseEventImpl.class + ".resolveObject"), true); } + @Override public ObjectType getRequesteeObject() { return resolveObject(requestee); } + @SuppressWarnings("WeakerAccess") public ObjectType getRequesterObject() { return resolveObject(requester); } + @Override public PolyStringType getRequesteeDisplayName() { return getDisplayName(getRequesteeObject()); } + @SuppressWarnings("unused") public PolyStringType getRequesterDisplayName() { return getDisplayName(getRequesterObject()); } @@ -211,10 +162,12 @@ private PolyStringType getName(ObjectType object) { return object != null ? object.getName() : null; } + @SuppressWarnings("unused") public PolyStringType getRequesteeName() { return getName(getRequesteeObject()); } + @SuppressWarnings("unused") public PolyStringType getRequesterName() { return getName(getRequesterObject()); } @@ -223,15 +176,6 @@ public void setRequestee(SimpleObjectRef requestee) { this.requestee = requestee; } - boolean changeTypeMatchesOperationType(ChangeType changeType, EventOperationType eventOperationType) { - switch (eventOperationType) { - case ADD: return changeType == ChangeType.ADD; - case MODIFY: return changeType == ChangeType.MODIFY; - case DELETE: return changeType == ChangeType.DELETE; - default: throw new IllegalStateException("Unexpected EventOperationType: " + eventOperationType); - } - } - public void createExpressionVariables(VariablesMap variables, OperationResult result) { variables.put(ExpressionConstants.VAR_EVENT, this, Event.class); variables.put(ExpressionConstants.VAR_REQUESTER, resolveTypedObject(requester, false, result)); @@ -243,27 +187,29 @@ TypedValue resolveTypedObject(SimpleObjectRef ref, boolean allowNotF if (resolved != null) { return new TypedValue<>(resolved, resolved.asPrismObject().getDefinition()); } else { - PrismObjectDefinition def = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(ObjectType.class); + PrismObjectDefinition def = getPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(ObjectType.class); return new TypedValue<>(null, def); } } // Finding items in deltas/objects - // this is similar to delta.hasItemDelta but much, much more relaxed (we completely ignore ID path segments and we take subpaths into account) + // this is similar to delta.hasItemDelta but much, much more relaxed (we completely ignore ID path segments and we take sub-paths into account) // // Very experimental implementation. Needs a bit of time to clean up and test adequately. - public boolean containsItem(ObjectDelta delta, ItemPath itemPath) { + @SuppressWarnings("WeakerAccess") + public boolean containsItem(ObjectDelta delta, ItemPath itemPath) { if (delta.getChangeType() == ChangeType.ADD) { return containsItem(delta.getObjectToAdd(), itemPath); - } else if (delta.getChangeType() == ChangeType.MODIFY) { + } else //noinspection SimplifiableIfStatement + if (delta.getChangeType() == ChangeType.MODIFY) { return containsItemInModifications(delta.getModifications(), itemPath); } else { return false; } } - private boolean containsItemInModifications(Collection modifications, ItemPath itemPath) { - for (ItemDelta itemDelta : modifications) { + private boolean containsItemInModifications(Collection> modifications, ItemPath itemPath) { + for (ItemDelta itemDelta : modifications) { if (containsItem(itemDelta, itemPath)) { return true; } @@ -271,7 +217,7 @@ private boolean containsItemInModifications(Collection modi return false; } - private boolean containsItem(ItemDelta itemDelta, ItemPath itemPath) { + private boolean containsItem(ItemDelta itemDelta, ItemPath itemPath) { ItemPath namesOnlyPathTested = itemPath.namedSegmentsOnly(); ItemPath namesOnlyPathInDelta = itemDelta.getPath().namedSegmentsOnly(); if (namesOnlyPathTested.isSubPathOrEquivalent(namesOnlyPathInDelta)) { @@ -294,13 +240,13 @@ private boolean containsItem(ItemDelta itemDelta, ItemPath itemPath) { } // remainder contains only named segments and is not empty - private boolean containsItemInValues(Collection values, ItemPath remainder) { + private boolean containsItemInValues(Collection values, ItemPath remainder) { if (values == null) { return false; } - for (PrismValue value : values) { + for (Object value : values) { if (value instanceof PrismContainerValue) { // we do not want to look inside references nor primitive values - if (containsItem((PrismContainerValue) value, remainder)) { + if (containsItem((PrismContainerValue) value, remainder)) { return true; } } @@ -309,7 +255,7 @@ private boolean containsItemInValues(Collection values, ItemPath rem } boolean containsItem(List> deltas, ItemPath itemPath) { - for (ObjectDelta objectDelta : deltas) { + for (ObjectDelta objectDelta : deltas) { if (containsItem(objectDelta, itemPath)) { return true; } @@ -318,7 +264,7 @@ boolean containsItem(List> deltas, ItemPath it } // itemPath is empty or starts with named item path segment - private boolean containsItem(PrismContainer container, ItemPath itemPath) { + private boolean containsItem(PrismContainer container, ItemPath itemPath) { if (container.size() == 0) { return false; // there is a container, but no values } @@ -326,7 +272,7 @@ private boolean containsItem(PrismContainer container, ItemPath itemPath) { return true; } for (Object o : container.getValues()) { - if (containsItem((PrismContainerValue) o, itemPath)) { + if (containsItem((PrismContainerValue) o, itemPath)) { return true; } } @@ -334,18 +280,19 @@ private boolean containsItem(PrismContainer container, ItemPath itemPath) { } // path starts with named item path segment - private boolean containsItem(PrismContainerValue prismContainerValue, ItemPath itemPath) { + private boolean containsItem(PrismContainerValue prismContainerValue, ItemPath itemPath) { ItemName first = ItemPath.toName(itemPath.first()); - Item item = prismContainerValue.findItem(first); + Item item = prismContainerValue.findItem(first); if (item == null) { return false; } ItemPath pathTail = stripFirstIds(itemPath); if (item instanceof PrismContainer) { - return containsItem((PrismContainer) item, pathTail); + return containsItem((PrismContainer) item, pathTail); } else if (item instanceof PrismReference) { return pathTail.isEmpty(); // we do not want to look inside references - } else if (item instanceof PrismProperty) { + } else //noinspection SimplifiableIfStatement + if (item instanceof PrismProperty) { return pathTail.isEmpty(); // ...neither inside atomic values } else { return false; // should not occur anyway @@ -368,22 +315,7 @@ public void setChannel(String channel) { this.channel = channel; } - public NotificationFunctions getNotificationFunctions() { - return notificationFunctions; - } - - public void setNotificationFunctions(NotificationFunctions notificationFunctions) { - this.notificationFunctions = notificationFunctions; - } - - public PrismContext getPrismContext() { - return prismContext; - } - - public void setPrismContext(PrismContext prismContext) { - this.prismContext = prismContext; - } - + @Override public String getStatusAsText() { if (isSuccess()) { return "SUCCESS"; @@ -413,4 +345,25 @@ protected void debugDumpCommon(StringBuilder sb, int indent) { public void shortDump(StringBuilder sb) { sb.append(this.getClass().getSimpleName()).append("(").append(getId()).append(")"); } + + MidpointFunctions getMidpointFunctions() { + if (midpointFunctions == null) { + midpointFunctions = ApplicationContextHolder.getBean(MidpointFunctions.class); + } + return midpointFunctions; + } + + PrismContext getPrismContext() { + if (prismContext == null) { + prismContext = ApplicationContextHolder.getBean(PrismContext.class); + } + return prismContext; + } + + TextFormatter getTextFormatter() { + if (textFormatter == null) { + textFormatter = ApplicationContextHolder.getBean(TextFormatter.class); + } + return textFormatter; + } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignEventImpl.java new file mode 100644 index 00000000000..e32c508e5ed --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignEventImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.CertCampaignEvent; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; + +/** + * Event related to certification campaign. + * ADD = campaign started + * MODIFY = some state change was done (covered in detail via CertCampaignStageEvent) + * DELETE = campaign closed (_not_ deleted; that is not supported via this kind of events) + */ +public class CertCampaignEventImpl extends AccessCertificationEventImpl implements CertCampaignEvent { + + public CertCampaignEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, AccessCertificationCampaignType campaign, EventOperationType opType) { + super(lightweightIdentifierGenerator, campaign, opType); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return super.isCategoryType(eventCategory) || + EventCategoryType.CERT_CAMPAIGN_EVENT.equals(eventCategory); + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignStageEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignStageEventImpl.java new file mode 100644 index 00000000000..b5f9eaa036c --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertCampaignStageEventImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.CertCampaignStageEvent; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; + +/** + * Event related to certification campaign stage. + * ADD = stage opened (including remediation stage) + * MODIFY = stage deadline is approaching + * DELETE = stage closed + * + * @author mederly + */ +public class CertCampaignStageEventImpl extends AccessCertificationEventImpl implements CertCampaignStageEvent { + + public CertCampaignStageEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, AccessCertificationCampaignType campaign, EventOperationType opType) { + super(lightweightIdentifierGenerator, campaign, opType); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return super.isCategoryType(eventCategory) || + EventCategoryType.CERT_CAMPAIGN_STAGE_EVENT.equals(eventCategory); + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + return sb.toString(); + } + +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertReviewEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertReviewEventImpl.java new file mode 100644 index 00000000000..980ee3e78fb --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CertReviewEventImpl.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.CertReviewEvent; +import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.schema.util.WorkItemTypeUtil; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + + +/** + * Event related to given certification case. + * ADD = case created (first stage) + * MODIFY = stage deadline is approaching + * DELETE = stage closed + * + * @author mederly + */ +public class CertReviewEventImpl extends AccessCertificationEventImpl implements CertReviewEvent { + + private List cases; + /** + * Actual reviewer - the person which the work item is assigned to. I.e. _not_ his deputy. + * Must be set to non-null value just after instantiation (TODO do more cleanly) + */ + private SimpleObjectRef actualReviewer; + + public CertReviewEventImpl(LightweightIdentifierGenerator idGenerator, List cases, + AccessCertificationCampaignType campaign, EventOperationType opType) { + super(idGenerator, campaign, opType); + this.cases = cases; + } + + @Override + public SimpleObjectRef getActualReviewer() { + return actualReviewer; + } + + public void setActualReviewer(SimpleObjectRef actualReviewer) { + this.actualReviewer = actualReviewer; + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return super.isCategoryType(eventCategory) || + EventCategoryType.CERT_CASE_EVENT.equals(eventCategory); + } + + @NotNull + @Override + public Collection getCasesAwaitingResponseFromActualReviewer() { + List rv = new ArrayList<>(); + for (AccessCertificationCaseType aCase : cases) { + if (awaitsResponseFrom(aCase, actualReviewer.getOid(), campaign.getStageNumber())) { + rv.add(aCase); + } + } + return rv; + } + + private boolean awaitsResponseFrom(AccessCertificationCaseType aCase, String reviewerOid, int currentStageNumber) { + for (AccessCertificationWorkItemType workItem : aCase.getWorkItem()) { + if (workItem.getStageNumber() == currentStageNumber + && WorkItemTypeUtil.getOutcome(workItem) == null + && workItem.getCloseTimestamp() == null + && ObjectTypeUtil.containsOid(workItem.getAssigneeRef(), reviewerOid)) { + return true; + } + } + return false; + } + + @Override + public List getCases() { + return cases; + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelLn(sb, "cases", cases, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CustomEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CustomEventImpl.java new file mode 100644 index 00000000000..76f023b1942 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/CustomEventImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.evolveum.midpoint.notifications.api.events.CustomEvent; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +public class CustomEventImpl extends BaseEventImpl implements CustomEvent { + + private final String subtype; + /** + * Any object, e.g. PrismObject, any Item, any PrismValue, any real value. It can be even null. + */ + private final Object object; + @NotNull private final EventStatusType status; + @NotNull private final EventOperationType operationType; + + public CustomEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, @Nullable String subtype, @Nullable EventHandlerType adHocHandler, + @Nullable Object object, @NotNull EventOperationType operationType, @NotNull EventStatusType status, String channel) { + super(lightweightIdentifierGenerator, adHocHandler); + this.subtype = subtype; + this.object = object; + this.status = status; + this.operationType = operationType; + setChannel(channel); + } + + @Override + @NotNull + public EventOperationType getOperationType() { + return operationType; + } + + @Override + @NotNull + public EventStatusType getStatus() { + return status; + } + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + return status == eventStatus; + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + return this.operationType == eventOperation; + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.CUSTOM_EVENT; + } + + @Override + @Nullable + public String getSubtype() { + return subtype; + } + + @Override + @Nullable + public Object getObject() { + return object; + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + // TODO implement if needed + return false; + } + + @Override + public boolean isUserRelated() { + if (object instanceof UserType) { + return true; + } else if (object instanceof PrismObject) { + PrismObject prismObject = (PrismObject) object; + return prismObject.getCompileTimeClass() != null && UserType.class.isAssignableFrom(prismObject.getCompileTimeClass()); + } else { + return false; + } + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabel(sb, "subtype", subtype, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ModelEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ModelEventImpl.java new file mode 100644 index 00000000000..bb838da4bc1 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ModelEventImpl.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.model.api.context.ModelContext; +import com.evolveum.midpoint.model.api.context.ModelElementContext; +import com.evolveum.midpoint.model.api.context.ModelProjectionContext; +import com.evolveum.midpoint.notifications.api.events.ModelEvent; +import com.evolveum.midpoint.prism.PrismContainerDefinition; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.ObjectDeltaCollectionsUtil; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.ObjectDeltaOperation; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +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.xml.ns._public.common.common_3.*; + +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.singletonList; + +public class ModelEventImpl extends BaseEventImpl implements ModelEvent { + + private static final Trace LOGGER = TraceManager.getTrace(ModelEventImpl.class); + + @NotNull private final ModelContext modelContext; + @NotNull private final ModelElementContext focusContext; + + public ModelEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ModelContext modelContext) { + super(lightweightIdentifierGenerator); + this.modelContext = modelContext; + this.focusContext = modelContext.getFocusContext(); + } + + @NotNull + @Override + public ModelContext getModelContext() { + return modelContext; + } + + @NotNull + @Override + public ModelElementContext getFocusContext() { + return focusContext; + } + + @Override + public Collection getProjectionContexts() { + return modelContext.getProjectionContexts(); + } + + @Override + public List getFocusExecutedDeltas() { + return getFocusContext().getExecutedDeltas(); + } + + @Override + public List getAllExecutedDeltas() { + List retval = new ArrayList<>(focusContext.getExecutedDeltas()); + for (Object o : modelContext.getProjectionContexts()) { + ModelProjectionContext modelProjectionContext = (ModelProjectionContext) o; + retval.addAll(modelProjectionContext.getExecutedDeltas()); + } + return retval; + } + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + boolean allSuccess = true, anySuccess = false, allFailure = true, anyFailure = false, anyInProgress = false; + for (ObjectDeltaOperation objectDeltaOperation : getAllExecutedDeltas()) { + if (objectDeltaOperation.getExecutionResult() != null) { + switch (objectDeltaOperation.getExecutionResult().getStatus()) { + case SUCCESS: + case WARNING: + case HANDLED_ERROR: + anySuccess = true; allFailure = false; + break; + case FATAL_ERROR: + case PARTIAL_ERROR: + allSuccess = false; anyFailure = true; + break; + case IN_PROGRESS: + allSuccess = false; allFailure = false; anyInProgress = true; + break; + case NOT_APPLICABLE: + break; + case UNKNOWN: + allSuccess = false; allFailure = false; + break; + default: LOGGER.warn("Unknown execution result: {}", objectDeltaOperation.getExecutionResult().getStatus()); + } + } else { + allSuccess = false; + allFailure = false; + anyInProgress = true; + } + } + + switch (eventStatus) { + case ALSO_SUCCESS: return anySuccess; + case SUCCESS: return allSuccess; + case FAILURE: return anyFailure; + case ONLY_FAILURE: return allFailure; + case IN_PROGRESS: return anyInProgress; + default: throw new IllegalStateException("Invalid eventStatusType: " + eventStatus); + } + } + + // a bit of hack but ... + @Override + public ChangeType getChangeType() { + if (isOperationType(EventOperationType.ADD)) { + return ChangeType.ADD; + } else if (isOperationType(EventOperationType.DELETE)) { + return ChangeType.DELETE; + } else { + return ChangeType.MODIFY; + } + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + + // we consider an operation to be 'add' when there is 'add' delta among deltas + // in a similar way with 'delete' + // + // alternatively, we could summarize deltas and then decide based on the type of summarized delta (would be a bit inefficient) + + for (Object o : getFocusExecutedDeltas()) { + ObjectDeltaOperation objectDeltaOperation = (ObjectDeltaOperation) o; + if (objectDeltaOperation.getObjectDelta().isAdd()) { + return eventOperation == EventOperationType.ADD; + } else if (objectDeltaOperation.getObjectDelta().isDelete()) { + return eventOperation == EventOperationType.DELETE; + } + } + return eventOperation == EventOperationType.MODIFY; + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.MODEL_EVENT; + } + + @Override + public ObjectDelta getFocusPrimaryDelta() { + return focusContext.getPrimaryDelta(); + } + + @Override + public ObjectDelta getFocusSecondaryDelta() { + return focusContext.getSecondaryDelta(); + } + + @Override + public List> getFocusDeltas() { + List> retval = new ArrayList<>(); + Class c = modelContext.getFocusClass(); + if (c != null && AssignmentHolderType.class.isAssignableFrom(c)) { + for (Object o : getFocusExecutedDeltas()) { + ObjectDeltaOperation objectDeltaOperation = (ObjectDeltaOperation) o; + //noinspection unchecked + retval.add(objectDeltaOperation.getObjectDelta()); + } + } + return retval; + } + + @Override + public ObjectDelta getSummarizedFocusDeltas() throws SchemaException { + return ObjectDeltaCollectionsUtil.summarize(getFocusDeltas()); + } + + @Override + public boolean hasFocusOfType(Class clazz) { + return focusContext.isOfType(clazz); + } + + @Override + public boolean hasFocusOfType(QName focusType) { + PrismContainerDefinition pcd = + getPrismContext().getSchemaRegistry().findContainerDefinitionByType(focusType); + if (pcd != null) { + Class expectedClass = pcd.getCompileTimeClass(); + if (expectedClass != null) { + return hasFocusOfType(expectedClass); + } else { + LOGGER.warn("Couldn't find class for type {}", focusType); + return false; + } + } else { + LOGGER.warn("Couldn't find definition for type {}", focusType); + return false; + } + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return containsItem(getFocusDeltas(), itemPath); + } + + @Override + public boolean isUserRelated() { + return hasFocusOfType(UserType.class); + } + + @Override + public String getFocusTypeName() { + if (focusContext.getObjectTypeClass() == null) { + return null; + } else { + String simpleName = getFocusContext().getObjectTypeClass().getSimpleName(); + return StringUtils.substringBeforeLast(simpleName, "Type"); // should usually work ;) + } + } + + @Override + public boolean hasContentToShow(boolean watchAuxiliaryAttributes) { + ObjectDelta summarizedDelta; + try { + summarizedDelta = getSummarizedFocusDeltas(); + if (!summarizedDelta.isModify()) { + return true; + } else if (!getTextFormatter().containsVisibleModifiedItems(summarizedDelta.getModifications(), + false, watchAuxiliaryAttributes)) { + LOGGER.trace("No relevant attributes in modify delta (watchAux={})", watchAuxiliaryAttributes); + return false; + } else { + return true; + } + } catch (Throwable t) { + LoggingUtils.logUnexpectedException(LOGGER, "Unable to check if there's content to show; focus context = {}", t, focusContext.debugDump()); + return false; + } + } + + @Override + public String getContentAsFormattedList(boolean showAuxiliaryAttributes) { + try { + ObjectDelta summarizedDelta = getSummarizedFocusDeltas(); + if (summarizedDelta.isAdd()) { + return getTextFormatter().formatObject(summarizedDelta.getObjectToAdd(), false, showAuxiliaryAttributes); + } else if (summarizedDelta.isModify()) { + ModelElementContext focusContext = modelContext.getFocusContext(); + return getTextFormatter().formatObjectModificationDelta(summarizedDelta, false, showAuxiliaryAttributes, focusContext.getObjectOld(), + focusContext.getObjectNew()); + } else { + return ""; + } + } catch (Throwable t) { + LoggingUtils.logUnexpectedException(LOGGER, "Unable to determine the focus change; focus context = {}", t, focusContext.debugDump()); + return("(unable to determine the change because of schema exception: " + t.getMessage() + ")\n"); + } + } + + public String getFocusPassword() { + List> focusDeltas = getFocusDeltas(); + if (focusDeltas.isEmpty()) { + LOGGER.trace("getFocusPasswordFromEvent: No user deltas in event"); + return null; + } + if (!hasFocusOfType(FocusType.class)) { + LOGGER.trace("getFocusPasswordFromEvent: Not a FocusType context"); + return null; + } + //noinspection unchecked,rawtypes + String passwordFromDeltas = getPasswordFromDeltas((List) focusDeltas); + if (passwordFromDeltas != null) { + LOGGER.trace("getFocusPasswordFromEvent: Found password in user executed delta(s)"); + return passwordFromDeltas; + } + + //noinspection unchecked + ObjectDelta focusPrimaryDelta = (ObjectDelta) getFocusPrimaryDelta(); + //noinspection unchecked + ObjectDelta focusSecondaryDelta = (ObjectDelta) getFocusSecondaryDelta(); + if (focusPrimaryDelta == null && focusSecondaryDelta == null) { + LOGGER.trace("getFocusPasswordFromEvent: No password in executed delta(s) and no primary/secondary deltas"); + return null; + } + if (focusPrimaryDelta != null) { + String password = getPasswordFromDeltas(singletonList(focusPrimaryDelta)); + if (password != null) { + LOGGER.trace("getFocusPasswordFromEvent: Found password in user primary delta, continuing"); + return password; + } + } + if (focusSecondaryDelta != null) { + String password = getPasswordFromDeltas(singletonList(focusSecondaryDelta)); + if (password != null) { + LOGGER.trace("getFocusPasswordFromEvent: Found password in user secondary delta(s)"); + return password; + } + } + LOGGER.trace("getFocusPasswordFromEvent: No password in executed delta(s) nor in primary/secondary deltas"); + return null; + } + + private String getPasswordFromDeltas(List> deltas) { + try { + return getMidpointFunctions().getPlaintextUserPasswordFromDeltas(deltas); + } catch (EncryptionException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't decrypt password from user deltas: {}", e, DebugUtil.debugDump(deltas)); + return null; + } + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelToString(sb, "modelContext", modelContext, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/PolicyRuleEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/PolicyRuleEventImpl.java new file mode 100644 index 00000000000..994771448fe --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/PolicyRuleEventImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; +import com.evolveum.midpoint.notifications.api.events.PolicyRuleEvent; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; +import org.jetbrains.annotations.NotNull; + +/** + * Any event that is triggered by the 'notify' policy rule action. + */ +public class PolicyRuleEventImpl extends BaseEventImpl implements PolicyRuleEvent { + + @NotNull private final EvaluatedPolicyRule policyRule; + + public PolicyRuleEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull EvaluatedPolicyRule policyRule) { + super(lightweightIdentifierGenerator); + this.policyRule = policyRule; + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return false; // not supported for this kind of events + } + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + return eventStatus == EventStatusType.SUCCESS || eventStatus == EventStatusType.ALSO_SUCCESS; + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + return eventOperation == EventOperationType.ADD; + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.POLICY_RULE_EVENT; + } + + @Override + @NotNull + public EvaluatedPolicyRule getPolicyRule() { + return policyRule; + } + + @Override + public String getRuleName() { + return policyRule.getName(); + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelToStringLn(sb, "policyRule", policyRule, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ResourceObjectEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ResourceObjectEventImpl.java new file mode 100644 index 00000000000..dfa3e70a9ae --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/ResourceObjectEventImpl.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import java.util.Collection; + +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.notifications.api.OperationStatus; +import com.evolveum.midpoint.notifications.api.events.ResourceObjectEvent; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.provisioning.api.ResourceOperationDescription; +import com.evolveum.midpoint.schema.processor.ResourceAttribute; +import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +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.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; + +public class ResourceObjectEventImpl extends BaseEventImpl implements ResourceObjectEvent { + + private static final Trace LOGGER = TraceManager.getTrace(ResourceObjectEventImpl.class); + + @NotNull private final ResourceOperationDescription operationDescription; + @NotNull private final OperationStatus operationStatus; + @NotNull private final ChangeType changeType; + + public ResourceObjectEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, + @NotNull ResourceOperationDescription operationDescription, @NotNull OperationStatus status) { + super(lightweightIdentifierGenerator); + this.operationDescription = operationDescription; + this.operationStatus = status; + this.changeType = operationDescription.getObjectDelta().getChangeType(); + } + + @NotNull + @Override + public ResourceOperationDescription getOperationDescription() { + return operationDescription; + } + + @NotNull + @Override + public OperationStatus getOperationStatus() { + return operationStatus; + } + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + return operationStatus.matchesEventStatusType(eventStatus); + } + + @NotNull + @Override + public ChangeType getChangeType() { + return changeType; + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + return changeTypeMatchesOperationType(changeType, eventOperation); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.RESOURCE_OBJECT_EVENT; + } + + @Override + public boolean isShadowKind(ShadowKindType shadowKindType) { + ShadowKindType actualKind = operationDescription.getCurrentShadow().asObjectable().getKind(); + if (actualKind != null) { + return actualKind.equals(shadowKindType); + } else { + return ShadowKindType.ACCOUNT.equals(shadowKindType); + } + } + + @Override + public ShadowType getShadow() { + PrismObject shadow = operationDescription.getCurrentShadow(); + return shadow != null ? shadow.asObjectable() : null; + } + + @Override + public boolean isShadowIntent(String intent) { + if (StringUtils.isNotEmpty(intent)) { + return intent.equals(operationDescription.getCurrentShadow().asObjectable().getIntent()); + } else { + return StringUtils.isEmpty(operationDescription.getCurrentShadow().asObjectable().getIntent()); + } + } + + @Override + public ObjectDelta getShadowDelta() { + //noinspection unchecked + return (ObjectDelta) operationDescription.getObjectDelta(); + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return containsItem(getShadowDelta(), itemPath); + } + + @Override + public String toString() { + return "ResourceObjectEvent{" + + "base=" + super.toString() + + ", changeType=" + changeType + + ", operationStatus=" + operationStatus + + '}'; + } + + @Override + public String getShadowName() { + PrismObject shadow = operationDescription.getCurrentShadow(); + if (shadow == null) { + return null; + } else if (shadow.asObjectable().getName() != null) { + return shadow.asObjectable().getName().getOrig(); + } else { + Collection> secondaryIdentifiers = ShadowUtil.getSecondaryIdentifiers(shadow); + LOGGER.trace("secondary identifiers: {}", secondaryIdentifiers); + if (secondaryIdentifiers != null) { + // first phase = looking for "name" identifier + for (ResourceAttribute ra : secondaryIdentifiers) { + if (ra.getElementName() != null && ra.getElementName().getLocalPart().contains("name")) { + LOGGER.trace("Considering {} as a name", ra); + return String.valueOf(ra.getAnyRealValue()); + } + } + // second phase = returning any value + if (!secondaryIdentifiers.isEmpty()) { + return String.valueOf(secondaryIdentifiers.iterator().next().getAnyRealValue()); + } + } + return null; + } + } + + @Override + public PolyStringType getResourceName() { + return operationDescription.getResource().asObjectable().getName(); + } + + @Override + public String getResourceOid() { + return operationDescription.getResource().getOid(); + } + + @Override + public String getPlaintextPassword() { + ObjectDelta delta = operationDescription.getObjectDelta(); + if (delta != null) { + try { + return getMidpointFunctions().getPlaintextAccountPasswordFromDelta(delta); + } catch (EncryptionException e) { + LoggingUtils.logException(LOGGER, "Couldn't decrypt password from shadow delta: {}", e, delta.debugDump()); + return null; + } + } else { + return null; + } + } + + @Override + public boolean hasContentToShow() { + return hasContentToShow(false, false); + } + + @Override + public boolean hasContentToShow(boolean watchSynchronizationAttributes, boolean watchAuxiliaryAttributes) { + ObjectDelta delta = getShadowDelta(); + if (!delta.isModify()) { + return true; + } else if (!getTextFormatter().containsVisibleModifiedItems(delta.getModifications(), + watchSynchronizationAttributes, watchAuxiliaryAttributes)) { + LOGGER.trace("No relevant attributes in modify delta (watchSync={}, watchAux={})", + watchSynchronizationAttributes, watchAuxiliaryAttributes); + return false; + } else { + return true; + } + } + + @Override + public String getContentAsFormattedList() { + return getContentAsFormattedList(false, false); + } + + @Override + public String getContentAsFormattedList(boolean showSynchronizationItems, boolean showAuxiliaryAttributes) { + final ObjectDelta shadowDelta = getShadowDelta(); + if (shadowDelta == null) { + return ""; + } + if (shadowDelta.isAdd()) { + return getTextFormatter() + .formatShadowAttributes(shadowDelta.getObjectToAdd().asObjectable(), showSynchronizationItems, false); + } else if (shadowDelta.isModify()) { + return getModifiedAttributesAsFormattedList(shadowDelta, showSynchronizationItems, showAuxiliaryAttributes); + } else { + return ""; + } + } + + private String getModifiedAttributesAsFormattedList(ObjectDelta shadowDelta, + boolean showSynchronizationItems, boolean showAuxiliaryAttributes) { + + if (operationStatus != OperationStatus.IN_PROGRESS) { + // todo we do not have objectOld + objectNew, only the current status + // it is used to explain modified containers with identifiers -- however, currently I don't know of use + // of such containers in shadows, which would be visible in notifications + return getTextFormatter() + .formatObjectModificationDelta(shadowDelta, showSynchronizationItems, showAuxiliaryAttributes, + operationDescription.getCurrentShadow(), null); + } else { + // TODO implement extraction from pending operations + return getTextFormatter() + .formatObjectModificationDelta(shadowDelta, showSynchronizationItems, true, + operationDescription.getCurrentShadow(), null); + } + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelToStringLn(sb, "operationStatus", operationStatus, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "accountOperationDescription", operationDescription, indent + 1); + DebugUtil.debugDumpWithLabelToStringLn(sb, "changeType", changeType, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/TaskEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/TaskEventImpl.java new file mode 100644 index 00000000000..248e34e235a --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/TaskEventImpl.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.TaskEvent; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.result.OperationResultStatus; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.task.api.TaskRunResult; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class TaskEventImpl extends BaseEventImpl implements TaskEvent { + + private static final Trace LOGGER = TraceManager.getTrace(TaskEventImpl.class); + + @NotNull private final Task task; + @Nullable private final TaskRunResult taskRunResult; // nullable only if operationType == ADD + @NotNull private final EventOperationType operationType; // only ADD or DELETE + + public TaskEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull Task task, @Nullable TaskRunResult runResult, + @NotNull EventOperationType operationType, String channel) { + super(lightweightIdentifierGenerator); + this.task = task; + this.taskRunResult = runResult; + this.operationType = operationType; + setChannel(channel); + } + + @Override + @NotNull + public Task getTask() { + return task; + } + + @Override + @Nullable + public TaskRunResult getTaskRunResult() { + return taskRunResult; + } + + @Override + @NotNull + public EventOperationType getOperationType() { + return operationType; + } + + @Override + public boolean isTemporaryError() { + return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.TEMPORARY_ERROR; + } + + @Override + public boolean isPermanentError() { + return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.PERMANENT_ERROR; + } + + @Override + public boolean isFinished() { + return taskRunResult != null && + (taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.FINISHED || + taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.FINISHED_HANDLER); + } + + @Override + public boolean isInterrupted() { + return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.INTERRUPTED; + } + + @Override + public boolean isRestartRequested() { + return taskRunResult != null && taskRunResult.getRunResultStatus() == TaskRunResult.TaskRunResultStatus.RESTART_REQUESTED; + } + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + if (eventStatus == null) { + return false; + } + if (taskRunResult == null || taskRunResult.getOperationResult() == null) { + // TODO consider if we really want to return 'true' for both success and in_progress here + return eventStatus == EventStatusType.SUCCESS || eventStatus == EventStatusType.ALSO_SUCCESS || eventStatus == EventStatusType.IN_PROGRESS; + } + OperationResult result = taskRunResult.getOperationResult(); + switch (eventStatus) { + case SUCCESS: + case ALSO_SUCCESS: return result.isSuccess() || result.isHandledError() || result.isWarning(); + case IN_PROGRESS: return false; + case FAILURE: return result.isError(); + case ONLY_FAILURE: return result.isFatalError(); + default: throw new IllegalStateException("Invalid eventStatusType: " + eventStatus); + } + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + return this.operationType == eventOperation; + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.TASK_EVENT; + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return false; + } + + @Override + public OperationResultStatus getOperationResultStatus() { + return taskRunResult != null && taskRunResult.getOperationResult() != null ? taskRunResult.getOperationResult().getStatus() : null; + } + + @Override + public String getMessage() { + return taskRunResult != null && taskRunResult.getOperationResult() != null ? taskRunResult.getOperationResult().getMessage() : null; + } + + @Override + public long getProgress() { + return taskRunResult != null && taskRunResult.getProgress() != null ? taskRunResult.getProgress() : task.getProgress(); + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelToStringLn(sb, "task", task, indent + 1); + DebugUtil.debugDumpWithLabelToStringLn(sb, "taskRunResult", taskRunResult, indent + 1); + DebugUtil.debugDumpWithLabelToString(sb, "operationType", operationType, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemAllocationEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemAllocationEventImpl.java new file mode 100644 index 00000000000..c05b9935ca8 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemAllocationEventImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; +import com.evolveum.midpoint.notifications.api.events.WorkItemAllocationEvent; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.wf.api.WorkItemOperationInfo; +import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.xml.datatype.Duration; + +public class WorkItemAllocationEventImpl extends WorkItemEventImpl implements WorkItemAllocationEvent { + + public WorkItemAllocationEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, + @NotNull ChangeType changeType, + @NotNull CaseWorkItemType workItem, @Nullable SimpleObjectRef assignee, @Nullable SimpleObjectRef initiator, + @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, + @Nullable ApprovalContextType approvalContext, @NotNull CaseType aCase, + @Nullable Duration timeBefore) { + super(lightweightIdentifierGenerator, changeType, workItem, assignee, initiator, operationInfo, sourceInfo, + approvalContext, aCase, null, timeBefore); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.WORK_ITEM_ALLOCATION_EVENT + || eventCategory == EventCategoryType.WORK_ITEM_EVENT + || eventCategory == EventCategoryType.WORKFLOW_EVENT; + } + + @Override + public String toString() { + return "WorkItemAllocationEvent:" + super.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemCustomEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemCustomEventImpl.java new file mode 100644 index 00000000000..73506fa4894 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemCustomEventImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; +import com.evolveum.midpoint.notifications.api.events.WorkItemCustomEvent; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorkItemCustomEventImpl extends WorkItemEventImpl implements WorkItemCustomEvent { + + public WorkItemCustomEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, + @NotNull ChangeType changeType, + @NotNull CaseWorkItemType workItem, + @Nullable SimpleObjectRef assignee, @Nullable WorkItemOperationSourceInfo sourceInfo, + @Nullable ApprovalContextType approvalContext, CaseType aCase, + @Nullable EventHandlerType handler) { + super(lightweightIdentifierGenerator, changeType, workItem, assignee, null, null, + sourceInfo, approvalContext, aCase, handler, null); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.WORK_ITEM_CUSTOM_EVENT + || eventCategory == EventCategoryType.WORK_ITEM_EVENT + || eventCategory == EventCategoryType.WORKFLOW_EVENT; + } + + @Override + public WorkItemNotificationActionType getNotificationAction() { + return (WorkItemNotificationActionType) getSource(); + } + + @Override + public String toString() { + return "WorkItemCustomEvent:" + super.toString(); + } + +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemEventImpl.java new file mode 100644 index 00000000000..541313c66a0 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemEventImpl.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; +import com.evolveum.midpoint.notifications.api.events.WorkItemEvent; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.expression.VariablesMap; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.wf.api.WorkItemOperationInfo; +import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; +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 javax.xml.datatype.Duration; + +public class WorkItemEventImpl extends WorkflowEventImpl implements WorkItemEvent { + + @NotNull protected final CaseWorkItemType workItem; + // (Currently) Each work item event is related to at most one assignee. So, if a work item has more assignees, + // more events will be generated. This might change in a future. + protected final SimpleObjectRef assignee; + /** + * User who "pressed the button". I.e. the one that really approved, rejected or delegated/escalated a work item. + * In case of automated actions (completion, delegation/escalation) this is not filled-in. + */ + protected final SimpleObjectRef initiator; + protected final WorkItemOperationInfo operationInfo; + protected final WorkItemOperationSourceInfo sourceInfo; + protected final Duration timeBefore; + + WorkItemEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ChangeType changeType, + @NotNull CaseWorkItemType workItem, + @Nullable SimpleObjectRef assignee, @Nullable SimpleObjectRef initiator, + @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, + @Nullable ApprovalContextType approvalContext, + @NotNull CaseType aCase, + @Nullable EventHandlerType handler, @Nullable Duration timeBefore) { + super(lightweightIdentifierGenerator, changeType, approvalContext, aCase, handler); + Validate.notNull(workItem); + this.workItem = workItem; + this.assignee = assignee; + this.initiator = initiator; + this.operationInfo = operationInfo; + this.sourceInfo = sourceInfo; + this.timeBefore = timeBefore; + } + + public String getWorkItemName() { + return PolyString.getOrig(workItem.getName()); // todo MID-5916 + } + + @NotNull + public CaseWorkItemType getWorkItem() { + return workItem; + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.WORK_ITEM_EVENT || eventCategory == EventCategoryType.WORKFLOW_EVENT; + } + + public SimpleObjectRef getAssignee() { + return assignee; + } + + public SimpleObjectRef getInitiator() { + return initiator; + } + + public WorkItemOperationKindType getOperationKind() { + return operationInfo != null ? operationInfo.getOperationKind() : null; + } + + public AbstractWorkItemActionType getSource() { + return sourceInfo != null ? sourceInfo.getSource() : null; + } + + public WorkItemEventCauseInformationType getCause() { + return sourceInfo != null ? sourceInfo.getCause() : null; + } + + public Duration getTimeBefore() { + return timeBefore; + } + + public WorkItemOperationInfo getOperationInfo() { + return operationInfo; + } + + public WorkItemOperationSourceInfo getSourceInfo() { + return sourceInfo; + } + + @Override + public void createExpressionVariables(VariablesMap variables, OperationResult result) { + super.createExpressionVariables(variables, result); + variables.put(ExpressionConstants.VAR_ASSIGNEE, resolveTypedObject(assignee, false, result)); + variables.put(ExpressionConstants.VAR_WORK_ITEM, workItem, CaseWorkItemType.class); + } + + public AbstractWorkItemOutputType getOutput() { + return workItem.getOutput(); + } + + @Override + public String getOutcome() { + AbstractWorkItemOutputType output = getOutput(); + return output != null ? output.getOutcome() : null; + } + + @Override + public String toString() { + return "WorkflowProcessEvent{" + + "workflowEvent=" + super.toString() + + ", workItemName=" + getWorkItemName() + + ", assignee=" + assignee + + '}'; + + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelLn(sb, "workItemName", getWorkItemName(), indent + 1); + DebugUtil.debugDumpWithLabelToString(sb, "assignee", assignee, indent + 1); + return sb.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemLifecycleEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemLifecycleEventImpl.java new file mode 100644 index 00000000000..49a904ae141 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkItemLifecycleEventImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; +import com.evolveum.midpoint.notifications.api.events.WorkItemLifecycleEvent; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.wf.api.WorkItemOperationInfo; +import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorkItemLifecycleEventImpl extends WorkItemEventImpl implements WorkItemLifecycleEvent { + + public WorkItemLifecycleEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ChangeType changeType, + @NotNull CaseWorkItemType workItem, + @Nullable SimpleObjectRef assignee, @Nullable SimpleObjectRef initiator, + @Nullable WorkItemOperationInfo operationInfo, @Nullable WorkItemOperationSourceInfo sourceInfo, + @Nullable ApprovalContextType approvalContext, @NotNull CaseType aCase) { + super(lightweightIdentifierGenerator, changeType, workItem, assignee, initiator, + operationInfo, sourceInfo, approvalContext, aCase, null, null); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.WORK_ITEM_LIFECYCLE_EVENT + || eventCategory == EventCategoryType.WORK_ITEM_EVENT + || eventCategory == EventCategoryType.WORKFLOW_EVENT; + } + + @Override + public String toString() { + return "WorkItemLifecycleEvent:" + super.toString(); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowEventImpl.java new file mode 100644 index 00000000000..4a817d40eeb --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowEventImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.OperationStatus; +import com.evolveum.midpoint.notifications.api.events.WorkflowEvent; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.wf.util.ApprovalUtils; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +abstract public class WorkflowEventImpl extends BaseEventImpl implements WorkflowEvent { + + @Nullable protected final ApprovalContextType approvalContext; + @NotNull private final ChangeType changeType; + @NotNull protected final CaseType aCase; + + WorkflowEventImpl(@NotNull LightweightIdentifierGenerator lightweightIdentifierGenerator, @NotNull ChangeType changeType, + @Nullable ApprovalContextType approvalContext, @NotNull CaseType aCase, EventHandlerType handler) { + super(lightweightIdentifierGenerator, handler); + this.changeType = changeType; + this.approvalContext = approvalContext; + this.aCase = aCase; + } + + @Override + @NotNull + public CaseType getCase() { + return aCase; + } + + @Override + public String getProcessInstanceName() { + return aCase.getName().getOrig(); + } + + @Override + public OperationStatus getOperationStatus() { + return outcomeToStatus(changeType, getOutcome()); + } + + protected abstract String getOutcome(); + + @Override + public boolean isStatusType(EventStatusType eventStatus) { + return getOperationStatus().matchesEventStatusType(eventStatus); + } + + @Override + public ChangeType getChangeType() { + return changeType; + } + + @Override + public boolean isOperationType(EventOperationType eventOperation) { + return changeTypeMatchesOperationType(changeType, eventOperation); + } + + @Override + public boolean isApprovalCase() { + return ObjectTypeUtil.hasArchetype(aCase, SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value()); + } + + @Override + public boolean isManualResourceCase() { + return ObjectTypeUtil.hasArchetype(aCase, SystemObjectsType.ARCHETYPE_MANUAL_CASE.value()); + } + + @Override + public boolean isResultKnown() { + return !isInProgress(); // for now + } + + @Override + public boolean isApproved() { + return isSuccess(); // for now + } + + @Override + public boolean isRejected() { + return isFailure(); // for now + } + + private OperationStatus outcomeToStatus(ChangeType changeType, String outcome) { + if (changeType != ChangeType.DELETE) { + return OperationStatus.SUCCESS; + } else { + if (outcome == null) { + return OperationStatus.IN_PROGRESS; + } else if (QNameUtil.matchUri(outcome, SchemaConstants.MODEL_APPROVAL_OUTCOME_APPROVE)) { + return OperationStatus.SUCCESS; + } else if (QNameUtil.matchUri(outcome, SchemaConstants.MODEL_APPROVAL_OUTCOME_REJECT)) { + return OperationStatus.FAILURE; + } else { + return OperationStatus.OTHER; + } + } + } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return false; + } + + @Override + @NotNull + public ApprovalContextType getApprovalContext() { + return approvalContext; + } + + @Override + @NotNull + public CaseType getWorkflowTask() { + return aCase; + } + + @Override + public String toString() { + return "WorkflowEvent{" + + "event=" + super.toString() + + ", processInstanceName='" + getProcessInstanceName() + '\'' + + ", changeType=" + changeType + + ", outcome=" + getOutcome() + + '}'; + } + + // This method is not used. It is here just for maven dependency plugin to detect the + // dependency on workflow-api + @SuppressWarnings("unused") + private void notUsed() { + ApprovalUtils.approvalBooleanValueFromUri(""); + } + + @Override + protected void debugDumpCommon(StringBuilder sb, int indent) { + super.debugDumpCommon(sb, indent); + DebugUtil.debugDumpWithLabelLn(sb, "processInstanceName", getProcessInstanceName(), indent + 1); + DebugUtil.debugDumpWithLabelToStringLn(sb, "changeType", changeType, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "outcome", getOutcome(), indent + 1); + } + +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowProcessEventImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowProcessEventImpl.java new file mode 100644 index 00000000000..2b6fccbe3b0 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/WorkflowProcessEventImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events; + +import com.evolveum.midpoint.notifications.api.events.WorkflowProcessEvent; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; + +public class WorkflowProcessEventImpl extends WorkflowEventImpl implements WorkflowProcessEvent { + + public WorkflowProcessEventImpl(LightweightIdentifierGenerator lightweightIdentifierGenerator, ChangeType changeType, CaseType aCase) { + super(lightweightIdentifierGenerator, changeType, aCase.getApprovalContext(), aCase, null); + } + + @Override + public boolean isCategoryType(EventCategoryType eventCategory) { + return eventCategory == EventCategoryType.WORKFLOW_PROCESS_EVENT || eventCategory == EventCategoryType.WORKFLOW_EVENT; + } + + @Override + protected String getOutcome() { + return aCase.getOutcome(); + } + + @Override + public String toString() { + return "WorkflowProcessEvent{" + + "workflowEvent=" + super.toString() + + '}'; + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); + debugDumpCommon(sb, indent); + return sb.toString(); + } + +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/cert/CertEventFactory.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CertEventFactory.java similarity index 63% rename from model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/cert/CertEventFactory.java rename to model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CertEventFactory.java index 10d06af686a..287daadaf13 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/cert/CertEventFactory.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CertEventFactory.java @@ -5,28 +5,33 @@ * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.midpoint.notifications.impl.events.cert; +package com.evolveum.midpoint.notifications.impl.events.factory; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; -import com.evolveum.midpoint.notifications.api.events.AccessCertificationEvent; import com.evolveum.midpoint.notifications.api.events.CertCampaignEvent; import com.evolveum.midpoint.notifications.api.events.CertCampaignStageEvent; import com.evolveum.midpoint.notifications.api.events.CertReviewEvent; import com.evolveum.midpoint.notifications.impl.NotificationFunctionsImpl; import com.evolveum.midpoint.notifications.impl.SimpleObjectRefImpl; -import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.notifications.impl.events.AccessCertificationEventImpl; +import com.evolveum.midpoint.notifications.impl.events.CertCampaignEventImpl; +import com.evolveum.midpoint.notifications.impl.events.CertCampaignStageEventImpl; +import com.evolveum.midpoint.notifications.impl.events.CertReviewEventImpl; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.List; /** - * @author mederly + * Creates certification events + * + * TODO do we really need this class? */ @Component public class CertEventFactory { @@ -37,13 +42,13 @@ public class CertEventFactory { @Autowired private NotificationFunctionsImpl notificationsUtil; - public CertCampaignEvent createOnCampaignStartEvent(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignEvent event = new CertCampaignEvent(idGenerator, campaign, EventOperationType.ADD); + public CertCampaignEvent createOnCampaignStartEvent(AccessCertificationCampaignType campaign, Task task) { + CertCampaignEventImpl event = new CertCampaignEventImpl(idGenerator, campaign, EventOperationType.ADD); fillInEvent(campaign, task, event); return event; } - protected void fillInEvent(AccessCertificationCampaignType campaign, Task task, AccessCertificationEvent event) { + private void fillInEvent(AccessCertificationCampaignType campaign, Task task, AccessCertificationEventImpl event) { event.setRequestee(new SimpleObjectRefImpl(notificationsUtil, campaign.getOwnerRef())); if (task != null) { event.setRequester(new SimpleObjectRefImpl(notificationsUtil, task.getOwner())); @@ -51,47 +56,47 @@ protected void fillInEvent(AccessCertificationCampaignType campaign, Task task, } } - protected void fillInReviewerRelatedEvent(ObjectReferenceType reviewerOrDeputyRef, ObjectReferenceType actualReviewerRef, - Task task, CertReviewEvent event) { + private void fillInReviewerRelatedEvent(ObjectReferenceType reviewerOrDeputyRef, ObjectReferenceType actualReviewerRef, + Task task, CertReviewEventImpl event) { event.setRequestee(new SimpleObjectRefImpl(notificationsUtil, reviewerOrDeputyRef)); event.setRequester(new SimpleObjectRefImpl(notificationsUtil, task.getOwner())); // or campaign owner? event.setActualReviewer(new SimpleObjectRefImpl(notificationsUtil, actualReviewerRef)); } - public CertCampaignEvent createOnCampaignEndEvent(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignEvent event = new CertCampaignEvent(idGenerator, campaign, EventOperationType.DELETE); + public CertCampaignEvent createOnCampaignEndEvent(AccessCertificationCampaignType campaign, Task task) { + CertCampaignEventImpl event = new CertCampaignEventImpl(idGenerator, campaign, EventOperationType.DELETE); fillInEvent(campaign, task, event); return event; } - public CertCampaignStageEvent createOnCampaignStageStartEvent(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignStageEvent event = new CertCampaignStageEvent(idGenerator, campaign, EventOperationType.ADD); + public CertCampaignStageEvent createOnCampaignStageStartEvent(AccessCertificationCampaignType campaign, Task task) { + CertCampaignStageEventImpl event = new CertCampaignStageEventImpl(idGenerator, campaign, EventOperationType.ADD); fillInEvent(campaign, task, event); return event; } - public CertCampaignStageEvent createOnCampaignStageDeadlineApproachingEvent(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignStageEvent event = new CertCampaignStageEvent(idGenerator, campaign, EventOperationType.MODIFY); + public CertCampaignStageEvent createOnCampaignStageDeadlineApproachingEvent(AccessCertificationCampaignType campaign, Task task) { + CertCampaignStageEventImpl event = new CertCampaignStageEventImpl(idGenerator, campaign, EventOperationType.MODIFY); fillInEvent(campaign, task, event); return event; } - public CertCampaignStageEvent createOnCampaignStageEndEvent(AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertCampaignStageEvent event = new CertCampaignStageEvent(idGenerator, campaign, EventOperationType.DELETE); + public CertCampaignStageEvent createOnCampaignStageEndEvent(AccessCertificationCampaignType campaign, Task task) { + CertCampaignStageEventImpl event = new CertCampaignStageEventImpl(idGenerator, campaign, EventOperationType.DELETE); fillInEvent(campaign, task, event); return event; } public CertReviewEvent createReviewRequestedEvent(ObjectReferenceType reviewerOrDeputyRef, ObjectReferenceType actualReviewerRef, - List cases, AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertReviewEvent event = new CertReviewEvent(idGenerator, cases, campaign, EventOperationType.ADD); + List cases, AccessCertificationCampaignType campaign, Task task) { + CertReviewEventImpl event = new CertReviewEventImpl(idGenerator, cases, campaign, EventOperationType.ADD); fillInReviewerRelatedEvent(reviewerOrDeputyRef, actualReviewerRef, task, event); return event; } public CertReviewEvent createReviewDeadlineApproachingEvent(ObjectReferenceType reviewerOrDeputyRef, ObjectReferenceType actualReviewerRef, - List cases, AccessCertificationCampaignType campaign, Task task, OperationResult result) { - CertReviewEvent event = new CertReviewEvent(idGenerator, cases, campaign, EventOperationType.MODIFY); + List cases, AccessCertificationCampaignType campaign, Task task) { + CertReviewEventImpl event = new CertReviewEventImpl(idGenerator, cases, campaign, EventOperationType.MODIFY); fillInReviewerRelatedEvent(reviewerOrDeputyRef, actualReviewerRef, task, event); return event; } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CustomEventFactoryImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CustomEventFactoryImpl.java new file mode 100644 index 00000000000..c9fe3e39495 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/factory/CustomEventFactoryImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.events.factory; + +import com.evolveum.midpoint.model.api.PipelineItem; +import com.evolveum.midpoint.notifications.api.events.CustomEvent; +import com.evolveum.midpoint.notifications.api.events.factory.CustomEventFactory; +import com.evolveum.midpoint.notifications.impl.events.CustomEventImpl; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * Factory for custom events. + */ +@Component +public class CustomEventFactoryImpl implements CustomEventFactory { + + @Autowired private LightweightIdentifierGenerator lightweightIdentifierGenerator; + + @Override + @NotNull + public CustomEvent createEvent(String subtype, EventHandlerType handler, PrismValue value, EventOperationType operation, + EventStatusType status, String channel) { + return new CustomEventImpl(lightweightIdentifierGenerator, subtype, handler, value, operation, status, channel); + } + + @Override + @NotNull + public CustomEvent createEvent(String subtype, EventHandlerType handler, List data, + EventOperationType operation, EventStatusType status, String channel) { + return new CustomEventImpl(lightweightIdentifierGenerator, subtype, handler, data, operation, status, channel); + } + +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/DeltaFormatter.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/DeltaFormatter.java index 1f3bf4d218b..83402f4e162 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/DeltaFormatter.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/DeltaFormatter.java @@ -13,11 +13,12 @@ import java.util.Locale; import javax.xml.namespace.QName; +import com.evolveum.midpoint.util.DebugUtil; + import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import com.evolveum.midpoint.common.LocalizationService; @@ -27,7 +28,6 @@ import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPathCollectionsUtil; -import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -52,11 +52,11 @@ public String formatObjectModificationDelta(ObjectDelta ob // objectOld and objectNew are used for explaining changed container values, e.g. assignment[1]/tenantRef (see MID-2047) // if null, they are ignored - public String formatObjectModificationDelta(@NotNull ObjectDelta objectDelta, List hiddenPaths, + String formatObjectModificationDelta(@NotNull ObjectDelta objectDelta, Collection hiddenPaths, boolean showOperationalAttributes, PrismObject objectOld, PrismObject objectNew) { Validate.isTrue(objectDelta.isModify(), "objectDelta is not a modification delta"); - PrismObjectDefinition objectDefinition; + PrismObjectDefinition objectDefinition; if (objectNew != null && objectNew.getDefinition() != null) { objectDefinition = objectNew.getDefinition(); } else if (objectOld != null && objectOld.getDefinition() != null) { @@ -70,31 +70,32 @@ public String formatObjectModificationDelta(@NotNull ObjectDelta toBeDisplayed = filterAndOrderItemDeltas(objectDelta, hiddenPaths, showOperationalAttributes); - for (ItemDelta itemDelta : toBeDisplayed) { + List> visibleDeltas = filterAndOrderModifications(objectDelta, hiddenPaths, showOperationalAttributes); + for (ItemDelta itemDelta : visibleDeltas) { sb.append(" - "); sb.append(getItemDeltaLabel(itemDelta, objectDefinition)); sb.append(":\n"); formatItemDeltaContent(sb, itemDelta, objectOld, hiddenPaths, showOperationalAttributes); } - explainPaths(sb, toBeDisplayed, objectDefinition, objectOld, objectNew, hiddenPaths, showOperationalAttributes); + explainPaths(sb, visibleDeltas, objectDefinition, objectOld, objectNew, hiddenPaths, showOperationalAttributes); return sb.toString(); } - private void explainPaths(StringBuilder sb, List deltas, PrismObjectDefinition objectDefinition, PrismObject objectOld, PrismObject objectNew, List hiddenPaths, boolean showOperationalAttributes) { + private void explainPaths(StringBuilder sb, List> deltas, PrismObjectDefinition objectDefinition, + PrismObject objectOld, PrismObject objectNew, Collection hiddenPaths, boolean showOperationalAttributes) { if (objectOld == null && objectNew == null) { return; // no data - no point in trying } boolean first = true; List alreadyExplained = new ArrayList<>(); - for (ItemDelta itemDelta : deltas) { + for (ItemDelta itemDelta : deltas) { ItemPath pathToExplain = getPathToExplain(itemDelta); if (pathToExplain == null || ItemPathCollectionsUtil.containsSubpathOrEquivalent(alreadyExplained, pathToExplain)) { continue; // null or already processed } - PrismObject source = null; + PrismObject source = null; Object item = null; if (objectNew != null) { item = objectNew.find(pathToExplain); @@ -116,15 +117,15 @@ private void explainPaths(StringBuilder sb, List deltas, PrismObjectD // the item should be a PrismContainerValue if (item instanceof PrismContainerValue) { sb.append(" - ").append(label).append(":\n"); - valueFormatter.formatContainerValue(sb, " ", (PrismContainerValue) item, false, hiddenPaths, showOperationalAttributes); + valueFormatter.formatContainerValue(sb, " ", (PrismContainerValue) item, false, hiddenPaths, showOperationalAttributes); } else { LOGGER.warn("{} in {} was expected to be a PrismContainerValue; it is {} instead", pathToExplain, source, item.getClass()); if (item instanceof PrismContainer) { - valueFormatter.formatPrismContainer(sb, " ", (PrismContainer) item, false, hiddenPaths, showOperationalAttributes); + valueFormatter.formatPrismContainer(sb, " ", (PrismContainer) item, false, hiddenPaths, showOperationalAttributes); } else if (item instanceof PrismReference) { valueFormatter.formatPrismReference(sb, " ", (PrismReference) item, false); } else if (item instanceof PrismProperty) { - valueFormatter.formatPrismProperty(sb, " ", (PrismProperty) item); + valueFormatter.formatPrismProperty(sb, " ", (PrismProperty) item); } else { sb.append("Unexpected item: ").append(item).append("\n"); } @@ -133,22 +134,22 @@ private void explainPaths(StringBuilder sb, List deltas, PrismObjectD } } - private void formatItemDeltaContent(StringBuilder sb, ItemDelta itemDelta, PrismObject objectOld, - List hiddenPaths, boolean showOperationalAttributes) { + private void formatItemDeltaContent(StringBuilder sb, ItemDelta itemDelta, PrismObject objectOld, + Collection hiddenPaths, boolean showOperationalAttributes) { formatItemDeltaValues(sb, "ADD", itemDelta.getValuesToAdd(), false, itemDelta.getPath(), objectOld, hiddenPaths, showOperationalAttributes); formatItemDeltaValues(sb, "DELETE", itemDelta.getValuesToDelete(), true, itemDelta.getPath(), objectOld, hiddenPaths, showOperationalAttributes); formatItemDeltaValues(sb, "REPLACE", itemDelta.getValuesToReplace(), false, itemDelta.getPath(), objectOld, hiddenPaths, showOperationalAttributes); } private void formatItemDeltaValues(StringBuilder sb, String type, Collection values, - boolean isDelete, ItemPath path, PrismObject objectOld, - List hiddenPaths, boolean showOperationalAttributes) { + boolean isDelete, ItemPath path, PrismObject objectOld, + Collection hiddenPaths, boolean showOperationalAttributes) { if (values != null) { for (PrismValue prismValue : values) { sb.append(" - ").append(type).append(": "); String prefix = " "; if (isDelete && prismValue instanceof PrismContainerValue) { - prismValue = fixEmptyContainerValue((PrismContainerValue) prismValue, path, objectOld); + prismValue = fixEmptyContainerValue((PrismContainerValue) prismValue, path, objectOld); } valueFormatter.formatPrismValue(sb, prefix, prismValue, isDelete, hiddenPaths, showOperationalAttributes); if (!(prismValue instanceof PrismContainerValue)) { // container values already end with newline @@ -158,24 +159,24 @@ private void formatItemDeltaValues(StringBuilder sb, String type, Collection pcv, ItemPath path, PrismObject objectOld) { if (pcv.getId() == null || CollectionUtils.isNotEmpty(pcv.getItems())) { return pcv; } - PrismContainer oldContainer = objectOld.findContainer(path); + PrismContainer oldContainer = objectOld.findContainer(path); if (oldContainer == null) { return pcv; } - PrismContainerValue oldValue = oldContainer.getValue(pcv.getId()); + PrismContainerValue oldValue = oldContainer.getValue(pcv.getId()); return oldValue != null ? oldValue : pcv; } // we call this on filtered list of item deltas - all of they have definition set - private String getItemDeltaLabel(ItemDelta itemDelta, PrismObjectDefinition objectDefinition) { + private String getItemDeltaLabel(ItemDelta itemDelta, PrismObjectDefinition objectDefinition) { return getItemPathLabel(itemDelta.getPath(), itemDelta.getDefinition(), objectDefinition); } - private String getItemPathLabel(ItemPath path, Definition deltaDefinition, PrismObjectDefinition objectDefinition) { + private String getItemPathLabel(ItemPath path, Definition deltaDefinition, PrismObjectDefinition objectDefinition) { int lastNameIndex = path.lastNameIndex(); @@ -218,7 +219,7 @@ private String resolve(String key) { } // we call this on filtered list of item deltas - all of they have definition set - private ItemPath getPathToExplain(ItemDelta itemDelta) { + private ItemPath getPathToExplain(ItemDelta itemDelta) { ItemPath path = itemDelta.getPath(); for (int i = 0; i < path.size(); i++) { @@ -238,13 +239,36 @@ private ItemPath getPathToExplain(ItemDelta itemDelta) { return null; } - private List filterAndOrderItemDeltas(ObjectDelta objectDelta, List hiddenPaths, boolean showOperationalAttributes) { - List toBeDisplayed = new ArrayList<>(objectDelta.getModifications().size()); + private List> filterAndOrderModifications(ObjectDelta objectDelta, Collection hiddenPaths, boolean showOperationalAttributes) { + List> visibleModifications = getVisibleModifications(objectDelta.getModifications(), hiddenPaths, + showOperationalAttributes, objectDelta.debugDumpLazily()); + visibleModifications.sort((delta1, delta2) -> compareDisplayOrders(delta1.getDefinition(), delta2.getDefinition())); + return visibleModifications; + } + + static int compareDisplayOrders(ItemDefinition definition1, ItemDefinition definition2) { + Integer order1 = definition1.getDisplayOrder(); + Integer order2 = definition2.getDisplayOrder(); + if (order1 != null && order2 != null) { + return order1 - order2; + } else if (order1 == null && order2 == null) { + return 0; + } else if (order1 == null) { + return 1; + } else { + return -1; + } + } + + @NotNull + private List> getVisibleModifications(Collection> modifications, + Collection hiddenPaths, boolean showOperational, Object context) { + List> toBeDisplayed = new ArrayList<>(modifications.size()); List noDefinition = new ArrayList<>(); - for (ItemDelta itemDelta: objectDelta.getModifications()) { + for (ItemDelta itemDelta: modifications) { if (itemDelta.getDefinition() != null) { - if ((showOperationalAttributes || !itemDelta.getDefinition().isOperational()) && !NotificationFunctionsImpl - .isAmongHiddenPaths(itemDelta.getPath(), hiddenPaths)) { + if ((showOperational || !itemDelta.getDefinition().isOperational()) + && !TextFormatter.isAmongHiddenPaths(itemDelta.getPath(), hiddenPaths)) { toBeDisplayed.add(itemDelta); } } else { @@ -252,22 +276,16 @@ private List filterAndOrderItemDeltas(ObjectDelta { - Integer order1 = delta1.getDefinition().getDisplayOrder(); - Integer order2 = delta2.getDefinition().getDisplayOrder(); - if (order1 != null && order2 != null) { - return order1 - order2; - } else if (order1 == null && order2 == null) { - return 0; - } else if (order1 == null) { - return 1; - } else { - return -1; - } - }); return toBeDisplayed; } + + boolean containsVisibleModifiedItems(Collection> modifications, + Collection hiddenPaths, boolean showOperational) { + List> visibleModifications = getVisibleModifications(modifications, hiddenPaths, showOperational, + DebugUtil.debugDumpLazily(modifications)); + return !visibleModifications.isEmpty(); + } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/TextFormatter.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/TextFormatter.java index fb84d2fb10a..84e0a6fe807 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/TextFormatter.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/TextFormatter.java @@ -7,13 +7,10 @@ package com.evolveum.midpoint.notifications.impl.formatters; -import java.util.List; +import java.util.*; -import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; -import com.evolveum.midpoint.prism.xml.XmlTypeConverter; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; @@ -23,11 +20,6 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; - -import javax.xml.datatype.XMLGregorianCalendar; - -import static com.evolveum.midpoint.prism.polystring.PolyString.getOrig; /** * Prepares text output for notification purposes. @@ -38,14 +30,62 @@ @Component public class TextFormatter { + public static final List SYNCHRONIZATION_PATHS = Collections.unmodifiableList(Arrays.asList( + ShadowType.F_SYNCHRONIZATION_SITUATION, + ShadowType.F_SYNCHRONIZATION_SITUATION_DESCRIPTION, + ShadowType.F_SYNCHRONIZATION_TIMESTAMP, + ShadowType.F_FULL_SYNCHRONIZATION_TIMESTAMP)); + + public static final List AUXILIARY_PATHS = Collections.unmodifiableList(Arrays.asList( + ShadowType.F_METADATA, + ShadowType.F_ACTIVATION.append(ActivationType.F_VALIDITY_STATUS), // works for user activation as well + ShadowType.F_ACTIVATION.append(ActivationType.F_VALIDITY_CHANGE_TIMESTAMP), + ShadowType.F_ACTIVATION.append(ActivationType.F_EFFECTIVE_STATUS), + ShadowType.F_ACTIVATION.append(ActivationType.F_DISABLE_TIMESTAMP), + ShadowType.F_ACTIVATION.append(ActivationType.F_ARCHIVE_TIMESTAMP), + ShadowType.F_ACTIVATION.append(ActivationType.F_ENABLE_TIMESTAMP), + ShadowType.F_ITERATION, + ShadowType.F_ITERATION_TOKEN, + FocusType.F_LINK_REF, + ShadowType.F_TRIGGER)); + @Autowired ValueFormatter valueFormatter; @Autowired DeltaFormatter deltaFormatter; - public String formatAccountAttributes(ShadowType shadowType, List hiddenAttributes, boolean showOperationalAttributes) { - return valueFormatter.formatAccountAttributes(shadowType, hiddenAttributes, showOperationalAttributes); + static boolean isAmongHiddenPaths(ItemPath path, Collection hiddenPaths) { + if (hiddenPaths == null) { + return false; + } + for (ItemPath hiddenPath : hiddenPaths) { + if (hiddenPath.isSubPathOrEquivalent(path)) { + return true; + } + } + return false; + } + + public String formatShadowAttributes(ShadowType shadowType, boolean showSynchronizationItems, boolean showAuxiliaryItems) { + Collection hiddenAttributes = getHiddenPaths(showSynchronizationItems, showAuxiliaryItems); + return valueFormatter.formatAccountAttributes(shadowType, hiddenAttributes, showAuxiliaryItems); + } + + private Collection getHiddenPaths(boolean showSynchronizationItems, boolean showAuxiliaryAttributes) { + List hiddenPaths = new ArrayList<>(); + if (!showSynchronizationItems) { + hiddenPaths.addAll(TextFormatter.SYNCHRONIZATION_PATHS); + } + if (!showAuxiliaryAttributes) { + hiddenPaths.addAll(TextFormatter.AUXILIARY_PATHS); + } + return hiddenPaths; + } + + public String formatObject(PrismObject object, boolean showSynchronizationAttributes, boolean showAuxiliaryAttributes) { + Collection hiddenPaths = getHiddenPaths(showSynchronizationAttributes, showAuxiliaryAttributes); + return formatObject(object, hiddenPaths, showAuxiliaryAttributes); } - public String formatObject(PrismObject object, List hiddenPaths, boolean showOperationalAttributes) { + public String formatObject(PrismObject object, Collection hiddenPaths, boolean showOperationalAttributes) { return valueFormatter.formatObject(object, hiddenPaths, showOperationalAttributes); } @@ -55,8 +95,21 @@ public String formatObjectModificationDelta(ObjectDelta ob return deltaFormatter.formatObjectModificationDelta(objectDelta, hiddenPaths, showOperationalAttributes, null, null); } - public String formatObjectModificationDelta(@NotNull ObjectDelta objectDelta, List hiddenPaths, + public String formatObjectModificationDelta(@NotNull ObjectDelta objectDelta, boolean showSynchronizationAttributes, boolean showAuxiliaryAttributes, + PrismObject objectOld, PrismObject objectNew) { + Collection hiddenPaths = getHiddenPaths(showSynchronizationAttributes, showAuxiliaryAttributes); + return formatObjectModificationDelta(objectDelta, hiddenPaths, showAuxiliaryAttributes, objectOld, objectNew); + } + + public String formatObjectModificationDelta(@NotNull ObjectDelta objectDelta, Collection hiddenPaths, boolean showOperationalAttributes, PrismObject objectOld, PrismObject objectNew) { return deltaFormatter.formatObjectModificationDelta(objectDelta, hiddenPaths, showOperationalAttributes, objectOld, objectNew); } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean containsVisibleModifiedItems(Collection> modifications, + boolean showSynchronizationAttributes, boolean showAuxiliaryAttributes) { + Collection hiddenPaths = getHiddenPaths(showSynchronizationAttributes, showAuxiliaryAttributes); + return deltaFormatter.containsVisibleModifiedItems(modifications, hiddenPaths, showAuxiliaryAttributes); + } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/ValueFormatter.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/ValueFormatter.java index 38b75beecbe..ec0fef76171 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/ValueFormatter.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/formatters/ValueFormatter.java @@ -16,7 +16,6 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; @@ -27,10 +26,7 @@ import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; import com.evolveum.midpoint.notifications.impl.NotificationFunctionsImpl; import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.path.ItemPathCollectionsUtil; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.repo.api.RepositoryService; @@ -39,7 +35,6 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ValueDisplayUtil; import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.LoggingUtils; @@ -54,14 +49,13 @@ public class ValueFormatter { @Autowired @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; - @Autowired private PrismContext prismContext; @Autowired protected NotificationFunctionsImpl functions; @Autowired private LocalizationService localizationService; private static final Trace LOGGER = TraceManager.getTrace(ValueFormatter.class); // todo - should each hiddenAttribute be prefixed with something like F_ATTRIBUTE? Currently it should not be. - public String formatAccountAttributes(ShadowType shadowType, List hiddenAttributes, boolean showOperationalAttributes) { + public String formatAccountAttributes(ShadowType shadowType, Collection hiddenAttributes, boolean showOperationalAttributes) { Validate.notNull(shadowType, "shadowType is null"); StringBuilder retval = new StringBuilder(); @@ -90,23 +84,20 @@ public String formatAccountAttributes(ShadowType shadowType, List hidd return retval.toString(); } - public String formatObject(PrismObject object, List hiddenPaths, boolean showOperationalAttributes) { - - Validate.notNull(object, "object is null"); - + String formatObject(@NotNull PrismObject object, Collection hiddenPaths, boolean showOperationalAttributes) { StringBuilder retval = new StringBuilder(); formatContainerValue(retval, "", object.getValue(), false, hiddenPaths, showOperationalAttributes); return retval.toString(); } - void formatPrismValue(StringBuilder sb, String prefix, PrismValue prismValue, boolean mightBeRemoved, List hiddenPaths, boolean showOperationalAttributes) { + void formatPrismValue(StringBuilder sb, String prefix, PrismValue prismValue, boolean mightBeRemoved, Collection hiddenPaths, boolean showOperationalAttributes) { if (prismValue instanceof PrismPropertyValue) { - sb.append(ValueDisplayUtil.toStringValue((PrismPropertyValue) prismValue)); + sb.append(ValueDisplayUtil.toStringValue((PrismPropertyValue) prismValue)); } else if (prismValue instanceof PrismReferenceValue) { sb.append(formatReferenceValue((PrismReferenceValue) prismValue, mightBeRemoved)); } else if (prismValue instanceof PrismContainerValue) { sb.append("\n"); - formatContainerValue(sb, prefix, (PrismContainerValue) prismValue, mightBeRemoved, hiddenPaths, showOperationalAttributes); + formatContainerValue(sb, prefix, (PrismContainerValue) prismValue, mightBeRemoved, hiddenPaths, showOperationalAttributes); } else { sb.append("Unexpected PrismValue type: "); sb.append(prismValue); @@ -114,13 +105,10 @@ void formatPrismValue(StringBuilder sb, String prefix, PrismValue prismValue, bo } } - void formatContainerValue(StringBuilder sb, String prefix, PrismContainerValue containerValue, boolean mightBeRemoved, List hiddenPaths, boolean showOperationalAttributes) { -// sb.append("Container of type " + containerValue.getParent().getDefinition().getTypeName()); -// sb.append("\n"); - - List toBeDisplayed = filterAndOrderItems(containerValue.getItems(), hiddenPaths, showOperationalAttributes); + void formatContainerValue(StringBuilder sb, String prefix, PrismContainerValue containerValue, boolean mightBeRemoved, Collection hiddenPaths, boolean showOperationalAttributes) { + List> visibleItems = filterAndOrderItems(containerValue.getItems(), hiddenPaths, showOperationalAttributes); - for (Item item : toBeDisplayed) { + for (Item item : visibleItems) { if (item instanceof PrismProperty) { formatPrismProperty(sb, prefix, item); } else if (item instanceof PrismReference) { @@ -136,8 +124,8 @@ void formatContainerValue(StringBuilder sb, String prefix, PrismContainerValue c } } - void formatPrismContainer(StringBuilder sb, String prefix, Item item, boolean mightBeRemoved, List hiddenPaths, boolean showOperationalAttributes) { - for (PrismContainerValue subContainerValue : ((PrismContainer) item).getValues()) { + void formatPrismContainer(StringBuilder sb, String prefix, Item item, boolean mightBeRemoved, Collection hiddenPaths, boolean showOperationalAttributes) { + for (PrismContainerValue subContainerValue : ((PrismContainer) item).getValues()) { String prefixSubContainer = prefix + " "; StringBuilder valueSb = new StringBuilder(); formatContainerValue(valueSb, prefixSubContainer, subContainerValue, mightBeRemoved, hiddenPaths, showOperationalAttributes); @@ -154,7 +142,7 @@ void formatPrismContainer(StringBuilder sb, String prefix, Item item, boolean mi } } - void formatPrismReference(StringBuilder sb, String prefix, Item item, boolean mightBeRemoved) { + void formatPrismReference(StringBuilder sb, String prefix, Item item, boolean mightBeRemoved) { sb.append(prefix); sb.append(" - "); sb.append(getItemLabel(item)); @@ -171,13 +159,13 @@ void formatPrismReference(StringBuilder sb, String prefix, Item item, boolean mi sb.append("\n"); } - void formatPrismProperty(StringBuilder sb, String prefix, Item item) { + void formatPrismProperty(StringBuilder sb, String prefix, Item item) { sb.append(prefix); sb.append(" - "); sb.append(getItemLabel(item)); sb.append(": "); if (item.size() > 1) { - for (PrismPropertyValue propertyValue : ((PrismProperty) item).getValues()) { + for (PrismPropertyValue propertyValue : ((PrismProperty) item).getValues()) { sb.append("\n"); sb.append(prefix).append(" - "); sb.append(ValueDisplayUtil.toStringValue(propertyValue)); @@ -192,6 +180,7 @@ private String formatReferenceValue(PrismReferenceValue value, boolean mightBeRe OperationResult result = new OperationResult("dummy"); + //noinspection unchecked PrismObject object = value.getObject(); if (object == null) { @@ -202,12 +191,13 @@ private String formatReferenceValue(PrismReferenceValue value, boolean mightBeRe if (object != null && object.asObjectable() instanceof ShadowType) { ShadowType shadowType = (ShadowType) object.asObjectable(); ObjectReferenceType resourceRef = shadowType.getResourceRef(); + //noinspection unchecked PrismObject resource = resourceRef.asReferenceValue().getObject(); ResourceType resourceType = null; if (resource == null) { resource = getPrismObject(resourceRef.getOid(), false, result); if (resource != null) { - resourceType = (ResourceType) resource.asObjectable(); + resourceType = resource.asObjectable(); } } else { resourceType = resource.asObjectable(); @@ -241,6 +231,7 @@ private String formatReferenceValue(PrismReferenceValue value, boolean mightBeRe private PrismObject getPrismObject(String oid, boolean mightBeRemoved, OperationResult result) { try { Collection> options = SelectorOptions.createCollection(GetOperationOptions.createReadOnly()); + //noinspection unchecked return (PrismObject) cacheRepositoryService.getObject(ObjectType.class, oid, options, result); } catch (ObjectNotFoundException e) { if (!mightBeRemoved) { @@ -258,45 +249,6 @@ private String localPart(QName qname) { return qname == null ? null : qname.getLocalPart(); } - // we call this on filtered list of item deltas - all of they have definition set - private String getItemDeltaLabel(ItemDelta itemDelta, PrismObjectDefinition objectDefinition) { - return getItemPathLabel(itemDelta.getPath(), itemDelta.getDefinition(), objectDefinition); - } - - private String getItemPathLabel(ItemPath path, Definition deltaDefinition, PrismObjectDefinition objectDefinition) { - - int lastNameIndex = path.lastNameIndex(); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < path.size(); i++) { - Object segment = path.getSegment(i); - if (ItemPath.isName(segment)) { - if (sb.length() > 0) { - sb.append("/"); - } - Definition itemDefinition; - if (objectDefinition == null) { - if (i == lastNameIndex) { // definition for last segment is the definition taken from delta - itemDefinition = deltaDefinition; // this may be null but we don't care - } else { - itemDefinition = null; // definitions for previous segments are unknown - } - } else { - // todo we could make this iterative (resolving definitions while walking down the path); but this is definitely simpler to implement and debug :) - itemDefinition = objectDefinition.findItemDefinition(path.allUpToIncluding(i)); - } - if (itemDefinition != null && itemDefinition.getDisplayName() != null) { - sb.append(resolve(itemDefinition.getDisplayName())); - } else { - sb.append(ItemPath.toName(segment).getLocalPart()); - } - } else if (ItemPath.isId(segment)) { - sb.append("[").append(ItemPath.toId(segment)).append("]"); - } - } - return sb.toString(); - } - private String resolve(String key) { if (key != null) { return localizationService.translate(key, null, Locale.getDefault(), key); @@ -306,22 +258,30 @@ private String resolve(String key) { } // we call this on filtered list of items - all of them have definition set - private String getItemLabel(Item item) { + private String getItemLabel(Item item) { return item.getDefinition().getDisplayName() != null ? resolve(item.getDefinition().getDisplayName()) : item.getElementName().getLocalPart(); } - private List filterAndOrderItems(Collection items, List hiddenPaths, boolean showOperationalAttributes) { + private List> filterAndOrderItems(Collection> items, Collection hiddenPaths, boolean showOperationalAttributes) { if (items == null) { return new ArrayList<>(); } - List toBeDisplayed = new ArrayList<>(items.size()); + List> visibleItems = getVisibleItems(items, hiddenPaths, showOperationalAttributes); + visibleItems.sort((item1, item2) -> DeltaFormatter.compareDisplayOrders(item1.getDefinition(), item2.getDefinition())); + return visibleItems; + } + + @NotNull + private List> getVisibleItems(Collection> items, Collection hiddenPaths, + boolean showOperationalAttributes) { + List> visibleItems = new ArrayList<>(items.size()); List noDefinition = new ArrayList<>(); - for (Item item : items) { + for (Item item : items) { if (item.getDefinition() != null) { - boolean isHidden = NotificationFunctionsImpl.isAmongHiddenPaths(item.getPath(), hiddenPaths); + boolean isHidden = TextFormatter.isAmongHiddenPaths(item.getPath(), hiddenPaths); if (!isHidden && (showOperationalAttributes || !item.getDefinition().isOperational()) && !item.isEmpty()) { - toBeDisplayed.add(item); + visibleItems.add(item); } } else { noDefinition.add(item.getElementName()); @@ -331,20 +291,7 @@ private List filterAndOrderItems(Collection items, List hi LOGGER.error("Items {} without definition - THEY WILL NOT BE INCLUDED IN NOTIFICATION.\nAll items:\n{}", noDefinition, DebugUtil.debugDump(items)); } - toBeDisplayed.sort((item1, item2) -> { - Integer order1 = item1.getDefinition().getDisplayOrder(); - Integer order2 = item2.getDefinition().getDisplayOrder(); - if (order1 != null && order2 != null) { - return order1 - order2; - } else if (order1 == null && order2 == null) { - return 0; - } else if (order1 == null) { - return 1; - } else { - return -1; - } - }); - return toBeDisplayed; + return visibleItems; } public String formatUserName(SimpleObjectRef ref, OperationResult result) { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/FocusTypeFilterHelper.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/FocusTypeFilterHelper.java index 2a150677ad6..656b7737a36 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/FocusTypeFilterHelper.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/FocusTypeFilterHelper.java @@ -7,18 +7,16 @@ package com.evolveum.midpoint.notifications.impl.helpers; +import javax.xml.namespace.QName; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType; -import org.springframework.stereotype.Component; - -import javax.xml.namespace.QName; -/** - * @author mederly - */ @Component public class FocusTypeFilterHelper extends BaseHelper { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/KindIntentFilterHelper.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/KindIntentFilterHelper.java index 2f0e7e07b6a..31d818d55f2 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/KindIntentFilterHelper.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/KindIntentFilterHelper.java @@ -7,17 +7,15 @@ package com.evolveum.midpoint.notifications.impl.helpers; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.ResourceObjectEvent; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; -import org.springframework.stereotype.Component; -/** - * @author mederly - */ @Component public class KindIntentFilterHelper extends BaseHelper { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/NotificationExpressionHelper.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/NotificationExpressionHelper.java index 32f89acbb17..219c2a0baec 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/NotificationExpressionHelper.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/helpers/NotificationExpressionHelper.java @@ -10,6 +10,7 @@ import com.evolveum.midpoint.model.common.SystemObjectCache; import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder; import com.evolveum.midpoint.notifications.api.NotificationFunctions; +import com.evolveum.midpoint.notifications.impl.events.BaseEventImpl; import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.impl.NotificationFunctionsImpl; import com.evolveum.midpoint.notifications.impl.formatters.TextFormatter; @@ -40,9 +41,6 @@ import javax.xml.namespace.QName; import java.util.*; -/** - * @author mederly - */ @Component public class NotificationExpressionHelper { @@ -166,7 +164,7 @@ public List evaluateNotificationMessageAttach public ExpressionVariables getDefaultVariables(Event event, OperationResult result) { ExpressionVariables expressionVariables = new ExpressionVariables(); VariablesMap variables = new VariablesMap(); - event.createExpressionVariables(variables, result); + ((BaseEventImpl) event).createExpressionVariables(variables, result); variables.put(ExpressionConstants.VAR_TEXT_FORMATTER, textFormatter, TextFormatter.class); variables.put(ExpressionConstants.VAR_NOTIFICATION_FUNCTIONS, notificationsUtil, NotificationFunctions.class); PrismObject systemConfiguration = getSystemConfiguration(result); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractGeneralNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractGeneralNotifier.java index a017a31403e..c4bd3af7773 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractGeneralNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractGeneralNotifier.java @@ -7,22 +7,30 @@ package com.evolveum.midpoint.notifications.impl.notifiers; -import com.evolveum.midpoint.notifications.impl.TransportRegistry; -import com.evolveum.midpoint.notifications.impl.formatters.ValueFormatter; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; +import java.util.ArrayList; +import java.util.List; + +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.util.logging.LoggingUtils; + +import org.apache.cxf.common.util.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; import com.evolveum.midpoint.notifications.api.transports.Message; import com.evolveum.midpoint.notifications.api.transports.Transport; import com.evolveum.midpoint.notifications.impl.NotificationFunctionsImpl; +import com.evolveum.midpoint.notifications.impl.TransportRegistry; import com.evolveum.midpoint.notifications.impl.formatters.TextFormatter; +import com.evolveum.midpoint.notifications.impl.formatters.ValueFormatter; import com.evolveum.midpoint.notifications.impl.handlers.AggregatedEventHandler; import com.evolveum.midpoint.notifications.impl.handlers.BaseHandler; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; @@ -31,16 +39,9 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.apache.cxf.common.util.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; /** - * + * Base class for most notifiers. */ @Component public abstract class AbstractGeneralNotifier extends BaseHandler { @@ -69,26 +70,29 @@ public boolean processEvent(E event, N notifierConfiguration, Task task, Operati try { logStart(getLogger(), event, notifierConfiguration); - boolean applies = aggregatedEventHandler.processEvent(event, notifierConfiguration, task, result); - if (applies) { - if (!quickCheckApplicability(event, notifierConfiguration, result)) { - // nothing to do -- an appropriate message should have been logged in quickCheckApplicability method - result.recordNotApplicable(); - } else if (!checkApplicability(event, notifierConfiguration, result)) { - // nothing to do -- an appropriate message should have been logged in checkApplicability method - result.recordNotApplicable(); - } else if (notifierConfiguration.getTransport().isEmpty()) { - getLogger().warn("No transports for this notifier, exiting without sending any notifications."); - result.recordStatus(OperationResultStatus.NOT_APPLICABLE, "No transports"); - } else { - reportNotificationStart(event); - try { - ExpressionVariables variables = getDefaultVariables(event, result); - for (String transportName : notifierConfiguration.getTransport()) { - messagesSent += prepareAndSend(event, notifierConfiguration, variables, transportName, task, result); + boolean applies = false; + if (!quickCheckApplicability(event, notifierConfiguration, result)) { + // nothing to do -- an appropriate message should have been logged in quickCheckApplicability method + result.recordNotApplicable(); + } else { + if (aggregatedEventHandler.processEvent(event, notifierConfiguration, task, result)) { + if (!checkApplicability(event, notifierConfiguration, result)) { + // nothing to do -- an appropriate message should have been logged in checkApplicability method + result.recordNotApplicable(); + } else if (notifierConfiguration.getTransport().isEmpty()) { + getLogger().warn("No transports for this notifier, exiting without sending any notifications."); + result.recordStatus(OperationResultStatus.NOT_APPLICABLE, "No transports"); + } else { + applies = true; + reportNotificationStart(event); + try { + ExpressionVariables variables = getDefaultVariables(event, result); + for (String transportName : notifierConfiguration.getTransport()) { + messagesSent += prepareAndSend(event, notifierConfiguration, variables, transportName, task, result); + } + } finally { + reportNotificationEnd(event, result); } - } finally { - reportNotificationEnd(event, result); } } } @@ -194,6 +198,14 @@ protected String getContentType() { return null; } + /** + * Checks the event/notifier applicability _before_ nested filters are applied. So this check should be: + * 1) quick + * 2) safe - it should not make any assumptions about event content that would cause it to throw an exception + * 3) filter out events that obviously do not match the notifier - e.g. simpleUserNotifier should ensure that + * the focus type is really UserType; this allows nested filters to assume existence of + * e.g. requestee.fullName element. + */ protected boolean quickCheckApplicability(E event, N generalNotifierType, OperationResult result) { return true; } @@ -333,17 +345,6 @@ private List getAttachmentsFromExpression(E e } } - // TODO implement more efficiently - // precondition: delta is MODIFY delta - boolean deltaContainsOtherPathsThan(ObjectDelta delta, List paths) { - for (ItemDelta itemDelta : delta.getModifications()) { - if (!NotificationFunctionsImpl.isAmongHiddenPaths(itemDelta.getPath(), paths)) { - return true; - } - } - return false; - } - boolean isWatchAuxiliaryAttributes(N configuration) { return Boolean.TRUE.equals(configuration.isWatchAuxiliaryAttributes()); } @@ -361,4 +362,25 @@ String formatRequester(E event, OperationResult result) { return name; } } + + void addRequesterAndChannelInformation(StringBuilder body, Event event, OperationResult result) { + if (event.getRequester() != null) { + body.append("Requester: "); + try { + ObjectType requester = event.getRequester().resolveObjectType(result, false); + if (requester instanceof UserType) { + UserType requesterUser = (UserType) requester; + body.append(requesterUser.getFullName()).append(" (").append(requester.getName()).append(")"); + } else { + body.append(ObjectTypeUtil.toShortString(requester)); + } + } catch (RuntimeException e) { + body.append("couldn't be determined: ").append(e.getMessage()); + LoggingUtils.logUnexpectedException(getLogger(), "Couldn't determine requester for a notification", e); + } + body.append("\n"); + } + body.append("Channel: ").append(event.getChannel()).append("\n\n"); + } + } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractPolicyRuleNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractPolicyRuleNotifier.java index 4ad44a9f410..6f5e6aa3e8b 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractPolicyRuleNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AbstractPolicyRuleNotifier.java @@ -13,8 +13,6 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.SimplePolicyRuleNotifierType; /** diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountActivationNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountActivationNotifier.java index 2e84ce3e169..abd3eef8d66 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountActivationNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountActivationNotifier.java @@ -13,15 +13,12 @@ import org.springframework.stereotype.Component; import com.evolveum.midpoint.model.api.context.ModelElementContext; -import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccountActivationNotifierType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.GeneralNotifierType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @@ -75,9 +72,9 @@ protected String getBody(ModelEvent event, AccountActivationNotifierType configu String message = "Your accounts was successfully created. To activate your accounts, please click on the link bellow."; - String accountsToActivate = "Shadow to be activated: \n"; - for (ShadowType shadow : getShadowsToActivate((ModelEvent) event)) { - accountsToActivate = accountsToActivate + shadow.asPrismObject().debugDump() + "\n"; + StringBuilder accountsToActivate = new StringBuilder("Shadow to be activated: \n"); + for (ShadowType shadow : getShadowsToActivate(event)) { + accountsToActivate.append(shadow.asPrismObject().debugDump()).append("\n"); } return message + "\n\n" + createConfirmationLink(getUser(event), configuration, result) + "\n\n" + accountsToActivate; diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountPasswordNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountPasswordNotifier.java index 196851da1fc..329f40537ba 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountPasswordNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/AccountPasswordNotifier.java @@ -8,13 +8,11 @@ package com.evolveum.midpoint.notifications.impl.notifiers; import com.evolveum.midpoint.notifications.api.events.ResourceObjectEvent; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccountPasswordNotifierType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import org.springframework.stereotype.Component; @Component @@ -37,9 +35,11 @@ protected boolean checkApplicability(ResourceObjectEvent event, AccountPasswordN if (!event.isSuccess()) { LOGGER.trace("Operation was not successful, exiting."); return false; + } else if (event.getPlaintextPassword() == null) { + LOGGER.trace("No password in delta, exiting."); + return false; } else { - ObjectDelta delta = event.getAccountOperationDescription().getObjectDelta(); - return delta != null && functions.getPlaintextPasswordFromDelta(delta) != null; + return true; } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/PasswordResetNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/PasswordResetNotifier.java index 91e4b7522ff..791bfa59799 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/PasswordResetNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/PasswordResetNotifier.java @@ -7,16 +7,15 @@ package com.evolveum.midpoint.notifications.impl.notifiers; +import com.evolveum.midpoint.notifications.api.events.ModelEvent; + import org.springframework.stereotype.Component; -import com.evolveum.midpoint.notifications.api.events.Event; -import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.GeneralNotifierType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordResetNotifierType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/RegistrationConfirmationNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/RegistrationConfirmationNotifier.java index 30317740167..b3aae040dd6 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/RegistrationConfirmationNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/RegistrationConfirmationNotifier.java @@ -8,9 +8,10 @@ package com.evolveum.midpoint.notifications.impl.notifiers; +import com.evolveum.midpoint.notifications.api.events.ModelEvent; + import org.springframework.stereotype.Component; -import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java index b430b2a64dd..6f54844d842 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java @@ -87,7 +87,7 @@ protected String getBody(CertCampaignEvent event, SimpleCampaignNotifierType con certHelper.appendStatistics(body, campaign, task, result); body.append("\n\n"); - functions.addRequesterAndChannelInformation(body, event, result); + addRequesterAndChannelInformation(body, event, result); return body.toString(); } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java index c346332455f..008a26dcd46 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java @@ -110,7 +110,7 @@ protected String getBody(CertCampaignStageEvent event, SimpleCampaignStageNotifi certHelper.appendStatistics(body, campaign, task, result); body.append("\n\n"); - functions.addRequesterAndChannelInformation(body, event, result); + addRequesterAndChannelInformation(body, event, result); return body.toString(); } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java index c9c57126579..830ddea790f 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java @@ -10,7 +10,6 @@ import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; import java.util.Date; -import java.util.List; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -54,7 +53,8 @@ Class getFocusClass() { @Override protected boolean quickCheckApplicability(ModelEvent event, SimpleFocalObjectNotifierType configuration, OperationResult result) { - if (event.getFocusContext() == null || !event.getFocusContext().isOfType(getFocusClass())) { + event.getFocusContext(); + if (!event.hasFocusOfType(getFocusClass())) { LOGGER.trace("{} is not applicable to non-{} related model operations, continuing in the handler chain", getClass().getSimpleName(), getFocusClass()); return false; @@ -65,20 +65,11 @@ protected boolean quickCheckApplicability(ModelEvent event, SimpleFocalObjectNot @Override protected boolean checkApplicability(ModelEvent event, SimpleFocalObjectNotifierType configuration, OperationResult result) { - List> deltas = event.getFocusDeltas(); - if (deltas.isEmpty()) { - LOGGER.trace("No deltas found, skipping the notification"); + if (!event.hasContentToShow(isWatchAuxiliaryAttributes(configuration))) { + LOGGER.trace("No modifications to show, skipping the notification"); return false; - } else if (isWatchAuxiliaryAttributes(configuration)) { - return true; } else { - for (ObjectDelta delta : deltas) { - if (!delta.isModify() || deltaContainsOtherPathsThan(delta, functions.getAuxiliaryPaths())) { - return true; - } - } - LOGGER.trace("No deltas for non-auxiliary attributes found, skipping the notification"); - return false; + return true; } } @@ -100,16 +91,16 @@ protected String getSubject(ModelEvent event, SimpleFocalObjectNotifierType conf } @Override - protected String getBody(ModelEvent modelEvent, SimpleFocalObjectNotifierType configuration, String transport, + protected String getBody(ModelEvent event, SimpleFocalObjectNotifierType configuration, String transport, Task task, OperationResult result) throws SchemaException { - String typeName = modelEvent.getFocusTypeName(); + String typeName = event.getFocusTypeName(); String typeNameLower = typeName.toLowerCase(); boolean techInfo = Boolean.TRUE.equals(configuration.isShowTechnicalInformation()); //noinspection unchecked - ModelContext modelContext = (ModelContext) modelEvent.getModelContext(); + ModelContext modelContext = (ModelContext) event.getModelContext(); ModelElementContext focusContext = modelContext.getFocusContext(); PrismObject focusObject = focusContext.getObjectNew() != null ? focusContext.getObjectNew() : focusContext.getObjectOld(); AssignmentHolderType focus = focusObject.asObjectable(); @@ -117,12 +108,12 @@ protected String getBody(ModelEvent modelEvent, SimpleFocalObjectNotifierType co String fullName = emptyIfNull(getFullName(focus)); - ObjectDelta delta = ObjectDeltaCollectionsUtil.summarize(modelEvent.getFocusDeltas()); + ObjectDelta delta = ObjectDeltaCollectionsUtil.summarize(event.getFocusDeltas()); StringBuilder body = new StringBuilder(); - String status = modelEvent.getStatusAsText(); - String attemptedTo = modelEvent.isSuccess() ? "" : "(attempted to be) "; + String status = event.getStatusAsText(); + String attemptedTo = event.isSuccess() ? "" : "(attempted to be) "; body.append("Notification about ").append(typeNameLower).append("-related operation (status: ").append(status).append(")\n\n"); body.append(typeName).append(": ").append(fullName).append(" (").append(focus.getName()).append(", oid ").append(oid).append(")\n"); @@ -131,20 +122,20 @@ protected String getBody(ModelEvent modelEvent, SimpleFocalObjectNotifierType co final boolean watchAuxiliaryAttributes = isWatchAuxiliaryAttributes(configuration); if (delta.isAdd()) { body.append("The ").append(typeNameLower).append(" record was ").append(attemptedTo).append("created with the following data:\n"); - body.append(modelEvent.getContentAsFormattedList(false, watchAuxiliaryAttributes)); + body.append(event.getContentAsFormattedList( watchAuxiliaryAttributes)); } else if (delta.isModify()) { body.append("The ").append(typeNameLower).append(" record was ").append(attemptedTo).append("modified. Modified attributes are:\n"); - body.append(modelEvent.getContentAsFormattedList(false, watchAuxiliaryAttributes)); + body.append(event.getContentAsFormattedList(watchAuxiliaryAttributes)); } else if (delta.isDelete()) { body.append("The ").append(typeNameLower).append(" record was ").append(attemptedTo).append("removed.\n"); } body.append("\n"); - if (!modelEvent.isSuccess()) { + if (!event.isSuccess()) { body.append("More information about the status of the request was displayed and/or is present in log files.\n\n"); } - functions.addRequesterAndChannelInformation(body, modelEvent, result); + addRequesterAndChannelInformation(body, event, result); if (techInfo) { body.append("----------------------------------------\n"); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleResourceObjectNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleResourceObjectNotifier.java index 9d3856a42a7..e5bf7fc0b38 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleResourceObjectNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleResourceObjectNotifier.java @@ -21,9 +21,6 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -/** - * - */ @Component public class SimpleResourceObjectNotifier extends AbstractGeneralNotifier { @@ -41,23 +38,9 @@ public Class getEventHandlerConfigurationType( @Override protected boolean checkApplicability(ResourceObjectEvent event, SimpleResourceObjectNotifierType configuration, OperationResult result) { - - ObjectDelta delta = event.getShadowDelta(); - if (!delta.isModify()) { - return true; - } - - boolean otherThanSyncPresent = deltaContainsOtherPathsThan(delta, functions.getSynchronizationPaths()); - boolean otherThanAuxPresent = deltaContainsOtherPathsThan(delta, functions.getAuxiliaryPaths()); - boolean watchSync = isWatchSynchronizationAttributes(configuration); - boolean watchAux = isWatchAuxiliaryAttributes(configuration); - if ((watchSync || otherThanSyncPresent) && (watchAux || otherThanAuxPresent)) { - return true; - } - - LOGGER.trace("No relevant attributes in delta, skipping the notifier (watchSync = " + watchSync + ", otherThanSyncPresent = " + otherThanSyncPresent + - ", watchAux = " + watchAux + ", otherThanAuxPresent = " + otherThanAuxPresent + ")"); - return false; + return event.hasContentToShow( + isWatchSynchronizationAttributes(configuration), + isWatchAuxiliaryAttributes(configuration)); } private boolean isWatchSynchronizationAttributes(SimpleResourceObjectNotifierType configuration) { @@ -66,7 +49,7 @@ private boolean isWatchSynchronizationAttributes(SimpleResourceObjectNotifierTyp @Override protected String getSubject(ResourceObjectEvent event, SimpleResourceObjectNotifierType configuration, String transport, Task task, OperationResult result) { - ResourceOperationDescription rod = event.getAccountOperationDescription(); + ResourceOperationDescription rod = event.getOperationDescription(); //noinspection unchecked ObjectDelta delta = (ObjectDelta) rod.getObjectDelta(); @@ -91,7 +74,7 @@ protected String getBody(ResourceObjectEvent event, SimpleResourceObjectNotifier StringBuilder body = new StringBuilder(); FocusType owner = (FocusType) event.getRequesteeObject(); - ResourceOperationDescription rod = event.getAccountOperationDescription(); + ResourceOperationDescription rod = event.getOperationDescription(); //noinspection unchecked ObjectDelta delta = (ObjectDelta) rod.getObjectDelta(); @@ -152,11 +135,11 @@ protected String getBody(ResourceObjectEvent event, SimpleResourceObjectNotifier if (event.getOperationStatus() == OperationStatus.IN_PROGRESS) { body.append("The operation will be retried.\n\n"); } else if (event.getOperationStatus() == OperationStatus.FAILURE) { - body.append("Error: ").append(event.getAccountOperationDescription().getResult().getMessage()).append("\n\n"); + body.append("Error: ").append(event.getOperationDescription().getResult().getMessage()).append("\n\n"); } body.append("\n\n"); - functions.addRequesterAndChannelInformation(body, event, result); + addRequesterAndChannelInformation(body, event, result); if (techInfo) { body.append("----------------------------------------\n"); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleWorkflowNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleWorkflowNotifier.java index 285057c2af8..273f62d21ac 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleWorkflowNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleWorkflowNotifier.java @@ -12,6 +12,8 @@ import java.util.stream.Collectors; import javax.xml.datatype.XMLGregorianCalendar; +import com.evolveum.midpoint.notifications.impl.events.*; + import org.apache.commons.lang3.time.DurationFormatUtils; import org.jetbrains.annotations.Nullable; import org.springframework.stereotype.Component; @@ -28,8 +30,6 @@ /** * Default implementation of a notifier dealing with workflow events (related to both work items and process instances). - * - * @author mederly */ @Component public class SimpleWorkflowNotifier extends AbstractGeneralNotifier { @@ -49,10 +49,10 @@ public Class getEventHandlerConfigurationType() { @Override protected UserType getDefaultRecipient(WorkflowEvent event, SimpleWorkflowNotifierType configuration, OperationResult result) { @Nullable SimpleObjectRef recipientRef; - if (event instanceof WorkflowProcessEvent) { + if (event instanceof WorkflowProcessEventImpl) { recipientRef = event.getRequester(); - } else if (event instanceof WorkItemEvent) { - recipientRef = ((WorkItemEvent) event).getAssignee(); + } else if (event instanceof WorkItemEventImpl) { + recipientRef = ((WorkItemEventImpl) event).getAssignee(); } else { return null; } @@ -66,17 +66,17 @@ protected UserType getDefaultRecipient(WorkflowEvent event, SimpleWorkflowNotifi @Override protected String getSubject(WorkflowEvent event, SimpleWorkflowNotifierType configuration, String transport, Task task, OperationResult result) { - if (event instanceof WorkflowProcessEvent) { + if (event instanceof WorkflowProcessEventImpl) { return event.isAdd() ? "Workflow process instance has been started" : "Workflow process instance has finished"; - } else if (event instanceof WorkItemEvent) { - return getSubjectFromWorkItemEvent((WorkItemEvent) event); + } else if (event instanceof WorkItemEventImpl) { + return getSubjectFromWorkItemEvent((WorkItemEventImpl) event); } else { throw new UnsupportedOperationException("Unsupported event type for event=" + event); } } - private String getSubjectFromWorkItemEvent(WorkItemEvent event) { - if (event instanceof WorkItemLifecycleEvent) { + private String getSubjectFromWorkItemEvent(WorkItemEventImpl event) { + if (event instanceof WorkItemLifecycleEventImpl) { if (event.isAdd()) { return "A new work item has been created"; } else if (event.isDelete()) { @@ -88,7 +88,7 @@ private String getSubjectFromWorkItemEvent(WorkItemEvent event) { } else { throw new UnsupportedOperationException("workItemLifecycle event with MODIFY operation is not supported"); } - } else if (event instanceof WorkItemAllocationEvent) { + } else if (event instanceof WorkItemAllocationEventImpl) { if (event.isAdd()) { return "Work item has been allocated to you"; } else if (event.isModify()) { @@ -104,7 +104,7 @@ private String getSubjectFromWorkItemEvent(WorkItemEvent event) { } else { return "Work item has been " + getOperationPastTenseVerb(event.getOperationKind()); } - } else if (event instanceof WorkItemCustomEvent) { + } else if (event instanceof WorkItemCustomEventImpl) { return "A notification about work item"; } else { throw new UnsupportedOperationException("Unsupported event type for event=" + event); @@ -124,8 +124,8 @@ protected String getBody(WorkflowEvent event, SimpleWorkflowNotifierType configu appendGeneralInformation(body, event); // process instance name, work item name, stage, escalation level - if (event instanceof WorkItemEvent) { - WorkItemEvent workItemEvent = (WorkItemEvent) event; + if (event instanceof WorkItemEventImpl) { + WorkItemEventImpl workItemEvent = (WorkItemEventImpl) event; appendAssigneeInformation(body, workItemEvent, result); appendResultAndOriginInformation(body, workItemEvent, result); appendDeadlineInformation(body, workItemEvent); @@ -137,8 +137,8 @@ protected String getBody(WorkflowEvent event, SimpleWorkflowNotifierType configu if (techInfo) { body.append("----------------------------------------\n"); body.append("Technical information:\n\n"); - if (event instanceof WorkItemEvent) { - WorkItemEvent workItemEvent = (WorkItemEvent) event; + if (event instanceof WorkItemEventImpl) { + WorkItemEventImpl workItemEvent = (WorkItemEventImpl) event; body.append("WorkItem:\n") .append(PrismUtil.serializeQuietly(prismContext, workItemEvent.getWorkItem())) .append("\n"); @@ -151,8 +151,8 @@ protected String getBody(WorkflowEvent event, SimpleWorkflowNotifierType configu private void appendGeneralInformation(StringBuilder sb, WorkflowEvent workflowEvent) { sb.append("Process instance name: ").append(workflowEvent.getProcessInstanceName()).append("\n"); - if (workflowEvent instanceof WorkItemEvent) { - WorkItemEvent event = (WorkItemEvent) workflowEvent; + if (workflowEvent instanceof WorkItemEventImpl) { + WorkItemEventImpl event = (WorkItemEventImpl) workflowEvent; sb.append("Work item: ").append(event.getWorkItemName()).append("\n"); appendStageInformation(sb, event); appendEscalationInformation(sb, event); @@ -174,7 +174,7 @@ private boolean appendResultInformation(StringBuilder body, WorkflowEvent workfl } } - private void appendDeadlineInformation(StringBuilder sb, WorkItemEvent event) { + private void appendDeadlineInformation(StringBuilder sb, WorkItemEventImpl event) { CaseWorkItemType workItem = event.getWorkItem(); if (!isDone(event) && workItem.getDeadline() != null) { appendDeadlineInformation(sb, workItem); @@ -198,7 +198,7 @@ private void appendDeadlineInformation(StringBuilder sb, AbstractWorkItemType wo sb.append("\n"); } - private void appendResultAndOriginInformation(StringBuilder sb, WorkItemEvent event, OperationResult result) { + private void appendResultAndOriginInformation(StringBuilder sb, WorkItemEventImpl event, OperationResult result) { boolean atLeastOne = appendResultInformation(sb, event, false); WorkItemEventCauseInformationType cause = event.getCause(); if (cause != null && cause.getType() == WorkItemEventCauseTypeType.TIMED_ACTION) { @@ -225,7 +225,7 @@ private void appendResultAndOriginInformation(StringBuilder sb, WorkItemEvent ev } } - private void appendAssigneeInformation(StringBuilder sb, WorkItemEvent event, OperationResult result) { + private void appendAssigneeInformation(StringBuilder sb, WorkItemEventImpl event, OperationResult result) { CaseWorkItemType workItem = event.getWorkItem(); ObjectReferenceType originalAssignee = workItem.getOriginalAssigneeRef(); List currentAssignees = workItem.getAssigneeRef(); @@ -257,10 +257,10 @@ private void appendAssigneeInformation(StringBuilder sb, WorkItemEvent event, Op // a bit of heuristics... @SuppressWarnings("SimplifiableIfStatement") - private boolean isDone(WorkItemEvent event) { - if (event instanceof WorkItemLifecycleEvent) { + private boolean isDone(WorkItemEventImpl event) { + if (event instanceof WorkItemLifecycleEventImpl) { return event.isDelete(); - } else if (event instanceof WorkItemAllocationEvent) { + } else if (event instanceof WorkItemAllocationEventImpl) { return event.isDelete() && (event.getOperationKind() == null || event.getOperationKind() == WorkItemOperationKindType.CANCEL || event.getOperationKind() == WorkItemOperationKindType.COMPLETE); @@ -269,13 +269,13 @@ private boolean isDone(WorkItemEvent event) { } } - private boolean isCancelled(WorkItemEvent event) { - return (event instanceof WorkItemLifecycleEvent || event instanceof WorkItemAllocationEvent) + private boolean isCancelled(WorkItemEventImpl event) { + return (event instanceof WorkItemLifecycleEventImpl || event instanceof WorkItemAllocationEventImpl) && event.isDelete() && (event.getOperationKind() == null || event.getOperationKind() == WorkItemOperationKindType.CANCEL); } - private void appendEscalationInformation(StringBuilder sb, WorkItemEvent workItemEvent) { + private void appendEscalationInformation(StringBuilder sb, WorkItemEventImpl workItemEvent) { String info = ApprovalContextUtil.getEscalationLevelInfo(workItemEvent.getWorkItem()); if (info != null) { sb.append("Escalation level: ").append(info).append("\n"); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/ApplicationContextHolder.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/ApplicationContextHolder.java new file mode 100644 index 00000000000..a06ac82e7e3 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/ApplicationContextHolder.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.util; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Holds the Spring application context. + * It is needed to provide necessary Spring beans to serializable lightweight objects (events, references). + */ +@Component +public class ApplicationContextHolder implements ApplicationContextAware { + + private static ApplicationContext context; + + @Override + public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } + + public static ApplicationContext getContext() { + return context; + } + + public static T getBean(Class beanClass) { + return context.getBean(beanClass); + } +} diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/EventHelper.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/EventHelper.java new file mode 100644 index 00000000000..1d8745547b7 --- /dev/null +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/util/EventHelper.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.notifications.impl.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.notifications.api.NotificationManager; +import com.evolveum.midpoint.notifications.api.events.Event; +import com.evolveum.midpoint.schema.result.OperationResult; +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; + +@Component +public class EventHelper { + + private static final Trace LOGGER = TraceManager.getTrace(EventHelper.class); + + @Autowired private NotificationManager notificationManager; + + public void processEvent(Event event, Task task, OperationResult result) { + try { + notificationManager.processEvent(event, task, result); + } 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(); + } +} diff --git a/model/notifications-impl/src/test/java/com/evolveum/midpoint/notifications/impl/TestTextFormatter.java b/model/notifications-impl/src/test/java/com/evolveum/midpoint/notifications/impl/TestTextFormatter.java index 683121ef9bb..77d0f29ee9d 100644 --- a/model/notifications-impl/src/test/java/com/evolveum/midpoint/notifications/impl/TestTextFormatter.java +++ b/model/notifications-impl/src/test/java/com/evolveum/midpoint/notifications/impl/TestTextFormatter.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Locale; +import com.evolveum.midpoint.notifications.impl.formatters.ValueFormatter; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.constants.ObjectTypes; @@ -105,6 +106,7 @@ public class TestTextFormatter extends AbstractTestNGSpringContextTests { ); @Autowired private TextFormatter textFormatter; + @Autowired private ValueFormatter valueFormatter; @Autowired private PrismContext prismContext; static { @@ -251,10 +253,10 @@ public void test030FormatAccount() throws Exception { when(); - String jackFormattedHideNone = textFormatter.formatAccountAttributes(jack.asObjectable(), null, true); + String jackFormattedHideNone = valueFormatter.formatAccountAttributes(jack.asObjectable(), null, true); System.out.println("no hidden paths + show operational attributes: " + jackFormattedHideNone); - String jackFormattedHideAux = textFormatter.formatAccountAttributes(jack.asObjectable(), auxiliaryPaths, true); + String jackFormattedHideAux = valueFormatter.formatAccountAttributes(jack.asObjectable(), auxiliaryPaths, true); System.out.println("hide auxiliary paths + show operational attributes: " + jackFormattedHideAux); then(); diff --git a/repo/task-api/src/main/java/com/evolveum/midpoint/task/api/LightweightIdentifier.java b/repo/task-api/src/main/java/com/evolveum/midpoint/task/api/LightweightIdentifier.java index 18ad24a4662..903a682bb60 100644 --- a/repo/task-api/src/main/java/com/evolveum/midpoint/task/api/LightweightIdentifier.java +++ b/repo/task-api/src/main/java/com/evolveum/midpoint/task/api/LightweightIdentifier.java @@ -6,6 +6,7 @@ */ package com.evolveum.midpoint.task.api; +import java.io.Serializable; import java.util.Objects; /** @@ -19,7 +20,7 @@ * * @author semancik */ -public class LightweightIdentifier { +public class LightweightIdentifier implements Serializable { private static final String SEPARATOR = "-"; @@ -74,5 +75,4 @@ public int hashCode() { public String toString() { return string; } - }