Skip to content

Commit

Permalink
Add setting xsi:type when exporting objects
Browse files Browse the repository at this point in the history
This is only a preliminary (approximate) solution, see the comment in
PrismMarshaller.shouldPutTypeInExportMode.
  • Loading branch information
mederly committed May 17, 2018
1 parent 0ca2ba7 commit cc7f15f
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 10 deletions.
Expand Up @@ -46,6 +46,10 @@ public static boolean isSerializeCompositeObjects(SerializationContext ctx) {
return ctx != null && SerializationOptions.isSerializeCompositeObjects(ctx.getOptions());
}

public static boolean isSerializeForExport(SerializationContext ctx) {
return ctx != null && SerializationOptions.isSerializeForExport(ctx.getOptions());
}

public static SerializationContext forOptions(SerializationOptions options) {
return new SerializationContext(options);
}
Expand Down
Expand Up @@ -24,6 +24,13 @@ public class SerializationOptions implements Cloneable {
private boolean serializeCompositeObjects;
private boolean serializeReferenceNames;
private ItemNameQualificationStrategy itemNameQualificationStrategy;

/**
* Makes the serialized form "standalone". Currently this means that values for items that are not present in the
* schema registry (like attributes or connector configuration properties) will get xsi:type information.
*/
private boolean serializeForExport;

// private NameQualificationStrategy itemTypeQualificationStrategy;
// private NameQualificationStrategy itemPathQualificationStrategy;
// private NameQualificationStrategy genericQualificationStrategy;
Expand Down Expand Up @@ -64,6 +71,24 @@ public static boolean isSerializeCompositeObjects(SerializationOptions options)
return options != null && options.isSerializeCompositeObjects();
}

public boolean isSerializeForExport() {
return serializeForExport;
}

public void setSerializeForExport(boolean serializeForExport) {
this.serializeForExport = serializeForExport;
}

public static SerializationOptions createSerializeForExport() {
SerializationOptions serializationOptions = new SerializationOptions();
serializationOptions.setSerializeForExport(true);
return serializationOptions;
}

public static boolean isSerializeForExport(SerializationOptions options) {
return options != null && options.isSerializeForExport();
}

// public ItemNameQualificationStrategy getItemNameQualificationStrategy() {
// return itemNameQualificationStrategy;
// }
Expand Down Expand Up @@ -115,14 +140,15 @@ public static boolean isUseNsProperty(SerializationOptions opts) {

@Override
protected SerializationOptions clone() {
SerializationOptions clone = null;
SerializationOptions clone;
try {
clone = (SerializationOptions) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
clone.serializeReferenceNames = this.serializeReferenceNames;
clone.serializeReferenceNames = serializeReferenceNames;
clone.itemNameQualificationStrategy = itemNameQualificationStrategy;
clone.serializeForExport = serializeForExport;
return clone;
}
}
Expand Up @@ -38,6 +38,7 @@
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* @author semancik
Expand Down Expand Up @@ -204,7 +205,7 @@ private XNode marshalItemValue(@NotNull PrismValue itemValue, @Nullable ItemDefi
} else {
throw new IllegalArgumentException("Unsupported value type "+itemValue.getClass());
}
if (definition != null && definition.isDynamic() && isInstantiable(definition)) {
if (definition != null && (definition.isDynamic() || shouldPutTypeInExportMode(ctx, definition)) && isInstantiable(definition)) {
if (xnode.getTypeQName() == null) {
xnode.setTypeQName(definition.getTypeName());
}
Expand All @@ -213,7 +214,22 @@ private XNode marshalItemValue(@NotNull PrismValue itemValue, @Nullable ItemDefi
return xnode;
}

// TODO FIXME first of all, Extension definition should not be marked as dynamic
private boolean shouldPutTypeInExportMode(SerializationContext ctx, ItemDefinition definition) {
if (!SerializationContext.isSerializeForExport(ctx) || definition == null || !definition.isRuntimeSchema()) {
return false;
}
QName itemName = definition.getName();
if (StringUtils.isEmpty(itemName.getNamespaceURI())) {
return true; // ambiguous item name - let's put xsi:type, to be on the safe side
}
// we assume that all runtime elements which are part of the schema registry are retrievable by element name
// (might not be the case for sub-items of custom extension containers! we hope providing xsi:type there will cause no harm)
List<ItemDefinition> definitionsInRegistry = getSchemaRegistry()
.findItemDefinitionsByElementName(itemName, ItemDefinition.class);
return definitionsInRegistry.isEmpty(); // no definition in registry => xsi:type should be put
}

// TODO FIXME first of all, Extension definition should not be marked as dynamic
private boolean isInstantiable(ItemDefinition definition) {
if (definition.isAbstract()) {
return false;
Expand Down
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.schema;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.util.PrismAsserts;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.schema.constants.MidPointConstants;
import com.evolveum.midpoint.schema.util.SchemaTestConstants;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
import org.xml.sax.SAXException;

import javax.xml.namespace.QName;
import java.io.IOException;

import static com.evolveum.midpoint.prism.SerializationOptions.createSerializeForExport;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;

/**
* @author mederly
*
*/
public class TestExport {

@BeforeSuite
public void setup() throws SchemaException, SAXException, IOException {
PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX);
PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY);
}

@Test
public void testExportShadow() throws Exception {
System.out.println("===[ testExportShadow ]===");

PrismContext prismContext = PrismTestUtil.getPrismContext();
PrismObject<ShadowType> shadow = prismContext.createObjectable(ShadowType.class)
.name("shadow1")
.asPrismObject();
PrismContainer<Containerable> attributes = shadow.findOrCreateContainer(ShadowType.F_ATTRIBUTES);

final QName INT_ATTRIBUTE_NAME = new QName(MidPointConstants.NS_RI, "intAttribute");
PrismPropertyDefinitionImpl<Integer> intAttributeDef = new PrismPropertyDefinitionImpl<>(
INT_ATTRIBUTE_NAME, DOMUtil.XSD_INT, prismContext);
intAttributeDef.setRuntimeSchema(true);
PrismProperty<Integer> intAttribute = intAttributeDef.instantiate();
intAttribute.addRealValue(101);
attributes.add(intAttribute);

final QName STRING_ATTRIBUTE_NAME = new QName(MidPointConstants.NS_RI, "stringAttribute");
PrismPropertyDefinitionImpl<String> stringAttributeDef = new PrismPropertyDefinitionImpl<>(
STRING_ATTRIBUTE_NAME, DOMUtil.XSD_STRING, prismContext);
stringAttributeDef.setRuntimeSchema(true);
PrismProperty<String> stringAttribute = stringAttributeDef.instantiate();
stringAttribute.addRealValue("abc");
attributes.add(stringAttribute);

// intentionally created ad-hoc, not retrieved from the registry
PrismPropertyDefinitionImpl<Long> longTypeExtensionDef = new PrismPropertyDefinitionImpl<>(
SchemaTestConstants.EXTENSION_LONG_TYPE_ELEMENT, DOMUtil.XSD_LONG, prismContext);
longTypeExtensionDef.setRuntimeSchema(true);
PrismProperty<Long> longExtension = longTypeExtensionDef.instantiate();
longExtension.addRealValue(110L);
shadow.addExtensionItem(longExtension);

//noinspection unchecked
PrismPropertyDefinition<Double> doubleTypeExtensionDef = prismContext.getSchemaRegistry()
.findItemDefinitionByElementName(SchemaTestConstants.EXTENSION_DOUBLE_TYPE_ELEMENT, PrismPropertyDefinition.class);
PrismProperty<Double> doubleExtension = doubleTypeExtensionDef.instantiate();
doubleExtension.addRealValue(-1.0);
shadow.addExtensionItem(doubleExtension);

String xml = prismContext.xmlSerializer().options(createSerializeForExport()).serialize(shadow);
System.out.println("Serialized:\n" + xml);

PrismObject<ShadowType> shadowReparsed = prismContext.parseObject(xml);
System.out.println("Reparsed:\n" + shadowReparsed.debugDump());
PrismAsserts.assertEquals("objects differ", shadow, shadowReparsed);

Item<?, ?> intAttributeReparsed = shadowReparsed.findItem(new ItemPath(ShadowType.F_ATTRIBUTES, INT_ATTRIBUTE_NAME));
assertNotNull(intAttributeReparsed);
assertFalse(intAttributeReparsed.getValue(0).isRaw());
Item<?, ?> stringAttributeReparsed = shadowReparsed.findItem(new ItemPath(ShadowType.F_ATTRIBUTES, STRING_ATTRIBUTE_NAME));
assertNotNull(stringAttributeReparsed);
assertFalse(stringAttributeReparsed.getValue(0).isRaw());
Item<?, ?> longExtensionReparsed = shadowReparsed.findItem(new ItemPath(ShadowType.F_EXTENSION, SchemaTestConstants.EXTENSION_LONG_TYPE_ELEMENT));
assertNotNull(longExtensionReparsed);
assertFalse(longExtensionReparsed.getValue(0).isRaw());
Item<?, ?> doubleExtensionReparsed = shadowReparsed.findItem(new ItemPath(ShadowType.F_EXTENSION, SchemaTestConstants.EXTENSION_DOUBLE_TYPE_ELEMENT));
assertNotNull(doubleExtensionReparsed);
assertFalse(doubleExtensionReparsed.getValue(0).isRaw());
}

}
Expand Up @@ -75,11 +75,11 @@
<!-- Configuration specific for the LDAP connector -->
<icfc:configurationProperties>

<icfcldap:port>10389</icfcldap:port>
<icfcldap:host>localhost</icfcldap:host>
<icfcldap:port xsi:type="xsd:int">10389</icfcldap:port> <!-- xsi:type is here just for fun; to see if serialization-parsing cycle works correctly also in such cases -->
<icfcldap:host xsi:type="xsd:string">localhost</icfcldap:host>
<icfcldap:baseContexts>dc=example,dc=com</icfcldap:baseContexts>
<icfcldap:principal>cn=directory manager</icfcldap:principal>
<icfcldap:credentials>
<icfcldap:credentials xsi:type="t:ProtectedStringType">
<t:clearValue>secret</t:clearValue>
</icfcldap:credentials>
<icfcldap:vlvSortAttribute>uid</icfcldap:vlvSortAttribute>
Expand Down
Expand Up @@ -37,12 +37,10 @@
import com.evolveum.midpoint.repo.sql.testing.SqlRepoTestUtil;
import com.evolveum.midpoint.schema.DeltaConvertor;
import com.evolveum.midpoint.schema.MidPointPrismContextFactory;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.constants.MidPointConstants;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.internals.InternalsConfig;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.SchemaTestConstants;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.DebugUtil;
Expand Down Expand Up @@ -74,6 +72,8 @@
import java.util.Collection;
import java.util.List;

import static com.evolveum.midpoint.prism.SerializationOptions.createSerializeForExport;
import static com.evolveum.midpoint.schema.GetOperationOptions.createRawCollection;
import static org.testng.AssertJUnit.*;

/**
Expand Down Expand Up @@ -904,6 +904,17 @@ public void test200ReplaceAttributes() throws Exception {
obj = (RObject) session.createQuery("from RObject where oid = :o").setParameter("o", account.getOid()).getSingleResult();
assertEquals(1, obj.getStrings().size());
close(session);

// now test the "export" serialization option

PrismObject<ShadowType> shadow = repositoryService.getObject(ShadowType.class, accountOid, createRawCollection(), result);
String xml = prismContext.xmlSerializer().options(createSerializeForExport()).serialize(shadow);
System.out.println("Serialized for export:\n" + xml);
PrismObject<Objectable> shadowReparsed = prismContext.parseObject(xml);
System.out.println("Reparsed:\n" + shadowReparsed.debugDump());
Item<PrismValue, ItemDefinition> attr1Reparsed = shadowReparsed.findItem(new ItemPath(ShadowType.F_ATTRIBUTES, ATTR1_QNAME));
assertNotNull(attr1Reparsed);
assertFalse("Reparsed attribute is raw", attr1Reparsed.getValue(0).isRaw());
}

private <T> void assertAttribute(PrismObject<ShadowType> shadow, String attrName, T... expectedValues) {
Expand Down
Expand Up @@ -641,11 +641,13 @@ private void applyShadowAttributeDefinitions(Class<? extends RAnyValue> anyValue
PrismPropertyDefinitionImpl<Object> def = new PrismPropertyDefinitionImpl<>(name, type, object.getPrismContext());
def.setMinOccurs(0);
def.setMaxOccurs(-1);
def.setRuntimeSchema(true);
item.applyDefinition(def, true);
} else if (rValType == RItemKind.REFERENCE) {
PrismReferenceDefinitionImpl def = new PrismReferenceDefinitionImpl(name, type, object.getPrismContext());
def.setMinOccurs(0);
def.setMaxOccurs(-1);
def.setRuntimeSchema(true);
item.applyDefinition(def, true);
} else {
throw new UnsupportedOperationException("Unsupported value type " + rValType);
Expand Down

0 comments on commit cc7f15f

Please sign in to comment.