Skip to content

Commit

Permalink
Support for "minimal" fetch strategy (solves MID-2419)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Jul 3, 2015
1 parent fdfcf81 commit 31c52e2
Show file tree
Hide file tree
Showing 26 changed files with 475 additions and 73 deletions.
Expand Up @@ -32,14 +32,18 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.common.security.GuardedString.Accessor;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder;
Expand All @@ -52,8 +56,21 @@
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.AndFilter;
import org.identityconnectors.framework.common.objects.filter.AttributeFilter;
import org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilter;
import org.identityconnectors.framework.common.objects.filter.ContainsFilter;
import org.identityconnectors.framework.common.objects.filter.EndsWithFilter;
import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
import org.identityconnectors.framework.common.objects.filter.Filter;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.common.objects.filter.GreaterThanFilter;
import org.identityconnectors.framework.common.objects.filter.GreaterThanOrEqualFilter;
import org.identityconnectors.framework.common.objects.filter.LessThanFilter;
import org.identityconnectors.framework.common.objects.filter.LessThanOrEqualFilter;
import org.identityconnectors.framework.common.objects.filter.NotFilter;
import org.identityconnectors.framework.common.objects.filter.OrFilter;
import org.identityconnectors.framework.common.objects.filter.StartsWithFilter;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.Connector;
import org.identityconnectors.framework.spi.ConnectorClass;
Expand Down Expand Up @@ -915,23 +932,35 @@ public void executeQuery(ObjectClass objectClass, Filter query, ResultsHandler h
Collection<DummyAccount> accounts = resource.listAccounts();
for (DummyAccount account : accounts) {
ConnectorObject co = convertToConnectorObject(account, attributesToGet);
handler.handle(co);
if (matches(query, co)) {
co = filterOutAttributesToGet(co, attributesToGet);
handler.handle(co);
}
}

} else if (ObjectClass.GROUP.is(objectClass.getObjectClassValue())) {

Collection<DummyGroup> groups = resource.listGroups();
for (DummyGroup group : groups) {
ConnectorObject co = convertToConnectorObject(group, attributesToGet);
handler.handle(co);
if (matches(query, co)) {
if (attributesToGetHasAttribute(attributesToGet, DummyGroup.ATTR_MEMBERS_NAME)) {
resource.recordGroupMembersReadCount();
}
co = filterOutAttributesToGet(co, attributesToGet);
handler.handle(co);
}
}

} else if (objectClass.is(OBJECTCLASS_PRIVILEGE_NAME)) {

Collection<DummyPrivilege> privs = resource.listPrivileges();
for (DummyPrivilege priv : privs) {
ConnectorObject co = convertToConnectorObject(priv, attributesToGet);
handler.handle(co);
if (matches(query, co)) {
co = filterOutAttributesToGet(co, attributesToGet);
handler.handle(co);
}
}

} else {
Expand All @@ -949,6 +978,109 @@ public void executeQuery(ObjectClass objectClass, Filter query, ResultsHandler h
log.info("executeQuery::end");
}

private boolean matches(Filter query, ConnectorObject co) {
if (query == null) {
return true;
}
if (configuration.getCaseIgnoreValues() || configuration.getCaseIgnoreId()) {
return normalize(query).accept(normalize(co));
}
return query.accept(co);
}

private ConnectorObject normalize(ConnectorObject co) {
ConnectorObjectBuilder cob = new ConnectorObjectBuilder();
if (configuration.getCaseIgnoreId()) {
cob.setUid(co.getUid().getUidValue().toLowerCase());
cob.setName(co.getName().getName().toLowerCase());
} else {
cob.setUid(co.getUid());
cob.setName(co.getName());
}
cob.setObjectClass(co.getObjectClass());
for (Attribute attr : co.getAttributes()) {
cob.addAttribute(normalize(attr));
}
return cob.build();
}

private Filter normalize(Filter filter) {
if (filter instanceof ContainsFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new ContainsFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof EndsWithFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new EndsWithFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof EqualsFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new EqualsFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof GreaterThanFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new GreaterThanFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof GreaterThanOrEqualFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new GreaterThanOrEqualFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof LessThanFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new LessThanFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof LessThanOrEqualFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new LessThanOrEqualFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof StartsWithFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new StartsWithFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof ContainsAllValuesFilter) {
AttributeFilter afilter = (AttributeFilter) filter;
return new ContainsAllValuesFilter(normalize(afilter.getAttribute()));
} else if (filter instanceof NotFilter) {
NotFilter notFilter = (NotFilter) filter;
return new NotFilter(normalize(notFilter.getFilter()));
} else if (filter instanceof AndFilter) {
AndFilter andFilter = (AndFilter) filter;
return new AndFilter(normalize(andFilter.getLeft()), normalize(andFilter.getRight()));
} else if (filter instanceof OrFilter) {
OrFilter orFilter = (OrFilter) filter;
return new OrFilter(normalize(orFilter.getLeft()), normalize(orFilter.getRight()));
} else {
return filter;
}
}

private Attribute normalize(Attribute attr) {
if (configuration.getCaseIgnoreValues()) {
AttributeBuilder ab = new AttributeBuilder();
ab.setName(attr.getName());
for (Object value: attr.getValue()) {
if (value instanceof String) {
ab.addValue(((String)value).toLowerCase());
} else {
ab.addValue(value);
}
}
return ab.build();
} else {
return attr;
}
}

private ConnectorObject filterOutAttributesToGet(ConnectorObject co, Collection<String> attributesToGet) {
if (attributesToGet == null) {
return co;
}
ConnectorObjectBuilder cob = new ConnectorObjectBuilder();
cob.setUid(co.getUid());
cob.setName(co.getName());
cob.setObjectClass(co.getObjectClass());
Set<Attribute> attrs = new HashSet<Attribute>(co.getAttributes().size());
for (Attribute attr : co.getAttributes()) {
if (attributesToGet.contains(attr.getName())) {
cob.addAttribute(attr);
}
}
cob.addAttributes(attrs);
return cob.build();
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -1117,24 +1249,23 @@ private ConnectorObjectBuilder createConnectorObjectBuilderCommon(DummyObject du
builder.addAttribute(Name.NAME, dummyObject.getName());

for (String name : dummyObject.getAttributeNames()) {
if (attributesToGet != null) {
if (!attributesToGet.contains(name)) {
continue;
}
} else {
DummyAttributeDefinition attrDef = objectClass.getAttributeDefinition(name);
if (attrDef == null) {
throw new IllegalArgumentException("Unknown account attribute '"+name+"'");
}
if (!attrDef.isReturnedByDefault()) {
DummyAttributeDefinition attrDef = objectClass.getAttributeDefinition(name);
if (attrDef == null) {
throw new IllegalArgumentException("Unknown account attribute '"+name+"'");
}
if (!attrDef.isReturnedByDefault()) {
if (attributesToGet != null && !attributesToGet.contains(name)) {
continue;
}
}
// Return all attributes that are returned by default. We will filter them out later.
Set<Object> values = dummyObject.getAttributeValues(name, Object.class);
if (configuration.isVaryLetterCase()) {
name = varyLetterCase(name);
}
builder.addAttribute(name, values);
if (values != null && !values.isEmpty()) {
builder.addAttribute(name, values);
}
}

if (supportActivation) {
Expand Down Expand Up @@ -1213,7 +1344,7 @@ private ConnectorObject convertToConnectorObject(DummyGroup group, Collection<St
builder.setObjectClass(ObjectClass.GROUP);
return builder.build();
}

private ConnectorObject convertToConnectorObject(DummyPrivilege priv, Collection<String> attributesToGet) {
ConnectorObjectBuilder builder = createConnectorObjectBuilderCommon(priv, resource.getPrivilegeObjectClass(),
attributesToGet, false);
Expand Down Expand Up @@ -1410,27 +1541,12 @@ public void access(char[] passwdChars) {
}
});
}

// /* (non-Javadoc)
// * @see org.identityconnectors.framework.spi.AttributeNormalizer#normalizeAttribute(org.identityconnectors.framework.common.objects.ObjectClass, org.identityconnectors.framework.common.objects.Attribute)
// */
// @Override
// public Attribute normalizeAttribute(ObjectClass ObjectClass, Attribute attribute) {
// if (!configuration.getCaseIgnoreId()) {
// return attribute;
// }
// String attrName = attribute.getName();
// if (Uid.NAME.equals(attrName) || Name.NAME.equals(attrName)) {
// List<String> values = (List) attribute.getValue();
// AttributeBuilder builder = new AttributeBuilder();
// builder.setName(attrName);
// for (String origVal: values) {
// builder.addValue(StringUtils.lowerCase(origVal));
// }
// return builder.build();
// } else {
// return attribute;
// }
// }

private boolean attributesToGetHasAttribute(Collection<String> attributesToGet, String attrName) {
if (attributesToGet == null) {
return true;
}
return attributesToGet.contains(attrName);
}

}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2013 Evolveum
* Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,14 +24,15 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;

import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

/**
* Resource for use with dummy ICF connector.
Expand Down Expand Up @@ -65,7 +66,10 @@
*
*/
public class DummyResource implements DebugDumpable {

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

private String instanceName;
private Map<String,DummyObject> allObjects;
private Map<String,DummyAccount> accounts;
private Map<String,DummyGroup> groups;
Expand All @@ -84,6 +88,7 @@ public class DummyResource implements DebugDumpable {
private boolean caseIgnoreId = false;
private boolean caseIgnoreValues = false;
private int connectionCount = 0;
private int groupMembersReadCount = 0;

private BreakMode schemaBreakMode = BreakMode.NONE;
private BreakMode getBreakMode = BreakMode.NONE;
Expand Down Expand Up @@ -140,11 +145,20 @@ public static DummyResource getInstance(String instanceName) {
DummyResource instance = instances.get(instanceName);
if (instance == null) {
instance = new DummyResource();
instance.setInstanceName(instanceName);
instances.put(instanceName, instance);
}
return instance;
}

public String getInstanceName() {
return instanceName;
}

public void setInstanceName(String instanceName) {
this.instanceName = instanceName;
}

public boolean isTolerateDuplicateValues() {
return tolerateDuplicateValues;
}
Expand Down Expand Up @@ -277,6 +291,18 @@ public void assertNoConnections() {
assert connectionCount == 0 : "Dummy resource: "+connectionCount+" connections still open";
}

public int getGroupMembersReadCount() {
return groupMembersReadCount;
}

public void setGroupMembersReadCount(int groupMembersReadCount) {
this.groupMembersReadCount = groupMembersReadCount;
}

public void recordGroupMembersReadCount() {
groupMembersReadCount++;
traceOperation("groupMembersRead", groupMembersReadCount);
}

public DummyObjectClass getAccountObjectClass() throws ConnectException, FileNotFoundException {
if (schemaBreakMode == BreakMode.NONE) {
Expand Down Expand Up @@ -779,6 +805,32 @@ public List<DummyDelta> getDeltasSince(int syncToken) {
return result;
}

private void traceOperation(String opName, long counter) {
LOGGER.info("MONITOR dummy '{}' {} ({})", instanceName, opName, counter);
if (LOGGER.isDebugEnabled()) {
StackTraceElement[] fullStack = Thread.currentThread().getStackTrace();
String immediateClass = null;
String immediateMethod = null;
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackElement: fullStack) {
if (stackElement.getClassName().equals(DummyResource.class.getName()) ||
stackElement.getClassName().equals(Thread.class.getName())) {
// skip our own calls
continue;
}
if (immediateClass == null) {
immediateClass = stackElement.getClassName();
immediateMethod = stackElement.getMethodName();
}
sb.append(stackElement.toString());
sb.append("\n");
}
LOGGER.debug("MONITOR dummy '{}' {} ({}): {} {}", new Object[]{instanceName, opName, counter, immediateClass, immediateMethod});
LOGGER.trace("MONITOR dummy '{}' {} ({}):\n{}", new Object[]{instanceName, opName, counter, sb});
}
}


@Override
public String debugDump() {
return debugDump(0);
Expand Down
Expand Up @@ -739,7 +739,9 @@ public static <T> void assertSets(String message, Collection<T> actualValues, T.

public static <T> void assertSets(String message, MatchingRule<T> matchingRule, Collection<T> actualValues, T... expectedValues) {
assertNotNull("Null set in " + message, actualValues);
assertEquals("Wrong number of values in " + message, expectedValues.length, actualValues.size());
assertEquals("Wrong number of values in " + message+ "; expected (real values) "
+PrettyPrinter.prettyPrint(expectedValues)+"; has (pvalues) "+actualValues,
expectedValues.length, actualValues.size());
for (T actualValue: actualValues) {
boolean found = false;
for (T value: expectedValues) {
Expand Down

0 comments on commit 31c52e2

Please sign in to comment.