diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java index 827b4504dd0..c3022fb0d3c 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java @@ -59,8 +59,16 @@ public ItemPath(QName... qnames) { add(qname); } } - - public ItemPath(ItemPath parentPath, QName subName) { + + public ItemPath(String... names) { + this.segments = new ArrayList(names.length); + for (String name : names) { + add(new QName(name)); + } + } + + + public ItemPath(ItemPath parentPath, QName subName) { this.segments = new ArrayList(parentPath.segments.size()+1); segments.addAll(parentPath.segments); add(subName); diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java index 12e81f09716..04d65dca5d3 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/BaseEvent.java @@ -16,7 +16,18 @@ package com.evolveum.midpoint.notifications.api.events; +import com.evolveum.midpoint.prism.Item; +import com.evolveum.midpoint.prism.PrismContainer; +import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.PrismProperty; +import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.path.IdItemPathSegment; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.path.NameItemPathSegment; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifier; @@ -24,9 +35,13 @@ 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.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import javax.xml.namespace.QName; +import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -159,4 +174,117 @@ public void createExpressionVariables(Map variables, OperationRes variables.put(SchemaConstants.C_REQUESTER, requester != null ? requester.resolveObjectType(result) : null); variables.put(SchemaConstants.C_REQUESTEE, requestee != null ? requestee.resolveObjectType(result) : null); } + + // 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) + // + // Very experimental implementation. Needs a bit of time to clean up and test adequately. + public boolean containsItem(ObjectDelta delta, ItemPath itemPath) { + if (delta.getChangeType() == ChangeType.ADD) { + return containsItem(delta.getObjectToAdd(), itemPath); + } else if (delta.getChangeType() == ChangeType.MODIFY) { + return containsItemInModifications(delta.getModifications(), itemPath); + } else { + return false; + } + } + + private boolean containsItemInModifications(Collection modifications, ItemPath itemPath) { + for (ItemDelta itemDelta : modifications) { + if (containsItem(itemDelta, itemPath)) { + return true; + } + } + return false; + } + + private boolean containsItem(ItemDelta itemDelta, ItemPath itemPath) { + ItemPath namesOnlyPathTested = itemPath.namedSegmentsOnly(); + ItemPath namesOnlyPathInDelta = itemDelta.getPath().namedSegmentsOnly(); + if (namesOnlyPathTested.isSubPathOrEquivalent(namesOnlyPathInDelta)) { + return true; + } + // however, we can add/delete whole container (containing part of the path) + // e.g. we can test for activation/administrativeStatus, and the delta is: + // ADD activation VALUE (administrativeStatus=ENABLED) + if (!namesOnlyPathInDelta.isSubPath(namesOnlyPathTested)) { + return false; + } + // for ADD values we know + // for REPLACE values we know - for values being added, but NOT for values being left behind + // for DELETE we have a problem if we are deleting "by ID" - we just don't know if the value being deleted contains the path in question or not + + ItemPath remainder = namesOnlyPathTested.remainder(namesOnlyPathInDelta); + return containsItemInValues(itemDelta.getValuesToAdd(), remainder) || + containsItemInValues(itemDelta.getValuesToReplace(), remainder) || + containsItemInValues(itemDelta.getValuesToDelete(), remainder); + } + + // remainder contains only named segments and is not empty + private boolean containsItemInValues(Collection values, ItemPath remainder) { + if (values == null) { + return false; + } + for (PrismValue value : values) { + if (value instanceof PrismContainerValue) { // we do not want to look inside references nor primitive values + if (containsItem((PrismContainerValue) value, remainder)) { + return true; + } + } + } + return false; + } + + public boolean containsItem(List> deltas, ItemPath itemPath) { + for (ObjectDelta objectDelta : deltas) { + if (containsItem(objectDelta, itemPath)) { + return true; + } + } + return false; + } + + // itemPath is empty or starts with named item path segment + private boolean containsItem(PrismContainer container, ItemPath itemPath) { + if (container.size() == 0) { + return false; // there is a container, but no values + } + if (itemPath.isEmpty()) { + return true; + } + for (Object o : container.getValues()) { + if (containsItem((PrismContainerValue) o, itemPath)) { + return true; + } + } + return false; + } + + // path starts with named item path segment + private boolean containsItem(PrismContainerValue prismContainerValue, ItemPath itemPath) { + QName first = ((NameItemPathSegment) itemPath.first()).getName(); + Item item = prismContainerValue.findItem(first); + if (item == null) { + return false; + } + ItemPath pathTail = pathTail(itemPath); + if (item instanceof PrismContainer) { + 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) { + return pathTail.isEmpty(); // ...neither inside atomic values + } else { + return false; // should not occur anyway + } + } + + private ItemPath pathTail(ItemPath itemPath) { + while (!itemPath.isEmpty() && itemPath.first() instanceof IdItemPathSegment) { + itemPath = itemPath.tail(); + } + return itemPath; + } + + } 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 5173a90a21a..ee391d21882 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 @@ -16,6 +16,7 @@ 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.task.api.LightweightIdentifier; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -78,4 +79,26 @@ public interface Event { void setRequestee(SimpleObjectRef requestee); void createExpressionVariables(Map variables, OperationResult result); + + /** + * Checks if the event is related to an item with a given path. + * The meaning of the result depends on a kind of event (focal, resource object, workflow) + * and on operation (add, modify, delete). + * + * Namely, this method is currently defined for ADD and MODIFY (not for DELETE) operations, + * for focal and resource objects events (not for workflow ones). + * + * For MODIFY it checks whether an item with a given path is touched. + * For ADD it checks whether there is a value for an item with a given path in the object created. + * + * For unsupported events the method returns false. + * + * 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); } 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 6fa8d6f7e84..002cb4a0902 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 @@ -23,6 +23,7 @@ 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.path.ItemPath; import com.evolveum.midpoint.schema.ObjectDeltaOperation; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.util.exception.SchemaException; @@ -180,4 +181,10 @@ public boolean hasFocusOfType(QName focusType) { } return hasFocusOfType(expectedClass); } + + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return containsItem(getFocusDeltas(), itemPath); + } + } 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 e32c2da7e9a..9d6e7b0e7a4 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 @@ -19,6 +19,7 @@ import com.evolveum.midpoint.notifications.api.OperationStatus; 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.logging.Trace; @@ -128,6 +129,11 @@ public boolean isStatusType(EventStatusType eventStatusType) { return operationStatus.matchesEventStatusType(eventStatusType); } + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return containsItem(getShadowDelta(), itemPath); + } + @Override public String toString() { return "ResourceObjectEvent{" + 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 bc0bdacad3a..826e8906b07 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 @@ -19,6 +19,7 @@ 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.path.ItemPath; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.wf.util.ApprovalUtils; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; @@ -114,6 +115,10 @@ private OperationStatus resultToStatus(ChangeType changeType, String decision) { } } + @Override + public boolean isRelatedToItem(ItemPath itemPath) { + return false; + } @Override public String toString() {