Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Evolveum/midpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Dec 2, 2014
2 parents c853bbb + 73b6685 commit a0576c7
Show file tree
Hide file tree
Showing 10 changed files with 653 additions and 43 deletions.
Expand Up @@ -833,6 +833,10 @@ public String getHumanReadableName() {
return getKind()+":"+getIntent();
}
}

public <T extends CapabilityType> T getEffectiveCapability(Class<T> capabilityClass) {
return ResourceTypeUtil.getEffectiveCapability(resourceType, schemaHandlingObjectTypeDefinitionType, capabilityClass);
}

public PagedSearchCapabilityType getPagedSearches() {
return ResourceTypeUtil.getEffectiveCapability(resourceType, schemaHandlingObjectTypeDefinitionType, PagedSearchCapabilityType.class);
Expand All @@ -842,7 +846,4 @@ public boolean isPagedSearchEnabled() {
return getPagedSearches() != null; // null means nothing or disabled
}

public CapabilityType getEffectiveCapability(Class<? extends CapabilityType> capabilityClass) {
return ResourceTypeUtil.getEffectiveCapability(resourceType, schemaHandlingObjectTypeDefinitionType, capabilityClass);
}
}
Expand Up @@ -451,6 +451,66 @@

<xsd:element name="pagedSearch" type="tns:PagedSearchCapabilityType"/>

<xsd:complexType name="CountObjectsCapabilityType">
<xsd:annotation>
<xsd:documentation>
Ability to efficiently count objects.
Although deals primarily with GUI issues, may be useful for other kinds of access as well.
EXPERIMENTAL.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="tns:CapabilityType">
<xsd:sequence>
<xsd:element name="simulate" type="tns:CountObjectsSimulateType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Strategy to simulate count operation using a different operation.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

<xsd:element name="countObjects" type="tns:CountObjectsCapabilityType"/>

<xsd:simpleType name="CountObjectsSimulateType">
<xsd:annotation>
<xsd:documentation>
Strategy to simulate count operation using a different operation.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumClass/>
</xsd:appinfo>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="pagedSearchEstimate">
<xsd:annotation>
<xsd:documentation>
Try a paged search with the page of one. Use the approximate number of entries
returned in search metadata as count approximation.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="PAGED_SEARCH_ESTIMATE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="sequentialSearch">
<xsd:annotation>
<xsd:documentation>
Execute the actual search and sequentially count the entries.
WARNING: this may be very inefficient.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="SEQUENTIAL_SEARCH"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="AddRemoveAttributeValuesCapabilityType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Expand Up @@ -445,7 +445,7 @@ <T extends ObjectType> SearchResultMetadata searchObjectsIterative(Class<T> type
* @param parentResult
* parent OperationResult (in/out)
* @return number of objects of specified type that match search criteria (subject
* to paging)
* to paging). May return null if the number of objects is not known.
*
* @throws SchemaException
* unknown property used in search query
Expand All @@ -461,7 +461,7 @@ <T extends ObjectType> SearchResultMetadata searchObjectsIterative(Class<T> type
* @throws IllegalArgumentException
* wrong query format
*/
<T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options,
<T extends ObjectType> Integer countObjects(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options,
Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, SecurityViolationException, ConfigurationException, CommunicationException;

Expand Down
Expand Up @@ -1149,7 +1149,7 @@ private void processSearchException(Exception e, GetOperationOptions rootOptions
}

@Override
public <T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query,
public <T extends ObjectType> Integer countObjects(Class<T> type, ObjectQuery query,
Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, ConfigurationException, SecurityViolationException, CommunicationException {

Expand All @@ -1167,7 +1167,7 @@ public <T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query,
return 0;
}

int count;
Integer count;
try {
GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options);

Expand Down
Expand Up @@ -231,7 +231,7 @@ public <T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(Cla
SecurityViolationException;


public <T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query, OperationResult parentResult)
public <T extends ObjectType> Integer countObjects(Class<T> type, ObjectQuery query, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
SecurityViolationException;

Expand Down
Expand Up @@ -27,8 +27,8 @@
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance;

import com.evolveum.midpoint.schema.RetrieveOption;

import org.apache.commons.lang.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
Expand Down Expand Up @@ -85,6 +85,8 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CountObjectsCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CountObjectsSimulateType;

/**
* Implementation of provisioning service.
Expand Down Expand Up @@ -697,7 +699,7 @@ private <T extends ObjectType> PrismObject<T> completeObject(Class<T> type, Pris

}

public <T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query, OperationResult parentResult)
public <T extends ObjectType> Integer countObjects(Class<T> type, ObjectQuery query, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
SecurityViolationException {

Expand All @@ -721,42 +723,106 @@ public <T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query,

RefinedResourceSchema refinedSchema = RefinedResourceSchema.getRefinedSchema(resourceType);
if (refinedSchema == null) {
throw new ConfigurationException("No schema for "+resourceType);
ConfigurationException e = new ConfigurationException("No schema for "+resourceType);
result.recordFatalError(e);
throw e;
}
final RefinedObjectClassDefinition objectClassDef = refinedSchema.getRefinedDefinition(objectClassName);

if (!objectClassDef.isPagedSearchEnabled()) {
// traditional way of counting objects (i.e. counting them one by one)
final Holder<Integer> countHolder = new Holder<Integer>(0);

final ResultHandler<T> handler = new ResultHandler<T>() {
@Override
public boolean handle(PrismObject<T> object, OperationResult parentResult) {
int count = countHolder.getValue();
count++;
countHolder.setValue(count);
return true;
CountObjectsCapabilityType countObjectsCapabilityType = objectClassDef.getEffectiveCapability(CountObjectsCapabilityType.class);
if (countObjectsCapabilityType == null) {
// Unable to count. Return null which means "I do not know"
result.recordNotApplicableIfUnknown();
return null;
} else {
CountObjectsSimulateType simulate = countObjectsCapabilityType.getSimulate();
if (simulate == null) {
// We have native capability

// TODO shouldn't this be moved into ShadowCache?
ConnectorInstance connector = getShadowCache(Mode.STANDARD).getConnectorInstance(resourceType, parentResult);
try {
ObjectQuery attributeQuery = getShadowCache(Mode.STANDARD).createAttributeQuery(query);
int count;
try {
count = connector.count(objectClassDef.getObjectClassDefinition(), attributeQuery, objectClassDef.getPagedSearches(), result);
} catch (CommunicationException | GenericFrameworkException| SchemaException | UnsupportedOperationException e) {
result.recordFatalError(e);
throw e;
}
result.computeStatus();
result.cleanupResult();
return count;
} catch (GenericFrameworkException|UnsupportedOperationException e) {
SystemException ex = new SystemException("Couldn't count objects on resource " + resourceType + ": " + e.getMessage(), e);
result.recordFatalError(ex);
throw ex;
}
};

Collection<SelectorOptions<GetOperationOptions>> options =
SelectorOptions.createCollection(new ItemPath(ShadowType.F_ASSOCIATION), GetOperationOptions.createRetrieve(RetrieveOption.EXCLUDE));
searchObjectsIterativeInternal(type, query, options, handler, false, result);
// TODO: better error handling
result.computeStatus();
result.cleanupResult();
return countHolder.getValue();
}

// counting objects using paged search
// TODO shouldn't this be moved into ShadowCache?
ConnectorInstance connector = getShadowCache(Mode.STANDARD).getConnectorInstance(resourceType, parentResult);
try {
ObjectQuery attributeQuery = getShadowCache(Mode.STANDARD).createAttributeQuery(query);
return connector.count(objectClassDef.getObjectClassDefinition(), attributeQuery, objectClassDef.getPagedSearches(), result);
} catch (GenericFrameworkException|UnsupportedOperationException e) {
throw new SystemException("Couldn't count objects on resource " + resourceType + ": " + e.getMessage(), e);

} else if (simulate == CountObjectsSimulateType.PAGED_SEARCH_ESTIMATE) {

if (!objectClassDef.isPagedSearchEnabled()) {
throw new ConfigurationException("Configured count object capability to be simulated using a paged search but paged search capability is not present");
}

final Holder<Integer> countHolder = new Holder<Integer>(0);

final ResultHandler<T> handler = new ResultHandler<T>() {
@Override
public boolean handle(PrismObject<T> object, OperationResult parentResult) {
int count = countHolder.getValue();
count++;
countHolder.setValue(count);
return true;
}
};

query = query.clone();
ObjectPaging paging = ObjectPaging.createEmptyPaging();
paging.setMaxSize(1);
query.setPaging(paging);
Collection<SelectorOptions<GetOperationOptions>> options =
SelectorOptions.createCollection(new ItemPath(ShadowType.F_ASSOCIATION), GetOperationOptions.createRetrieve(RetrieveOption.EXCLUDE));
SearchResultMetadata resultMetadata;
try {
resultMetadata = searchObjectsIterativeInternal(type, query, options, handler, false, result);
} catch (SchemaException | ObjectNotFoundException | ConfigurationException | SecurityViolationException e) {
result.recordFatalError(e);
throw e;
}
result.computeStatus();
result.cleanupResult();

return resultMetadata.getApproxNumberOfAllResults();

} else if (simulate == CountObjectsSimulateType.SEQUENTIAL_SEARCH) {

// traditional way of counting objects (i.e. counting them one by one)
final Holder<Integer> countHolder = new Holder<Integer>(0);

final ResultHandler<T> handler = new ResultHandler<T>() {
@Override
public boolean handle(PrismObject<T> object, OperationResult parentResult) {
int count = countHolder.getValue();
count++;
countHolder.setValue(count);
return true;
}
};

Collection<SelectorOptions<GetOperationOptions>> options =
SelectorOptions.createCollection(new ItemPath(ShadowType.F_ASSOCIATION), GetOperationOptions.createRetrieve(RetrieveOption.EXCLUDE));
searchObjectsIterativeInternal(type, query, options, handler, false, result);
// TODO: better error handling
result.computeStatus();
result.cleanupResult();
return countHolder.getValue();

} else {
throw new IllegalArgumentException("Unknown count capability simulate type "+simulate);
}
}

}

@SuppressWarnings("rawtypes")
Expand Down
Expand Up @@ -1371,6 +1371,27 @@ public void test230SearchObjectsPaged() throws Exception {
}
}

@Test
public void test250CountObjects() throws Exception {
final String TEST_NAME = "test250CountObjects";
TestUtil.displayTestTile(TEST_NAME);

OperationResult result = new OperationResult(TestOpenDJ.class.getName() + "." + TEST_NAME);

QueryType queryType = PrismTestUtil.parseAtomicValue(new File("src/test/resources/impl/query-filter-all-accounts.xml"),
QueryType.COMPLEX_TYPE);
ObjectQuery query = QueryJaxbConvertor.createObjectQuery(ShadowType.class, queryType, prismContext);

// WHEN
Integer count = provisioningService.countObjects(ShadowType.class, query, result);

// THEN
result.computeStatus();
assertSuccess(result);

assertEquals("Unexpected number of search results", (Integer)14, count);
}

/**
* The exception comes from the resource. There is no shadow for this object.
*/
Expand Down
Expand Up @@ -107,6 +107,9 @@
<cap:pagedSearch>
<cap:defaultSortField>ri:uid</cap:defaultSortField>
</cap:pagedSearch>
<cap:countObjects>
<cap:simulate>sequentialSearch</cap:simulate>
</cap:countObjects>
</configuredCapabilities>
</objectType>

Expand Down

0 comments on commit a0576c7

Please sign in to comment.