Skip to content

Commit

Permalink
Fixed unmarshalling objects embedded in beans (e.g. objectToAdd in Ob…
Browse files Browse the repository at this point in the history
…jectDeltaType).
  • Loading branch information
mederly committed Oct 27, 2016
1 parent 08b1b6b commit bd3ace5
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 43 deletions.
Expand Up @@ -1373,6 +1373,22 @@ public int hashCode() {
return result;
}

public boolean equivalent(ObjectDelta other) {
if (changeType != other.changeType)
return false;
if (objectToAdd == null) {
if (other.objectToAdd != null)
return false;
} else if (!objectToAdd.equivalent(other.objectToAdd))
return false;
if (!MiscUtil.unorderedCollectionEquals(this.modifications, other.modifications, (o1, o2) ->
((ItemDelta) o1).equivalent((ItemDelta) o2) ? 0 : 1)) {
return false;
}
return Objects.equals(objectTypeClass, other.objectTypeClass)
&& Objects.equals(oid, other.oid);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
Expand Down
Expand Up @@ -85,7 +85,6 @@ public <T> XNode marshall(@Nullable T bean, @Nullable SerializationContext ctx)
if (marshaller != null) {
return marshaller.marshal(bean, ctx);
} else if (bean instanceof Containerable) {
// we shouldn't get here but ...
return prismContext.xnodeSerializer().serializeRealValue(bean, new QName("dummy")).getSubnode();
} else if (bean instanceof Enum) {
return marshalEnum((Enum) bean, ctx);
Expand Down
Expand Up @@ -187,9 +187,10 @@ public boolean canProcess(Class<?> clazz) {
private <T> T unmarshalFromMap(@NotNull MapXNode xmap, @NotNull Class<T> beanClass, @NotNull ParsingContext pc) throws SchemaException {

if (Containerable.class.isAssignableFrom(beanClass)) {
// This could have come from inside
PrismValue value = prismContext.parserFor(xmap.toRootXNode()).type(beanClass).parseItemValue();
return (T) value.getRealValue();
// This could have come from inside; note we MUST NOT parse this as PrismValue, because for objects we would lose oid/version
@SuppressWarnings("unchecked")
T value = (T) prismContext.parserFor(xmap.toRootXNode()).type(beanClass).parseRealValue();
return value;
//throw new IllegalArgumentException("Couldn't process Containerable: " + beanClass + " from " + xmap.debugDump());
} else if (SearchFilterType.class.isAssignableFrom(beanClass)) {
T bean = (T) unmarshalSearchFilterType(xmap, (Class<? extends SearchFilterType>) beanClass, pc);
Expand Down
Expand Up @@ -57,6 +57,8 @@ public PrismUnmarshaller(@NotNull PrismContext prismContext) {
* Please note: methods in this section should NOT be called from inside of parsing process!
* It is to avoid repeatedly calling ItemInfo.determine, if at all possible.
* (An exception is only if we know we have the definition ... TODO ...)
*
* TODO migrate to parseItem eventually (now we treat objects in parseItemInternal!)
*/
@SuppressWarnings("unchecked")
<O extends Objectable> PrismObject<O> parseObject(@NotNull RootXNode root, ItemDefinition<?> itemDefinition, QName itemName,
Expand All @@ -73,8 +75,10 @@ <O extends Objectable> PrismObject<O> parseObject(@NotNull RootXNode root, ItemD
return (PrismObject<O>) (Item) parseItemInternal(child, itemInfo.getItemName(), itemInfo.getItemDefinition(), pc);
}

// TODO migrate to parseItem eventually
@SuppressWarnings("unchecked")
<O extends Objectable> PrismObject<O> parseObject(MapXNode map, PrismObjectDefinition<O> objectDefinition, ParsingContext pc) throws SchemaException {
private <O extends Objectable> PrismObject<O> parseObject(MapXNode map, PrismObjectDefinition<O> objectDefinition,
ParsingContext pc) throws SchemaException {
ItemInfo itemInfo = ItemInfo.determine(objectDefinition,
null, null, ARTIFICIAL_OBJECT_NAME,
map.getTypeQName(), null,
Expand All @@ -95,7 +99,7 @@ <O extends Objectable> PrismObject<O> parseObject(MapXNode map, PrismObjectDefin
if (itemInfo.getItemDefinition() == null && itemInfo.getComplexTypeDefinition() != null) {
// let's create container definition dynamically
QName actualTypeName = itemInfo.getComplexTypeDefinition().getTypeName();
if (getSchemaRegistry().isContainer(actualTypeName)) {
if (getSchemaRegistry().isContainer(actualTypeName)) { // TODO what about objects?
PrismContainerDefinitionImpl def = new PrismContainerDefinitionImpl(itemInfo.getItemName(),
itemInfo.getComplexTypeDefinition(), prismContext);
def.setDynamic(true);
Expand Down Expand Up @@ -171,21 +175,32 @@ private <C extends Containerable> PrismContainer<C> parseContainer(@NotNull XNod
@NotNull PrismContainerDefinition<C> containerDef, @NotNull ParsingContext pc) throws SchemaException {
PrismContainer<C> container = containerDef.instantiate(itemName);
if (node instanceof ListXNode) {
for (XNode subNode : (ListXNode) node) {
container.add(parseContainerValue(subNode, containerDef, pc));
ListXNode list = (ListXNode) node;
if (containerDef instanceof PrismObject && list.size() > 1) {
pc.warnOrThrow(LOGGER, "Multiple values for a PrismObject: " + node.debugDump());
parseContainerValueToContainer(container, list.get(0), pc);
} else {
for (XNode subNode : list) {
parseContainerValueToContainer(container, subNode, pc);
}
}
} else {
container.add(parseContainerValue(node, containerDef, pc));
if (node instanceof MapXNode && container instanceof PrismObject) {
MapXNode map = (MapXNode) node;
PrismObject object = (PrismObject) container;
object.setOid(getOid(map));
object.setVersion(getVersion(map));
}
parseContainerValueToContainer(container, node, pc);
}
return container;
}

private <C extends Containerable> void parseContainerValueToContainer(PrismContainer<C> container, XNode node,
@NotNull ParsingContext pc) throws SchemaException {
container.add(parseContainerValue(node, container.getDefinition(), pc));
if (node instanceof MapXNode && container instanceof PrismObject) {
MapXNode map = (MapXNode) node;
PrismObject object = (PrismObject) container;
object.setOid(getOid(map));
object.setVersion(getVersion(map));
}
}

private String getOid(MapXNode xmap) throws SchemaException {
return xmap.getParsedPrimitiveValue(XNode.KEY_OID, DOMUtil.XSD_STRING);
}
Expand Down
Expand Up @@ -99,7 +99,7 @@ public class ObjectDeltaType implements Serializable {
protected ObjectType objectToAdd;
@XmlElement(required = true)
protected String oid;
protected List<ItemDeltaType> itemDelta;
protected final List<ItemDeltaType> itemDelta = new ArrayList<>();

public final static QName COMPLEX_TYPE = new QName(PrismConstants.NS_TYPES, "ObjectDeltaType");
public final static QName F_CHANGE_TYPE = new QName(PrismConstants.NS_TYPES, "changeType");
Expand Down Expand Up @@ -230,9 +230,6 @@ public void setOid(String value) {
*
*/
public List<ItemDeltaType> getItemDelta() {
if (itemDelta == null) {
itemDelta = new ArrayList<ItemDeltaType>();
}
return this.itemDelta;
}

Expand Down
Expand Up @@ -16,6 +16,8 @@

package com.evolveum.midpoint.schema;

import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext;
import static com.evolveum.midpoint.schema.DeltaConvertor.toObjectDeltaType;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
Expand All @@ -26,9 +28,11 @@
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.prism.delta.ReferenceDelta;
import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder;
import com.evolveum.midpoint.prism.path.IdItemPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.util.PrismAsserts;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.prism.xnode.MapXNode;
Expand All @@ -37,22 +41,14 @@
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import com.evolveum.prism.xml.ns._public.types_3.RawType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.prism.xml.ns._public.types_3.*;

import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
Expand All @@ -79,7 +75,7 @@ public void testRefWithObject() throws SchemaException, IOException, JAXBExcepti
ObjectModificationType.COMPLEX_TYPE);

ObjectDelta<UserType> objectDelta = DeltaConvertor.createObjectDelta(objectChange, UserType.class,
PrismTestUtil.getPrismContext());
getPrismContext());

System.out.println("delta: " + objectDelta.debugDump());

Expand All @@ -105,7 +101,7 @@ public void testPasswordChange() throws Exception {

// WHEN
ObjectDelta<UserType> objectDelta = DeltaConvertor.createObjectDelta(objectChange, UserType.class,
PrismTestUtil.getPrismContext());
getPrismContext());

// THEN
assertNotNull("No object delta", objectDelta);
Expand Down Expand Up @@ -138,7 +134,7 @@ public void testModifyGivenName() throws Exception {

// WHEN
ObjectDelta<UserType> objectDelta = DeltaConvertor.createObjectDelta(objectChange, UserType.class,
PrismTestUtil.getPrismContext());
getPrismContext());

// THEN
assertNotNull("No object delta", objectDelta);
Expand Down Expand Up @@ -169,7 +165,7 @@ public void testAddAssignment() throws Exception {

// WHEN
ObjectDelta<UserType> objectDelta = DeltaConvertor.createObjectDelta(objectChange, UserType.class,
PrismTestUtil.getPrismContext());
getPrismContext());

System.out.println("Delta:");
System.out.println(objectDelta.debugDump());
Expand Down Expand Up @@ -217,7 +213,7 @@ public void testAccountRefDelta() throws Exception {
modificationDeleteAccountRef.setModificationType(ModificationTypeType.DELETE);
ObjectReferenceType accountRefToDelete = new ObjectReferenceType();
accountRefToDelete.setOid("54321");
PrismContext prismContext = PrismTestUtil.getPrismContext();
PrismContext prismContext = getPrismContext();
RawType modificationValue = new RawType(((PrismContextImpl) prismContext).getBeanMarshaller().marshall(accountRefToDelete), prismContext);
modificationDeleteAccountRef.getValue().add(modificationValue);
objectChange.getItemDelta().add(modificationDeleteAccountRef);
Expand All @@ -244,13 +240,13 @@ public void testProtectedStringObjectDelta() throws Exception {
ProtectedStringType protectedString = new ProtectedStringType();
protectedString.setClearValue("abrakadabra");
ObjectDelta<UserType> objectDelta = ObjectDelta.createModificationReplaceProperty(UserType.class, "12345",
path, PrismTestUtil.getPrismContext(), protectedString);
path, getPrismContext(), protectedString);

System.out.println("ObjectDelta");
System.out.println(objectDelta.debugDump());

// WHEN
ObjectDeltaType objectDeltaType = DeltaConvertor.toObjectDeltaType(objectDelta);
ObjectDeltaType objectDeltaType = toObjectDeltaType(objectDelta);

// THEN
System.out.println("ObjectDeltaType (XML)");
Expand Down Expand Up @@ -290,13 +286,13 @@ public void testObjectDeltaRoundtrip() throws Exception {
final String OID = "13235545";
final String VALUE = "Very Costly Center";
ObjectDelta<UserType> objectDelta = ObjectDelta.createModificationReplaceProperty(UserType.class, OID,
UserType.F_COST_CENTER, PrismTestUtil.getPrismContext(), VALUE);
UserType.F_COST_CENTER, getPrismContext(), VALUE);

System.out.println("ObjectDelta");
System.out.println(objectDelta.debugDump());

// WHEN
ObjectDeltaType objectDeltaType = DeltaConvertor.toObjectDeltaType(objectDelta);
ObjectDeltaType objectDeltaType = toObjectDeltaType(objectDelta);

// THEN
System.out.println("ObjectDeltaType (XML)");
Expand Down Expand Up @@ -326,7 +322,7 @@ public void testObjectDeltaRoundtrip() throws Exception {
// assertEquals("Wrong element value", VALUE, valueElement);

// WHEN
ObjectDelta<Objectable> objectDeltaRoundtrip = DeltaConvertor.createObjectDelta(objectDeltaType, PrismTestUtil.getPrismContext());
ObjectDelta<Objectable> objectDeltaRoundtrip = DeltaConvertor.createObjectDelta(objectDeltaType, getPrismContext());

// THEN
System.out.println("ObjectDelta (roundtrip)");
Expand All @@ -353,10 +349,10 @@ public void testTaskExtensionDeleteDelta() throws Exception {

final QName CUSTOM_OBJECT = new QName("http://delta.example.com", "object");

PrismContext context = PrismTestUtil.getPrismContext();
PrismContext context = getPrismContext();

// WHEN
ObjectDeltaType xmlDelta = DeltaConvertor.toObjectDeltaType(delta);
ObjectDeltaType xmlDelta = toObjectDeltaType(delta);

// THEN
Map<String, Object> properties = new HashMap<String, Object>();
Expand Down Expand Up @@ -487,7 +483,7 @@ public void testModifyInducement() throws Exception {

// WHEN
ObjectDelta<RoleType> objectDelta = DeltaConvertor.createObjectDelta(objectChange, RoleType.class,
PrismTestUtil.getPrismContext());
getPrismContext());

System.out.println("Delta:");
System.out.println(objectDelta.debugDump());
Expand All @@ -510,4 +506,60 @@ public void testModifyInducement() throws Exception {
assertEquals("wrong target type in targetRef", RoleType.COMPLEX_TYPE, targetRefVal.getTargetType());
}

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

UserType user = new UserType(getPrismContext());
user.setName(PolyStringType.fromOrig("john"));
user.setOid("1234567890");

ObjectDelta delta = ObjectDelta.createAddDelta(user.asPrismObject());
roundTrip(delta);
}

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

ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, getPrismContext())
.asObjectDelta("123456");
roundTrip(delta);
}

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

ObjectDelta<?> delta = DeltaBuilder.deltaFor(UserType.class, getPrismContext())
.item(UserType.F_NAME).replace(PolyString.fromOrig("jack"))
.asObjectDelta("123456");
roundTrip(delta);
}

private void roundTrip(ObjectDelta delta) throws Exception {

ObjectDeltaType deltaType = DeltaConvertor.toObjectDeltaType(delta);

System.out.println("Serialized to bean");
System.out.println(deltaType);

String xml = getPrismContext().xmlSerializer().serializeRealValue(deltaType, new QName("aDelta"));

System.out.println("Serialized to XML");
System.out.println(xml);

ObjectDeltaType deltaTypeParsed = getPrismContext().parserFor(xml).parseRealValue();

System.out.println("Parsed from XML to bean");
System.out.println(deltaTypeParsed);

ObjectDelta deltaParsed = DeltaConvertor.createObjectDelta(deltaTypeParsed, getPrismContext());

System.out.println("Parsed from XML to bean to delta");
System.out.println(deltaParsed);

assertTrue("Deltas (native) do not match", delta.equivalent(deltaParsed));
// note: comparing beans is problematic because e.g. item paths are not equal ({common-3}name vs {c=common-3}c:name)
}
}

0 comments on commit bd3ace5

Please sign in to comment.