Skip to content

Commit

Permalink
searchOnResource migrated to searchStrategy, allowing reaching resour…
Browse files Browse the repository at this point in the history
…ce only if no repo shadow exists (MID-2551)
  • Loading branch information
mederly committed Sep 10, 2015
1 parent d6908f6 commit 40f8387
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 26 deletions.
50 changes: 50 additions & 0 deletions infra/schema/src/main/resources/xml/ns/public/common/common-3.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -5514,7 +5514,17 @@
<xsd:element name="filter" type="q:SearchFilterType" minOccurs="0" maxOccurs="1">
</xsd:element>
<xsd:element name="searchOnResource" type="xsd:boolean" minOccurs="0" maxOccurs="1" default="false">
<xsd:annotation>
<xsd:documentation>
DEPRECATED, use searchStrategy instead.
</xsd:documentation>
<xsd:appinfo>
<a:deprecated/>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="searchStrategy" type="tns:ObjectSearchStrategyType" minOccurs="0" maxOccurs="1">
</xsd:element>
<xsd:element name="createOnDemand" type="xsd:boolean" minOccurs="0" maxOccurs="1" default="false">
</xsd:element>
<xsd:element name="populateObject" type="tns:PopulateObjectType" minOccurs="0">
Expand All @@ -5530,6 +5540,46 @@
</xsd:complexType>
<xsd:element name="associationTargetSearch" type="tns:SearchObjectExpressionEvaluatorType" substitutionGroup="tns:expressionEvaluator"/>

<xsd:simpleType name="ObjectSearchStrategyType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="inRepository">
<xsd:annotation>
<xsd:documentation>
Search is carried out in repository only.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="IN_REPOSITORY"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="onResource">
<xsd:annotation>
<xsd:documentation>
Search is always carried out on the resource.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ON_RESOURCE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="onResourceIfNeeded">
<xsd:annotation>
<xsd:documentation>
Search is carried out in the repository first. If not found, then the resource is queried.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ON_RESOURCE_IF_NEEDED"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="SearchObjectRefExpressionEvaluatorType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.security.api.SecurityEnforcer;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectSearchStrategyType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;

import org.apache.commons.lang.BooleanUtils;
Expand Down Expand Up @@ -205,40 +206,86 @@ protected Object extractCachingRelevantParams(ExpressionEvaluationContext params
private <O extends ObjectType> List<V> executeSearchUsingCache(Class<O> targetTypeClass,
final QName targetTypeQName, ObjectQuery query, final ExpressionEvaluationContext params, String contextDescription, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {

ObjectSearchStrategyType searchStrategy = getSearchStrategy();

if (!useCache) {
return executeSearch(targetTypeClass, targetTypeQName, query, params, contextDescription, result);
return executeSearch(targetTypeClass, targetTypeQName, query, searchStrategy, params, contextDescription, result);
}

AbstractSearchExpressionEvaluatorCache cache = AbstractSearchExpressionEvaluatorCache.getCache();
if (cache == null) {
LOGGER.trace("Caching is enabled but no cache is present.");
return executeSearch(targetTypeClass, targetTypeQName, query, params, contextDescription, result);
return executeSearch(targetTypeClass, targetTypeQName, query, searchStrategy, params, contextDescription, result);
}

boolean searchOnResource = BooleanUtils.isTrue(getExpressionEvaluatorType().isSearchOnResource());

Object qualifier = extractCachingRelevantParams(params);

List<V> list = cache.getQueryResult(targetTypeClass, query, searchOnResource, qualifier, prismContext);
List<V> list = cache.getQueryResult(targetTypeClass, query, searchStrategy, qualifier, prismContext);
if (list != null) {
LOGGER.trace("Cache: HIT {} ({})", query, targetTypeClass.getSimpleName());
return CloneUtil.clone(list);
}
LOGGER.trace("Cache: MISS {} ({})", query, targetTypeClass.getSimpleName());
list = executeSearch(targetTypeClass, targetTypeQName, query, params, contextDescription, result);
list = executeSearch(targetTypeClass, targetTypeQName, query, searchStrategy, params, contextDescription, result);
if (list != null && !list.isEmpty()) {
// we don't want to cache negative results (for future use with focal objects it might mean that they would be attempted to create multiple times)
cache.putQueryResult(targetTypeClass, query, searchOnResource, qualifier, CloneUtil.clone(list), prismContext);
cache.putQueryResult(targetTypeClass, query, searchStrategy, qualifier, CloneUtil.clone(list), prismContext);
}
return list;
}

private <O extends ObjectType> List<V> executeSearch(Class<O> targetTypeClass,
final QName targetTypeQName, ObjectQuery query, final ExpressionEvaluationContext params, String contextDescription, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
private ObjectSearchStrategyType getSearchStrategy() {
SearchObjectExpressionEvaluatorType evaluator = getExpressionEvaluatorType();
if (evaluator.getSearchStrategy() != null) {
return evaluator.getSearchStrategy();
}
if (BooleanUtils.isTrue(evaluator.isSearchOnResource())) {
return ObjectSearchStrategyType.ON_RESOURCE;
}
return ObjectSearchStrategyType.IN_REPOSITORY;
}

private <O extends ObjectType> List<V> executeSearch(Class<O> targetTypeClass, final QName targetTypeQName, ObjectQuery query,
ObjectSearchStrategyType searchStrategy,
ExpressionEvaluationContext params, String contextDescription,
OperationResult result)
throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {


// TODO think about handling of CommunicationException | ConfigurationException | SecurityViolationException
// Currently if tryAlsoRepository=true (for ON_RESOURCE strategy), such errors result in searching pure repo. And if there's no such
// object in the repo, probably no exception is raised.
// But if ON_RESOURCE_IF_NEEDED, and the object does not exist in repo, an exception WILL be raised.
//
// Probably we could create specific types of fetch strategies to reflect various error handling requirements.
// (Or treat it via separate parameter.)

switch (searchStrategy) {
case IN_REPOSITORY:
return executeSearchAttempt(targetTypeClass, targetTypeQName, query, false, false, params, contextDescription, result);
case ON_RESOURCE:
return executeSearchAttempt(targetTypeClass, targetTypeQName, query, true, true, params, contextDescription, result);
case ON_RESOURCE_IF_NEEDED:
List<V> inRepo = executeSearchAttempt(targetTypeClass, targetTypeQName, query, false, false, params, contextDescription, result);
if (!inRepo.isEmpty()) {
return inRepo;
}
return executeSearchAttempt(targetTypeClass, targetTypeQName, query, true, false, params, contextDescription, result);
default:
throw new IllegalArgumentException("Unknown search strategy: " + searchStrategy);
}
}

private <O extends ObjectType> List<V> executeSearchAttempt(Class<O> targetTypeClass, final QName targetTypeQName,
ObjectQuery query, boolean searchOnResource, boolean tryAlsoRepository,
final ExpressionEvaluationContext params, String contextDescription,
OperationResult result) throws ExpressionEvaluationException,
ObjectNotFoundException, SchemaException {

final List<V> list = new ArrayList<V>();

Collection<SelectorOptions<GetOperationOptions>> options = null;
if (!BooleanUtils.isTrue(getExpressionEvaluatorType().isSearchOnResource())) {
if (!searchOnResource) {
options = SelectorOptions.createCollection(GetOperationOptions.createNoFetch());
}

Expand All @@ -259,7 +306,7 @@ public boolean handle(PrismObject<O> object, OperationResult parentResult) {
throw new SchemaException(e.getMessage()+" in "+contextDescription, e);
} catch (CommunicationException | ConfigurationException
| SecurityViolationException e) {
if (BooleanUtils.isTrue(getExpressionEvaluatorType().isSearchOnResource())) {
if (searchOnResource && tryAlsoRepository) {
options = SelectorOptions.createCollection(GetOperationOptions.createNoFetch());
try {
objectResolver.searchIterative(targetTypeClass, query, options, handler, result);
Expand All @@ -273,10 +320,8 @@ public boolean handle(PrismObject<O> object, OperationResult parentResult) {
throw new ExpressionEvaluationException("Unexpected expression exception "+e+": "+e.getMessage(), e);
}
} else {
throw new ExpressionEvaluationException("Unexpected expression exception "+e+": "+e.getMessage(), e);
}


throw new ExpressionEvaluationException("Unexpected expression exception "+e+": "+e.getMessage(), e);
}
} catch (ObjectNotFoundException e) {
throw e;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectSearchStrategyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.prism.xml.ns._public.query_3.QueryType;

Expand Down Expand Up @@ -65,24 +66,24 @@ public String description() {
return "Q:"+queries.size();
}

public <T extends ObjectType> void putQueryResult(Class<T> type, ObjectQuery query, boolean searchOnResource, Object qualifier, List resultList, PrismContext prismContext) {
QueryKey queryKey = createQueryKey(type, query, searchOnResource, qualifier, prismContext);
public <T extends ObjectType> void putQueryResult(Class<T> type, ObjectQuery query, ObjectSearchStrategyType searchStrategy, Object qualifier, List resultList, PrismContext prismContext) {
QueryKey queryKey = createQueryKey(type, query, searchStrategy, qualifier, prismContext);
if (queryKey != null) { // TODO BRUTAL HACK
queries.put(queryKey, resultList);
}
}

private QueryKey createQueryKey(Class<? extends ObjectType> type, ObjectQuery query, boolean searchOnResource, Object qualifier, PrismContext prismContext) {
private QueryKey createQueryKey(Class<? extends ObjectType> type, ObjectQuery query, ObjectSearchStrategyType searchStrategy, Object qualifier, PrismContext prismContext) {
try {
return new QueryKey(type, query, searchOnResource, qualifier, prismContext);
return new QueryKey(type, query, searchStrategy, qualifier, prismContext);
} catch (Exception e) { // TODO THIS IS REALLY UGLY HACK - query converter / prism serializer refuse to serialize some queries - should be fixed RSN!
LoggingUtils.logException(LOGGER, "Couldn't create query key. Although this particular exception is harmless, please fix prism implementation!", e);
return null; // we "treat" it so that we simply pretend the entry is not in the cache and/or refuse to enter it into the cache
}
}

public List getQueryResult(Class<? extends ObjectType> type, ObjectQuery query, boolean searchOnResource, Object qualifier, PrismContext prismContext) {
QueryKey queryKey = createQueryKey(type, query, searchOnResource, qualifier, prismContext);
public List getQueryResult(Class<? extends ObjectType> type, ObjectQuery query, ObjectSearchStrategyType searchStrategy, Object qualifier, PrismContext prismContext) {
QueryKey queryKey = createQueryKey(type, query, searchStrategy, qualifier, prismContext);
if (queryKey != null) { // TODO BRUTAL HACK
return queries.get(queryKey);
} else {
Expand All @@ -94,17 +95,17 @@ public class QueryKey {

private Class<? extends ObjectType> type;
private QueryType query;
private boolean searchOnResource;
private ObjectSearchStrategyType searchStrategy;
private Object qualifier;

public <T extends ObjectType> QueryKey(Class<T> type, ObjectQuery query, boolean searchOnResource, Object qualifier, PrismContext prismContext) {
public <T extends ObjectType> QueryKey(Class<T> type, ObjectQuery query, ObjectSearchStrategyType searchStrategy, Object qualifier, PrismContext prismContext) {
this.type = type;
try {
this.query = query != null ? QueryJaxbConvertor.createQueryType(query, prismContext) : null;
} catch (SchemaException e) {
throw new SystemException(e);
}
this.searchOnResource = searchOnResource;
this.searchStrategy = searchStrategy;
this.qualifier = qualifier;
}

Expand All @@ -117,7 +118,7 @@ public boolean equals(Object o) {

if (query != null ? !query.equals(queryKey.query) : queryKey.query != null) return false;
if (type != null ? !type.equals(queryKey.type) : queryKey.type != null) return false;
if (searchOnResource != queryKey.searchOnResource) return false;
if (searchStrategy != null ? !searchStrategy.equals(queryKey.searchStrategy) : queryKey.searchStrategy != null) return false;
if (qualifier != null ? !qualifier.equals(queryKey.qualifier) : queryKey.qualifier != null) return false;

return true;
Expand All @@ -127,7 +128,7 @@ public boolean equals(Object o) {
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (query != null ? query.hashCode() : 0);
result = 31 * result + (searchOnResource ? 1 : 0);
result = 31 * result + (searchStrategy != null ? searchStrategy.hashCode() : 0);
result = 31 * result + (qualifier != null ? qualifier.hashCode() : 0);
return result;
}
Expand Down

0 comments on commit 40f8387

Please sign in to comment.