From 2b79ef3cd30eaf1a95bde9b5e935d3d3b73ead0a Mon Sep 17 00:00:00 2001
From: lskublik
Date: Thu, 3 Sep 2020 15:07:18 +0200
Subject: [PATCH 01/12] adding of using query api for audit reports and
dashboard with audit widgets instead of SQL query
---
.../schema/constants/AuditConstants.java | 47 -----
.../xml/ns/public/common/audit-3.xsd | 69 +++++-
.../xml/ns/public/common/common-core-3.xsd | 2 +
.../api/interaction/DashboardService.java | 3 +-
.../model/common/util/DefaultColumnUtils.java | 107 +++-------
.../impl/controller/DashboardServiceImpl.java | 197 ++++++++++++++----
.../controller/fileformat/CsvController.java | 37 ++--
.../fileformat/FileFormatController.java | 135 +++---------
.../controller/fileformat/HtmlController.java | 32 +--
.../object-collection-all-audit-records.xml | 17 +-
.../midpoint/audit/api/AuditEventRecord.java | 10 +
11 files changed, 353 insertions(+), 303 deletions(-)
delete mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/AuditConstants.java
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/AuditConstants.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/AuditConstants.java
deleted file mode 100644
index dc92bae3a04..00000000000
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/AuditConstants.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2010-2019 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.schema.constants;
-
-/**
- * Constants for localization key of COLUMN_NAMEs labels of audit widget report.
- *
- * @author skublik
- */
-public class AuditConstants {
-
- public static final String TIME_COLUMN = "time";
- public static final String INITIATOR_COLUMN = "initiator";
- public static final String EVENT_STAGE_COLUMN = "eventStage";
- public static final String EVENT_TYPE_COLUMN = "eventType";
- public static final String TARGET_COLUMN = "target";
- public static final String DELTA_COLUMN = "delta";
- public static final String MESSAGE_COLUMN = "message";
- public static final String TARGET_OWNER_COLUMN = "targetOwner";
- public static final String CHANNEL_COLUMN = "channel";
- public static final String OUTCOME_COLUMN = "outcome";
- public static final String TASK_OID_COLUMN = "taskOid";
- public static final String NODE_IDENTIFIER_COLUMN = "nodeIdentifier";
- public static final String ATTORNEY_COLUMN = "attorney";
- public static final String RESULT_COLUMN = "result";
- public static final String RESOURCE_OID_COLUMN = "resourceOid";
-
- public static final String TIME_COLUMN_KEY = "AuditLocalizationConstants.time";
- public static final String INITIATOR_COLUMN_KEY = "AuditLocalizationConstants.initiator";
- public static final String EVENT_STAGE_COLUMN_KEY = "AuditLocalizationConstants.eventStage";
- public static final String EVENT_TYPE_COLUMN_KEY = "AuditLocalizationConstants.eventType";
- public static final String TARGET_COLUMN_KEY = "AuditLocalizationConstants.target";
- public static final String DELTA_COLUMN_KEY = "AuditLocalizationConstants.delta";
- public static final String MESSAGE_COLUMN_KEY = "AuditLocalizationConstants.message";
- public static final String TARGET_OWNER_COLUMN_KEY = "AuditLocalizationConstants.targetOwner";
- public static final String CHANNEL_COLUMN_KEY = "AuditLocalizationConstants.channel";
- public static final String OUTCOME_COLUMN_KEY = "AuditLocalizationConstants.outcome";
- public static final String TASK_OID_COLUMN_KEY = "AuditLocalizationConstants.taskOid";
- public static final String NODE_IDENTIFIER_COLUMN_KEY = "AuditLocalizationConstants.nodeIdentifier";
- public static final String ATTORNEY_COLUMN_KEY = "AuditLocalizationConstants.attorney";
- public static final String RESULT_COLUMN_KEY = "AuditLocalizationConstants.result";
- public static final String RESOURCE_OID_COLUMN_KEY = "AuditLocalizationConstants.resourceOid";
-}
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/audit-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/audit-3.xsd
index cc89ec9588c..8ab87d1b5ed 100644
--- a/infra/schema/src/main/resources/xml/ns/public/common/audit-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/audit-3.xsd
@@ -77,6 +77,9 @@
Timestamp when the event occurred.
+
+ AuditEventRecordType.timestamp
+
@@ -85,6 +88,9 @@
Unique identification of the event.
Every record should have unique event identifier.
+
+ AuditEventRecordType.eventIdentifier
+
@@ -92,6 +98,9 @@
Identification of (interactive) session in which the event occurred.
+
+ AuditEventRecordType.sessionIdentifier
+
@@ -112,6 +121,9 @@
operation will have a separate request identifier.
+
+ AuditEventRecordType.requestIdentifier
+
@@ -133,6 +145,9 @@
operation will have a separate request identifier.
+
+ AuditEventRecordType.taskIdentifier
+
@@ -141,6 +156,9 @@
Task OID. This field is used for records that are executed in the context
of a persistent task.
+
+ AuditEventRecordType.taskOID
+
@@ -149,6 +167,9 @@
Identifier of a host that generated the audit record.
This is the host name corresponding to a network interface that accepted the HTTP request.
+
+ AuditEventRecordType.hostIdentifier
+
@@ -156,6 +177,9 @@
TODO
+
+ AuditEventRecordType.nodeIdentifier
+
@@ -163,6 +187,9 @@
TODO
+
+ AuditEventRecordType.remoteHostAddress
+
@@ -177,6 +204,7 @@
c:UserType
+ AuditEventRecordType.initiatorRef
@@ -191,6 +219,7 @@
c:UserType
+ AuditEventRecordType.attorneyRef
@@ -201,6 +230,9 @@
In case that operation targets more than one object, the "primary" or
the most important is recorded (e.g. the user object)
+
+ AuditEventRecordType.targetRef
+
@@ -212,6 +244,7 @@
c:UserType
+ AuditEventRecordType.targetOwnerRef
@@ -221,6 +254,9 @@
Type of audit event. It describes what kind of operation was executed
(adding an object, modification, user login, ...)
+
+ AuditEventRecordType.eventType
+
@@ -228,14 +264,26 @@
Stage of event processing: request or execution.
+
+ AuditEventRecordType.eventStage
+
+
+
+
+
+
+ AuditEventRecordType.delta
+
-
The channel that was the source of the operation that the record describes
+
+ AuditEventRecordType.channel
+
@@ -243,6 +291,9 @@
Operation outcome (success, failure)
+
+ AuditEventRecordType.outcome
+
@@ -250,6 +301,9 @@
result (e.g. number of entries, returned object, business result of workflow task or process instance - approved, rejected)
+
+ AuditEventRecordType.result
+
@@ -257,6 +311,9 @@
TODO
+
+ AuditEventRecordType.parameter
+
@@ -264,6 +321,9 @@
TODO
+
+ AuditEventRecordType.message
+
@@ -277,6 +337,7 @@
4.2
+ AuditEventRecordType.changedItem
@@ -287,6 +348,7 @@
3.6
+ AuditEventRecordType.property
@@ -297,6 +359,7 @@
3.6
+ AuditEventRecordType.reference
@@ -307,6 +370,7 @@
4.0
+ AuditEventRecordType.resourceOid
@@ -317,7 +381,8 @@
4.0
- true
+ true
+ AuditEventRecordType.customColumnProperty
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
index aeff665cf1d..93a0f34909a 100755
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
@@ -17549,6 +17549,7 @@
ObjectCollectionType.auditSearch
true
+ true
@@ -17965,6 +17966,7 @@
4.0
true
+ true
diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/interaction/DashboardService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/interaction/DashboardService.java
index 27486942d1d..3f9ac75be24 100644
--- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/interaction/DashboardService.java
+++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/interaction/DashboardService.java
@@ -22,6 +22,7 @@
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
+import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import javax.xml.namespace.QName;
@@ -40,7 +41,7 @@ List> searchObjectFromCollection(CollectionRefSpecificat
Collection> options, ExpressionType condition, Task task, OperationResult result) throws SchemaException,
ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException;
- List searchObjectFromCollection(CollectionRefSpecificationType collectionConfig, ExpressionType condition, Task task, OperationResult result)
+ List searchObjectFromCollection(CollectionRefSpecificationType collectionConfig, ExpressionType condition, Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException,
ExpressionEvaluationException;
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java
index ffa9d9f410a..564d0091569 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java
@@ -13,19 +13,20 @@
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
+import com.evolveum.midpoint.prism.Containerable;
+import com.evolveum.midpoint.prism.PrismContainer;
+
+import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordCustomColumnPropertyType;
+
import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.Nullable;
-import com.evolveum.midpoint.audit.api.AuditEventRecord;
import com.evolveum.midpoint.common.LocalizationService;
-import com.evolveum.midpoint.prism.PrismObject;
-import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.GetOperationOptionsBuilder;
import com.evolveum.midpoint.schema.SchemaHelper;
import com.evolveum.midpoint.schema.SelectorOptions;
-import com.evolveum.midpoint.schema.constants.AuditConstants;
import com.evolveum.midpoint.task.api.TaskUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
@@ -67,23 +68,6 @@ public class DefaultColumnUtils {
.put(ItemPath.create(ResourceType.F_CONNECTOR_REF, ConnectorType.F_CONNECTOR_VERSION),
"ConnectorType.connectorVersion")
.build())
- .put(AuditEventRecordType.class, ImmutableMap.builder()
- .put(new ItemName(AuditConstants.TIME_COLUMN), AuditConstants.TIME_COLUMN_KEY)
- .put(new ItemName(AuditConstants.INITIATOR_COLUMN), AuditConstants.INITIATOR_COLUMN_KEY)
- .put(new ItemName(AuditConstants.EVENT_STAGE_COLUMN), AuditConstants.EVENT_STAGE_COLUMN_KEY)
- .put(new ItemName(AuditConstants.EVENT_TYPE_COLUMN), AuditConstants.EVENT_TYPE_COLUMN_KEY)
- .put(new ItemName(AuditConstants.TARGET_COLUMN), AuditConstants.TARGET_COLUMN_KEY)
- .put(new ItemName(AuditConstants.OUTCOME_COLUMN), AuditConstants.OUTCOME_COLUMN_KEY)
- .put(new ItemName(AuditConstants.MESSAGE_COLUMN), AuditConstants.MESSAGE_COLUMN_KEY)
- .put(new ItemName(AuditConstants.DELTA_COLUMN), AuditConstants.DELTA_COLUMN_KEY)
- .put(new ItemName(AuditConstants.TARGET_OWNER_COLUMN), AuditConstants.TARGET_OWNER_COLUMN_KEY)
- .put(new ItemName(AuditConstants.CHANNEL_COLUMN), AuditConstants.CHANNEL_COLUMN_KEY)
- .put(new ItemName(AuditConstants.TASK_OID_COLUMN), AuditConstants.TASK_OID_COLUMN_KEY)
- .put(new ItemName(AuditConstants.NODE_IDENTIFIER_COLUMN), AuditConstants.NODE_IDENTIFIER_COLUMN_KEY)
- .put(new ItemName(AuditConstants.ATTORNEY_COLUMN), AuditConstants.ATTORNEY_COLUMN_KEY)
- .put(new ItemName(AuditConstants.RESULT_COLUMN), AuditConstants.RESULT_COLUMN_KEY)
- .put(new ItemName(AuditConstants.RESOURCE_OID_COLUMN), AuditConstants.RESOURCE_OID_COLUMN_KEY)
- .build())
.build();
NUMBER_COLUMNS = Collections.singletonList(
@@ -92,14 +76,14 @@ public class DefaultColumnUtils {
OBJECT_COLUMNS_DEF = Collections.singletonList(ObjectType.F_NAME);
DEFAULT_AUDIT_COLUMNS_DEF = Arrays.asList(
- new ItemName(AuditConstants.TIME_COLUMN),
- new ItemName(AuditConstants.INITIATOR_COLUMN),
- new ItemName(AuditConstants.EVENT_STAGE_COLUMN),
- new ItemName(AuditConstants.EVENT_TYPE_COLUMN),
- new ItemName(AuditConstants.TARGET_COLUMN),
- new ItemName(AuditConstants.OUTCOME_COLUMN),
- new ItemName(AuditConstants.MESSAGE_COLUMN),
- new ItemName(AuditConstants.DELTA_COLUMN)
+ AuditEventRecordType.F_TIMESTAMP,
+ AuditEventRecordType.F_INITIATOR_REF,
+ AuditEventRecordType.F_EVENT_STAGE,
+ AuditEventRecordType.F_EVENT_TYPE,
+ AuditEventRecordType.F_TARGET_REF,
+ AuditEventRecordType.F_OUTCOME,
+ AuditEventRecordType.F_MESSAGE,
+ AuditEventRecordType.F_DELTA
);
COLUMNS_DEF = ImmutableMap., List>builder()
@@ -280,52 +264,9 @@ private static void createColumns(GuiObjectListViewType v
}
}
- public static Object getObjectByAuditColumn(AuditEventRecord record, ItemPath path) {
- switch (path.toString()) {
- case AuditConstants.TIME_COLUMN:
- return record.getTimestamp();
- case AuditConstants.INITIATOR_COLUMN:
- return record.getInitiatorRef();
- case AuditConstants.EVENT_STAGE_COLUMN:
- return record.getEventStage();
- case AuditConstants.EVENT_TYPE_COLUMN:
- return record.getEventType();
- case AuditConstants.TARGET_COLUMN:
- return record.getTargetRef();
- case AuditConstants.TARGET_OWNER_COLUMN:
- return record.getTargetOwnerRef();
- case AuditConstants.CHANNEL_COLUMN:
- return record.getChannel();
- case AuditConstants.OUTCOME_COLUMN:
- return record.getOutcome();
- case AuditConstants.MESSAGE_COLUMN:
- return record.getMessage();
- case AuditConstants.DELTA_COLUMN:
- return record.getDeltas();
- case AuditConstants.TASK_OID_COLUMN:
- return record.getTaskOid();
- case AuditConstants.NODE_IDENTIFIER_COLUMN:
- return record.getNodeIdentifier();
- case AuditConstants.ATTORNEY_COLUMN:
- return record.getAttorneyRef();
- case AuditConstants.RESULT_COLUMN:
- return record.getResult();
- case AuditConstants.RESOURCE_OID_COLUMN:
- return record.getResourceOids();
- default:
- String value = record.getCustomColumnProperty().get(path.toString());
- if (value != null) {
- return value;
- }
-
- LOGGER.error("Unknown name of column for AuditReport " + path);
- throw new IllegalArgumentException("Unknown name of column for AuditReport " + path);
- }
- }
-
- public static String processSpecialColumn(
- ItemPath itemPath, PrismObject object, LocalizationService localization) {
- @Nullable Class type = object.getCompileTimeClass();
+ public static String processSpecialColumn(
+ ItemPath itemPath, PrismContainer extends Containerable> object, LocalizationService localization) {
+ @Nullable Class type = object.getCompileTimeClass();
if (type == null || itemPath == null) {
return null;
}
@@ -362,12 +303,18 @@ public static String processSpecialColumn(
Object[] params = localizationObject.isEmpty() ? null : localizationObject.toArray();
return localization.translate(key, params, Locale.getDefault(), key);
}
+ } else if (type.isAssignableFrom(AuditEventRecordType.class)) {
+ for (AuditEventRecordCustomColumnPropertyType customColumn : ((AuditEventRecordType)object.getValue().asContainerable()).getCustomColumnProperty()) {
+ if (customColumn.getName().equals(itemPath.toString())) {
+ return customColumn.getValue();
+ }
+ }
}
return null;
}
- public static boolean isSpecialColumn(ItemPath itemPath, PrismObject object) {
- @Nullable Class type = object.getCompileTimeClass();
+ public static boolean isSpecialColumn(ItemPath itemPath, PrismContainer extends Containerable> object) {
+ @Nullable Class type = object.getCompileTimeClass();
if (type == null || itemPath == null) {
return false;
}
@@ -379,6 +326,12 @@ public static boolean isSpecialColumn(ItemPath itemPath,
|| itemPath.equivalent(TaskType.F_PROGRESS)) {
return true;
}
+ } else if (type.isAssignableFrom(AuditEventRecordType.class)) {
+ for (AuditEventRecordCustomColumnPropertyType customColumn : ((AuditEventRecordType)object.getValue().asContainerable()).getCustomColumnProperty()) {
+ if (customColumn.getName().equals(itemPath.toString())) {
+ return true;
+ }
+ }
}
return false;
}
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/DashboardServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/DashboardServiceImpl.java
index 9f6791ccf4a..db3d9c5aae9 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/DashboardServiceImpl.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/DashboardServiceImpl.java
@@ -15,14 +15,20 @@
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.schema.GetOperationOptions;
+import com.evolveum.midpoint.schema.GetOperationOptionsBuilder;
+import com.evolveum.midpoint.schema.SchemaHelper;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
+import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
import com.evolveum.midpoint.util.QNameUtil;
+import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -75,9 +81,11 @@ public class DashboardServiceImpl implements DashboardService {
@Autowired private ExpressionFactory expressionFactory;
@Autowired private ModelObjectResolver objectResolver;
@Autowired private CollectionProcessor collectionProcessor;
+ @Autowired private SchemaHelper schemaHelper;
@Override
- public DashboardWidget createWidgetData(DashboardWidgetType widget, Task task, OperationResult result) throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException, ObjectNotFoundException {
+ public DashboardWidget createWidgetData(DashboardWidgetType widget, Task task, OperationResult result)
+ throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException, ObjectNotFoundException {
Validate.notNull(widget, "Widget is null");
@@ -171,7 +179,8 @@ public DashboardWidgetSourceTypeType getSourceType(DashboardWidgetType widget) {
return widget.getData().getSourceType();
}
- private String getNumberMessage(DashboardWidgetType widget, DashboardWidget data, Task task, OperationResult result) throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException, ObjectNotFoundException {
+ private String getNumberMessage(DashboardWidgetType widget, DashboardWidget data, Task task, OperationResult result)
+ throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException, ObjectNotFoundException {
DashboardWidgetSourceTypeType sourceType = getSourceType(widget);
DashboardWidgetPresentationType presentation = widget.getPresentation();
switch (sourceType) {
@@ -194,7 +203,8 @@ private String getNumberMessage(DashboardWidgetType widget, DashboardWidget data
return null;
}
- private String generateNumberMessageForObject(DashboardWidgetType widget, DashboardWidget data, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ private String generateNumberMessageForObject(DashboardWidgetType widget, DashboardWidget data, Task task, OperationResult result)
+ throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
ObjectType object = getObjectFromObjectRef(widget, task, result);
if(object == null) {
return null;
@@ -202,43 +212,141 @@ private String generateNumberMessageForObject(DashboardWidgetType widget, Dashbo
return generateNumberMessage(widget, createVariables(object.asPrismObject(), null, null), data);
}
- private String generateNumberMessageForAuditSearch(DashboardWidgetType widget, DashboardWidget data, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ private String generateNumberMessageForAuditSearch(DashboardWidgetType widget, DashboardWidget data, Task task, OperationResult result)
+ throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
ObjectCollectionType collection = getObjectCollectionType(widget, task, result);
+ CollectionRefSpecificationType collectionRef = getCollectionRefSpecificationType(widget, task, result);
if(collection == null) {
return null;
}
AuditSearchType auditSearch = collection.getAuditSearch();
- if(auditSearch == null) {
- LOGGER.error("AuditSearch of ObjectCollection is not found in widget " +
+ SearchFilterType filter = collection.getFilter();
+ Integer value = 0;
+ Integer domainValue = null;
+ if (filter != null) {
+ value = countAuditEvents(collectionRef, collection, task, result);
+ if (value == null) {
+ return null;
+ }
+ if (collection.getDomain() != null && collection.getDomain().getCollectionRef() != null
+ && collection.getDomain().getCollectionRef().getOid() != null) {
+ @NotNull PrismObject domainCollection = modelService.getObject(ObjectCollectionType.class, collection.getDomain().getCollectionRef().getOid(),
+ null, task, result);
+ domainValue = countAuditEvents(collection.getDomain(), domainCollection.asObjectable(), task, result);
+ }
+ } else if (auditSearch != null) {
+ if (auditSearch.getRecordQuery() == null) {
+ LOGGER.error("RecordQuery of auditSearch is not defined in widget " +
+ widget.getIdentifier());
+ return null;
+ }
+
+ Map parameters = new HashMap<>();
+ String query = getQueryForCount(createQuery(collection,
+ parameters, false, clock));
+ LOGGER.debug("Parameters for select: " + parameters);
+ value = (int) auditService.countObjects(
+ query, parameters);
+ domainValue = null;
+ if (auditSearch.getDomainQuery() == null) {
+ LOGGER.error("DomainQuery of auditSearch is not defined");
+ } else {
+ parameters = new HashMap<>();
+ query = getQueryForCount(createQuery(collection,
+ parameters, true, clock));
+ LOGGER.debug("Parameters for select: " + parameters);
+ domainValue = (int) auditService.countObjects(
+ query, parameters);
+ }
+ } else {
+ LOGGER.error("Filter or auditSearch of ObjectCollection is not found in widget " +
widget.getIdentifier());
return null;
}
- if(auditSearch.getRecordQuery() == null) {
- LOGGER.error("RecordQuery of auditSearch is not defined in widget " +
- widget.getIdentifier());
+ LOGGER.debug("Value: {}, Domain value: {}", value, domainValue);
+ IntegerStatType statType = generateIntegerStat(value, domainValue);
+ return generateNumberMessage(widget, createVariables(null, statType, null), data);
+ }
+
+ private Integer countAuditEvents(CollectionRefSpecificationType collectionRef, ObjectCollectionType collection, Task task, OperationResult result)
+ throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException {
+
+ if (collectionRef == null || collectionRef.getCollectionRef() == null || collectionRef.getCollectionRef().getOid() == null) {
return null;
}
- Map parameters = new HashMap<>();
- String query = getQueryForCount(createQuery(collection,
- parameters, false, clock));
- LOGGER.debug("Parameters for select: " + parameters);
- int value = (int) auditService.countObjects(
- query, parameters);
- Integer domainValue = null;
- if(auditSearch.getDomainQuery() == null) {
- LOGGER.error("DomainQuery of auditSearch is not defined");
+ if (collection.getType() != null && !QNameUtil.match(collection.getType(), AuditEventRecordType.COMPLEX_TYPE)) {
+ LOGGER.error("Unsupported type for audit query " + collection.getType() + " in object collection " + collection.getName());
+ return null;
+ }
+
+ SearchFilterType filter = null;
+ if (collection != null) {
+ filter = collection.getFilter();
+ }
+ ObjectFilter objectFilter = combineAuditFilter(collectionRef, filter, task, result);
+ ObjectQuery query;
+ if (objectFilter == null) {
+ query = prismContext.queryFor(AuditEventRecordType.class).build();
} else {
- parameters = new HashMap<>();
- query = getQueryForCount(createQuery(collection,
- parameters, true, clock));
- LOGGER.debug("Parameters for select: " + parameters);
- domainValue = (int) auditService.countObjects(
- query, parameters);
+ query = prismContext.queryFactory().createQuery();
+ ObjectFilter evaluatedFilter = ExpressionUtil.evaluateFilterExpressions(objectFilter, new ExpressionVariables(), MiscSchemaUtil.getExpressionProfile(),
+ expressionFactory, prismContext, "collection filter", task, result);
+ query.setFilter(evaluatedFilter);
}
- LOGGER.debug("Value: {}, Domain value: {}", value, domainValue);
- IntegerStatType statType = generateIntegerStat(value, domainValue);
- return generateNumberMessage(widget, createVariables(null, statType, null), data);
+ @NotNull Collection> option = combineAuditOption(collectionRef, collection, task, result);
+
+ return auditService.countObjects(query, option, result);
+ }
+
+ private @NotNull Collection> combineAuditOption(CollectionRefSpecificationType collectionRef, ObjectCollectionType collection, Task task, OperationResult result)
+ throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
+
+ List> collectionOptions;
+ if (collection != null) {
+ collectionOptions = MiscSchemaUtil.optionsTypeToOptions(collection.getGetOptions(), prismContext);
+ } else {
+ @NotNull PrismObject collectionFromRef = modelService.getObject(ObjectCollectionType.class, collectionRef.getCollectionRef().getOid(), null, task, result);
+ collectionOptions = MiscSchemaUtil.optionsTypeToOptions(collectionFromRef.asObjectable().getGetOptions(), prismContext);
+ }
+ GetOperationOptionsBuilder optionsBuilder = schemaHelper.getOperationOptionsBuilder().setFrom(collectionOptions);
+ if (collectionRef.getBaseCollectionRef() != null && collectionRef.getBaseCollectionRef().getCollectionRef() != null
+ && collectionRef.getBaseCollectionRef().getCollectionRef().getOid() != null) {
+ @NotNull PrismObject baseCollection = modelService.getObject(ObjectCollectionType.class, collectionRef.getCollectionRef().getOid(), null, task, result);
+ List> baseCollectionOptions = MiscSchemaUtil.optionsTypeToOptions(baseCollection.asObjectable().getGetOptions(), prismContext);
+ optionsBuilder.mergeFrom(baseCollectionOptions);
+ }
+ return optionsBuilder.build();
+
+ }
+
+ private ObjectFilter combineAuditFilter(CollectionRefSpecificationType collectionRef, SearchFilterType baseFilter, Task task, OperationResult result)
+ throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
+ SearchFilterType filter = baseFilter;
+ if (filter == null) {
+ @NotNull PrismObject collection = modelService.getObject(ObjectCollectionType.class, collectionRef.getCollectionRef().getOid(), null, task, result);
+ filter = collection.asObjectable().getFilter();
+ }
+ if (collectionRef.getBaseCollectionRef() != null && collectionRef.getBaseCollectionRef().getCollectionRef() != null
+ && collectionRef.getBaseCollectionRef().getCollectionRef().getOid() != null) {
+ @NotNull PrismObject baseCollection = modelService.getObject(ObjectCollectionType.class, collectionRef.getCollectionRef().getOid(), null, task, result);
+ if (filter == null && baseCollection.asObjectable().getFilter() == null) {
+ return null;
+ } else if (filter == null) {
+ return prismContext.getQueryConverter().parseFilter(baseCollection.asObjectable().getFilter(), AuditEventRecordType.class);
+ } else if (baseCollection.asObjectable().getFilter() == null) {
+ return prismContext.getQueryConverter().parseFilter(filter, AuditEventRecordType.class);
+ } else {
+ ObjectFilter baseFilterFromCollection = prismContext.getQueryConverter().parseFilter(baseCollection.asObjectable().getFilter(), AuditEventRecordType.class);
+ ObjectFilter baseObjectFilter = prismContext.getQueryConverter().parseFilter(filter, AuditEventRecordType.class);
+ ObjectQueryUtil.filterAnd(baseFilterFromCollection, baseObjectFilter, prismContext);
+ return prismContext.getQueryConverter().parseFilter(filter, AuditEventRecordType.class);
+ }
+ }
+ if (filter == null) {
+ return null;
+ }
+ return prismContext.getQueryConverter().parseFilter(filter, AuditEventRecordType.class);
}
private String getQueryForCount(String query) {
@@ -436,24 +544,41 @@ public List> searchObjectFromCollection(CollectionRefSpe
}
@Override
- public List searchObjectFromCollection(CollectionRefSpecificationType collectionConfig, ExpressionType condition, Task task, OperationResult result)
+ public List searchObjectFromCollection(CollectionRefSpecificationType collectionConfig, ExpressionType condition, Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
- List auditRecords = new ArrayList<>();
+ List auditRecords = new ArrayList<>();
if (collectionConfig.getCollectionRef() != null) {
ObjectReferenceType ref = collectionConfig.getCollectionRef();
Class refType = prismContext.getSchemaRegistry().determineClassForType(ref.getType());
ObjectCollectionType collection = (ObjectCollectionType) modelService
.getObject(refType, ref.getOid(), null, task, result).asObjectable();
- if (collection.getAuditSearch() != null) {
- Map parameters = new HashMap<>();
- String query = DashboardUtils.getQueryForListRecords(DashboardUtils.createQuery(collection, parameters, false, clock));
- auditRecords = auditService.listRecords(query, parameters, result);
+ if (collection != null && collection.getFilter() != null) {
+ ObjectFilter objectFilter = combineAuditFilter(collectionConfig, collection.getFilter(), task, result);
+ ObjectFilter evaluatedFilter = ExpressionUtil.evaluateFilterExpressions(objectFilter, new ExpressionVariables(), MiscSchemaUtil.getExpressionProfile(),
+ expressionFactory, prismContext, "collection filter", task, result);
+ ObjectQuery query = prismContext.queryFactory().createQuery();
+ query.setFilter(evaluatedFilter);
+ @NotNull Collection> option = combineAuditOption(collectionConfig, collection, task, result);
+ auditRecords.addAll(auditService.searchObjects(query, option, result));
if (auditRecords == null) {
auditRecords = new ArrayList<>();
}
if (condition != null) {
return evaluateCondition(condition, auditRecords, task, result);
}
+ } else if (collection != null && collection.getAuditSearch() != null) {
+ Map parameters = new HashMap<>();
+ String query = DashboardUtils.getQueryForListRecords(DashboardUtils.createQuery(collection, parameters, false, clock));
+ List oldAuditRecords = auditService.listRecords(query, parameters, result);
+ if (oldAuditRecords == null) {
+ oldAuditRecords = new ArrayList<>();
+ }
+ for (AuditEventRecord auditRecord : oldAuditRecords) {
+ auditRecords.add(auditRecord.createAuditEventRecordType(true));
+ }
+ if (condition != null) {
+ return evaluateCondition(condition, auditRecords, task, result);
+ }
}
}
return auditRecords;
@@ -475,7 +600,8 @@ private List evaluateCondition(ExpressionType condition, L
}
@Override
- public ObjectCollectionType getObjectCollectionType(DashboardWidgetType widget, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ public ObjectCollectionType getObjectCollectionType(DashboardWidgetType widget, Task task, OperationResult result)
+ throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
if (isCollectionRefOfCollectionNull(widget)) {
return null;
}
@@ -491,7 +617,8 @@ public CollectionRefSpecificationType getCollectionRefSpecificationType(Dashboar
return widget.getData().getCollection();
}
- private ObjectType getObjectFromObjectRef(DashboardWidgetType widget, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ private ObjectType getObjectFromObjectRef(DashboardWidgetType widget, Task task, OperationResult result)
+ throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
if(isDataNull(widget)) {
return null;
}
diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/CsvController.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/CsvController.java
index 98dbd2c06e9..c09fe92822b 100644
--- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/CsvController.java
+++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/CsvController.java
@@ -23,9 +23,11 @@
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import com.evolveum.midpoint.task.api.RunningTask;
import com.evolveum.midpoint.task.api.Task;
+import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
+import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
@@ -131,8 +133,7 @@ public byte[] processCollection(String nameOfReport, ObjectCollectionReportEngin
CompiledObjectCollectionView compiledCollection = createCompiledView(collectionConfig, collection);
byte[] csvFile;
- boolean isAuditCollection = collection != null && collection.getAuditSearch() != null;
- if (!isAuditCollection) {
+ if (!isAuditCollection(collection)) {
csvFile = createTableBoxForObjectView(collectionRefSpecification, compiledCollection,
collectionConfig.getCondition(), task, result);
} else {
@@ -192,7 +193,7 @@ private CompiledObjectCollectionView createCompiledView (ObjectCollectionReportE
private byte[] createTableForAuditView(CollectionRefSpecificationType collectionRef, CompiledObjectCollectionView compiledCollection,
ExpressionType condition, Task task, OperationResult result) throws CommunicationException, ObjectNotFoundException, SchemaException,
SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
- List auditRecords = getReportService().getDashboardService().searchObjectFromCollection(collectionRef, condition, task, result);
+ List auditRecords = getReportService().getDashboardService().searchObjectFromCollection(collectionRef, condition, task, result);
if (compiledCollection.getColumns().isEmpty()) {
getReportService().getModelInteractionService().applyView(compiledCollection, DefaultColumnUtils.getDefaultAuditEventsView());
@@ -201,17 +202,11 @@ private byte[] createTableForAuditView(CollectionRefSpecificationType collection
List> records = new ArrayList<>();
List columns = MiscSchemaUtil.orderCustomColumns(compiledCollection.getColumns());
-
+ PrismContainerDefinition def = getReportService().getPrismContext().getSchemaRegistry()
+ .findItemDefinitionByCompileTimeClass(AuditEventRecordType.class, PrismContainerDefinition.class);
columns.forEach(column -> {
Validate.notNull(column.getName(), "Name of column is null");
-
- DisplayType columnDisplay = column.getDisplay();
- String label;
- if(columnDisplay != null && columnDisplay.getLabel() != null) {
- label = getMessage(columnDisplay.getLabel().getOrig());
- } else {
- label = column.getName();
- }
+ String label = getColumnLabel(column, def);
headers.add(label);
});
@@ -220,12 +215,17 @@ private byte[] createTableForAuditView(CollectionRefSpecificationType collection
task.setExpectedTotal((long) auditRecords.size());
recordProgress(task, i, result, LOGGER);
- for (AuditEventRecord auditRecord : auditRecords) {
+ for (AuditEventRecordType auditRecord : auditRecords) {
List items = new ArrayList<>();
columns.forEach(column -> {
ExpressionType expression = column.getExport() != null ? column.getExport().getExpression() : null;
ItemPath path = column.getPath() == null ? null : column.getPath().getItemPath();
- items.add(getStringValueByAuditColumn(auditRecord, path, expression, task, result));
+ try {
+ items.add(getRealValueAsString(column, getAuditRecordAsContainer(auditRecord), path, expression, task, result)
+ );
+ } catch (SchemaException e) {
+ LOGGER.error("Couldn't create singleValueContainer for audit record " + auditRecord);
+ }
});
records.add(items);
i++;
@@ -326,19 +326,12 @@ private byte[] createTableBoxForObjectView(CollectionRefSpecificationType collec
}
@Override
- protected String getRealValueAsString(GuiObjectColumnType column, PrismObject object, ItemPath itemPath, ExpressionType expression, Task task, OperationResult result) {
+ protected String getRealValueAsString(GuiObjectColumnType column, PrismContainer extends Containerable> object, ItemPath itemPath, ExpressionType expression, Task task, OperationResult result) {
String value = super.getRealValueAsString(column, object, itemPath, expression, task, result);
value = removeNewLine(value);
return value;
}
- protected String getStringValueByAuditColumn(AuditEventRecord record, ItemPath path,
- ExpressionType expression, Task task, OperationResult result) {
- String value = super.getStringValueByAuditColumn(record, path, expression, task, result);
- value = removeNewLine(value);
- return value;
- }
-
@Override
public void importCollectionReport(ReportType report, VariablesMap variables, RunningTask task, OperationResult result) {
if (report.getObjectCollection() != null) {
diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/FileFormatController.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/FileFormatController.java
index fd5d8f8dd74..a04d625072e 100644
--- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/FileFormatController.java
+++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/FileFormatController.java
@@ -8,12 +8,14 @@
import java.io.IOException;
import java.util.*;
+import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
+import com.evolveum.midpoint.schema.DeltaConvertor;
+
import com.google.common.collect.ImmutableSet;
import org.jetbrains.annotations.NotNull;
-import com.evolveum.midpoint.audit.api.AuditEventRecord;
import com.evolveum.midpoint.model.api.authentication.CompiledObjectCollectionView;
import com.evolveum.midpoint.model.common.util.DefaultColumnUtils;
import com.evolveum.midpoint.prism.*;
@@ -24,7 +26,6 @@
import com.evolveum.midpoint.report.impl.ReportServiceImpl;
import com.evolveum.midpoint.report.impl.ReportUtils;
import com.evolveum.midpoint.schema.ObjectDeltaOperation;
-import com.evolveum.midpoint.schema.constants.AuditConstants;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.expression.ExpressionProfile;
import com.evolveum.midpoint.schema.expression.VariablesMap;
@@ -106,7 +107,7 @@ protected String getMessage(String key, Object... params) {
return getReportService().getLocalizationService().translate(key, params, Locale.getDefault(), key);
}
- protected String getRealValueAsString(GuiObjectColumnType column, PrismObject object, ItemPath itemPath,
+ protected String getRealValueAsString(GuiObjectColumnType column, PrismContainer extends Containerable> object, ItemPath itemPath,
ExpressionType expression, Task task, OperationResult result) {
Item valueObject = object;
@@ -173,22 +174,35 @@ protected String getRealValueAsString(GuiObjectColumnType column, PrismObject values) {
+ private String processListOfRealValues(Collection> values) {
StringBuilder sb = new StringBuilder();
values.forEach(value -> {
if (!sb.toString().isEmpty()) {
appendMultivalueDelimiter(sb);
}
if (value instanceof PrismPropertyValue) {
- Object realObject = ((PrismPropertyValue>) value).getRealValue();
+ String stringValue;
+ O realObject = ((PrismPropertyValue) value).getRealValue();
if (realObject == null) {
- realObject = "";
+ stringValue = "";
} else if (realObject instanceof Collection) {
- realObject = processListOfRealValues((Collection) realObject);
+ stringValue = processListOfRealValues((Collection) realObject);
+ } else if (realObject instanceof Enum){
+ stringValue = ReportUtils.prettyPrintForReport((Enum) realObject);
+ } else if (realObject instanceof XMLGregorianCalendar) {
+ stringValue = ReportUtils.prettyPrintForReport((XMLGregorianCalendar) realObject);
+ } else if (realObject instanceof ObjectDeltaOperationType) {
+ try {
+ ObjectDeltaOperation convertedDelta = DeltaConvertor.createObjectDeltaOperation((ObjectDeltaOperationType) realObject, getReportService().getPrismContext());
+ stringValue = ReportUtils.printDelta(convertedDelta);
+ } catch (SchemaException e) {
+ LOGGER.error("Couldn't convert delta from ObjectDeltaOperationType to ObjectDeltaOperation " + realObject.toString());
+ stringValue = "";
+ }
} else {
- realObject = ReportUtils.prettyPrintForReport(realObject);
+ stringValue = ReportUtils.prettyPrintForReport(realObject);
}
- sb.append(realObject);
+ sb.append(stringValue);
} else if (value instanceof PrismReferenceValue) {
sb.append(getObjectNameFromRef(((PrismReferenceValue) value).getRealValue()));
} else {
@@ -323,98 +337,6 @@ protected PrismObject getObjectFromReference(Referencable ref) {
return object;
}
- protected String getStringValueByAuditColumn(AuditEventRecord record, ItemPath path,
- ExpressionType expression, Task task, OperationResult result) {
- if (expression == null) {
- return getStringValueByAuditColumn(record, path);
- }
- Object object;
- if (path == null) {
- object = record;
- } else {
- object = DefaultColumnUtils.getObjectByAuditColumn(record, path);
- }
- Object value = evaluateExportExpression(expression, object, task, result);
- if (value instanceof Collection) {
- return processListOfRealValues((Collection) value);
- }
- return processListOfRealValues(Collections.singletonList(value));
- }
-
- private String getStringValueByAuditColumn(AuditEventRecord record, ItemPath path) {
- switch (path.toString()) {
- case AuditConstants.TIME_COLUMN:
- return ReportUtils.prettyPrintForReport(new Date(record.getTimestamp()));
- case AuditConstants.INITIATOR_COLUMN:
- return record.getInitiatorRef() == null ? ""
- : getObjectNameFromRef(record.getInitiatorRef().getRealValue());
- case AuditConstants.EVENT_STAGE_COLUMN:
- return record.getEventStage() == null ? "" : ReportUtils.prettyPrintForReport(record.getEventStage());
- case AuditConstants.EVENT_TYPE_COLUMN:
- return record.getEventType() == null ? "" : ReportUtils.prettyPrintForReport(record.getEventType());
- case AuditConstants.TARGET_COLUMN:
- return record.getTargetRef() == null ? ""
- : getObjectNameFromRef(record.getTargetRef().getRealValue());
- case AuditConstants.TARGET_OWNER_COLUMN:
- return record.getTargetOwnerRef() == null ? ""
- : getObjectNameFromRef(record.getTargetOwnerRef().getRealValue());
- case AuditConstants.CHANNEL_COLUMN:
- return record.getChannel() == null ? "" : ReportUtils.prettyPrintForReport(QNameUtil.uriToQName(record.getChannel()));
- case AuditConstants.OUTCOME_COLUMN:
- return record.getOutcome() == null ? "" : ReportUtils.prettyPrintForReport(record.getOutcome());
- case AuditConstants.MESSAGE_COLUMN:
- return record.getMessage() == null ? "" : record.getMessage();
- case AuditConstants.DELTA_COLUMN:
- if (record.getDeltas().isEmpty()) {
- return "";
- }
- StringBuilder sbDelta = new StringBuilder();
- Collection> deltas = record.getDeltas();
- Iterator> iterator = deltas.iterator();
- int index = 0;
- while (iterator.hasNext()) {
- ObjectDeltaOperation delta = iterator.next();
- sbDelta.append(ReportUtils.printDelta(delta));
- if ((index + 1) != deltas.size()) {
- sbDelta.append("\n");
- }
- index++;
- }
- return sbDelta.toString();
- case AuditConstants.TASK_OID_COLUMN:
- return record.getTaskOid() == null ? "" : record.getTaskOid();
- case AuditConstants.NODE_IDENTIFIER_COLUMN:
- return record.getNodeIdentifier() == null ? "" : record.getNodeIdentifier();
- case AuditConstants.ATTORNEY_COLUMN:
- return record.getAttorneyRef() == null ? ""
- : getObjectNameFromRef(record.getAttorneyRef().getRealValue());
- case AuditConstants.RESULT_COLUMN:
- return record.getResult() == null ? "" : record.getResult();
- case AuditConstants.RESOURCE_OID_COLUMN:
- Set resourceOids = record.getResourceOids();
- if (resourceOids == null || resourceOids.isEmpty()) {
- return "";
- }
- StringBuilder sb = new StringBuilder();
- int i = 1;
- for (String oid : resourceOids) {
- sb.append(oid);
- if (i != resourceOids.size()) {
- sb.append("\n");
- }
- i++;
- }
- return sb.toString();
- default:
- if (record.getCustomColumnProperty().containsKey(path.toString())) {
- return record.getCustomColumnProperty().get(path.toString());
- } else {
- LOGGER.error("Unknown name of column for AuditReport " + path);
- throw new IllegalArgumentException("Unknown name of column for AuditReport " + path);
- }
- }
- }
-
protected QName resolveTypeQname(CollectionRefSpecificationType collectionRef, CompiledObjectCollectionView compiledCollection) {
QName type;
if (collectionRef.getCollectionRef() != null) {
@@ -443,6 +365,17 @@ protected Class resolveType(CollectionRefSpecificationType collectio
.getCompileTimeClassForObjectType(type);
}
+ protected boolean isAuditCollection(ObjectCollectionType collection) {
+ return collection != null
+ && (collection.getAuditSearch() != null || QNameUtil.match(AuditEventRecordType.COMPLEX_TYPE, collection.getType()));
+ }
+
+ protected PrismContainer extends Containerable> getAuditRecordAsContainer(AuditEventRecordType record) throws SchemaException {
+ PrismContainerValue prismValue = record.asPrismContainerValue();
+ prismValue.setPrismContext(getReportService().getPrismContext());
+ return prismValue.asSingleValuedContainer(AuditEventRecordType.COMPLEX_TYPE);
+ }
+
public abstract void importCollectionReport(ReportType report, VariablesMap listOfVariables, RunningTask task, OperationResult result);
public abstract List createVariablesFromFile(ReportType report, ReportDataType reportData, boolean useImportScript, Task task, OperationResult result) throws IOException;
diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/HtmlController.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/HtmlController.java
index e0844e99594..a0d3807b780 100644
--- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/HtmlController.java
+++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/fileformat/HtmlController.java
@@ -13,10 +13,13 @@
import java.util.*;
import javax.xml.namespace.QName;
+import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.schema.expression.VariablesMap;
import com.evolveum.midpoint.task.api.RunningTask;
+import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType;
+
import j2html.TagCreator;
import j2html.tags.ContainerTag;
import org.apache.commons.io.IOUtils;
@@ -220,8 +223,7 @@ public byte[] processCollection(String nameOfReport, ObjectCollectionReportEngin
}
ContainerTag tableBox;
- boolean isAuditCollection = collection != null && collection.getAuditSearch() != null;
- if (!isAuditCollection) {
+ if (!isAuditCollection(collection)) {
tableBox = createTableBoxForObjectView(label, collectionRefSpecification, compiledCollection,
collectionConfig.getCondition(), task, result, true);
} else {
@@ -293,23 +295,20 @@ private ContainerTag createTable(CompiledObjectCollectionView compiledCollection
return table;
}
- private ContainerTag createTable(CompiledObjectCollectionView compiledCollection, List records, boolean recordProgress,
+ private ContainerTag createTable(CompiledObjectCollectionView compiledCollection, List records, boolean recordProgress,
Task task, OperationResult result) {
ContainerTag table = createTable();
ContainerTag tHead = TagCreator.thead();
ContainerTag tBody = TagCreator.tbody();
List columns = MiscSchemaUtil.orderCustomColumns(compiledCollection.getColumns());
ContainerTag trForHead = TagCreator.tr().withStyle("width: 100%;");
+ PrismContainerDefinition def = getReportService().getPrismContext().getSchemaRegistry()
+ .findItemDefinitionByCompileTimeClass(AuditEventRecordType.class, PrismContainerDefinition.class);
columns.forEach(column -> {
Validate.notNull(column.getName(), "Name of column is null");
DisplayType columnDisplay = column.getDisplay();
- String label;
- if (columnDisplay != null && columnDisplay.getLabel() != null) {
- label = getMessage(columnDisplay.getLabel().getOrig());
- } else {
- label = column.getName();
- }
+ String label = getColumnLabel(column, def);
ContainerTag th = TagCreator.th(TagCreator.div(TagCreator.span(label).withClass("sortableLabel")));
if (columnDisplay != null) {
if (StringUtils.isNotBlank(columnDisplay.getCssClass())) {
@@ -329,14 +328,19 @@ private ContainerTag createTable(CompiledObjectCollectionView compiledCollection
task.setExpectedTotal((long) records.size());
recordProgress(task, i, result, LOGGER);
}
- for (AuditEventRecord record : records) {
+ for (AuditEventRecordType record : records) {
ContainerTag tr = TagCreator.tr();
columns.forEach(column -> {
ExpressionType expression = column.getExport() != null ? column.getExport().getExpression() : null;
ItemPath path = column.getPath() == null ? null : column.getPath().getItemPath();
- tr.with(TagCreator
- .th(TagCreator.div(getStringValueByAuditColumn(record, path, expression, task, result))
- .withStyle("white-space: pre-wrap")));
+ try {
+ tr.with(TagCreator
+ .th(TagCreator.div(getRealValueAsString(column, getAuditRecordAsContainer(record),
+ path, expression, task, result))
+ .withStyle("white-space: pre-wrap")));
+ } catch (SchemaException e) {
+ LOGGER.error("Couldn't create singleValueContainer for audit record " + record);
+ }
});
tBody.with(tr);
if (recordProgress) {
@@ -398,7 +402,7 @@ private ContainerTag createTableBoxForAuditView(
ExpressionType condition, Task task, OperationResult result, boolean recordProgress) throws CommunicationException, ObjectNotFoundException,
SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
long startMillis = getReportService().getClock().currentTimeMillis();
- List records = getReportService().getDashboardService().searchObjectFromCollection(collection, condition, task, result);
+ List records = getReportService().getDashboardService().searchObjectFromCollection(collection, condition, task, result);
if (compiledCollection.getColumns().isEmpty()) {
getReportService().getModelInteractionService().applyView(compiledCollection, DefaultColumnUtils.getDefaultAuditEventsView());
diff --git a/model/report-impl/src/test/resources/common/object-collection-all-audit-records.xml b/model/report-impl/src/test/resources/common/object-collection-all-audit-records.xml
index 7b88f52b125..1ae4c90b4b9 100644
--- a/model/report-impl/src/test/resources/common/object-collection-all-audit-records.xml
+++ b/model/report-impl/src/test/resources/common/object-collection-all-audit-records.xml
@@ -7,9 +7,18 @@
-->
Audit records
-
- select * from m_audit_event as aer where aer.eventStage=1
- P1D
-
+ AuditEventRecordType
+
+
+
+ eventStage
+ execution
+
+
+ timestamp
+ 2020-01-01T00:00:00.000+02:00
+
+
+
diff --git a/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java b/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java
index 1ce93b0b8ff..25080f8e8c8 100644
--- a/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java
+++ b/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java
@@ -10,6 +10,8 @@
import java.text.SimpleDateFormat;
import java.util.*;
+import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordCustomColumnPropertyType;
+
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -591,6 +593,14 @@ public AuditEventRecordType createAuditEventRecordType(boolean tolerateInconsist
referenceEntry.getValue().forEach(v -> referenceType.getValue().add(v.toXml()));
auditRecordType.getReference().add(referenceType);
}
+
+ for (Map.Entry customColumnEntry : customColumnProperty.entrySet()) {
+ AuditEventRecordCustomColumnPropertyType customColumn = new AuditEventRecordCustomColumnPropertyType();
+ customColumn.setName(customColumnEntry.getKey());
+ customColumn.setValue(customColumnEntry.getValue());
+ auditRecordType.getCustomColumnProperty().add(customColumn);
+ }
+
// TODO MID-5531 convert custom properties too? What about other than string types?
return auditRecordType;
}
From 5606a17592bb74f9424c54a28ba533601677a64a Mon Sep 17 00:00:00 2001
From: kate
Date: Thu, 3 Sep 2020 15:41:19 +0200
Subject: [PATCH 02/12] save search to page storage
---
.../api/component/ContainerableListPanel.java | 7 +++++++
.../gui/api/component/ObjectListPanel.java | 18 +++++++++---------
.../web/component/search/SearchFormPanel.java | 9 +++++++++
.../web/component/search/SearchPanel.java | 5 +++++
4 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ContainerableListPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ContainerableListPanel.java
index 396ae7be6a0..f258d09bbb1 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ContainerableListPanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ContainerableListPanel.java
@@ -95,6 +95,13 @@ protected WebMarkupContainer createHeader(String headerId) {
protected void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
ContainerableListPanel.this.searchPerformed(target);
}
+
+ @Override
+ protected void saveSearch(Search search, AjaxRequestTarget target) {
+ if (getPageStorage() != null) {
+ getPageStorage().setSearch(search);
+ }
+ }
};
searchPanel.add(new VisibleBehaviour(() -> isSearchVisible()));
return searchPanel;
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java
index c7bcb4d8ba2..ef07ce98928 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java
@@ -636,6 +636,14 @@ protected void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
ObjectListPanel.this.searchPerformed(query, target);
}
+ @Override
+ protected void saveSearch(Search search, AjaxRequestTarget target) {
+ PageStorage storage = getPageStorage(getStorageKey());
+ if (storage != null) {
+ storage.setSearch(search);
+ }
+ }
+
};
return searchPanel;
@@ -823,15 +831,7 @@ private void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
// }
provider.setQuery(customQuery);
- String storageKey = getStorageKey();
- if (StringUtils.isNotEmpty(storageKey)) {
- PageStorage storage = getPageStorage(storageKey);
- if (storage != null) {
- storage.setSearch(searchModel.getObject());
- storage.setPaging(null);
- }
- }
-
+ saveSearchModel(null);
Table table = getTable();
table.setCurrentPage(null);
target.add((Component) table);
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchFormPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchFormPanel.java
index 43bb65470f5..2f03e5bada4 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchFormPanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchFormPanel.java
@@ -33,11 +33,17 @@ protected void initLayout() {
searchForm.setOutputMarkupId(true);
SearchPanel search = new SearchPanel(ID_SEARCH, getModel()) {
+ private static final long serialVersionUID = 1L;
@Override
public void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
SearchFormPanel.this.searchPerformed(query, target);
}
+
+ @Override
+ protected void saveSearch(Search search, AjaxRequestTarget target) {
+ SearchFormPanel.this.saveSearch(search, target);
+ }
};
searchForm.add(search);
}
@@ -45,4 +51,7 @@ public void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
protected void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
}
+
+ protected void saveSearch(Search search, AjaxRequestTarget target) {
+ }
}
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java
index de496e4e199..68bd83a0d6d 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java
@@ -571,6 +571,7 @@ private void initPopover() {
ListView properties = new ListView(ID_PROPERTIES,
new PropertyModel<>(moreDialogModel, MoreDialogDto.F_PROPERTIES)) {
+ private static final long serialVersionUID = 1L;
@Override
protected void populateItem(final ListItem item) {
@@ -740,6 +741,10 @@ void searchPerformed(AjaxRequestTarget target) {
void refreshSearchForm(AjaxRequestTarget target) {
target.add(get(ID_FORM), get(ID_POPOVER));
+ saveSearch(getModelObject(), target);
+ }
+
+ protected void saveSearch(Search search, AjaxRequestTarget target) {
}
public void searchPerformed(ObjectQuery query, AjaxRequestTarget target) {
From 6c1c748ba1a038bdc327dcabd030af74c1666950 Mon Sep 17 00:00:00 2001
From: Tony Tkacik
Date: Thu, 3 Sep 2020 16:05:21 +0200
Subject: [PATCH 03/12] Added AnyType to Axiom
Signed-off-by: Tony Tkacik
---
infra/axiom/src/main/resources/axiom-types.axiom | 1 +
.../prism/impl/schema/axiom/AxiomEnabledSchemaRegistry.java | 3 +++
2 files changed, 4 insertions(+)
diff --git a/infra/axiom/src/main/resources/axiom-types.axiom b/infra/axiom/src/main/resources/axiom-types.axiom
index e78be9ce099..4730c58dbf9 100644
--- a/infra/axiom/src/main/resources/axiom-types.axiom
+++ b/infra/axiom/src/main/resources/axiom-types.axiom
@@ -19,6 +19,7 @@ model axiom-base-types {
type Integer;
type Binary;
type DateTime;
+ type AnyType;
type QName {
supertype Uri;
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/axiom/AxiomEnabledSchemaRegistry.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/axiom/AxiomEnabledSchemaRegistry.java
index 16d3e88453d..402892cfec0 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/axiom/AxiomEnabledSchemaRegistry.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/axiom/AxiomEnabledSchemaRegistry.java
@@ -59,9 +59,12 @@ public class AxiomEnabledSchemaRegistry extends SchemaRegistryImpl {
private static final BiMap AXIOM_XSD_TYPES = ImmutableBiMap.builder()
.put(AxiomName.builtIn("String"), DOMUtil.XSD_STRING)
+ .put(AxiomName.builtIn("Boolean"), DOMUtil.XSD_BOOLEAN)
.put(AxiomName.builtIn("DateTime"), DOMUtil.XSD_DATETIME)
.put(AxiomName.builtIn("Uri"), DOMUtil.XSD_ANYURI)
+ .put(AxiomName.builtIn("Binary"), DOMUtil.XSD_BASE64BINARY)
.put(AxiomName.builtIn("Integer"), DOMUtil.XSD_INT)
+ .put(AxiomName.builtIn("AnyType"), DOMUtil.XSD_ANYTYPE)
.put(AxiomName.from(PRISM_NAMESPACE, "ItemPath"), new QName(PRISM_TYPES,"ItemPathType"))
.build();
private static final AxiomName DISPLAY_NAME = PROPERTY_ITEM.localName("displayName");
From 7e94fb90f50ae167e2aac17449f94d94b9282cd1 Mon Sep 17 00:00:00 2001
From: kate
Date: Thu, 3 Sep 2020 16:23:56 +0200
Subject: [PATCH 04/12] styles fixing
---
.../web/component/search/ReferenceValueSearchPanel.html | 7 ++++---
.../src/main/resources/static/less/midpoint-theme.less | 1 +
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html
index 6f031e6655d..90686719c4b 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html
@@ -9,9 +9,10 @@
-
-
-
+
+
diff --git a/gui/admin-gui/src/main/resources/static/less/midpoint-theme.less b/gui/admin-gui/src/main/resources/static/less/midpoint-theme.less
index 931bbd91313..7ce671c8474 100644
--- a/gui/admin-gui/src/main/resources/static/less/midpoint-theme.less
+++ b/gui/admin-gui/src/main/resources/static/less/midpoint-theme.less
@@ -1657,6 +1657,7 @@ th.composited-icon {
.search-item {
display: inline-block;
+ min-width: 270px;
border-color: #ddd;
border-radius: 3px;
background-color: #f4f4f4;
From 6a48d387a06b4e7f3cccd515bedbb24448d1702e Mon Sep 17 00:00:00 2001
From: Pavol Mederly
Date: Thu, 3 Sep 2020 17:03:22 +0200
Subject: [PATCH 05/12] Add support for transformation metadata
1) Changed schema for transformation metadata and created built-in
mappings for them.
2) Added support for transient prism values.
3) Added support for metadata item persistence.
Also:
4) Clearing pre-existing metadata in asIs mappings. This resolves a bug
when there was two sets of metadata from asIs mapping: with metadata
enabled (having correct provenance) and without metadata enabled
(having incorrect provenance).
5) Fixed RawType serialization (at couple of places).
6) Added per-item metadata configuration ($template/item/meta).
---
.../prism/show/PreviewChangesTabPanel.java | 2 +-
.../evolveum/midpoint/prism/PrismValue.java | 10 +
.../midpoint/prism/SerializationOptions.java | 22 ++
.../prism/impl/PrismContainerValueImpl.java | 5 +-
.../prism/impl/PrismPropertyValueImpl.java | 3 +
.../prism/impl/PrismReferenceValueImpl.java | 3 +
.../midpoint/prism/impl/PrismValueImpl.java | 15 +-
.../prism/impl/marshaller/BeanMarshaller.java | 2 +
.../impl/marshaller/PrismMarshaller.java | 72 ++++++-
.../impl/xml/XmlTypeConverterInternal.java | 52 ++---
.../midpoint/schema/DeltaConvertor.java | 7 +-
.../schema/MidPointPrismContextFactory.java | 2 +-
.../xml/ns/public/common/common-core-3.xsd | 16 +-
.../ns/public/common/common-metadata-3.axiom | 48 ++---
.../ns/public/common/common-metadata-3.xsd | 76 ++-----
.../common/mapping/AbstractMappingImpl.java | 8 +-
.../ItemValueMetadataProcessingSpec.java | 37 +++-
.../MetadataItemProcessingSpecImpl.java | 30 ++-
.../TransformationalMetadataComputation.java | 17 +-
.../metadata/ValueMetadataComputation.java | 16 ++
.../builtin/BaseBuiltinMetadataMapping.java | 19 ++
.../builtin/ProvenanceBuiltinMapping.java | 15 --
.../builtin/TransformationBuiltinMapping.java | 133 ++++++++++++
.../model/intest/TestValueMetadata.java | 23 +-
...template-provenance-metadata-recording.xml | 47 ++--
.../test/resources/metadata/user-alice.xml | 201 +++++++++++-------
.../AbstractExpressionEvaluator.java | 18 +-
.../repo/sql/helpers/ObjectUpdater.java | 7 +-
28 files changed, 624 insertions(+), 282 deletions(-)
create mode 100644 model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/TransformationBuiltinMapping.java
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/prism/show/PreviewChangesTabPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/prism/show/PreviewChangesTabPanel.java
index d4179acda1a..36e7cae3a72 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/prism/show/PreviewChangesTabPanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/prism/show/PreviewChangesTabPanel.java
@@ -85,7 +85,7 @@ private void initModels(){
if (modelContext != null) {
if (modelContext.getFocusContext() != null) {
addIgnoreNull(primaryDeltas, CloneUtil.clone(modelContext.getFocusContext().getPrimaryDelta()));
- addIgnoreNull(secondaryDeltas, CloneUtil.clone(modelContext.getFocusContext().getSecondaryDelta()));
+ addIgnoreNull(secondaryDeltas, CloneUtil.clone(modelContext.getFocusContext().getSecondaryDelta())); // todo adapt this, see MID-6465
}
for (ModelProjectionContext projCtx : modelContext.getProjectionContexts()) {
addIgnoreNull(primaryDeltas, CloneUtil.clone(projCtx.getPrimaryDelta()));
diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java
index 63210a795f4..0139c997b8f 100644
--- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java
+++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java
@@ -228,4 +228,14 @@ default Object getRealValueOrRawType(PrismContext prismContext) {
// Collection extends ItemDelta> deltas, boolean ignoreMetadata, boolean isLiteral);
Object find(ItemPath path);
+
+ /**
+ * @return True if the value is transient, so it won't be serialized if serialization
+ * of transient value is disabled.
+ */
+ @Experimental
+ boolean isTransient();
+
+ @Experimental
+ void setTransient(boolean value);
}
diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java
index d6d67635e68..2519d24e5ba 100644
--- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java
+++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java
@@ -23,6 +23,11 @@ public class SerializationOptions implements Cloneable {
*/
private boolean skipIndexOnly;
+ /**
+ * Should we skip values marked as transient?
+ */
+ private boolean skipTransient;
+
private ItemNameQualificationStrategy itemNameQualificationStrategy;
/**
@@ -165,6 +170,23 @@ public boolean isSkipIndexOnly() {
return skipIndexOnly;
}
+ public void setSkipTransient(boolean skipTransient) {
+ this.skipTransient = skipTransient;
+ }
+
+ public SerializationOptions skipTransient(boolean value) {
+ setSkipTransient(value);
+ return this;
+ }
+
+ public static SerializationOptions createSkipTransient() {
+ return new SerializationOptions().skipTransient(true);
+ }
+
+ public boolean isSkipTransient() {
+ return skipTransient;
+ }
+
// public ItemNameQualificationStrategy getItemNameQualificationStrategy() {
// return itemNameQualificationStrategy;
// }
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java
index c88bb82c400..9dace835cc8 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java
@@ -1399,7 +1399,7 @@ public boolean equivalent(PrismContainerValue> other) {
// TODO consider taking equivalence strategy into account
@Override
- public int hashCode(ParameterizedEquivalenceStrategy strategy) {
+ public int hashCode(@NotNull ParameterizedEquivalenceStrategy strategy) {
final int prime = 31;
int result = super.hashCode(strategy);
// Do not include id. containers with non-null id and null id may still be considered equivalent
@@ -1420,6 +1420,9 @@ public String toString() {
sb.append(getId());
sb.append("):");
sb.append(getItems());
+ if (isTransient()) {
+ sb.append(", transient");
+ }
return sb.toString();
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java
index 0fc8c00e8a7..865afb059ec 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java
@@ -565,6 +565,9 @@ private void dumpSuffix(StringBuilder builder) {
builder.append(", expression: ");
builder.append(getExpression());
}
+ if (isTransient()) {
+ builder.append(", transient");
+ }
}
@Override
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismReferenceValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismReferenceValueImpl.java
index 6dba5b1044b..90ce45f884b 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismReferenceValueImpl.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismReferenceValueImpl.java
@@ -543,6 +543,9 @@ public String toString() {
if (referentialIntegrity != null) {
sb.append(", RI=").append(referentialIntegrity);
}
+ if (isTransient()) {
+ sb.append(", transient");
+ }
sb.append(")");
return sb.toString();
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java
index b127cdeefb6..55090cb982a 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java
@@ -44,6 +44,8 @@ public abstract class PrismValueImpl extends AbstractFreezable implements PrismV
protected transient PrismContext prismContext;
+ private boolean isTransient;
+
PrismValueImpl() {
}
@@ -249,9 +251,10 @@ protected void copyValues(CloneStrategy strategy, PrismValueImpl clone) {
clone.prismContext = this.prismContext;
}
clone.valueMetadata = valueMetadata != null ? valueMetadata.clone() : null;
+ clone.isTransient = isTransient;
}
- EquivalenceStrategy getEqualsHashCodeStrategy() {
+ private EquivalenceStrategy getEqualsHashCodeStrategy() {
return defaultIfNull(defaultEquivalenceStrategy, EquivalenceStrategy.NOT_LITERAL);
}
@@ -449,4 +452,14 @@ protected void performFreeze() {
}
super.performFreeze();
}
+
+ @Override
+ public boolean isTransient() {
+ return isTransient;
+ }
+
+ @Override
+ public void setTransient(boolean value) {
+ isTransient = value;
+ }
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanMarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanMarshaller.java
index 0ab92b39e31..8300434e771 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanMarshaller.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanMarshaller.java
@@ -116,6 +116,8 @@ public XNodeImpl marshall(@Nullable Object inputBean, @Nullable SerializationCon
xnode.setTypeQName(ObjectUtils.defaultIfNull(prismContext.getDefaultReferenceTypeName(), ObjectReferenceType.COMPLEX_TYPE));
xnode.setExplicitTypeDeclaration(true); // probably not much correct, but...
return xnode;
+ } else if (bean instanceof RawType && ((RawType) bean).getXnode() != null) {
+ return (XNodeImpl) ((RawType) bean).getXnode();
} else {
return marshalToPrimitive(bean, ctx);
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java
index b1c3b1c209d..2a60995d1f3 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java
@@ -15,15 +15,18 @@
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.prism.xnode.MapXNode;
import com.evolveum.midpoint.prism.xnode.MetadataAware;
+import com.evolveum.midpoint.prism.xnode.XNode;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.JAXBUtil;
import com.evolveum.midpoint.util.QNameUtil;
+import com.evolveum.midpoint.util.annotation.Experimental;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_3.EvaluationTimeType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
+import com.evolveum.prism.xml.ns._public.types_3.RawType;
import com.evolveum.prism.xml.ns._public.types_3.ReferentialIntegrityType;
import org.apache.commons.collections4.CollectionUtils;
@@ -168,16 +171,18 @@ RootXNodeImpl marshalAnyData(@NotNull Object object, QName itemName, ItemDefinit
*/
@NotNull
private XNodeImpl marshalItemContent(@NotNull Item, ?> item,
- ItemDefinition itemDefinition, SerializationContext context,
+ ItemDefinition itemDefinition, SerializationContext ctx,
Collection extends QName> itemsToSkip) throws SchemaException {
- if (item.isEmpty() && item.isIncomplete()) {
+
+ List extends PrismValue> valuesToMarshal = getValuesToMarshal(item, ctx);
+ if (valuesToMarshal.isEmpty() && item.isIncomplete()) {
return new IncompleteMarkerXNodeImpl();
- } else if (item.size() == 1 && !item.isIncomplete()) {
- return marshalItemValue(item.getAnyValue(), itemDefinition, null, context, itemsToSkip);
+ } else if (valuesToMarshal.size() == 1 && !item.isIncomplete()) {
+ return marshalItemValue(valuesToMarshal.get(0), itemDefinition, null, ctx, itemsToSkip);
} else {
ListXNodeImpl xlist = new ListXNodeImpl();
- for (PrismValue val : item.getValues()) {
- xlist.add(marshalItemValue(val, itemDefinition, null, context, itemsToSkip));
+ for (PrismValue val : valuesToMarshal) {
+ xlist.add(marshalItemValue(val, itemDefinition, null, ctx, itemsToSkip));
}
if (item.isIncomplete()) {
xlist.add(new IncompleteMarkerXNodeImpl());
@@ -186,6 +191,20 @@ private XNodeImpl marshalItemContent(@NotNull Item, ?> item,
}
}
+ private List extends PrismValue> getValuesToMarshal(Item, ?> item, SerializationContext ctx) {
+ if (ctx == null || ctx.getOptions() == null || !ctx.getOptions().isSkipTransient()) {
+ return item.getValues();
+ } else {
+ List valuesToMarshal = new ArrayList<>();
+ for (PrismValue value : item.getValues()) {
+ if (value == null || !value.isTransient()) {
+ valuesToMarshal.add(value);
+ }
+ }
+ return valuesToMarshal;
+ }
+ }
+
@NotNull
private MapXNodeImpl marshalObjectContent(@NotNull PrismObject object, @NotNull PrismObjectDefinition objectDefinition, SerializationContext ctx) throws SchemaException {
MapXNodeImpl xmap = new MapXNodeImpl();
@@ -325,7 +344,7 @@ private void marshalContainerValue(MapXNodeImpl xmap, PrismContainerValue> con
for (ItemDefinition itemDef: containerDefinition.getDefinitions()) {
ItemName elementName = itemDef.getItemName();
Item,?> item = containerVal.findItem(elementName);
- if (item != null) {
+ if (item != null && !skipBecauseTransient(item, ctx)) {
XNodeImpl xsubnode;
if (shouldSkipItem(itemsToSkip, elementName, itemDef, ctx) && !item.hasNoValues()) {
xsubnode = new IncompleteMarkerXNodeImpl();
@@ -341,7 +360,7 @@ private void marshalContainerValue(MapXNodeImpl xmap, PrismContainerValue> con
// E.g. in run-time schema. Therefore we must also iterate over items and not just item definitions.
for (Item,?> item : containerVal.getItems()) {
QName elementName = item.getElementName();
- if (!marshaledItems.contains(elementName)) {
+ if (!marshaledItems.contains(elementName) && !skipBecauseTransient(item, ctx)) {
XNodeImpl xsubnode;
if (shouldSkipItem(itemsToSkip, elementName, item.getDefinition(), ctx) && !item.hasNoValues()) {
xsubnode = new IncompleteMarkerXNodeImpl();
@@ -353,6 +372,26 @@ private void marshalContainerValue(MapXNodeImpl xmap, PrismContainerValue> con
}
}
+ @Experimental
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean skipBecauseTransient(Item, ?> item, SerializationContext ctx) {
+ if (ctx == null || ctx.getOptions() == null || !ctx.getOptions().isSkipTransient()) {
+ return false;
+ }
+ if (item.isIncomplete()) {
+ return false; // we need to store the "incomplete" flag
+ }
+ if (item.hasNoValues()) {
+ return false; // backwards compatibility (rather questionable)
+ }
+ for (PrismValue value : item.getValues()) {
+ if (!value.isTransient()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean shouldSkipItem(Collection extends QName> itemsToSkip, QName elementName, ItemDefinition> itemDef,
SerializationContext ctx) {
return QNameUtil.contains(itemsToSkip, elementName) ||
@@ -476,7 +515,16 @@ private XNodeImpl serializePropertyValue(@NotNull PrismPropertyValue valu
return createExpressionXNode(expression);
}
T realValue = value.getValue();
- if (realValue instanceof PolyString) {
+ if (realValue instanceof RawType) {
+ RawType rawValue = (RawType) realValue;
+ if (rawValue.isParsed()) {
+ return marshalItemValue(rawValue.getAlreadyParsedValue(), definition, typeNameIfNoDefinition, ctx, emptySet());
+ } else if (rawValue.getXnode() != null) {
+ return (XNodeImpl) ((RawType) realValue).getXnode();
+ } else {
+ throw new SchemaException("Cannot marshall empty RawType: " + value);
+ }
+ } else if (realValue instanceof PolyString) {
return beanMarshaller.marshalPolyString((PolyString) realValue);
} else if (realValue instanceof PolyStringType) { // should not occur ...
return beanMarshaller.marshalPolyString(((PolyStringType) realValue).toPolyString());
@@ -522,6 +570,12 @@ private XNodeImpl serializePropertyRawValue(PrismPropertyValue value) {
}
T realValue = value.getValue();
if (realValue != null) {
+ if (realValue instanceof RawType) {
+ XNode xnode = ((RawType) realValue).getXnode();
+ if (xnode != null) {
+ return (XNodeImpl) xnode;
+ }
+ }
return createPrimitiveXNode(realValue, null);
} else {
throw new IllegalStateException("Neither real nor raw value present in " + value);
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xml/XmlTypeConverterInternal.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xml/XmlTypeConverterInternal.java
index 0f7bdff0058..de06111527c 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xml/XmlTypeConverterInternal.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xml/XmlTypeConverterInternal.java
@@ -7,6 +7,19 @@
package com.evolveum.midpoint.prism.impl.xml;
+import java.io.File;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.ZonedDateTime;
+import java.util.GregorianCalendar;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+
+import org.apache.commons.codec.binary.Base64;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.impl.marshaller.ItemPathSerializerTemp;
import com.evolveum.midpoint.prism.path.ItemPath;
@@ -16,47 +29,14 @@
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
-import org.apache.commons.codec.binary.Base64;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import javax.xml.datatype.DatatypeConfigurationException;
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.datatype.Duration;
-import javax.xml.datatype.XMLGregorianCalendar;
-import javax.xml.namespace.QName;
-import java.io.File;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.time.ZonedDateTime;
-import java.util.GregorianCalendar;
/**
* This is to be cleaned up later. Ideally, this functionality should be accessed by prism serializer interface.
*/
public class XmlTypeConverterInternal {
- private static DatatypeFactory datatypeFactory = null;
-
- private static DatatypeFactory getDatatypeFactory() {
- if (datatypeFactory == null) {
- try {
- datatypeFactory = DatatypeFactory.newInstance();
- } catch (DatatypeConfigurationException ex) {
- throw new IllegalStateException("Cannot construct DatatypeFactory: " + ex.getMessage(), ex);
- }
- }
- return datatypeFactory;
- }
-
-
/**
- * @param val
- * @param elementName
- * @param doc
- * @param recordType
* @return created element
- * @throws SchemaException
*/
public static Element toXsdElement(Object val, QName elementName, Document doc, boolean recordType) throws SchemaException {
if (val == null) {
@@ -133,9 +113,9 @@ public static String toXmlTextContent(Object val, QName elementName) {
return XsdTypeMapper.BOOLEAN_XML_VALUE_FALSE;
}
} else if (type.equals(BigInteger.class)) {
- return ((BigInteger) val).toString();
+ return val.toString();
} else if (type.equals(BigDecimal.class)) {
- return ((BigDecimal) val).toString();
+ return val.toString();
} else if (type.equals(GregorianCalendar.class)) {
XMLGregorianCalendar xmlCal = XmlTypeConverter.createXMLGregorianCalendar((GregorianCalendar) val);
return xmlCal.toXMLFormat();
@@ -146,7 +126,7 @@ public static String toXmlTextContent(Object val, QName elementName) {
} else if (XMLGregorianCalendar.class.isAssignableFrom(type)) {
return ((XMLGregorianCalendar) val).toXMLFormat();
} else if (Duration.class.isAssignableFrom(type)) {
- return ((Duration) val).toString();
+ return val.toString();
} else if (type.equals(UniformItemPath.class) || type.equals(ItemPath.class)) {
return ItemPathSerializerTemp.serializeWithDeclarations((ItemPath) val);
} else {
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java
index 1c2fe9f7db4..ec59c71e7a3 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java
@@ -222,14 +222,11 @@ public static ObjectDeltaType toObjectDeltaType(ObjectDelta extends ObjectType
return objectDeltaType;
}
- public static String toObjectDeltaTypeXml(ObjectDelta extends ObjectType> delta) throws SchemaException, JAXBException {
- return toObjectDeltaTypeXml(delta, null);
- }
-
- public static String toObjectDeltaTypeXml(ObjectDelta extends ObjectType> delta, DeltaConversionOptions options) throws SchemaException, JAXBException {
+ public static String toObjectDeltaTypeXml(ObjectDelta extends ObjectType> delta, DeltaConversionOptions options) throws SchemaException {
Validate.notNull(delta.getPrismContext(), "ObjectDelta without prismContext cannot be converted to XML");
ObjectDeltaType objectDeltaType = toObjectDeltaType(delta, options);
SerializationOptions serializationOptions = new SerializationOptions()
+ .skipTransient(true)
.serializeReferenceNames(DeltaConversionOptions.isSerializeReferenceNames(options))
.escapeInvalidCharacters(DeltaConversionOptions.isEscapeInvalidCharacters(options));
return delta.getPrismContext().xmlSerializer().options(serializationOptions)
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java
index 7863b34e60d..19050f6bcb1 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java
@@ -107,7 +107,7 @@ private SchemaRegistryImpl createSchemaRegistry() throws SchemaException, IOExce
}
private void registerAxiomSchemas(SchemaRegistryImpl schemaRegistry) {
- if(schemaRegistry instanceof AxiomEnabledSchemaRegistry) {
+ if (schemaRegistry instanceof AxiomEnabledSchemaRegistry) {
AxiomEnabledSchemaRegistry axiomRegistry = (AxiomEnabledSchemaRegistry) schemaRegistry;
AxiomModelStatementSource commonMetadata;
try {
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
index ff726fcf7eb..2eb5297c3d9 100755
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
@@ -8017,7 +8017,7 @@
-
+
Whether values of this item should be persisted.
@@ -8033,7 +8033,7 @@
-
+
Whether or how data should be persisted.
@@ -8084,6 +8084,7 @@
Data are held only during computation. They are not provided to the client.
+ (Not implemented yet.)
@@ -15034,6 +15035,17 @@
+
+
+
+ Definition of handling of a metadata items for this particular item.
+
+
+ true
+ 4.2
+
+
+
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.axiom b/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.axiom
index af4f73707ac..04b8c5e3ed9 100644
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.axiom
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.axiom
@@ -164,46 +164,36 @@ prism:model common-metadata {
container TransformationMetadataType {
displayName "Transformation Metadata";
operational true;
- container source {
- type ValueSourceType;
- documentation "Source(s) of the value";
- maxOccurs "unbounded";
- }
- container transformer {
- type ValueTransformerType;
- documentation "Transformer(s) that acted upon the value.";
+ container mappingTransformation {
+ type MappingTransformationType;
+ documentation "Mapping transformation that led to the current value.";
maxOccurs "unbounded";
}
}
- container ValueSourceType {
- property kind {
- type Uri;
- }
- property name {
- type String;
- }
- reference objectRef {
- documentation """
- A midPoint object representing the source of the value (resource object,
- object template, assignment holder, user, ...)
- """;
+ container MappingTransformationType {
+ documentation "Informs that the value was provided by a mapping (and how exactly).";
+ displayName "Transformation Metadata";
+ operational true;
+ container mappingSpecification {
+ type MappingSpecificationType;
+ documentation "Specification of the mapping.";
}
- property storage {
- type prism:ItemPath;
- documentation "Where was the original value stored.";
+ container source {
+ type MappingSourceType;
+ documentation "Sources for the mapping.";
+ maxOccurs "unbounded";
}
}
- container ValueTransformerType {
- property kind {
- type Uri;
- }
+ container MappingSourceType {
property name {
type String;
+ documentation "Name of the mapping source.";
}
- reference objectRef {
- documentation "A midPoint object holding the transformer";
+ property value {
+ type AnyType;
+ documentation "Source value itself. (Along with its own metadata.)";
}
}
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.xsd
index 6dbf1fa72f9..632c2f38538 100644
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-metadata-3.xsd
@@ -489,17 +489,10 @@
-
+
- Source(s) of the value.
-
-
-
-
-
-
- Transformer(s) that acted upon the value.
+ Mapping transformation that led to the current value.
@@ -507,100 +500,65 @@
-
+
- Specifies a value source.
+ Informs that the value was provided by a mapping (and how exactly).
true
+ 4.2
-
-
-
- Type of the source (resource, object template, assignment, user action, ...)
-
-
-
-
-
-
- Name of the source (whatever that means).
-
-
-
-
+
- A midPoint object representing the source of the value (resource object, object template, assignment holder, user, ...)
+ Specification of the mapping.
-
+
- Specific information needed to identify the source e.g. within the midPoint object
- (mapping name, assignment path, ...).
-
-
-
-
-
-
- Where was the original value stored.
+ Sources for the mapping.
-
+
-
+
- Specifies agent that transformed the value.
true
+ 4.2
+ Value source
-
-
-
- Type of the transformer. Currently the only supported one is "mapping".
-
-
-
- Name of the transformer. Currently mapping name.
-
-
-
-
-
-
- A midPoint object holding the transformer.
+ Name of the mapping source.
-
+
- Specific information needed to identify the transformer e.g. within the midPoint object
- (mapping name, assignment path, ...).
+ Source value itself. (Along with its own metadata.)
-
+
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/AbstractMappingImpl.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/AbstractMappingImpl.java
index f21e6fe5bc0..d30570b5047 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/AbstractMappingImpl.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/AbstractMappingImpl.java
@@ -266,7 +266,7 @@ public abstract class AbstractMappingImpl> sources = new ArrayList<>();
+ @NotNull final List> sources = new ArrayList<>();
/**
* State of the mapping evaluation.
@@ -1502,4 +1502,10 @@ public ModelCommonBeans getBeans() {
public @NotNull MappingSpecificationType getMappingSpecification() {
return mappingSpecification;
}
+
+ public List getSourceNames() {
+ return sources.stream()
+ .map(Source::getName)
+ .collect(Collectors.toList());
+ }
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ItemValueMetadataProcessingSpec.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ItemValueMetadataProcessingSpec.java
index 6b0f6ea7442..8aeb364389d 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ItemValueMetadataProcessingSpec.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ItemValueMetadataProcessingSpec.java
@@ -16,6 +16,7 @@
import com.evolveum.midpoint.model.common.util.ObjectTemplateIncludeProcessor;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ItemPathCollectionsUtil;
+import com.evolveum.midpoint.prism.path.PathSet;
import com.evolveum.midpoint.repo.common.ObjectResolver;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
@@ -117,15 +118,25 @@ private void addFromObjectTemplate(ObjectTemplateType rootTemplate, ItemPath dat
private void addFromObjectTemplate(ObjectTemplateType template, ItemPath dataPath) {
try {
- if (isHandlingApplicable(template.getMeta(), dataPath)) {
- addMetadataMappings(template.getMeta().getMapping());
- addMetadataItems(template.getMeta().getItem(), dataPath);
+ addHandling(template.getMeta(), dataPath);
+
+ for (ObjectTemplateItemDefinitionType itemDef : template.getItem()) {
+ if (itemDef.getRef() != null && itemDef.getRef().getItemPath().equivalent(dataPath)) {
+ addHandling(itemDef.getMeta(), dataPath);
+ }
}
} catch (SchemaException e) {
throw new TunnelException(e);
}
}
+ private void addHandling(MetadataHandlingType handling, ItemPath dataPath) throws SchemaException {
+ if (isHandlingApplicable(handling, dataPath)) {
+ addMetadataMappings(handling.getMapping());
+ addMetadataItems(handling.getItem(), dataPath);
+ }
+ }
+
private boolean isHandlingApplicable(MetadataHandlingType handling, ItemPath dataPath) throws SchemaException {
return handling != null && ProcessingUtil.doesApplicabilityMatch(handling.getApplicability(), dataPath);
}
@@ -219,6 +230,26 @@ public boolean isFullProcessing(ItemPath itemPath) throws SchemaException {
return getProcessing(itemPath) == ItemProcessingType.FULL;
}
+ public Collection getTransientPaths() {
+ PathSet transientPaths = new PathSet();
+ PathSet persistentPaths = new PathSet();
+ for (MetadataItemDefinitionType item : itemDefinitions) {
+ ItemPersistenceType persistence = item.getPersistence();
+ if (persistence != null) {
+ ItemPath path = item.getRef().getItemPath();
+ if (persistence == ItemPersistenceType.PERSISTENT) {
+ persistentPaths.add(path);
+ } else {
+ transientPaths.add(path);
+ }
+ }
+ }
+ for (ItemPath persistentPath : persistentPaths) {
+ transientPaths.remove(persistentPath);
+ }
+ return transientPaths;
+ }
+
// For item-contained mappings the target exclusion can be checked twice
private boolean isMetadataMappingApplicable(MetadataMappingType mapping) {
return hasCompatibleScope(mapping) &&
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/MetadataItemProcessingSpecImpl.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/MetadataItemProcessingSpecImpl.java
index 518d9b4d91d..2537fd29e6c 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/MetadataItemProcessingSpecImpl.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/MetadataItemProcessingSpecImpl.java
@@ -11,18 +11,18 @@
import java.util.Arrays;
import java.util.List;
-import com.evolveum.midpoint.model.api.MetadataItemProcessingSpec;
-
-import com.evolveum.midpoint.util.DebugDumpable;
-import com.evolveum.midpoint.util.DebugUtil;
+import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import org.jetbrains.annotations.NotNull;
+import com.evolveum.midpoint.model.api.MetadataItemProcessingSpec;
import com.evolveum.midpoint.model.common.util.ObjectTemplateIncludeProcessor;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.repo.common.ObjectResolver;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
+import com.evolveum.midpoint.util.DebugDumpable;
+import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
@@ -92,14 +92,20 @@ private void addFromObjectTemplate(ObjectTemplateType rootTemplate, ObjectResolv
}
private void addFromObjectTemplate(ObjectTemplateType template) {
+ addMetadataHandling(template.getMeta(), null);
+ template.getItem().forEach(
+ itemDef -> addMetadataHandling(itemDef.getMeta(), itemDef.getRef()));
+ }
+
+ private void addMetadataHandling(MetadataHandlingType metadataHandling, ItemPathType dataItemPathBean) {
try {
- MetadataHandlingType metadataHandling = template.getMeta();
if (metadataHandling != null) {
for (MetadataItemDefinitionType metadataItemDef : metadataHandling.getItem()) {
if (metadataItemDef.getRef() != null && metadataItemDef.getRef().getItemPath().equivalent(metadataItemPath)) {
ItemProcessingType processing = ProcessingUtil.getProcessingOfItem(metadataItemDef);
if (processing != null) {
- processingSpecs.add(new ProcessingSpec(processing, metadataHandling.getApplicability(), metadataItemDef.getApplicability()));
+ processingSpecs.add(new ProcessingSpec(processing, dataItemPathBean,
+ metadataHandling.getApplicability(), metadataItemDef.getApplicability()));
}
}
}
@@ -119,17 +125,22 @@ public String debugDump(int indent) {
private static class ProcessingSpec implements DebugDumpable {
private final ItemProcessingType processing;
+ private final ItemPath dataItemPath;
private final List applicabilityList;
- private ProcessingSpec(ItemProcessingType processing,
+ private ProcessingSpec(ItemProcessingType processing, ItemPathType dataItemPathBean,
MetadataProcessingApplicabilitySpecificationType... applicabilityList) {
this.processing = processing;
+ this.dataItemPath = dataItemPathBean != null ? dataItemPathBean.getItemPath() : null;
this.applicabilityList = Arrays.asList(applicabilityList);
}
- public boolean appliesTo(ItemPath dataItem) throws SchemaException {
+ public boolean appliesTo(ItemPath dataItemPath) throws SchemaException {
+ if (this.dataItemPath != null && !this.dataItemPath.equivalent(dataItemPath)) {
+ return false;
+ }
for (MetadataProcessingApplicabilitySpecificationType applicability : applicabilityList) {
- if (!ProcessingUtil.doesApplicabilityMatch(applicability, dataItem)) {
+ if (!ProcessingUtil.doesApplicabilityMatch(applicability, dataItemPath)) {
return false;
}
}
@@ -140,6 +151,7 @@ public boolean appliesTo(ItemPath dataItem) throws SchemaException {
public String debugDump(int indent) {
StringBuilder sb = new StringBuilder();
DebugUtil.debugDumpWithLabelLn(sb, "processing", String.valueOf(processing), indent);
+ DebugUtil.debugDumpWithLabelLn(sb, "dataItemPath", String.valueOf(dataItemPath), indent);
DebugUtil.debugDumpWithLabelLn(sb, "applicabilityList", applicabilityList, indent);
return sb.toString();
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/TransformationalMetadataComputation.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/TransformationalMetadataComputation.java
index 403b9a72360..b28613c041d 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/TransformationalMetadataComputation.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/TransformationalMetadataComputation.java
@@ -8,6 +8,7 @@
package com.evolveum.midpoint.model.common.mapping.metadata;
import com.evolveum.midpoint.model.common.ModelCommonBeans;
+import com.evolveum.midpoint.model.common.mapping.AbstractMappingImpl;
import com.evolveum.midpoint.model.common.mapping.MappingEvaluationEnvironment;
import com.evolveum.midpoint.model.common.mapping.MappingImpl;
import com.evolveum.midpoint.model.common.mapping.metadata.builtin.BuiltinMetadataMapping;
@@ -42,14 +43,20 @@ public class TransformationalMetadataComputation extends ValueMetadataComputatio
*/
@NotNull private final List inputValues;
+ /**
+ * Mapping in context of which this transformation is being executed.
+ */
+ @NotNull private final AbstractMappingImpl, ?, ?> mapping;
+
private TransformationalMetadataComputation(
@NotNull List inputValues,
@NotNull ItemValueMetadataProcessingSpec processingSpec,
- @Nullable MappingSpecificationType mappingSpecification,
+ @NotNull AbstractMappingImpl, ?, ?> mapping,
@NotNull ModelCommonBeans beans,
MappingEvaluationEnvironment env) {
- super(processingSpec, mappingSpecification, beans, env);
+ super(processingSpec, mapping.getMappingSpecification(), beans, env);
this.inputValues = inputValues;
+ this.mapping = mapping;
}
@Override
@@ -67,7 +74,7 @@ public static TransformationalMetadataComputation forMapping(List in
MappingEvaluationEnvironment env = new MappingEvaluationEnvironment(
"metadata evaluation in " + mapping.getMappingContextDescription(),
mapping.getNow(), mapping.getTask());
- return new TransformationalMetadataComputation(inputValues, processingSpec, mapping.getMappingSpecification(), mapping.getBeans(), env);
+ return new TransformationalMetadataComputation(inputValues, processingSpec, mapping, mapping.getBeans(), env);
}
@Override
@@ -91,6 +98,10 @@ public List getInputValues() {
return Collections.unmodifiableList(inputValues);
}
+ public List getSourceNames() {
+ return mapping.getSourceNames();
+ }
+
@Override
void createCustomMappingVariables(MetadataMappingBuilder, ?> builder, MetadataMappingType metadataMappingBean) {
super.createCustomMappingVariables(builder, metadataMappingBean);
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ValueMetadataComputation.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ValueMetadataComputation.java
index ab2fda66cac..a0ef01fd133 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ValueMetadataComputation.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/ValueMetadataComputation.java
@@ -107,6 +107,7 @@ public ValueMetadataType execute(OperationResult parentResult) throws Communicat
processCustomMappings();
processBuiltinMappings();
recordOutput();
+ applyPersistence();
return outputMetadata.asContainerable();
} catch (Throwable t) {
result.recordFatalError(t);
@@ -116,6 +117,21 @@ public ValueMetadataType execute(OperationResult parentResult) throws Communicat
}
}
+ private void applyPersistence() {
+ for (ItemPath transientPath : processingSpec.getTransientPaths()) {
+ markValuesTransient(transientPath);
+ }
+ }
+
+ // We assume there is only a single item corresponding to given path
+ private void markValuesTransient(ItemPath path) {
+ Item item = outputMetadata.findItem(path);
+ if (item != null) {
+ LOGGER.trace("Marking {} values of {} as transient", item.size(), path);
+ item.getValues().forEach(value -> value.setTransient(true));
+ }
+ }
+
private void recordOutput() {
result.addReturn("summary", outputMetadata.toString()); // temporary
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/BaseBuiltinMetadataMapping.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/BaseBuiltinMetadataMapping.java
index 2ab622e669c..482b3e1e75c 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/BaseBuiltinMetadataMapping.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/BaseBuiltinMetadataMapping.java
@@ -9,6 +9,8 @@
import javax.annotation.PostConstruct;
+import com.evolveum.midpoint.prism.PrismValue;
+
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,6 +20,8 @@
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.exception.SchemaException;
+import java.util.List;
+
/**
* TODO
*/
@@ -50,4 +54,19 @@ void addPropertyRealValue(PrismContainerValue> outputMetadata, Object value) t
property.addRealValue(value);
}
}
+
+ String dumpInput(List inputValues) {
+ if (inputValues.isEmpty()) {
+ return " (no values)";
+ } else {
+ StringBuilder sb = new StringBuilder();
+ for (PrismValue inputValue : inputValues) {
+ if (inputValue != null) {
+ sb.append(" - ").append(inputValue.toString()).append(" with metadata:\n");
+ sb.append(inputValue.getValueMetadata().debugDump(2)).append("\n");
+ }
+ }
+ return sb.toString();
+ }
+ }
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/ProvenanceBuiltinMapping.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/ProvenanceBuiltinMapping.java
index 46f84436f49..f40cdfae094 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/ProvenanceBuiltinMapping.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/ProvenanceBuiltinMapping.java
@@ -179,21 +179,6 @@ private ProvenanceMetadataType compute() {
}
}
- private String dumpInput(List inputValues) {
- if (inputValues.isEmpty()) {
- return " (no values)";
- } else {
- StringBuilder sb = new StringBuilder();
- for (PrismValue inputValue : inputValues) {
- if (inputValue != null) {
- sb.append(" - ").append(inputValue.toString()).append(" with metadata:\n");
- sb.append(inputValue.getValueMetadata().debugDump(2)).append("\n");
- }
- }
- return sb.toString();
- }
- }
-
// private String dumpInputMetadata(List values) {
// if (values.isEmpty()) {
// return " (no values)";
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/TransformationBuiltinMapping.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/TransformationBuiltinMapping.java
new file mode 100644
index 00000000000..419fbf48ac3
--- /dev/null
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/metadata/builtin/TransformationBuiltinMapping.java
@@ -0,0 +1,133 @@
+/*
+ * 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.model.common.mapping.metadata.builtin;
+
+import static com.evolveum.midpoint.util.DebugUtil.lazy;
+
+import java.util.List;
+
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+
+import com.evolveum.midpoint.model.common.mapping.metadata.ConsolidationMetadataComputation;
+import com.evolveum.midpoint.model.common.mapping.metadata.TransformationalMetadataComputation;
+import com.evolveum.midpoint.prism.PrismValue;
+import com.evolveum.midpoint.prism.path.ItemPath;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingSourceType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingTransformationType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.TransformationMetadataType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueMetadataType;
+import com.evolveum.prism.xml.ns._public.types_3.RawType;
+
+import javax.xml.namespace.QName;
+
+/**
+ * Mapping that manages transformation metadata.
+ */
+@Component
+public class TransformationBuiltinMapping extends BaseBuiltinMetadataMapping {
+
+ private static final Trace LOGGER = TraceManager.getTrace(TransformationBuiltinMapping.class);
+
+ private static final ItemPath PATH = ItemPath.create(ValueMetadataType.F_TRANSFORMATION);
+
+ @SuppressWarnings("unused")
+ TransformationBuiltinMapping() {
+ super(PATH);
+ }
+
+ /**
+ * Transformation merges the acquisitions into single yield.
+ */
+ @Override
+ public void applyForTransformation(@NotNull TransformationalMetadataComputation computation) {
+ List input = computation.getInputValues();
+
+ LOGGER.trace("Computing transformation metadata during value transformation. Input values:\n{}",
+ lazy(() -> dumpInput(input)));
+
+ MappingTransformationType mappingTransformation = new MappingTransformationType(prismContext)
+ .mappingSpecification(computation.getMappingSpecification());
+
+ List sourceNames = computation.getSourceNames();
+ if (computation.getInputValues().size() < sourceNames.size()) {
+ throw new IllegalStateException("Couldn't compute transformational metadata: there are less values ("
+ + computation.getInputValues().size() + ") than sources (" + sourceNames.size() + ") in "
+ + computation.getContextDescription());
+ }
+
+ for (int i = 0; i < sourceNames.size(); i++) {
+ MappingSourceType source = new MappingSourceType(prismContext);
+
+ QName sourceName = sourceNames.get(i);
+ if (sourceName != null) {
+ source.setName(sourceName.getLocalPart());
+ }
+
+ PrismValue inputValue = computation.getInputValues().get(i);
+ if (inputValue != null) {
+ PrismValue inputValueClone = inputValue.clone();
+ markNotTransient(inputValueClone);
+ source.setValue(new RawType(inputValueClone, null, prismContext));
+ }
+
+ mappingTransformation.getSource().add(source);
+ }
+
+ TransformationMetadataType transformation = new TransformationMetadataType(prismContext)
+ .mappingTransformation(mappingTransformation);
+
+ LOGGER.trace("Output: transformation:\n{}", lazy(() -> transformation.asPrismContainerValue().debugDump()));
+
+ computation.getOutputMetadataValueBean().setTransformation(transformation);
+ }
+
+ /**
+ * The value can contain transient values e.g. because during its computation the transformation metadata was
+ * marked as transient. But there can be situations when transformation metadata for the current value are
+ * non-transient. Then we could want to store them completely, i.e. with transformation metadata of source values.
+ * So we must mark them as not transient.
+ */
+ private void markNotTransient(PrismValue rootValue) {
+ rootValue.accept(visitable -> {
+ if (visitable instanceof PrismValue) {
+ PrismValue value = (PrismValue) visitable;
+ value.setTransient(false);
+ value.getValueMetadataAsContainer().valuesStream()
+ .forEach(this::markNotTransient);
+ }
+ });
+ }
+
+ /**
+ * During consolidation we simply take (any) value.
+ */
+ @Override
+ public void applyForConsolidation(@NotNull ConsolidationMetadataComputation computation) {
+ TransformationMetadataType transformation = selectTransformation(computation);
+ if (transformation != null) {
+ computation.getOutputMetadataValueBean().setTransformation(transformation.clone());
+ }
+ }
+
+ private TransformationMetadataType selectTransformation(ConsolidationMetadataComputation computation) {
+ for (ValueMetadataType nonNegativeValue : computation.getNonNegativeValues()) {
+ if (nonNegativeValue.getTransformation() != null) {
+ return nonNegativeValue.getTransformation();
+ }
+ }
+ for (ValueMetadataType existingValue : computation.getExistingValues()) {
+ if (existingValue.getTransformation() != null) {
+ return existingValue.getTransformation();
+ }
+ }
+ return null;
+ }
+}
diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java
index cd09a8c77ce..b0bd44eaab8 100644
--- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java
+++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java
@@ -182,7 +182,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti
addObject(TEMPLATE_LOA_USER, initTask, initResult);
addObject(USER_ALICE, initTask, initResult);
- setGlobalTracingOverride(createModelLoggingTracingProfile());
+// setGlobalTracingOverride(createModelLoggingTracingProfile());
}
@Override
@@ -242,17 +242,18 @@ public void test020ParsingMetadata() throws Exception {
.valueMetadata(UserType.F_NAME)
.assertHasDefinition()
.singleValue()
- .containerSingle(ValueMetadataType.F_TRANSFORMATION)
- .container(TransformationMetadataType.F_SOURCE)
- .assertSize(1)
- .singleValue()
- .assertPropertyEquals(ValueSourceType.F_KIND, "http://midpoint.evolveum.com/data-provenance/source#resource")
- .end()
- .end()
- .end()
- .assertPropertyValuesEqual(LOA_PATH, 10)
- .end()
+// .containerSingle(ValueMetadataType.F_TRANSFORMATION)
+// .container(TransformationMetadataType.F_SOURCE)
+// .assertSize(1)
+// .singleValue()
+// .assertPropertyEquals(ValueSourceType.F_KIND, "http://midpoint.evolveum.com/data-provenance/source#resource")
+// .end()
+// .end()
+// .end()
+// .assertPropertyValuesEqual(LOA_PATH, 10)
+// .end()
.end()
+ .end()
.valueMetadata(UserType.F_GIVEN_NAME)
.assertHasDefinition()
.singleValue()
diff --git a/model/model-intest/src/test/resources/metadata/provenance-metadata-recording/template-provenance-metadata-recording.xml b/model/model-intest/src/test/resources/metadata/provenance-metadata-recording/template-provenance-metadata-recording.xml
index 4bfab05fb6b..b90e9293930 100644
--- a/model/model-intest/src/test/resources/metadata/provenance-metadata-recording/template-provenance-metadata-recording.xml
+++ b/model/model-intest/src/test/resources/metadata/provenance-metadata-recording/template-provenance-metadata-recording.xml
@@ -36,19 +36,32 @@
-
- fullName to alias
- strong
-
- fullName
-
-
- extension/alias
-
- matchingProvenance
-
-
-
+ -
+
[extension/alias]
+
+ fullName to alias
+ strong
+
+ fullName
+
+
+
+ matchingProvenance
+
+
+
+
+
+ -
+
[transformation]
+
+ full
+
+ persistent
+
+
+
+
@@ -118,6 +131,14 @@
+ -
+
[transformation]
+
+ full
+
+ transient
+
+
-
[extension/loa]
diff --git a/model/model-intest/src/test/resources/metadata/user-alice.xml b/model/model-intest/src/test/resources/metadata/user-alice.xml
index 1edbf2dc730..c6f2706cc79 100644
--- a/model/model-intest/src/test/resources/metadata/user-alice.xml
+++ b/model/model-intest/src/test/resources/metadata/user-alice.xml
@@ -21,13 +21,16 @@
<_value>alice
<_metadata>
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- name
-
+
+
+ inbound name mapping
+
+
+ name
+
+ alice
+
+
10
@@ -40,24 +43,24 @@
5
-
- http://midpoint.evolveum.com/data-provenance/source#userAction
- willTurner
-
-
+
+
+ fullName mapping
+
+
+ title
+ pirate
+
+
experimental
<_value>manual
<_metadata>
-
-
- http://midpoint.evolveum.com/data-provenance/source#userAction
- willTurner
-
-
-
+
+ 3
+
@@ -84,13 +87,16 @@
<_value>Alice
<_metadata>
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- givenName
-
+
+
+ inbound givenName mapping
+
+
+ firstname
+
+ Alice
+
+
7
@@ -101,13 +107,16 @@
<_value>Green
<_metadata>
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- familyName
-
+
+
+ inbound familyName mapping
+
+
+ lastname
+
+ Green
+
+
7
@@ -118,13 +127,16 @@
<_value>Ph.D.
<_metadata>
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- honorificSuffix
-
+
+
+ inbound honorificSuffix mapping
+
+
+ aftertitle
+
+ Ph.D.
+
+
5
@@ -135,32 +147,81 @@
<_value>Alice Green, Ph.D.
<_metadata>
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- givenName
-
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- familyName
-
-
- http://midpoint.evolveum.com/data-provenance/source#resource
- Dummy Resource
-
-
- honorificSuffix
-
-
- http://midpoint.evolveum.com/data-provenance/transformer#mapping
- mapping-fullname
-
-
+
+
+ fullName mapping
+
+
+
+ givenName
+
+ <_value>Alice
+ <_metadata>
+
+
+
+ inbound givenName mapping
+
+
+ firstname
+
+ Alice
+
+
+
+
+ 7
+
+
+
+
+
+ familyName
+
+ <_value>Green
+ <_metadata>
+
+
+
+ inbound familyName mapping
+
+
+ lastname
+
+ Green
+
+
+
+
+ 7
+
+
+
+
+
+ honorificSuffix
+
+ <_value>Ph.D.
+ <_metadata>
+
+
+
+ inbound honorificSuffix mapping
+
+
+ aftertitle
+
+ Ph.D.
+
+
+
+
+ 5
+
+
+
+
+
5
@@ -171,14 +232,6 @@
<_value>Development
<_metadata>
-
-
- http://midpoint.evolveum.com/data-provenance/source#userAction
- jack
-
- organizationalUnit
-
-
7
diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/evaluator/AbstractExpressionEvaluator.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/evaluator/AbstractExpressionEvaluator.java
index 2fd3681542e..65612c3dd40 100644
--- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/evaluator/AbstractExpressionEvaluator.java
+++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/evaluator/AbstractExpressionEvaluator.java
@@ -192,7 +192,7 @@ public void addInternalOrigin(PrismValue value, ExpressionEvaluationContext cont
public void applyValueMetadata(PrismValueDeltaSetTriple triple, ExpressionEvaluationContext context,
OperationResult result) throws CommunicationException, ObjectNotFoundException, SchemaException,
SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
- if (triple != null && context.getValueMetadataComputer() != null) {
+ if (triple != null) {
for (V value : triple.getPlusSet()) {
applyValueMetadata(value, context, result);
}
@@ -203,7 +203,9 @@ public void applyValueMetadata(PrismValueDeltaSetTriple triple, ExpressionEva
// For values in minus set we must delete any existing metadata. The reason is that a mapping
// computes own metadata for its outputs - so metadata from the input values in minus set are no longer
// relevant on the output.
- value.getValueMetadata().clear();
+ if (value != null) {
+ value.getValueMetadata().clear();
+ }
}
}
}
@@ -212,9 +214,15 @@ private void applyValueMetadata(PrismValue value, ExpressionEvaluationContext co
throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException,
ConfigurationException, ExpressionEvaluationException {
if (value != null) {
- value.setValueMetadata(
- context.getValueMetadataComputer()
- .compute(Collections.singletonList(value), result));
+ if (context.getValueMetadataComputer() != null) {
+ value.setValueMetadata(
+ context.getValueMetadataComputer()
+ .compute(Collections.singletonList(value), result));
+ } else {
+ // This is to clear pre-existing metadata for asIs mappings that are not configured e.g. for provenance
+ // metadata propagation.
+ value.getValueMetadata().clear();
+ }
}
}
}
diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java
index 3fa210caaef..84c4240088c 100644
--- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java
+++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java
@@ -248,15 +248,14 @@ void updateFullObject(RObject object, PrismObject save
.itemsToSkip(itemsToSkip)
.options(SerializationOptions
.createSerializeReferenceNamesForNullOids()
- .skipIndexOnly(true))
+ .skipIndexOnly(true)
+ .skipTransient(true))
.serialize(savedObject);
byte[] fullObject = RUtil.getBytesFromSerializedForm(xml, getConfiguration().isUseZip());
object.setFullObject(fullObject);
- if (LOGGER.isTraceEnabled()) {
- LOGGER.trace("Updating full object xml column finished. Xml:\n{}", xml);
- }
+ LOGGER.trace("Updating full object xml column finished. Xml:\n{}", xml);
}
protected SqlRepositoryConfiguration getConfiguration() {
From 0b4307b70b44d300fe430411e0cfbb1f7b2ab112 Mon Sep 17 00:00:00 2001
From: Pavol Mederly
Date: Thu, 3 Sep 2020 17:30:47 +0200
Subject: [PATCH 06/12] Generalize basic.getMetadataExtensionValues method
This is just a suggestion of how this method could look like.
---
.../functions/BasicExpressionFunctions.java | 22 ++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java
index 86bef80bc72..ebdbc402110 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java
@@ -39,6 +39,7 @@
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.QNameUtil;
+import com.evolveum.midpoint.util.annotation.Experimental;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import org.apache.commons.io.FileUtils;
@@ -586,13 +587,18 @@ public Collection getAttributeStringValues(ShadowType shadow, javax.xml.
return ShadowUtil.getAttributeValues(shadow, attributeQname, String.class);
}
+ /**
+ * Generic method to extract all metadata values pointed-to by given item path (specified as segments).
+ * Note: does not support multivalued containers withing the path (e.g. collecting transformation/source/name,
+ * where transformation/source is a multivalued container).
+ */
+ @Experimental
@NotNull
- public Collection> getMetadataExtensionValues(PrismValue value, String itemLocalPart) {
- checkColon(itemLocalPart);
+ public Collection> getMetadataValues(PrismValue value, Object... pathSegments) {
if (value == null) {
return emptySet();
} else {
- ItemPath itemPath = ItemPath.create(ValueMetadataType.F_EXTENSION, itemLocalPart);
+ ItemPath itemPath = ItemPath.create(pathSegments);
return value.getValueMetadataAsContainer().valuesStream()
.map(md -> md.findItem(itemPath))
.filter(Objects::nonNull)
@@ -601,6 +607,16 @@ public Collection> getMetadataExtensionValues(PrismValue value, String itemLoc
}
}
+ /**
+ * Simplified version of getMetadataValue aimed at fetching single-segment extension paths.
+ */
+ @Experimental
+ @NotNull
+ public Collection> getMetadataExtensionValues(PrismValue value, String itemLocalPart) {
+ checkColon(itemLocalPart);
+ return getMetadataValues(value, ValueMetadataType.F_EXTENSION, itemLocalPart);
+ }
+
public void setExtensionRealValues(PrismContainerValue> containerValue, Map map) throws SchemaException {
PrismContainer ext = containerValue.findOrCreateContainer(ObjectType.F_EXTENSION);
Map qnameKeyedMap = new HashMap<>();
From e24a0df6e239366dcd4b44a2e561bbf3e1165373 Mon Sep 17 00:00:00 2001
From: lskublik
Date: Thu, 3 Sep 2020 21:38:56 +0200
Subject: [PATCH 07/12] fix for TestInitialObjects
---
.../com/evolveum/midpoint/web/TestInitialObjects.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/TestInitialObjects.java b/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/TestInitialObjects.java
index 066e2c23f47..0790670a22b 100644
--- a/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/TestInitialObjects.java
+++ b/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/TestInitialObjects.java
@@ -13,6 +13,7 @@
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectCollectionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType;
import org.testng.annotations.Test;
@@ -61,7 +62,7 @@ public void testInitialObjects() throws Exception {
private void testInitialObject(ObjectValidator validator, StringBuilder errorsSb, File file) throws SchemaException, IOException {
PrismObject object = getPrismContext().parseObject(file);
ValidationResult validationResult = validator.validate(object);
- if (validationResult.isEmpty() || isOnlyJasperWarning(validationResult)) {
+ if (validationResult.isEmpty() || isIgnoredWarning(validationResult)) {
display("Checked "+object+": no warnings");
return;
}
@@ -76,9 +77,10 @@ private void testInitialObject(ObjectValidator validator,
}
}
- private boolean isOnlyJasperWarning(ValidationResult validationResult) {
+ private boolean isIgnoredWarning(ValidationResult validationResult) {
for (ValidationItem item : validationResult.getItems()){
- if (!item.getItemPath().equivalent(ItemPath.create(ReportType.F_JASPER))
+ if ((!item.getItemPath().equivalent(ItemPath.create(ReportType.F_JASPER))
+ && !item.getItemPath().equivalent(ItemPath.create(ObjectCollectionType.F_AUDIT_SEARCH)))
|| !item.getStatus().equals(OperationResultStatus.WARNING)){
return false;
}
From fbf491b6bcee3f943f6c529b5e9c93ffe4d93adc Mon Sep 17 00:00:00 2001
From: Richard Richter
Date: Thu, 3 Sep 2020 23:01:26 +0200
Subject: [PATCH 08/12] AuditSearchTest.java: added test for LIKE with _,
Querydsl covers it!
_ in LIKE is "any character" (like . in regex) and needs to be escaped.
If we use Querydsl operations like starts/endsWith() or contains(),
the escaping is done automatically for us (not sure about like(), but
that one has lower level semantics anyway).
---
.../midpoint/repo/sql/AuditSearchTest.java | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditSearchTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditSearchTest.java
index 42fbade17f3..85052b1c4a5 100644
--- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditSearchTest.java
+++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditSearchTest.java
@@ -124,6 +124,7 @@ public void initSystem() throws Exception {
record2.addDelta(createDeltaWithIgnoredPath(UserType.F_FAMILY_NAME));
record2.getCustomColumnProperty().put("foo", "foo-value-2");
record2.getCustomColumnProperty().put("bar", "bar-val");
+ record2.setTaskOid("task_oid2");
auditService.audit(record2, NullTaskImpl.INSTANCE);
AuditEventRecord record3 = new AuditEventRecord();
@@ -419,6 +420,7 @@ public void test141SearchByTimestampEqual() throws SchemaException {
then("only audit events with the timestamp equal to are returned");
assertThat(result).hasSize(1);
+ // getParameter() is used to identify specific audit records (1, 2, 3) as mentioned in init
assertThat(result.get(0).getParameter()).isEqualTo("2");
}
@@ -680,6 +682,20 @@ public void test195SearchByTaskOid() throws SchemaException {
assertThat(result).allMatch(r -> r.getTaskOID().equals("task-oid"));
}
+ @Test
+ public void test196SearchByTaskOidLikeWithUnderscore() throws SchemaException {
+ when("searching audit filtered by task OID LIKE with underscore");
+ SearchResultList result = searchObjects(prismContext
+ .queryFor(AuditEventRecordType.class)
+ // we double down here with ignore-casing matcher
+ .item(AuditEventRecordType.F_TASK_OID).startsWith("TASK_").matchingCaseIgnore()
+ .build());
+
+ then("only audit events with matching task OID are returned (underscore is escaped)");
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).getParameter()).isEqualTo("2");
+ }
+
/*
* Our NOT means "complement" and must include NULL values for NOT(EQ...).
* This is difference from SQL logic, where NOT x='value' does NOT return rows where x IS NULL.
From 7dc98022c7c55f0b9ba794e96a5827df447bd854 Mon Sep 17 00:00:00 2001
From: kate
Date: Fri, 4 Sep 2020 00:28:38 +0200
Subject: [PATCH 09/12] search filter title fix
---
.../component/search/FilterSearchItem.java | 24 +++++++++++++++----
.../web/component/search/SearchItem.java | 4 +++-
.../web/component/search/SearchItemPanel.java | 2 +-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/FilterSearchItem.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/FilterSearchItem.java
index b4cd3101272..a03f8dc0df5 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/FilterSearchItem.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/FilterSearchItem.java
@@ -6,19 +6,25 @@
*/
package com.evolveum.midpoint.web.component.search;
-import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
-import com.evolveum.midpoint.xml.ns._public.common.common_3.SearchItemType;
-
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.jetbrains.annotations.NotNull;
+import com.evolveum.midpoint.gui.api.page.PageBase;
+import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
+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.SearchItemType;
+
/**
* @author honchar
*/
public class FilterSearchItem extends SearchItem {
private static final long serialVersionUID = 1L;
+ private static final Trace LOGGER = TraceManager.getTrace(FilterSearchItem.class);
public static final String F_APPLY_FILTER = "applyFilter";
@@ -42,8 +48,16 @@ public Type getType(){
}
@Override
- protected String getTitle(){
- return getPredefinedFilter().getFilter() != null ? getPredefinedFilter().getFilter().toString() : "";
+ protected String getTitle(PageBase pageBase) {
+ if (getPredefinedFilter() == null || getPredefinedFilter().getFilter() == null) {
+ return null;
+ }
+ try {
+ return pageBase.getPrismContext().xmlSerializer().serializeRealValue(getPredefinedFilter().getFilter());
+ } catch (SchemaException e) {
+ LoggingUtils.logUnexpectedException(LOGGER, "Cannot serialize filter", e);
+ }
+ return null;
}
public SearchItemType getPredefinedFilter() {
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItem.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItem.java
index 5a53658fbab..31b0c9ce51b 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItem.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItem.java
@@ -7,6 +7,8 @@
package com.evolveum.midpoint.web.component.search;
+import com.evolveum.midpoint.gui.api.page.PageBase;
+
import java.io.Serializable;
/**
@@ -33,7 +35,7 @@ public SearchItem(Search search) {
public abstract Type getType();
- protected String getTitle(){
+ protected String getTitle(PageBase pageBase){
return "";
}
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java
index e8b770fc224..68732664dd4 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java
@@ -231,7 +231,7 @@ private IModel createTitleModel() {
if (item == null){
return Model.of();
}
- return Model.of(item.getTitle());
+ return Model.of(item.getTitle(getPageBase()));
}
private void deletePerformed(AjaxRequestTarget target) {
From 88bd8b2d44e1c6415c6e73ee1204ceacb466a2bd Mon Sep 17 00:00:00 2001
From: Pavol Mederly
Date: Fri, 4 Sep 2020 01:13:15 +0200
Subject: [PATCH 10/12] Improve trace processing
1) Add ninja option to analyze and transform traces
2) Improve visualization of assignment traces
---
.../midpoint/schema/traces/OpNode.java | 6 +-
.../midpoint/schema/traces/OpNodeFactory.java | 4 +
.../schema/traces/OpNodeTreeBuilder.java | 24 ++-
.../midpoint/schema/traces/OpType.java | 124 ++-------------
.../schema/traces/TemplateExpander.java | 133 ++++++++++++++++
.../midpoint/schema/traces/TraceParser.java | 13 +-
.../midpoint/schema/traces/TraceWriter.java | 51 +++++++
.../AssignmentEvaluationOpNode.java | 67 ++++++++
.../AssignmentSegmentEvaluationOpNode.java | 69 +++++++++
.../traces/operations/ResolutionUtil.java | 43 ++++++
.../midpoint/schema/util/AssignmentUtil.java | 143 ++++++++++++++++++
.../midpoint/schema/util/MappingUtil.java | 41 +++++
.../schema/util/OperationResultUtil.java | 50 ++++++
.../schema/util/PolicyRuleTypeUtil.java | 8 +
.../resources/ctx-configuration-no-repo.xml | 55 +++++++
.../task/quartzimpl/tracing/TracerImpl.java | 28 ++--
.../ninja/action/trace/EditTraceAction.java | 141 +++++++++++++++++
.../ninja/action/trace/TraceStatistics.java | 126 +++++++++++++++
.../evolveum/midpoint/ninja/impl/Command.java | 5 +-
.../midpoint/ninja/impl/NinjaContext.java | 15 +-
.../ninja/opts/ConnectionOptions.java | 9 ++
.../midpoint/ninja/opts/EditTraceOptions.java | 68 +++++++++
.../src/main/resources/messages.properties | 10 +-
23 files changed, 1086 insertions(+), 147 deletions(-)
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TemplateExpander.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceWriter.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentEvaluationOpNode.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentSegmentEvaluationOpNode.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/ResolutionUtil.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/util/AssignmentUtil.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MappingUtil.java
create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/util/OperationResultUtil.java
create mode 100644 repo/system-init/src/main/resources/ctx-configuration-no-repo.xml
create mode 100644 tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java
create mode 100644 tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/TraceStatistics.java
create mode 100644 tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/EditTraceOptions.java
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNode.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNode.java
index cc49cd8f8e6..3b914609d78 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNode.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNode.java
@@ -607,7 +607,7 @@ public int getMappingsCount() {
}
public Integer getAssignmentEvaluationsCount() {
- return (int) getChildrenStream(2)
+ return (int) getChildrenStream(3)
.filter(child -> child.getKind() == OperationKindType.ASSIGNMENT_EVALUATION)
.count();
}
@@ -639,4 +639,8 @@ public OpNodePresentation getPresentation() {
public void setPresentation(OpNodePresentation presentation) {
this.presentation = presentation;
}
+
+ public void resolveReferenceTargetNames(OpNodeTreeBuilder.NameResolver nameResolver) {
+ }
+
}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeFactory.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeFactory.java
index fc48a85de3e..46d02f95dad 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeFactory.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeFactory.java
@@ -45,6 +45,10 @@ public static OpNode createOpNode(PrismContext prismContext, OperationResultType
case PROJECTOR_TEMPLATE_AFTER_ASSIGNMENTS:
case PROJECTOR_COMPONENT_OTHER:
return new ProjectorComponentOpNode(prismContext, result, info, parent, traceInfo);
+ case ASSIGNMENT_EVALUATION:
+ return new AssignmentEvaluationOpNode(prismContext, result, info, parent, traceInfo);
+ case ASSIGNMENT_SEGMENT_EVALUATION:
+ return new AssignmentSegmentEvaluationOpNode(prismContext, result, info, parent, traceInfo);
}
}
return new OpNode(prismContext, result, info, parent, traceInfo);
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeTreeBuilder.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeTreeBuilder.java
index ba4a8d0a3af..75256ef563e 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeTreeBuilder.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpNodeTreeBuilder.java
@@ -12,6 +12,8 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TracingOutputType;
+import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
+
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -23,25 +25,35 @@ public class OpNodeTreeBuilder {
@NotNull private final PrismContext prismContext;
- private IdentityHashMap infoMap = new IdentityHashMap<>();
+ private final IdentityHashMap infoMap = new IdentityHashMap<>();
- public OpNodeTreeBuilder(PrismContext prismContext) {
+ public OpNodeTreeBuilder(@NotNull PrismContext prismContext) {
this.prismContext = prismContext;
}
public List build(TracingOutputType tracingOutput) {
+ return build(tracingOutput, null);
+ }
+
+ public List build(TracingOutputType tracingOutput, NameResolver nameResolver) {
List rv = new ArrayList<>();
- addNode(null, rv, tracingOutput.getResult(), new TraceInfo(tracingOutput));
+ addNode(null, rv, tracingOutput.getResult(), new TraceInfo(tracingOutput), nameResolver);
return rv;
-
}
- private void addNode(OpNode parent, List rv, OperationResultType result, TraceInfo traceInfo) {
+ private void addNode(OpNode parent, List rv, OperationResultType result, TraceInfo traceInfo, NameResolver nameResolver) {
OpResultInfo info = OpResultInfo.create(result, infoMap);
OpNode newNode = OpNodeFactory.createOpNode(prismContext, result, info, parent, traceInfo);
+ if (nameResolver != null) {
+ newNode.resolveReferenceTargetNames(nameResolver);
+ }
rv.add(newNode);
for (OperationResultType child : result.getPartialResults()) {
- addNode(newNode, newNode.getChildren(), child, traceInfo);
+ addNode(newNode, newNode.getChildren(), child, traceInfo, nameResolver);
}
}
+
+ public interface NameResolver {
+ PolyStringType getName(String oid);
+ }
}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpType.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpType.java
index ae6301da554..967acdbe747 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpType.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/OpType.java
@@ -59,9 +59,17 @@ public enum OpType {
"com.evolveum.midpoint.model.impl.lens.projector.Projector.assignments",
"Assignments (${m:getAssignmentEvaluationsCount})"),
- ASSIGNMENT_EVALUATION(OperationKindType.ASSIGNMENT_EVALUATION, "Assignment evaluation",
+ ASSIGNMENT_EVALUATION_OUTER(OperationKindType.OTHER, "Assignment evaluation (outer)",
"com.evolveum.midpoint.model.impl.lens.projector.focus.AssignmentTripleEvaluator.evaluateAssignment",
- "Assignment evaluation (→ ${c:assignmentTargetName})"),
+ "Assignment evaluation: → ${c:assignmentTargetName}"),
+
+ ASSIGNMENT_EVALUATION(OperationKindType.ASSIGNMENT_EVALUATION, "Assignment evaluation",
+ "com.evolveum.midpoint.model.impl.lens.AssignmentEvaluator.evaluate",
+ "Assignment evaluation: ${m:getAssignmentInfo}"),
+
+ ASSIGNMENT_SEGMENT_EVALUATION(OperationKindType.OTHER, "Assignment segment evaluation",
+ "com.evolveum.midpoint.model.impl.lens.AssignmentEvaluator.evaluateFromSegment",
+ "${m:getSegmentLabel}"),
PROJECTION_ACTIVATION(OperationKindType.OTHER,"Projection activation",
"com.evolveum.midpoint.model.impl.lens.projector.ActivationProcessor.projectionActivation",
@@ -192,7 +200,8 @@ private static boolean isLoadedFromRepository(OperationResultType result) {
public String getFormattedName(OpNode node) {
if (nameTemplate != null) {
- return expandTemplate(node);
+ return new TemplateExpander()
+ .expandTemplate(node, nameTemplate);
}
OperationResultType opResult = node.getResult();
@@ -214,12 +223,6 @@ public String getFormattedName(OpNode node) {
String srcName = getContext(opResult, "segmentSourceName");
String tgtName = getContext(opResult, "segmentTargetName");
return "Segment: " + (srcName != null ? srcName + " " : "") + " → " + (tgtName != null ? " " + tgtName : "");
- } else if ("com.evolveum.midpoint.model.impl.lens.AssignmentEvaluator.evaluate".equals(operation)) {
- String tgtName = getContext(opResult, "assignmentTargetName");
- return "AssignmentEvaluator.evaluate" + (tgtName != null ? " (→ " + tgtName + ")" : "");
- } else if ("com.evolveum.midpoint.model.impl.lens.projector.focus.AssignmentTripleEvaluator.evaluateAssignment".equals(operation)) {
- String tgtName = getContext(opResult, "assignmentTargetName");
- return "AssignmentTripleEvaluator.evaluateAssignment" + (tgtName != null ? " (→ " + tgtName + ")" : "");
} else if ("com.evolveum.midpoint.model.impl.lens.projector.policy.PolicyRuleProcessor.evaluateRule".equals(operation)) {
String triggeredString = getReturn(opResult, "triggered");
int enabledActions = TraceUtil.getReturnsAsStringList(opResult, "enabledActions").size();
@@ -261,109 +264,6 @@ public String getFormattedName(OpNode node) {
return opResult.getOperation() + (qualifiers.isEmpty() ? "" : " (" + qualifiers + ")");
}
- private String expandTemplate(OpNode node) {
- return new StringSubstitutor(createResolver(node), "${", "}", '\\')
- .replace(nameTemplate);
- }
-
- private StringLookup createResolver(OpNode node) {
- return spec -> {
- String prefix;
- String suffix;
- String key;
-
- String[] parts = spec.split(":");
- if (parts.length == 1) {
- prefix = "";
- key = spec;
- suffix = "";
- } else if (parts.length == 2) {
- prefix = parts[0];
- key = parts[1];
- suffix = "";
- } else if (parts.length == 3) {
- prefix = parts[0];
- key = parts[1];
- suffix = parts[2];
- } else {
- return "???";
- }
-
- List values = new ArrayList<>();
- if (prefix.isEmpty() || prefix.equals("p")) {
- collectMatchingParams(values, key, node.getResult().getParams());
- }
- if (prefix.isEmpty() || prefix.equals("c")) {
- collectMatchingParams(values, key, node.getResult().getContext());
- }
- if (prefix.isEmpty() || prefix.equals("r")) {
- collectMatchingParams(values, key, node.getResult().getReturns());
- }
- if (prefix.isEmpty() || prefix.equals("t")) {
- collectMatchingValues(values, key, node.getResult().getTrace());
- }
- if (prefix.equals("m")) {
- collectFromMethod(values, key, node);
- }
- return String.join(", ", postprocess(values, suffix));
- };
- }
-
- private void collectFromMethod(List values, String key, OpNode node) {
- try {
- Object rv = MethodUtils.invokeExactMethod(node, key);
- if (rv instanceof Collection) {
- for (Object o : (Collection) rv) {
- values.add(String.valueOf(o));
- }
- } else if (rv != null) {
- values.add(String.valueOf(rv));
- }
- } catch (Throwable t) {
- values.add("??? " + t.getMessage());
- }
- }
-
- private void collectMatchingValues(List values, String path, List traces) {
- UniformItemPath itemPath = ItemPathParserTemp.parseFromString(path); // FIXME (hack)
- for (TraceType trace : traces) {
- PrismProperty property = trace.asPrismContainerValue().findProperty(itemPath);
- if (property != null) {
- for (Object realValue : property.getRealValues()) {
- values.add(String.valueOf(realValue));
- }
- }
- }
- }
-
- private void collectMatchingParams(List values, String key, ParamsType params) {
- int colon = key.indexOf(':');
- String processing;
- String realKey;
- if (colon >= 0) {
- realKey = key.substring(0, colon);
- processing = key.substring(colon + 1);
- } else {
- realKey = key;
- processing = "";
- }
- List rawStringValues = asStringList(selectByKey(params, realKey));
- values.addAll(postprocess(rawStringValues, processing));
- }
-
- private List postprocess(List raw, String processing) {
- return raw.stream()
- .map(s -> postprocess(s, processing))
- .collect(Collectors.toList());
- }
-
- private String postprocess(String string, String processing) {
- if (processing.contains("L")) {
- string = string.toLowerCase();
- }
- return string;
- }
-
private String getRepoCacheOpDescription(OpNode node, OperationResultType opResult, String last, String commaQualifiers) {
String postfix = "";
if ("getObject".equals(last)) {
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TemplateExpander.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TemplateExpander.java
new file mode 100644
index 00000000000..5fb61f0b0c1
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TemplateExpander.java
@@ -0,0 +1,133 @@
+/*
+ * 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.schema.traces;
+
+import com.evolveum.midpoint.prism.PrismProperty;
+import com.evolveum.midpoint.prism.impl.marshaller.ItemPathParserTemp;
+import com.evolveum.midpoint.prism.path.UniformItemPath;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ParamsType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.TraceType;
+
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.apache.commons.text.StringSubstitutor;
+import org.apache.commons.text.lookup.StringLookup;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.evolveum.midpoint.schema.traces.TraceUtil.asStringList;
+import static com.evolveum.midpoint.schema.traces.TraceUtil.selectByKey;
+
+public class TemplateExpander {
+
+ public String expandTemplate(OpNode node, String nameTemplate) {
+ return new StringSubstitutor(createResolver(node), "${", "}", '\\')
+ .replace(nameTemplate);
+ }
+
+ private StringLookup createResolver(OpNode node) {
+ return spec -> {
+ String prefix;
+ String suffix;
+ String key;
+
+ String[] parts = spec.split(":");
+ if (parts.length == 1) {
+ prefix = "";
+ key = spec;
+ suffix = "";
+ } else if (parts.length == 2) {
+ prefix = parts[0];
+ key = parts[1];
+ suffix = "";
+ } else if (parts.length == 3) {
+ prefix = parts[0];
+ key = parts[1];
+ suffix = parts[2];
+ } else {
+ return "???";
+ }
+
+ List values = new ArrayList<>();
+ if (prefix.isEmpty() || prefix.equals("p")) {
+ collectMatchingParams(values, key, node.getResult().getParams());
+ }
+ if (prefix.isEmpty() || prefix.equals("c")) {
+ collectMatchingParams(values, key, node.getResult().getContext());
+ }
+ if (prefix.isEmpty() || prefix.equals("r")) {
+ collectMatchingParams(values, key, node.getResult().getReturns());
+ }
+ if (prefix.isEmpty() || prefix.equals("t")) {
+ collectMatchingValues(values, key, node.getResult().getTrace());
+ }
+ if (prefix.equals("m")) {
+ collectFromMethod(values, key, node);
+ }
+ return String.join(", ", postprocess(values, suffix));
+ };
+ }
+
+ private void collectFromMethod(List values, String key, OpNode node) {
+ try {
+ Object rv = MethodUtils.invokeExactMethod(node, key);
+ if (rv instanceof Collection) {
+ for (Object o : (Collection) rv) {
+ values.add(String.valueOf(o));
+ }
+ } else if (rv != null) {
+ values.add(String.valueOf(rv));
+ }
+ } catch (Throwable t) {
+ values.add("??? " + t.getMessage());
+ }
+ }
+
+ private void collectMatchingValues(List values, String path, List traces) {
+ UniformItemPath itemPath = ItemPathParserTemp.parseFromString(path); // FIXME (hack)
+ for (TraceType trace : traces) {
+ PrismProperty property = trace.asPrismContainerValue().findProperty(itemPath);
+ if (property != null) {
+ for (Object realValue : property.getRealValues()) {
+ values.add(String.valueOf(realValue));
+ }
+ }
+ }
+ }
+
+ private void collectMatchingParams(List values, String key, ParamsType params) {
+ int colon = key.indexOf(':');
+ String processing;
+ String realKey;
+ if (colon >= 0) {
+ realKey = key.substring(0, colon);
+ processing = key.substring(colon + 1);
+ } else {
+ realKey = key;
+ processing = "";
+ }
+ List rawStringValues = asStringList(selectByKey(params, realKey));
+ values.addAll(postprocess(rawStringValues, processing));
+ }
+
+ private List postprocess(List raw, String processing) {
+ return raw.stream()
+ .map(s -> postprocess(s, processing))
+ .collect(Collectors.toList());
+ }
+
+ private String postprocess(String string, String processing) {
+ if (processing.contains("L")) {
+ string = string.toLowerCase();
+ }
+ return string;
+ }
+
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceParser.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceParser.java
index 7176ec156f9..7351cdb22fd 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceParser.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceParser.java
@@ -32,19 +32,26 @@ public class TraceParser {
@NotNull private final PrismContext prismContext;
- @SuppressWarnings("WeakerAccess") // used externally
public TraceParser(@NotNull PrismContext prismContext) {
this.prismContext = prismContext;
}
public TracingOutputType parse(File file) throws IOException, SchemaException {
+ return parse(file, false);
+ }
+
+ public TracingOutputType parse(File file, boolean raw) throws IOException, SchemaException {
boolean isZip = file.getName().toLowerCase().endsWith(".zip");
- return parse(new FileInputStream(file), isZip, file.getPath());
+ return parse(new FileInputStream(file), isZip, raw, file.getPath());
}
public TracingOutputType parse(InputStream inputStream, boolean isZip, String description) throws SchemaException, IOException {
+ return parse(inputStream, isZip, false, description);
+ }
+
+ public TracingOutputType parse(InputStream inputStream, boolean isZip, boolean raw, String description) throws SchemaException, IOException {
TracingOutputType wholeTracingOutput = getObject(inputStream, isZip, description);
- if (wholeTracingOutput != null) {
+ if (!raw && wholeTracingOutput != null) {
new DictionaryExpander(wholeTracingOutput).expand();
new OperationCategorizer(wholeTracingOutput).categorize();
}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceWriter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceWriter.java
new file mode 100644
index 00000000000..7789ffdd10f
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/TraceWriter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.schema.traces;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+
+import com.evolveum.midpoint.prism.SerializationOptions;
+import com.evolveum.midpoint.util.MiscUtil;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.evolveum.midpoint.prism.PrismContext;
+import com.evolveum.midpoint.util.annotation.Experimental;
+import com.evolveum.midpoint.util.exception.SchemaException;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.TracingOutputType;
+
+@Experimental
+public class TraceWriter {
+
+ private static final String ZIP_ENTRY_NAME = "trace.xml";
+
+ @NotNull private final PrismContext prismContext;
+
+ public TraceWriter(@NotNull PrismContext prismContext) {
+ this.prismContext = prismContext;
+ }
+
+ @NotNull
+ public String writeTrace(TracingOutputType tracingOutput, File file, boolean zip) throws SchemaException, IOException {
+ String xml = prismContext.xmlSerializer()
+ .options(
+ SerializationOptions
+ .createSerializeReferenceNames()
+ .escapeInvalidCharacters(true))
+ .serializeRealValue(tracingOutput);
+ if (zip) {
+ MiscUtil.writeZipFile(file, ZIP_ENTRY_NAME, xml, StandardCharsets.UTF_8);
+ } else {
+ try (PrintWriter pw = new PrintWriter(new FileWriter(file))) {
+ pw.write(xml);
+ }
+ }
+ return xml;
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentEvaluationOpNode.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentEvaluationOpNode.java
new file mode 100644
index 00000000000..c2ab79bd4ef
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentEvaluationOpNode.java
@@ -0,0 +1,67 @@
+/*
+ * 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.schema.traces.operations;
+
+import static com.evolveum.midpoint.schema.traces.operations.ResolutionUtil.resolveAssignmentReferenceNames;
+
+import com.evolveum.midpoint.prism.PrismContext;
+import com.evolveum.midpoint.schema.traces.*;
+import com.evolveum.midpoint.schema.util.AssignmentUtil;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentEvaluationTraceType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
+
+public class AssignmentEvaluationOpNode extends OpNode {
+
+ private final AssignmentEvaluationTraceType trace;
+ private final AssignmentType assignment;
+
+ public AssignmentEvaluationOpNode(PrismContext prismContext,
+ OperationResultType result,
+ OpResultInfo info, OpNode parent,
+ TraceInfo traceInfo) {
+ super(prismContext, result, info, parent, traceInfo);
+ trace = getTrace(AssignmentEvaluationTraceType.class);
+ assignment = getAssignmentAny();
+ }
+
+ private AssignmentType getAssignmentAny() {
+ if (trace != null) {
+ if (trace.getAssignmentNew() != null) {
+ return trace.getAssignmentNew();
+ } else {
+ return trace.getAssignmentOld();
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public AssignmentEvaluationTraceType getTrace() {
+ return trace;
+ }
+
+ public AssignmentType getAssignment() {
+ return assignment;
+ }
+
+ public String getAssignmentInfo() {
+ if (assignment != null) {
+ return "→ " + AssignmentUtil.getAssignmentInfo(assignment);
+ } else {
+ return new TemplateExpander()
+ .expandTemplate(this, "→ ${c:assignmentTargetName}");
+ }
+ }
+
+ @Override
+ public void resolveReferenceTargetNames(OpNodeTreeBuilder.NameResolver nameResolver) {
+ super.resolveReferenceTargetNames(nameResolver);
+ resolveAssignmentReferenceNames(assignment, nameResolver);
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentSegmentEvaluationOpNode.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentSegmentEvaluationOpNode.java
new file mode 100644
index 00000000000..afa1f828e16
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/AssignmentSegmentEvaluationOpNode.java
@@ -0,0 +1,69 @@
+/*
+ * 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.schema.traces.operations;
+
+import com.evolveum.midpoint.prism.PrismContext;
+import com.evolveum.midpoint.schema.traces.*;
+import com.evolveum.midpoint.schema.util.AssignmentUtil;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
+import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
+
+import static com.evolveum.midpoint.schema.traces.operations.ResolutionUtil.resolveAssignmentReferenceNames;
+import static com.evolveum.midpoint.schema.traces.operations.ResolutionUtil.resolveReferenceName;
+
+public class AssignmentSegmentEvaluationOpNode extends OpNode {
+
+ private final AssignmentSegmentEvaluationTraceType trace;
+ private final AssignmentPathSegmentType segment;
+
+ public AssignmentSegmentEvaluationOpNode(PrismContext prismContext,
+ OperationResultType result,
+ OpResultInfo info, OpNode parent,
+ TraceInfo traceInfo) {
+ super(prismContext, result, info, parent, traceInfo);
+ trace = getTrace(AssignmentSegmentEvaluationTraceType.class);
+ segment = trace != null ? trace.getSegment() : null;
+
+ fixSegmentTargetName();
+ }
+
+ private void fixSegmentTargetName() {
+ if (segment != null && segment.getTargetRef() != null && segment.getTargetRef().getTargetName() == null) {
+ String targetName = TraceUtil.getContext(result, "segmentTargetName");
+ if (targetName != null) {
+ segment.getTargetRef().setTargetName(PolyStringType.fromOrig(targetName));
+ }
+ }
+ }
+
+ public AssignmentSegmentEvaluationTraceType getTrace() {
+ return trace;
+ }
+
+ public AssignmentPathSegmentType getSegment() {
+ return segment;
+ }
+
+ public String getSegmentLabel() {
+ if (segment != null) {
+ return AssignmentUtil.getSegmentInfo(segment);
+ } else {
+ return new TemplateExpander()
+ .expandTemplate(this, "Segment: ${c:segmentSourceName} → ${c:segmentTargetName}");
+ }
+ }
+
+ @Override
+ public void resolveReferenceTargetNames(OpNodeTreeBuilder.NameResolver nameResolver) {
+ super.resolveReferenceTargetNames(nameResolver);
+ if (segment != null) {
+ resolveReferenceName(segment.getTargetRef(), nameResolver);
+ resolveAssignmentReferenceNames(segment.getAssignment(), nameResolver);
+ }
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/ResolutionUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/ResolutionUtil.java
new file mode 100644
index 00000000000..b5b6c979e52
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/traces/operations/ResolutionUtil.java
@@ -0,0 +1,43 @@
+/*
+ * 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.schema.traces.operations;
+
+import com.evolveum.midpoint.schema.traces.OpNodeTreeBuilder;
+import com.evolveum.midpoint.util.annotation.Experimental;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
+import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
+
+@Experimental
+public class ResolutionUtil {
+
+ private static final String COULD_NOT_RESOLVE = ResolutionUtil.class.getName() + ".couldNotResolve";
+
+ public static void resolveReferenceName(ObjectReferenceType reference, OpNodeTreeBuilder.NameResolver nameResolver) {
+ if (nameResolver != null && reference != null && reference.getOid() != null && reference.getTargetName() == null) {
+ if (reference.asReferenceValue().getUserData(COULD_NOT_RESOLVE) != null) {
+ return;
+ }
+ PolyStringType targetName = nameResolver.getName(reference.getOid());
+ if (targetName != null) {
+ reference.setTargetName(targetName);
+ } else {
+ reference.asReferenceValue().setUserData(COULD_NOT_RESOLVE, true);
+ }
+ }
+ }
+
+ public static void resolveAssignmentReferenceNames(AssignmentType assignment, OpNodeTreeBuilder.NameResolver nameResolver) {
+ if (nameResolver != null && assignment != null) {
+ resolveReferenceName(assignment.getTargetRef(), nameResolver);
+ if (assignment.getConstruction() != null) {
+ resolveReferenceName(assignment.getConstruction().getResourceRef(), nameResolver);
+ }
+ }
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/AssignmentUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/AssignmentUtil.java
new file mode 100644
index 00000000000..8da5e81564f
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/AssignmentUtil.java
@@ -0,0 +1,143 @@
+/*
+ * 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.schema.util;
+
+import com.evolveum.midpoint.schema.constants.SchemaConstants;
+import com.evolveum.midpoint.util.QNameUtil;
+import com.evolveum.midpoint.util.annotation.Experimental;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
+
+import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class AssignmentUtil {
+
+ @Experimental
+ public static String getAssignmentInfo(AssignmentType assignment) {
+ if (assignment == null) {
+ return null;
+ }
+ List infos = new ArrayList<>();
+ CollectionUtils.addIgnoreNull(infos, getReferenceInfo(assignment.getTargetRef()));
+ CollectionUtils.addIgnoreNull(infos, getConstructionInfo(assignment.getConstruction()));
+ infos.addAll(getMappingsInfo(assignment.getFocusMappings()));
+ CollectionUtils.addIgnoreNull(infos, PolicyRuleTypeUtil.toShortString(assignment.getPolicyRule()));
+
+ return String.join("; ", infos);
+ }
+
+ private static List getMappingsInfo(MappingsType focusMappings) {
+ if (focusMappings != null) {
+ return focusMappings.getMapping().stream()
+ .map(mapping -> "m:" + MappingUtil.getShortInfo(mapping))
+ .collect(Collectors.toList());
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private static String getConstructionInfo(ConstructionType construction) {
+ if (construction != null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("c:")
+ .append(getReferenceInfo(construction.getResourceRef()));
+ if (construction.getKind() != null) {
+ sb.append(", ").append(construction.getKind());
+ }
+ if (construction.getIntent() != null) {
+ sb.append(", ").append(construction.getIntent());
+ }
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
+
+ private static String getReferenceInfo(ObjectReferenceType targetRef) {
+ if (targetRef != null) {
+ StringBuilder sb = new StringBuilder();
+ PolyStringType targetName = targetRef.getTargetName();
+ if (targetName != null) {
+ sb.append(targetName.getOrig());
+ } else if (targetRef.getOid() != null) {
+ sb.append(targetRef.getOid());
+ } else if (targetRef.getFilter() != null) {
+ sb.append(targetRef.getFilter()); // todo
+ } else {
+ sb.append("(empty)");
+ }
+
+ if (targetRef.getRelation() != null && !QNameUtil.match(targetRef.getRelation(), SchemaConstants.ORG_DEFAULT)) {
+ sb.append(" [").append(targetRef.getRelation().getLocalPart()).append("]");
+ }
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
+
+ @Experimental
+ public static String getSegmentInfo(AssignmentPathSegmentType segment) {
+ if (segment == null) {
+ return null;
+ }
+ if (segment.getAssignment() == null) {
+ return "no assignment";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (Boolean.FALSE.equals(segment.isIsAssignment())) {
+ sb.append("Inducement: ");
+ } else {
+ sb.append("Assignment: ");
+ }
+ sb.append(getReferenceInfo(segment.getSourceRef()));
+ sb.append(" → ");
+ sb.append(getAssignmentInfo(segment.getAssignment()));
+
+ if (segment.getAssignment().getOrder() != null || !segment.getAssignment().getOrderConstraint().isEmpty()) {
+ sb.append(" (").append(getOrderInfo(segment.getAssignment())).append(")");
+ }
+
+ return sb.toString();
+ }
+
+ private static String getOrderInfo(AssignmentType assignment) {
+ List constraints = new ArrayList<>();
+ if (assignment.getOrder() != null) {
+ constraints.add("order " + assignment.getOrder());
+ }
+ for (OrderConstraintsType orderConstraint : assignment.getOrderConstraint()) {
+ constraints.add(getOrderConstraintInfo(orderConstraint));
+ }
+ return String.join("; ", constraints);
+ }
+
+ private static String getOrderConstraintInfo(OrderConstraintsType constraint) {
+ StringBuilder sb = new StringBuilder();
+ if (constraint.getRelation() != null) {
+ sb.append(constraint.getRelation().getLocalPart()).append("=");
+ }
+ if (constraint.getOrder() != null) {
+ sb.append(constraint.getOrder());
+ }
+ if (constraint.getOrderMin() != null || constraint.getOrderMax() != null) {
+ sb.append("<").append(constraint.getOrderMin()).append(",").append(constraint.getOrderMax()).append(">");
+ }
+ if (constraint.getResetOrder() != null) {
+ sb.append("!").append(constraint.getResetOrder());
+ }
+ return sb.toString();
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MappingUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MappingUtil.java
new file mode 100644
index 00000000000..5f9d1c24756
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MappingUtil.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.schema.util;
+
+import com.evolveum.midpoint.util.annotation.Experimental;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractMappingType;
+
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Experimental
+public class MappingUtil {
+
+ public static String getShortInfo(AbstractMappingType mapping) {
+ if (mapping != null) {
+ if (mapping.getName() != null) {
+ return mapping.getName();
+ } else if (mapping.getTarget() != null && mapping.getTarget().getPath() != null) {
+ return "→ " + mapping.getTarget().getPath();
+ } else if (!mapping.getSource().isEmpty()) {
+ return getSourcesInfo(mapping);
+ } else {
+ return "mapping";
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private static String getSourcesInfo(AbstractMappingType mapping) {
+ return mapping.getSource().stream()
+ .filter(Objects::nonNull)
+ .map(source -> String.valueOf(source.getPath()))
+ .collect(Collectors.joining(", "));
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/OperationResultUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/OperationResultUtil.java
new file mode 100644
index 00000000000..50b69d48edc
--- /dev/null
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/OperationResultUtil.java
@@ -0,0 +1,50 @@
+/*
+ * 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.schema.util;
+
+import com.evolveum.midpoint.prism.util.CloneUtil;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
+
+public class OperationResultUtil {
+
+ public static OperationResultType shallowClone(OperationResultType result, boolean subresults, boolean traces, boolean log) {
+ OperationResultType clone = new OperationResultType();
+ clone.setOperation(result.getOperation());
+ clone.getQualifier().addAll(result.getQualifier());
+ clone.setOperationKind(result.getOperationKind());
+ clone.setStatus(result.getStatus());
+ clone.setImportance(result.getImportance());
+ clone.setMinor(result.isMinor());
+ clone.setAsynchronousOperationReference(result.getAsynchronousOperationReference());
+ clone.setStart(CloneUtil.clone(result.getStart()));
+ clone.setEnd(CloneUtil.clone(result.getEnd()));
+ clone.setMicroseconds(result.getMicroseconds());
+ clone.setInvocationId(result.getInvocationId());
+ clone.setTraced(result.isTraced());
+ if (traces) {
+ clone.getTrace().addAll(result.getTrace());
+ }
+ clone.setCount(result.getCount());
+ clone.setHiddenRecordsCount(result.getHiddenRecordsCount());
+ clone.setParams(result.getParams());
+ clone.setContext(result.getContext());
+ clone.setReturns(result.getReturns());
+ clone.setToken(result.getToken());
+ clone.setMessageCode(result.getMessageCode());
+ clone.setMessage(result.getMessage());
+ clone.setUserFriendlyMessage(result.getUserFriendlyMessage());
+ clone.setDetails(result.getDetails());
+ if (log) {
+ clone.getLog().addAll(result.getLog());
+ }
+ if (subresults) {
+ clone.getPartialResults().addAll(result.getPartialResults());
+ }
+ return clone;
+ }
+}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java
index 7c014d82161..829d4679689 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java
@@ -77,6 +77,14 @@ public class PolicyRuleTypeUtil {
CONSTRAINT_NAMES.put(PolicyConstraintsType.F_ALWAYS_TRUE.getLocalPart(), SYMBOL_ALWAYS_TRUE);
}
+ public static String toShortString(PolicyRuleType rule) {
+ if (rule != null) {
+ return toShortString(rule.getPolicyConstraints()) + "→" + toShortString(rule.getPolicyActions());
+ } else {
+ return null;
+ }
+ }
+
public static String toShortString(PolicyConstraintsType constraints) {
return toShortString(constraints, JOIN_AND);
}
diff --git a/repo/system-init/src/main/resources/ctx-configuration-no-repo.xml b/repo/system-init/src/main/resources/ctx-configuration-no-repo.xml
new file mode 100644
index 00000000000..667f7229e23
--- /dev/null
+++ b/repo/system-init/src/main/resources/ctx-configuration-no-repo.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java
index 1a2667abffa..2640b2bec20 100644
--- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java
+++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java
@@ -8,10 +8,7 @@
package com.evolveum.midpoint.task.quartzimpl.tracing;
import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Objects;
import java.util.*;
@@ -20,6 +17,8 @@
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
+import com.evolveum.midpoint.schema.traces.TraceWriter;
+
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.text.StringSubstitutor;
@@ -31,7 +30,6 @@
import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration;
import com.evolveum.midpoint.prism.PrismContext;
-import com.evolveum.midpoint.prism.SerializationOptions;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.repo.api.SystemConfigurationChangeDispatcher;
import com.evolveum.midpoint.repo.api.SystemConfigurationChangeListener;
@@ -41,7 +39,6 @@
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.midpoint.task.api.Tracer;
-import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
@@ -66,7 +63,7 @@ public class TracerImpl implements Tracer, SystemConfigurationChangeListener {
private static final String MACRO_RANDOM = "random";
// To be used during tests to check for MID-5851
- @SuppressWarnings("WeakerAccess") @VisibleForTesting
+ @VisibleForTesting
public static boolean checkHashCodeEqualsRelation = false;
@Autowired private PrismContext prismContext;
@@ -83,7 +80,6 @@ public class TracerImpl implements Tracer, SystemConfigurationChangeListener {
private static final String OP_STORE_TRACE = TracerImpl.class.getName() + ".storeTrace";
private static final String TRACE_DIR_NAME = "trace";
- private static final String ZIP_ENTRY_NAME = "trace.xml";
private static final String DEFAULT_FILE_NAME_PATTERN = "trace-%{timestamp}";
@@ -122,23 +118,17 @@ public void storeTrace(Task task, OperationResult result, @Nullable OperationRes
try {
long start = System.currentTimeMillis();
TracingOutputType tracingOutput = tracingOutputCreator.createTracingOutput(task, result, tracingProfile);
- String xml = prismContext.xmlSerializer()
- .options(
- SerializationOptions
- .createSerializeReferenceNames()
- .escapeInvalidCharacters(true))
- .serializeRealValue(tracingOutput);
+ String xml = new TraceWriter(prismContext)
+ .writeTrace(tracingOutput, file, zip);
+
if (zip) {
- MiscUtil.writeZipFile(file, ZIP_ENTRY_NAME, xml, StandardCharsets.UTF_8);
LOGGER.info("Trace was written to {} ({} chars uncompressed) in {} milliseconds", file, xml.length(),
System.currentTimeMillis() - start);
} else {
- try (PrintWriter pw = new PrintWriter(new FileWriter(file))) {
- pw.write(xml);
- LOGGER.info("Trace was written to {} ({} chars) in {} milliseconds", file, xml.length(),
- System.currentTimeMillis() - start);
- }
+ LOGGER.info("Trace was written to {} ({} chars) in {} milliseconds", file, xml.length(),
+ System.currentTimeMillis() - start);
}
+
if (!Boolean.FALSE.equals(tracingProfile.isCreateRepoObject())) {
ReportDataType reportDataObject = new ReportDataType(prismContext)
.name(createObjectName(tracingProfile, templateParameters))
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java
new file mode 100644
index 00000000000..a11143eeabc
--- /dev/null
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.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.ninja.action.trace;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import com.evolveum.midpoint.ninja.action.RepositoryAction;
+import com.evolveum.midpoint.ninja.opts.EditTraceOptions;
+
+import com.evolveum.midpoint.schema.traces.TraceParser;
+
+import com.evolveum.midpoint.schema.traces.TraceWriter;
+import com.evolveum.midpoint.util.exception.SchemaException;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.TracingOutputType;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * TODO
+ */
+public class EditTraceAction extends RepositoryAction {
+
+ private static final String DEFAULT_OUTPUT = "output.zip";
+
+ private int killed;
+
+ @Override
+ public void execute() throws Exception {
+ TracingOutputType trace = parseInput();
+ if (options.isPrintStat() || options.isPrintStatExtra()) {
+ printStatistics(trace);
+ }
+
+ if (CollectionUtils.isNotEmpty(options.getKeep()) || CollectionUtils.isNotEmpty(options.getKill())) {
+ applyKeep(trace);
+ applyKill(trace);
+ writeTrace(trace);
+ }
+ }
+
+ private TracingOutputType parseInput() throws IOException, SchemaException {
+ String inputFile = options.getInput();
+ log.info("Starting parsing input file: {}", inputFile);
+
+ long start = System.currentTimeMillis();
+ TraceParser parser = new TraceParser(context.getPrismContext());
+ TracingOutputType trace = parser.parse(new File(inputFile), true);
+
+ log.info("Parsing finished; in {} seconds", (System.currentTimeMillis() - start) / 1000);
+ return trace;
+ }
+
+ private void printStatistics(TracingOutputType trace) {
+ TraceStatistics statistics = options.isPrintStatExtra() ? TraceStatistics.extra(trace) : TraceStatistics.simple(trace);
+ log.info("Trace statistics:\n{}", statistics.dump(TraceStatistics.SortBy.SIZE));
+ }
+
+ private void applyKeep(TracingOutputType trace) {
+ List patterns = getPatterns(options.getKeep());
+ if (!patterns.isEmpty()) {
+ List matchingChildren = new ArrayList<>();
+ applyKeep(trace.getResult(), matchingChildren, patterns);
+
+ log.info("Keeping {} matching nodes", matchingChildren.size());
+ trace.getResult().getPartialResults().clear();
+ trace.getResult().getPartialResults().addAll(matchingChildren);
+ }
+ }
+
+ private void applyKeep(OperationResultType result, List matchingChildren, List patterns) {
+ for (OperationResultType child : result.getPartialResults()) {
+ if (matches(child, patterns)) {
+ matchingChildren.add(child);
+ } else {
+ applyKeep(child, matchingChildren, patterns);
+ }
+ }
+ }
+
+ private void applyKill(TracingOutputType trace) {
+ List patterns = getPatterns(options.getKill());
+ if (!patterns.isEmpty()) {
+ applyKill(trace.getResult(), patterns);
+ log.info("Killed {} nodes", killed);
+ }
+ }
+
+ private void applyKill(OperationResultType result, List patterns) {
+ Iterator iterator = result.getPartialResults().iterator();
+ while (iterator.hasNext()) {
+ OperationResultType child = iterator.next();
+ if (matches(child, patterns)) {
+ iterator.remove();
+ killed++;
+ } else {
+ applyKill(child, patterns);
+ }
+ }
+ }
+
+ private boolean matches(OperationResultType result, List patterns) {
+ for (Pattern pattern : patterns) {
+ if (pattern.matcher(result.getOperation()).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List getPatterns(List templates) {
+ List compiledPatterns = new ArrayList<>();
+ for (String template : templates) {
+ String regex = toRegex(template);
+ compiledPatterns.add(Pattern.compile(regex));
+ }
+ return compiledPatterns;
+ }
+
+ private String toRegex(String template) {
+ return template.replace(".", "\\.").replace("*", ".*");
+ }
+
+ private void writeTrace(TracingOutputType trace) throws SchemaException, IOException {
+ String output = ObjectUtils.defaultIfNull(options.getOutput(), DEFAULT_OUTPUT);
+ log.info("Starting writing trace to {}", output);
+ new TraceWriter(context.getPrismContext()).writeTrace(trace, new File(output), true);
+ log.info("Trace written.");
+ }
+
+}
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/TraceStatistics.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/TraceStatistics.java
new file mode 100644
index 00000000000..0cab11f663f
--- /dev/null
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/TraceStatistics.java
@@ -0,0 +1,126 @@
+/*
+ * 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.ninja.action.trace;
+
+import com.evolveum.midpoint.prism.PrismContext;
+import com.evolveum.midpoint.schema.util.OperationResultUtil;
+import com.evolveum.midpoint.util.exception.SchemaException;
+import com.evolveum.midpoint.util.exception.SystemException;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.TracingOutputType;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ *
+ */
+public class TraceStatistics {
+
+ private final Map operationsMap = new HashMap<>();
+
+ private final PrismContext prismContext;
+ private final boolean extra;
+
+ private TraceStatistics(PrismContext prismContext, boolean extra) {
+ this.prismContext = prismContext;
+ this.extra = extra;
+ }
+
+ static TraceStatistics extra(TracingOutputType tracingOutput) {
+ TraceStatistics traceStatistics = new TraceStatistics(tracingOutput.asPrismContainerValue().getPrismContext(), true);
+ traceStatistics.update(tracingOutput.getResult());
+ return traceStatistics;
+ }
+
+ static TraceStatistics simple(TracingOutputType tracingOutput) {
+ TraceStatistics traceStatistics = new TraceStatistics(tracingOutput.asPrismContainerValue().getPrismContext(), false);
+ traceStatistics.update(tracingOutput.getResult());
+ return traceStatistics;
+ }
+
+ private void update(OperationResultType result) {
+ operationsMap.compute(result.getOperation(),
+ (op, info) -> Info.update(result, info, extra, prismContext));
+
+ result.getPartialResults().forEach(this::update);
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ String dump(SortBy sortBy) {
+
+ int maxOp = operationsMap.keySet().stream()
+ .mapToInt(String::length)
+ .max().orElse(0);
+
+ String details = operationsMap.entrySet().stream()
+ .sorted((e1, e2) -> compare(e1, e2, sortBy))
+ .map(entry -> String.format("%-" + maxOp + "s %,10d %,15d %,10d", entry.getKey(),
+ entry.getValue().count, entry.getValue().size, entry.getValue().getAvgSize()))
+ .collect(Collectors.joining("\n"));
+
+ int nodesCount = operationsMap.values().stream()
+ .mapToInt(info -> info.count)
+ .sum();
+
+ int totalSize = operationsMap.values().stream()
+ .mapToInt(info -> info.size)
+ .sum();
+
+ return "Total nodes: " + nodesCount + "\n" +
+ (extra ? "Total size: " + totalSize + "\n" : "") +
+ "\nDetails:\n\n" + details;
+ }
+
+ private int compare(Map.Entry e1, Map.Entry e2, SortBy sortBy) {
+ switch (sortBy) {
+ case OPERATION:
+ return e1.getKey().compareTo(e2.getKey());
+ case COUNT:
+ return Integer.compare(e2.getValue().count, e1.getValue().count);
+ case SIZE:
+ return Integer.compare(e2.getValue().size, e1.getValue().size);
+ default:
+ throw new AssertionError(sortBy);
+ }
+ }
+
+ private static class Info {
+ private int count;
+ private int size;
+
+ private static Info update(OperationResultType result, Info info, boolean extra, PrismContext prismContext) {
+ if (info == null) {
+ info = new Info();
+ }
+ info.count++;
+ if (extra) {
+ info.size += getSize(result, prismContext);
+ }
+ return info;
+ }
+
+ private static int getSize(OperationResultType result, PrismContext prismContext) {
+ OperationResultType resultNoChildren = OperationResultUtil.shallowClone(result, false, true, true);
+ try {
+ return prismContext.xmlSerializer().serializeRealValue(resultNoChildren).length();
+ } catch (SchemaException e) {
+ throw new SystemException(e);
+ }
+ }
+
+ public int getAvgSize() {
+ return size / count;
+ }
+ }
+
+ public enum SortBy {
+ OPERATION, COUNT, SIZE
+ }
+}
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/Command.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/Command.java
index ed729a0ffe3..406cc13b145 100644
--- a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/Command.java
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/Command.java
@@ -7,6 +7,7 @@
package com.evolveum.midpoint.ninja.impl;
import com.evolveum.midpoint.ninja.action.*;
+import com.evolveum.midpoint.ninja.action.trace.EditTraceAction;
import com.evolveum.midpoint.ninja.opts.*;
/**
@@ -30,7 +31,9 @@ public enum Command {
//
// TEST("test", TestResourceOptions.class, null, TestResourceRestAction.class),
//
- KEYS("keys", ListKeysOptions.class, ListKeysRepositoryAction.class, null);
+ KEYS("keys", ListKeysOptions.class, ListKeysRepositoryAction.class, null),
+
+ TRACE("trace", EditTraceOptions.class, EditTraceAction.class, null);
//
// TRANSFORM("transform", TransformOptions.class, TransformRepositoryAction.class, null),
//
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/NinjaContext.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/NinjaContext.java
index d6da82cb298..33894eba043 100644
--- a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/NinjaContext.java
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/NinjaContext.java
@@ -32,7 +32,7 @@ public class NinjaContext {
private static final String CTX_NINJA = "classpath:ctx-ninja.xml";
- private static final String[] CTX_MIDPOINT = new String[]{
+ private static final String[] CTX_MIDPOINT = new String[] {
"classpath:ctx-common.xml",
"classpath:ctx-configuration.xml",
"classpath:ctx-repository.xml",
@@ -40,6 +40,11 @@ public class NinjaContext {
"classpath:ctx-audit.xml"
};
+ private static final String[] CTX_MIDPOINT_NO_REPO = new String[] {
+ "classpath:ctx-common.xml",
+ "classpath:ctx-configuration-no-repo.xml"
+ };
+
private JCommander jc;
private Log log;
@@ -87,7 +92,9 @@ public void setLog(Log log) {
}
private RepositoryService setupRepositoryViaMidPointHome(ConnectionOptions options) {
- log.info("Initializing repository using midpoint home");
+ boolean connectRepo = !options.isOffline();
+
+ log.info("Initializing using midpoint home; {} repository connection", connectRepo ? "with" : "WITHOUT");
System.setProperty(MidpointConfiguration.MIDPOINT_SILENT_PROPERTY, "true");
@@ -107,12 +114,12 @@ private RepositoryService setupRepositoryViaMidPointHome(ConnectionOptions optio
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.addBeanFactoryPostProcessor(beanFactory -> beanFactory.addBeanPostProcessor(postprocessor));
ctx.load(CTX_NINJA);
- ctx.load(CTX_MIDPOINT);
+ ctx.load(connectRepo ? CTX_MIDPOINT : CTX_MIDPOINT_NO_REPO);
ctx.refresh();
context = ctx;
- return context.getBean(REPOSITORY_SERVICE_BEAN, RepositoryService.class);
+ return connectRepo ? context.getBean(REPOSITORY_SERVICE_BEAN, RepositoryService.class) : null;
}
public ApplicationContext getApplicationContext() {
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/ConnectionOptions.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/ConnectionOptions.java
index 555361487fe..fcb79a2ae75 100644
--- a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/ConnectionOptions.java
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/ConnectionOptions.java
@@ -34,6 +34,8 @@ public class ConnectionOptions {
public static final String P_WEBSERVICE = "-w";
public static final String P_WEBSERVICE_LONG = "--webservice";
+ public static final String P_OFFLINE_LONG = "--offline";
+
@Parameter(names = {P_URL, P_URL_LONG}, validateWith = URIConverter.class, descriptionKey = "connection.url")
private String url;
@@ -54,6 +56,9 @@ public class ConnectionOptions {
@Parameter(names = {P_WEBSERVICE, P_WEBSERVICE_LONG}, descriptionKey = "connection.useWebservice", hidden = true) //todo remove hidden when implementation is done
private boolean useWebservice;
+ @Parameter(names = {P_OFFLINE_LONG}, descriptionKey = "connection.offline")
+ private boolean offline;
+
public String getAskPassword() {
return askPassword;
}
@@ -77,4 +82,8 @@ public String getMidpointHome() {
public boolean isUseWebservice() {
return useWebservice;
}
+
+ public boolean isOffline() {
+ return offline;
+ }
}
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/EditTraceOptions.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/EditTraceOptions.java
new file mode 100644
index 00000000000..f53af44fe75
--- /dev/null
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/opts/EditTraceOptions.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010-2019 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.ninja.opts;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+import java.util.List;
+
+/**
+ *
+ */
+@Parameters(resourceBundle = "messages", commandDescriptionKey = "editTrace")
+public class EditTraceOptions {
+
+ private static final String P_INPUT_LONG = "--input";
+ private static final String P_OUTPUT_LONG = "--output";
+ private static final String P_PRINT_STAT_LONG = "--print-stat";
+ private static final String P_PRINT_STAT_EXTRA_LONG = "--print-stat-extra";
+ private static final String P_KILL_LONG = "--kill";
+ private static final String P_KEEP_LONG = "--keep";
+
+ @Parameter(names = { P_INPUT_LONG }, descriptionKey = "editTrace.input")
+ private String input;
+
+ @Parameter(names = { P_OUTPUT_LONG }, descriptionKey = "editTrace.output")
+ private String output;
+
+ @Parameter(names = { P_PRINT_STAT_LONG }, descriptionKey = "editTrace.printStat")
+ private boolean printStat;
+
+ @Parameter(names = { P_PRINT_STAT_EXTRA_LONG }, descriptionKey = "editTrace.printStatExtra")
+ private boolean printStatExtra;
+
+ @Parameter(names = { P_KILL_LONG }, descriptionKey = "editTrace.kill")
+ private List kill;
+
+ @Parameter(names = { P_KEEP_LONG }, descriptionKey = "editTrace.keep")
+ private List keep;
+
+ public String getInput() {
+ return input;
+ }
+
+ public String getOutput() {
+ return output;
+ }
+
+ public boolean isPrintStat() {
+ return printStat;
+ }
+
+ public boolean isPrintStatExtra() {
+ return printStatExtra;
+ }
+
+ public List getKill() {
+ return kill;
+ }
+
+ public List getKeep() {
+ return keep;
+ }
+}
diff --git a/tools/ninja/src/main/resources/messages.properties b/tools/ninja/src/main/resources/messages.properties
index 242da928fea..7f805b6e963 100644
--- a/tools/ninja/src/main/resources/messages.properties
+++ b/tools/ninja/src/main/resources/messages.properties
@@ -31,6 +31,7 @@ connection.askPassword=Please write rest/jdbc connection password
connection.midpointHome=Path to MidPoint home folder. If relative path is specified, it will be translated \
to absolute path.
connection.useWebservice=Use model webservice to connect to MidPoint
+connection.offline=Do not use repository (local nor via webservice)
delete=Delete objects from MidPoint
delete.oid=Object oid
delete.raw=Raw option
@@ -43,7 +44,7 @@ export.output=
export.overwrite=
export.split=
verify=Verify objects in midPoint repository
-verify.warn=List of displayed varning categories, e.g. deprecated,plannedRemoval
+verify.warn=List of displayed warning categories, e.g. deprecated,plannedRemoval
passwordReset=Command will reset password of user specified by oid
passwordReset.oid=
testResource=Test resource
@@ -53,6 +54,13 @@ unlock.oid=User oid
listKeys=List keys from keystore
listKeys.keyPassword=Key password
listKeys.askKeyPassword=Please write key password
+editTrace=Edit trace file
+editTrace.input=Input trace file
+editTrace.output=Output trace file (default is "output.zip")
+editTrace.printStat=Print statistics
+editTrace.printStatExtra=Print extra statistics i.e. the node size. Takes longer time.
+editTrace.kill=Operation results to be removed (with their children). Asterisk is allowed.
+editTrace.keep=Operation results to be kept (with their children). Asterisk is allowed. All above them will be removed.
schema=DB schema operations
schema.test=Validate DB schema
schema.init=Initialize DB schema
From dd223fa50ef852841715b3d62a03820b79ad56ad Mon Sep 17 00:00:00 2001
From: Pavol Mederly
Date: Fri, 4 Sep 2020 07:10:00 +0200
Subject: [PATCH 11/12] Add missing ninja dependency
---
tools/ninja/pom.xml | 4 ++++
.../evolveum/midpoint/ninja/action/trace/EditTraceAction.java | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/tools/ninja/pom.xml b/tools/ninja/pom.xml
index 5646778504e..63f66e698c8 100644
--- a/tools/ninja/pom.xml
+++ b/tools/ninja/pom.xml
@@ -160,6 +160,10 @@
commons-io
commons-io
+
+ org.apache.commons
+ commons-collections4
+
commons-codec
commons-codec
diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java
index a11143eeabc..b472c092181 100644
--- a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java
+++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/action/trace/EditTraceAction.java
@@ -23,7 +23,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TracingOutputType;
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
/**
From 7a8ea4009f810ddd8b625e3e27bee6437532c342 Mon Sep 17 00:00:00 2001
From: lskublik
Date: Fri, 4 Sep 2020 07:45:46 +0200
Subject: [PATCH 12/12] modification of audit collection initial objects
---
.../285-object-collection-audit.xml | 33 +++++++++++++++++++
.../290-object-collection-audit-errors.xml | 30 ++++++++++++++++-
...-object-collection-audit-modifications.xml | 30 ++++++++++++++++-
3 files changed, 91 insertions(+), 2 deletions(-)
create mode 100644 gui/admin-gui/src/main/resources/initial-objects/285-object-collection-audit.xml
diff --git a/gui/admin-gui/src/main/resources/initial-objects/285-object-collection-audit.xml b/gui/admin-gui/src/main/resources/initial-objects/285-object-collection-audit.xml
new file mode 100644
index 00000000000..fd0f01dd924
--- /dev/null
+++ b/gui/admin-gui/src/main/resources/initial-objects/285-object-collection-audit.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ Audit records
+ AuditEventRecordType
+
+
+
+ eventStage
+ execution
+
+
+ timestamp
+
+
+
+
+
+
+
diff --git a/gui/admin-gui/src/main/resources/initial-objects/290-object-collection-audit-errors.xml b/gui/admin-gui/src/main/resources/initial-objects/290-object-collection-audit-errors.xml
index 41aecd26827..77d7db5af5c 100644
--- a/gui/admin-gui/src/main/resources/initial-objects/290-object-collection-audit-errors.xml
+++ b/gui/admin-gui/src/main/resources/initial-objects/290-object-collection-audit-errors.xml
@@ -7,11 +7,39 @@
-->
Error audit records
select * from m_audit_event as aer where aer.outcome=3 and aer.eventStage=1
- select * from m_audit_event as aer where aer.eventStage=1
P1D
+ AuditEventRecordType
+
+
+
+ eventStage
+ execution
+
+
+ outcome
+ fatal_error
+
+
+ timestamp
+
+
+
+
+
+
+
+
+
diff --git a/gui/admin-gui/src/main/resources/initial-objects/300-object-collection-audit-modifications.xml b/gui/admin-gui/src/main/resources/initial-objects/300-object-collection-audit-modifications.xml
index 29d303638a3..162f90b53e9 100644
--- a/gui/admin-gui/src/main/resources/initial-objects/300-object-collection-audit-modifications.xml
+++ b/gui/admin-gui/src/main/resources/initial-objects/300-object-collection-audit-modifications.xml
@@ -7,11 +7,39 @@
-->
Modification audit records
+ AuditEventRecordType
+
+
+
+ eventStage
+ execution
+
+
+ eventType
+ modifyObject
+
+
+ timestamp
+
+
+
+
+
+
+
+
+
select * from m_audit_event as aer where aer.eventType=2 and aer.eventStage=1
- select * from m_audit_event as aer where aer.eventStage=1
P1D