Skip to content

Commit

Permalink
Basic implementation of object merge (model) (MID-3460)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Oct 21, 2016
1 parent ffb8019 commit 049147d
Show file tree
Hide file tree
Showing 12 changed files with 764 additions and 7 deletions.
Expand Up @@ -417,11 +417,15 @@ public static void assertPropertyDelete(Collection<? extends ItemDelta> modifica
assertSet("delta "+propertyDelta+" for "+propertyPath.last(), "delete", propertyDelta.getValuesToDelete(), expectedValues);
}

public static void assertNoItemDelta(ObjectDelta<?> userDelta, ItemPath propertyPath) {
if (userDelta == null) {
public static void assertNoItemDelta(ObjectDelta<?> objectDelta, QName itemName) {
assertNoItemDelta(objectDelta, new ItemPath(itemName));
}

public static void assertNoItemDelta(ObjectDelta<?> objectDelta, ItemPath itemPath) {
if (objectDelta == null) {
return;
}
assert !userDelta.hasItemDelta(propertyPath) : "Delta for item "+propertyPath+" present while not expecting it";
assert !objectDelta.hasItemDelta(itemPath) : "Delta for item "+itemPath+" present while not expecting it";
}

public static ContainerDelta<?> assertContainerAdd(ObjectDelta<?> objectDelta, QName name) {
Expand Down
Expand Up @@ -9360,6 +9360,20 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:element>

<xsd:element name="mergeConfiguration" type="tns:MergeConfigurationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Configuration for object merging. E.g. for merging two users.
Note: this is a single-valued item now. But it will most likely be
switched to multi-valued item in future midPoint versions.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.5</a:since>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>

</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
Expand Down Expand Up @@ -12318,5 +12332,83 @@
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="MergeConfigurationType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<a:container/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="name" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="description" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="item" type="tns:ItemRefMergeConfigurationType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="default" type="tns:ItemMergeConfigurationType" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="ItemMergeConfigurationType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<a:container/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="left" type="tns:MergeStategyType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="right" type="tns:MergeStategyType" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="ItemRefMergeConfigurationType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<a:container/>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="tns:ItemMergeConfigurationType">
<xsd:sequence>
<xsd:element name="ref" type="t:ItemPathType" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

<xsd:simpleType name="MergeStategyType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="ignore">
<xsd:annotation>
<xsd:documentation>
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="IGNORE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="take">
<xsd:annotation>
<xsd:documentation>
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="TAKE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

</xsd:schema>
Expand Up @@ -56,6 +56,8 @@ public interface ModelInteractionService {
static final String GET_CREDENTIALS_POLICY = CLASS_NAME_WITH_DOT + "getCredentialsPolicy";
static final String CHECK_PASSWORD = CLASS_NAME_WITH_DOT + "checkPassword";
static final String GET_CONNECTOR_OPERATIONAL_STATUS = CLASS_NAME_WITH_DOT + "getConnectorOperationalStatus";
static final String MERGE_OBJECTS_PREVIEW_DELTA = CLASS_NAME_WITH_DOT + "mergeObjectsPreviewDelta";
static final String MERGE_OBJECTS_PREVIEW_OBJECT = CLASS_NAME_WITH_DOT + "mergeObjectsPreviewObject";

/**
* Computes the most likely changes triggered by the provided delta. The delta may be any change of any object, e.g.
Expand Down Expand Up @@ -176,4 +178,13 @@ <F extends ObjectType> ModelContext<F> previewChanges(

ConnectorOperationalStatus getConnectorOperationalStatus(String resourceOid, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException;

<O extends ObjectType> ObjectDelta<O> mergeObjectsPreviewDelta(Class<O> type,
String leftOid, String rightOid, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException;

<O extends ObjectType> PrismObject<O> mergeObjectsPreviewObject(Class<O> type,
String leftOid, String rightOid, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException;

}
Expand Up @@ -107,6 +107,7 @@ public interface ModelService {
static final String IMPORT_OBJECTS_FROM_STREAM = CLASS_NAME_WITH_DOT + "importObjectsFromStream";
static final String POST_INIT = CLASS_NAME_WITH_DOT + "postInit";
static final String DISCOVER_CONNECTORS = CLASS_NAME_WITH_DOT + "discoverConnectors";
static final String MERGE_OBJECTS = CLASS_NAME_WITH_DOT + "mergeObjects";

static final String AUTZ_NAMESPACE = AuthorizationConstants.NS_AUTHORIZATION_MODEL;

Expand Down Expand Up @@ -641,17 +642,32 @@ public Set<ConnectorType> discoverConnectors(ConnectorHostType hostType, Task ta
* @param ignoreItemPaths
* @param task
* @param result
* @param <T>
* @param <O>
* @return
* @throws SchemaException
* @throws ObjectNotFoundException
* @throws SecurityViolationException
* @throws CommunicationException
* @throws ConfigurationException
*/
<T extends ObjectType> CompareResultType compareObject(PrismObject<T> object,
<O extends ObjectType> CompareResultType compareObject(PrismObject<O> object,
Collection<SelectorOptions<GetOperationOptions>> readOptions, ModelCompareOptions compareOptions,
@NotNull List<ItemPath> ignoreItemPaths, Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException,
ConfigurationException;

/**
* Merge two objects into one.
*
* EXPERIMENTAL feature. The method signature is likely to change in the future.
*
* @param type object type
* @param leftOid left-side object OID
* @param rightOid right-side object OID
* @param task
* @param result
* @return
*/
<O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type, String leftOid, String rightOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, PolicyViolationException, SecurityViolationException;

}
Expand Up @@ -191,7 +191,10 @@ public class ModelController implements ModelService, TaskService, WorkflowServi

@Autowired
private SchemaTransformer schemaTransformer;


@Autowired(required = true)
private ObjectMerger objectMerger;

public ModelObjectResolver getObjectResolver() {
return objectResolver;
}
Expand Down Expand Up @@ -2071,4 +2074,38 @@ public AccessCertificationCampaignType createCampaign(String definitionOid, Task
return getCertificationManagerChecked().createCampaign(definitionOid, task, parentResult);
}
//endregion

@Override
public <O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type, String leftOid, String rightOid, Task task,
OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, PolicyViolationException, SecurityViolationException {

OperationResult result = parentResult.createSubresult(MERGE_OBJECTS);
result.addParam("leftOid", leftOid);
result.addParam("rightOid", rightOid);
result.addParam("class", type);

RepositoryCache.enter();

try {

Collection<ObjectDeltaOperation<? extends ObjectType>> deltas =
objectMerger.mergeObjects(type, leftOid, rightOid, task, result);

result.computeStatus();
return deltas;

} catch (ObjectNotFoundException | SchemaException | ConfigurationException
| ObjectAlreadyExistsException | ExpressionEvaluationException | CommunicationException
| PolicyViolationException | SecurityViolationException e) {
ModelUtils.recordFatalError(result, e);
throw e;
} catch (RuntimeException | Error e) {
ModelUtils.recordFatalError(result, e);
throw e;
} finally {
QNameUtil.setTemporarilyTolerateUndeclaredPrefixes(false);
RepositoryCache.exit();
}

}
}
Expand Up @@ -113,6 +113,9 @@ public class ModelInteractionServiceImpl implements ModelInteractionService {

@Autowired(required = true)
private ModelObjectResolver objectResolver;

@Autowired(required = true)
private ObjectMerger objectMerger;

@Autowired(required = true)
@Qualifier("cacheRepositoryService")
Expand Down Expand Up @@ -691,4 +694,51 @@ public ConnectorOperationalStatus getConnectorOperationalStatus(String resourceO
return status;
}

@Override
public <O extends ObjectType> ObjectDelta<O> mergeObjectsPreviewDelta(Class<O> type, String leftOid,
String rightOid, Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
OperationResult result = parentResult.createMinorSubresult(MERGE_OBJECTS_PREVIEW_DELTA);

try {

ObjectDelta<O> objectDelta = objectMerger.computeMergeDelta(type, leftOid, rightOid, task, result);

result.computeStatus();
return objectDelta;

} catch (ObjectNotFoundException | SchemaException | ConfigurationException | RuntimeException | Error e) {
result.recordFatalError(e);
throw e;
}
}

@Override
public <O extends ObjectType> PrismObject<O> mergeObjectsPreviewObject(Class<O> type, String leftOid,
String rightOid, Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
OperationResult result = parentResult.createMinorSubresult(MERGE_OBJECTS_PREVIEW_OBJECT);

try {

ObjectDelta<O> objectDelta = objectMerger.computeMergeDelta(type, leftOid, rightOid, task, result);

final PrismObject<O> objectLeft = objectResolver.getObjectSimple(type, leftOid, null, task, result).asPrismObject();

if (objectDelta == null) {
result.computeStatus();
return objectLeft;
}

objectDelta.applyTo(objectLeft);

result.computeStatus();
return objectLeft;

} catch (ObjectNotFoundException | SchemaException | ConfigurationException | RuntimeException | Error e) {
result.recordFatalError(e);
throw e;
}
}

}

0 comments on commit 049147d

Please sign in to comment.