diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java index 1f5f0c370f0..5d617902754 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java @@ -12,8 +12,8 @@ import com.evolveum.midpoint.prism.delta.DeltaFactory; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.builder.S_ItemEntry; -import com.evolveum.midpoint.prism.marshaller.JaxbDomHack; import com.evolveum.midpoint.prism.marshaller.ParsingMigrator; +import com.evolveum.midpoint.prism.metadata.ValueMetadataFactory; import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; import com.evolveum.midpoint.prism.path.CanonicalItemPath; import com.evolveum.midpoint.prism.path.ItemPath; @@ -35,7 +35,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.w3c.dom.Element; -import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.SAXException; import javax.xml.namespace.QName; @@ -380,4 +379,10 @@ default ItemPath toPath(ItemPathType path) { */ @Experimental ValueMetadataMockUpFactory getValueMetadataMockUpFactory(); + + @Experimental + void setValueMetadataFactory(ValueMetadataFactory factory); + + @Experimental + ValueMetadataFactory getValueMetadataFactory(); } 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 ee826aff4ee..a8d62d7a902 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 @@ -49,6 +49,10 @@ public interface PrismValue extends Visitable, PathVisitable, Serializable, Debu @Experimental Optional valueMetadata() throws SchemaException; + @Experimental + default void createLiveMetadata() { + } + @NotNull ItemPath getPath(); diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ValueMetadata.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ValueMetadata.java index fb93b9f84f3..551e7b8dede 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ValueMetadata.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ValueMetadata.java @@ -5,4 +5,6 @@ @Experimental public interface ValueMetadata extends PrismContainerValue { + ValueMetadata clone(); + } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataFactory.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataFactory.java new file mode 100644 index 00000000000..acae893d717 --- /dev/null +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataFactory.java @@ -0,0 +1,24 @@ +/* + * 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.prism.metadata; + +import com.evolveum.midpoint.prism.ValueMetadata; +import com.evolveum.midpoint.util.annotation.Experimental; + +import org.jetbrains.annotations.NotNull; + +/** + * Provides empty value metadata. + */ +@Experimental +public interface ValueMetadataFactory { + + @NotNull + ValueMetadata createEmpty(); + +} diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.java index d981d42ed64..6b6b85fc56a 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.java @@ -9,6 +9,7 @@ import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.ValueMetadata; +import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.SchemaException; import java.util.Optional; @@ -16,6 +17,7 @@ /** * Provides mock up value metadata for given prism value. */ +@Experimental public interface ValueMetadataMockUpFactory { Optional createValueMetadata(PrismValue value) throws SchemaException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java index eda2ae4492c..7d8d41bcafd 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java @@ -23,6 +23,7 @@ import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; import com.evolveum.midpoint.prism.impl.lex.LexicalProcessorRegistry; import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; +import com.evolveum.midpoint.prism.metadata.ValueMetadataFactory; import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; import com.evolveum.midpoint.prism.path.*; import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer; @@ -92,6 +93,9 @@ public final class PrismContextImpl implements PrismContext { @Experimental // temporary private ValueMetadataMockUpFactory valueMetadataMockUpFactory; + @Experimental + private ValueMetadataFactory valueMetadataFactory; + private ParsingMigrator parsingMigrator; private PrismMonitor monitor = null; @@ -680,4 +684,14 @@ public void setValueMetadataMockUpFactory(ValueMetadataMockUpFactory factory) { public ValueMetadataMockUpFactory getValueMetadataMockUpFactory() { return valueMetadataMockUpFactory; } + + @Override + public void setValueMetadataFactory(ValueMetadataFactory valueMetadataFactory) { + this.valueMetadataFactory = valueMetadataFactory; + } + + @Override + public ValueMetadataFactory getValueMetadataFactory() { + return valueMetadataFactory; + } } 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 fc1efe67522..1ba7fab790b 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 @@ -13,6 +13,7 @@ import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.SchemaException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,9 +34,13 @@ public abstract class PrismValueImpl extends AbstractFreezable implements PrismV private OriginType originType; private Objectable originObject; private Itemable parent; + + private ValueMetadata valueMetadata; + + @SuppressWarnings("FieldMayBeFinal") // Cannot be final because it is transient private transient Map userData = new HashMap<>(); - // FIXME: allways null + // FIXME: always null protected EquivalenceStrategy defaultEquivalenceStrategy; transient protected PrismContext prismContext; @@ -244,6 +249,7 @@ protected void copyValues(CloneStrategy strategy, PrismValueImpl clone) { if (clone.prismContext == null) { clone.prismContext = this.prismContext; } + clone.valueMetadata = valueMetadata != null ? valueMetadata.clone() : null; } protected EquivalenceStrategy getEqualsHashCodeStrategy() { @@ -380,6 +386,22 @@ public Collection getAllValues(ItemPath path) { @Override public Optional valueMetadata() throws SchemaException { + if (valueMetadata != null) { + return Optional.of(valueMetadata); + } else { + return createMockUpValueMetadata(); + } + } + + @Override + @Experimental + public void createLiveMetadata() { + valueMetadata = Objects.requireNonNull(prismContext, "no prism context") + .getValueMetadataFactory() + .createEmpty(); + } + + private Optional createMockUpValueMetadata() throws SchemaException { PrismContext prismContext = getPrismContext(); if (prismContext != null) { ValueMetadataMockUpFactory factory = prismContext.getValueMetadataMockUpFactory(); @@ -389,4 +411,12 @@ public Optional valueMetadata() throws SchemaException { } return Optional.empty(); } + + @Override + protected void performFreeze() { + if (valueMetadata != null) { + valueMetadata.freeze(); + } + super.performFreeze(); + } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/metadata/ValueMetadataAdapter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/metadata/ValueMetadataAdapter.java index 4a836f280df..538d082c79e 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/metadata/ValueMetadataAdapter.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/metadata/ValueMetadataAdapter.java @@ -511,8 +511,8 @@ public void assertDefinitions(boolean tolerateRaw, String sourceDescription) thr delegate.assertDefinitions(tolerateRaw, sourceDescription); } - public PrismContainerValue clone() { - return delegate.clone(); + public ValueMetadata clone() { + return holding(delegate.clone()); } public PrismContainerValue createImmutableClone() { 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 a9e66808c32..d211fb76236 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 @@ -17,6 +17,7 @@ import com.evolveum.midpoint.schema.internals.InternalMonitor; import com.evolveum.midpoint.schema.internals.InternalsConfig; +import com.evolveum.midpoint.schema.metadata.MidpointValueMetadataFactory; import com.evolveum.midpoint.schema.metadata.MidpointValueMetadataMockUpFactory; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.model.model_3.ObjectFactory; @@ -66,6 +67,7 @@ public PrismContext createPrismContext() throws SchemaException, FileNotFoundExc } context.setParsingMigrator(new MidpointParsingMigrator()); context.setValueMetadataMockUpFactory(new MidpointValueMetadataMockUpFactory(context)); + context.setValueMetadataFactory(new MidpointValueMetadataFactory(context)); return context; } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/metadata/MidpointValueMetadataFactory.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/metadata/MidpointValueMetadataFactory.java new file mode 100644 index 00000000000..e635486e4db --- /dev/null +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/metadata/MidpointValueMetadataFactory.java @@ -0,0 +1,36 @@ +/* + * 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.metadata; + +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.ValueMetadata; +import com.evolveum.midpoint.prism.impl.metadata.ValueMetadataAdapter; +import com.evolveum.midpoint.prism.metadata.ValueMetadataFactory; +import com.evolveum.midpoint.util.annotation.Experimental; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueMetadataType; + +import org.jetbrains.annotations.NotNull; + +@Experimental +public class MidpointValueMetadataFactory implements ValueMetadataFactory { + + @NotNull private final PrismContext prismContext; + + public MidpointValueMetadataFactory(@NotNull PrismContext prismContext) { + this.prismContext = prismContext; + } + + @Override + @NotNull + public ValueMetadata createEmpty() { + return ValueMetadataAdapter.holding( + new ValueMetadataType(prismContext) + .asPrismContainerValue()); + } +} 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 37b4828776c..978e888ec38 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 @@ -11,14 +11,16 @@ import java.io.File; import java.util.Optional; +import com.evolveum.midpoint.prism.*; + +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.util.exception.SchemaException; + import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.Test; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.ValueMetadata; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.schema.result.OperationResult; @@ -26,6 +28,8 @@ import com.evolveum.midpoint.test.TestResource; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import javax.xml.datatype.XMLGregorianCalendar; + /** * Tests the value metadata handling. Currently the only "handling" is creation of metadata mock-up. */ @@ -44,6 +48,34 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti addObject(USER_ALICE, initTask, initResult); } + @Test + public void test010TestLiveMetadata() throws SchemaException { + given(); + UserType mark = new UserType(prismContext) + .name("mark"); + PrismPropertyValue nameValue = mark.asPrismObject() + .findProperty(UserType.F_NAME) + .getValue(PolyString.class); + nameValue.createLiveMetadata(); + + when(); + Optional metadata = nameValue.valueMetadata(); + assertThat(metadata).isPresent(); + + XMLGregorianCalendar now = XmlTypeConverter.createXMLGregorianCalendar(); + + ValueMetadataType realMetadataValue = (ValueMetadataType) metadata.get().asContainerable(); + realMetadataValue.setProvisioning(new ProvisioningMetadataType(prismContext)); + realMetadataValue.getProvisioning().setLastProvisioningTimestamp(now); + + then(); + Optional metadataAfter = nameValue.valueMetadata(); + assertThat(metadataAfter).isPresent(); + + ValueMetadataType realMetadataValueAfter = (ValueMetadataType) metadataAfter.get().asContainerable(); + assertThat(realMetadataValueAfter.getProvisioning().getLastProvisioningTimestamp()).isEqualTo(now); + } + @Test public void test100CheckValueMetadata() throws Exception { given(); diff --git a/model/model-intest/src/test/resources/schema/metadata.xsd b/model/model-intest/src/test/resources/schema/metadata.xsd new file mode 100644 index 00000000000..e768c9c3771 --- /dev/null +++ b/model/model-intest/src/test/resources/schema/metadata.xsd @@ -0,0 +1,90 @@ + + + + + + + + + For testing value metadata processing. + + + + + + + + + + + + + + + + + + + + + + Assurance metadata: an example of deployment-specific metadata. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +