Skip to content

Commit

Permalink
improved delta updater repository implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
1azyman committed Feb 19, 2018
1 parent 46bc44a commit a74ef8a
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 88 deletions.
Expand Up @@ -16,10 +16,7 @@

package com.evolveum.midpoint.repo.sql.helpers;

import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.path.IdItemPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
Expand Down Expand Up @@ -176,12 +173,12 @@ public <T extends ObjectType> RObject<T> modifyObject(Class<T> type, String oid,
continue;
}

handleAttribute(attribute, attributeStep.bean, delta);
handleAttribute(attribute, attributeStep.bean, delta, prismObject);

if ("name".equals(attribute.getName()) && RObject.class.isAssignableFrom(attribute.getDeclaringType().getJavaType())) {
// we also need to handle "nameCopy" column, we doesn't need path/segments/nameSegment for this call
Attribute nameCopyAttribute = findAttribute(attributeStep, "nameCopy", null, null, null);
handleAttribute(nameCopyAttribute, attributeStep.bean, delta);
handleAttribute(nameCopyAttribute, attributeStep.bean, delta, prismObject);
}
}
}
Expand Down Expand Up @@ -674,7 +671,7 @@ private AttributeStep stepThroughAttribute(Attribute attribute, AttributeStep st
return step;
}

private void handleAttribute(Attribute attribute, Object bean, ItemDelta delta) {
private void handleAttribute(Attribute attribute, Object bean, ItemDelta delta, PrismObject prismObject) {
Method method = (Method) attribute.getJavaMember();

switch (attribute.getPersistentAttributeType()) {
Expand All @@ -694,11 +691,11 @@ private void handleAttribute(Attribute attribute, Object bean, ItemDelta delta)
case ONE_TO_MANY:
// object extension is handled separately, only {@link Container} and references are handled here
Collection oneToMany = (Collection) invoke(bean, method);
handleOneToMany(oneToMany, delta, attribute, bean);
handleOneToMany(oneToMany, delta, attribute, bean, prismObject);
break;
case ELEMENT_COLLECTION:
Collection elementCollection = (Collection) invoke(bean, method);
handleElementCollection(elementCollection, delta, attribute, bean);
handleElementCollection(elementCollection, delta, attribute, bean, prismObject);
break;
}
}
Expand All @@ -725,17 +722,19 @@ private void handleBasicOrEmbedded(Object bean, ItemDelta delta, Attribute attri
}
}

private void handleElementCollection(Collection collection, ItemDelta delta, Attribute attribute, Object bean) {
handleOneToMany(collection, delta, attribute, bean);
private void handleElementCollection(Collection collection, ItemDelta delta, Attribute attribute, Object bean, PrismObject prismObject) {
handleOneToMany(collection, delta, attribute, bean, prismObject);
}

private void handleOneToMany(Collection collection, ItemDelta delta, Attribute attribute, Object bean) {
private void handleOneToMany(Collection collection, ItemDelta delta, Attribute attribute, Object bean, PrismObject prismObject) {
Class outputType = getRealOutputType(attribute);

Item item = prismObject.findItem(delta.getPath());

// handle replace
if (delta.isReplace()) {
Collection<PrismEntityPair<?>> valuesToReplace = processDeltaValues(delta.getValuesToReplace(), outputType, delta, bean);
replaceValues(collection, valuesToReplace);
replaceValues(collection, valuesToReplace, item);
return;
}

Expand All @@ -753,7 +752,7 @@ private void handleOneToMany(Collection collection, ItemDelta delta, Attribute a
((EntityState) pair.getRepository()).setTransient(false);
}
});
deleteValues(collection, valuesToDelete);
deleteValues(collection, valuesToDelete, item);
}
}

Expand Down
Expand Up @@ -16,106 +16,112 @@

package com.evolveum.midpoint.repo.sql.helpers.modify;

import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.repo.sql.data.common.container.Container;
import com.evolveum.midpoint.repo.sql.util.EntityState;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.util.*;
import java.util.stream.Collectors;

/**
* Created by Viliam Repan (lazyman).
*/
public class DeltaUpdaterUtils {

private static final Trace LOGGER = TraceManager.getTrace(DeltaUpdaterUtils.class);

public static void addValues(Collection existing, Collection<PrismEntityPair<?>> valuesToAdd) {
markNewOnesTransientAndAddToExisting(existing, valuesToAdd);
}

public static void deleteValues(Collection existing, Collection<PrismEntityPair<?>> valuesToDelete) {
public static void deleteValues(Collection existing, Collection<PrismEntityPair<?>> valuesToDelete, Item item) {
if (existing.isEmpty() || valuesToDelete.isEmpty()) {
return;
}

Collection<Object> repositoryObjects = valuesToDelete.stream().map(pair -> pair.getRepository()).collect(Collectors.toList());
Collection<PrismEntityPair<?>> existingPairs = createExistingPairs(existing, item);

Pair<Collection<Container>, Set<Long>> split = splitContainers(valuesToDelete);
Collection<Container> containersWithoutIds = split.getLeft();
if (!containersWithoutIds.isEmpty()) {
LOGGER.warn("Container without id found in delete delta, potential operation slowdown as we " +
"need to compare full container against database");
Collection toDelete = new ArrayList();
for (PrismEntityPair toDeletePair : valuesToDelete) {
PrismEntityPair existingPair = findMatch(existingPairs, toDeletePair);
if (existingPair != null) {
toDelete.add(existingPair.getRepository());
}
}
Set<Long> containerIds = split.getRight();

Collection toDelete = new ArrayList();
for (Object obj : existing) {
if (obj instanceof Container) {
Container container = (Container) obj;
existing.removeAll(toDelete);
}

private static PrismEntityPair findMatch(Collection<PrismEntityPair<?>> collection, PrismEntityPair pair) {
boolean isContainer = pair.getRepository() instanceof Container;

Object pairObject = pair.getRepository();

for (PrismEntityPair item : collection) {
if (isContainer) {
Container c = (Container) item.getRepository();
Container pairContainer = (Container) pairObject;

long id = container.getId().longValue();
if (containerIds.contains(id)
|| (!containersWithoutIds.isEmpty() && containersWithoutIds.contains(container))) {
toDelete.add(container);
if (Objects.equals(c.getId(), pairContainer.getId())
|| pair.getPrism().equals(item.getPrism(), true)) {
return item;
}
} else {
// e.g. RObjectReference
if (repositoryObjects.contains(obj)) {
toDelete.add(obj);
if (Objects.equals(item.getRepository(), pairObject)) {
return item;
}
}
}

existing.removeAll(toDelete);
return null;
}

public static void replaceValues(Collection existing, Collection<PrismEntityPair<?>> valuesToReplace) {
private static Collection<PrismEntityPair<?>> createExistingPairs(Collection existing, Item item) {
Collection<PrismEntityPair<?>> pairs = new ArrayList<>();

for (Object obj : existing) {
if (obj instanceof Container) {
Container container = (Container) obj;

PrismValue value = (PrismValue) item.find(new ItemPath(container.getId().longValue()));

pairs.add(new PrismEntityPair(value, container));
} else {
// todo improve somehow
pairs.add(new PrismEntityPair(null, obj));
}
}

return pairs;
}

public static void replaceValues(Collection existing, Collection<PrismEntityPair<?>> valuesToReplace, Item item) {
if (existing.isEmpty()) {
markNewOnesTransientAndAddToExisting(existing, valuesToReplace);
return;
}

Collection<Object> repositoryObjects = valuesToReplace.stream().map(pair -> pair.getRepository()).collect(Collectors.toList());

Pair<Collection<Container>, Set<Long>> split = splitContainers(valuesToReplace);
Collection<Container> containersWithoutIds = split.getLeft();
if (!containersWithoutIds.isEmpty()) {
LOGGER.warn("Container without id found in replace delta, potential operation slowdown as we " +
"need to compare full container against database");
}
Set<Long> containerIds = split.getRight();
Collection<PrismEntityPair<?>> existingPairs = createExistingPairs(existing, item);

Collection skipAddingTheseObjects = new ArrayList();
Collection skipAddingTheseIds = new ArrayList();

Collection toDelete = new ArrayList();

// mark existing object for deletion, skip if they would be replaced with the same value
for (Object obj : existing) {
if (obj instanceof Container) {
Container container = (Container) obj;

long id = container.getId().longValue();
if (!containerIds.contains(id)
|| (!containersWithoutIds.isEmpty() && !containersWithoutIds.contains(container))) {
toDelete.add(container);
} else {
skipAddingTheseIds.add(id);
skipAddingTheseObjects.add(container);
}
for (PrismEntityPair existingPair : existingPairs) {
PrismEntityPair toReplacePair = findMatch(valuesToReplace, existingPair);
if (toReplacePair == null) {
toDelete.add(existingPair.getRepository());
} else {
// e.g. RObjectReference
if (!repositoryObjects.contains(obj)) {
toDelete.add(obj);
} else {
skipAddingTheseObjects.add(obj);
Object existingObject = existingPair.getRepository();
if (existingObject instanceof Container) {
Container c = (Container) existingObject;
skipAddingTheseIds.add(c.getId());
}
skipAddingTheseObjects.add(existingObject);
}
}
existing.removeAll(toDelete);
Expand Down Expand Up @@ -145,27 +151,6 @@ public static void replaceValues(Collection existing, Collection<PrismEntityPair
markNewOnesTransientAndAddToExisting(existing, valuesToReplace);
}

public static Pair<Collection<Container>, Set<Long>> splitContainers(Collection<PrismEntityPair<?>> collection) {
Collection<Container> containers = new ArrayList<>();
Set<Long> containerIds = new HashSet<>();
for (PrismEntityPair pair : collection) {
if (!(pair.getRepository() instanceof Container)) {
continue;
}

Container container = (Container) pair.getRepository();

Integer id = container.getId();
if (id != null) {
containerIds.add(id.longValue());
} else {
containers.add(container);
}
}

return new ImmutablePair<>(containers, containerIds);
}

public static void markNewOnesTransientAndAddToExisting(Collection existing, Collection<PrismEntityPair<?>> newOnes) {
Set<Integer> usedIds = new HashSet<>();
for (Object obj : existing) {
Expand Down

0 comments on commit a74ef8a

Please sign in to comment.