Skip to content

Commit

Permalink
Support for paged searches on resources.
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Oct 22, 2014
1 parent 6685fa2 commit d2e4890
Show file tree
Hide file tree
Showing 12 changed files with 577 additions and 178 deletions.
Expand Up @@ -58,15 +58,21 @@ public abstract class BaseSortableDataProvider<T extends Serializable> extends S
private boolean useCache;

public BaseSortableDataProvider(Component component) {
this(component, false);
this(component, false, true);
}

public BaseSortableDataProvider(Component component, boolean useCache) {
this(component, useCache, true);
}

public BaseSortableDataProvider(Component component, boolean useCache, boolean useDefaultSortingField) {
Validate.notNull(component, "Component must not be null.");
this.component = component;
this.useCache = useCache;

setSort("name", SortOrder.ASCENDING);
if (useDefaultSortingField) {
setSort("name", SortOrder.ASCENDING);
}
}

protected ModelService getModel() {
Expand Down Expand Up @@ -139,15 +145,19 @@ public boolean isSizeAvailable() {

protected ObjectPaging createPaging(long first, long count) {
SortParam sortParam = getSort();
OrderDirection order;
if (sortParam.isAscending()) {
order = OrderDirection.ASCENDING;
if (sortParam != null) {
OrderDirection order;
if (sortParam.isAscending()) {
order = OrderDirection.ASCENDING;
} else {
order = OrderDirection.DESCENDING;
}

return ObjectPaging.createPaging(WebMiscUtil.safeLongToInteger(first), WebMiscUtil.safeLongToInteger(count),
(String) sortParam.getProperty(), SchemaConstantsGenerated.NS_COMMON, order);
} else {
order = OrderDirection.DESCENDING;
return ObjectPaging.createPaging(WebMiscUtil.safeLongToInteger(first), WebMiscUtil.safeLongToInteger(count));
}

return ObjectPaging.createPaging(WebMiscUtil.safeLongToInteger(first), WebMiscUtil.safeLongToInteger(count),
(String) sortParam.getProperty(), SchemaConstantsGenerated.NS_COMMON, order);
}

public void clearCache() {
Expand Down
Expand Up @@ -16,6 +16,7 @@

package com.evolveum.midpoint.web.page.admin.resources.content;

import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchema;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.ChangeType;
Expand Down Expand Up @@ -228,7 +229,7 @@ protected void clearSearchPerformed(AjaxRequestTarget target) {
add(mainForm);

AccountContentDataProvider provider = new AccountContentDataProvider(this,
new PropertyModel<String>(resourceModel, "oid"), createObjectClassModel()) {
new PropertyModel<String>(resourceModel, "oid"), createObjectClassModel(), createUseConnectorPagingModel()) {

@Override
protected void addInlineMenuToDto(AccountContentDto dto) {
Expand Down Expand Up @@ -600,6 +601,21 @@ protected QName load() {
};
}

private IModel<Boolean> createUseConnectorPagingModel() {
return new LoadableModel<Boolean>(false) {

@Override
protected Boolean load() {
try {
return isUseConnectorPaging();
} catch (Exception ex) {
throw new SystemException(ex.getMessage(), ex);
}
}
};
}


private QName getObjectClassDefinition() throws SchemaException {
ObjectClassComplexTypeDefinition def = getAccountDefinition();
return def != null ? def.getTypeName() : null;
Expand All @@ -621,6 +637,26 @@ private ObjectClassComplexTypeDefinition getAccountDefinition() throws SchemaExc
return null;
}

private boolean isUseConnectorPaging() throws SchemaException {
MidPointApplication application = (MidPointApplication) getApplication();
PrismObject<ResourceType> resource = resourceModel.getObject();
RefinedResourceSchema resourceSchema = RefinedResourceSchema.getRefinedSchema(resource, application.getPrismContext());

// hacking this for now ... in future, we get the type definition (and maybe kind+intent) directly from GUI model
// TODO here we should deal with the situation that one object class is mentioned in different
// kind/intent sections -- we would want to avoid mentioning paged search information in all
// these sections
ObjectClassComplexTypeDefinition typeDefinition = getAccountDefinition();
if (typeDefinition == null) {
// should not occur
LOGGER.warn("ObjectClass definition couldn't be found");
return false;
}

RefinedObjectClassDefinition refinedObjectClassDefinition = resourceSchema.getRefinedDefinition(typeDefinition.getTypeName());
return refinedObjectClassDefinition.isPagedSearchEnabled();
}

private void showModalWindow(String id, AjaxRequestTarget target) {
ModalWindow window = (ModalWindow) get(id);
window.show(target);
Expand Down
Expand Up @@ -29,6 +29,8 @@
import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
Expand Down Expand Up @@ -63,42 +65,36 @@ public class AccountContentDataProvider extends BaseSortableDataProvider<Account
private static final String DOT_CLASS = AccountContentDataProvider.class.getName() + ".";
private static final String OPERATION_LOAD_ACCOUNTS = DOT_CLASS + "loadAccounts";
private static final String OPERATION_LOAD_OWNER = DOT_CLASS + "loadOwner";
private static final String OPERATION_COUNT_ACCOUNTS = DOT_CLASS + "countAccounts";

private IModel<String> resourceOid;
private IModel<QName> objectClass;
private IModel<Boolean> useConnectorPagingModel;
private Integer cachedSize;

public AccountContentDataProvider(Component component, IModel<String> resourceOid, IModel<QName> objectClass) {
super(component);
public AccountContentDataProvider(Component component, IModel<String> resourceOid, IModel<QName> objectClass, IModel<Boolean> useConnectorPagingModel) {
super(component, false, false); // don't use cache, don't use default sorting field (c:name)

Validate.notNull(resourceOid, "Resource oid model must not be null.");
Validate.notNull(objectClass, "Object class model must not be null.");
Validate.notNull(useConnectorPagingModel, "'Use connector paging' model must not be null.");
this.resourceOid = resourceOid;
this.objectClass = objectClass;
this.useConnectorPagingModel = useConnectorPagingModel;
}

@Override
public Iterator<AccountContentDto> internalIterator(long first, long count) {
LOGGER.trace("begin::iterator() from {} count {}.", new Object[]{first, count});
boolean useConnectorPaging = isUseConnectorPaging();
LOGGER.trace("begin::iterator() from {} count {} useConnectorPaging {}.", new Object[]{first, count, useConnectorPaging});
getAvailableData().clear();

OperationResult result = new OperationResult(OPERATION_LOAD_ACCOUNTS);
try {
ObjectPaging paging = createPaging(first, count);
Task task = getPage().createSimpleTask(OPERATION_LOAD_ACCOUNTS);

ObjectQuery baseQuery = ObjectQueryUtil.createResourceAndAccountQuery(resourceOid.getObject(),
objectClass.getObject(), getPage().getPrismContext());
ObjectQuery query = getQuery();
if (query != null) {
ObjectFilter baseFilter = baseQuery.getFilter();
ObjectFilter filter = query.getFilter();

query = new ObjectQuery();
ObjectFilter andFilter = AndFilter.createAnd(baseFilter, filter);
query.setFilter(andFilter);
} else {
query = baseQuery;
}
ObjectPaging paging = createPaging(first, count);
ObjectQuery query = getObjectQuery();
query.setPaging(paging);

if (LOGGER.isTraceEnabled()) {
Expand Down Expand Up @@ -130,9 +126,72 @@ public Iterator<AccountContentDto> internalIterator(long first, long count) {
return getAvailableData().iterator();
}

private boolean isUseConnectorPaging() {
return Boolean.TRUE.equals(useConnectorPagingModel.getObject());
}

private ObjectQuery getObjectQuery() throws SchemaException {
ObjectQuery baseQuery = ObjectQueryUtil.createResourceAndAccountQuery(resourceOid.getObject(),
objectClass.getObject(), getPage().getPrismContext());
ObjectQuery query = getQuery();
if (query != null) {
ObjectFilter baseFilter = baseQuery.getFilter();
ObjectFilter filter = query.getFilter();

query = new ObjectQuery();
ObjectFilter andFilter = AndFilter.createAnd(baseFilter, filter);
query.setFilter(andFilter);
} else {
query = baseQuery;
}
return query;
}

@Override
public void setQuery(ObjectQuery query) {
super.setQuery(query);
cachedSize = null;
}

@Override
protected int internalSize() {
return Integer.MAX_VALUE;
if (!isUseConnectorPaging()) {
return Integer.MAX_VALUE;
}
if (cachedSize != null) {
LOGGER.trace("begin::internalSize() returning cached size of {}", cachedSize);
return cachedSize;
}

LOGGER.trace("begin::internalSize() useConnectorPaging is TRUE.");

OperationResult result = new OperationResult(OPERATION_COUNT_ACCOUNTS);
Task task = getPage().createSimpleTask(OPERATION_COUNT_ACCOUNTS);

int retval = Integer.MAX_VALUE; // default in case of problems

ObjectQuery query;
try {
query = getObjectQuery();
} catch (SchemaException e) {
LoggingUtils.logException(LOGGER, "Couldn't create object query for counting resource objects - using count of {} instead", e, retval);
return retval;
}

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Query filter for object counting:\n{}", query);
}

try {
retval = getModel().countObjects(ShadowType.class, query, null, task, result);
cachedSize = retval;
} catch (SchemaException|ObjectNotFoundException|SecurityViolationException|ConfigurationException|CommunicationException|RuntimeException e) {
LoggingUtils.logException(LOGGER, "Couldn't count resource objects - using count of {} instead", e, retval);
return retval;
}

LOGGER.trace("end::internalSize() retval = {}", retval);
return retval;
}

private AccountContentDto createAccountContentDto(PrismObject<ShadowType> object, OperationResult result)
Expand Down
Expand Up @@ -834,4 +834,21 @@ public String getHumanReadableName() {
}
}

public ResourcePagedSearchConfigurationType getPagedSearches() {
if (schemaHandlingObjectTypeDefinitionType != null) {
return schemaHandlingObjectTypeDefinitionType.getPagedSearches();
} else {
return null;
}
}

public boolean isPagedSearchEnabled() {
return schemaHandlingObjectTypeDefinitionType != null
&& isPagedSearchEnabled(schemaHandlingObjectTypeDefinitionType.getPagedSearches());
}

public static boolean isPagedSearchEnabled(ResourcePagedSearchConfigurationType configuration) {
return configuration != null
&& !Boolean.FALSE.equals(configuration.isEnabled()); // because default is TRUE (if whole element is defined)
}
}
Expand Up @@ -16,9 +16,20 @@

package com.evolveum.midpoint.prism.query;

import com.evolveum.prism.xml.ns._public.query_3.OrderDirectionType;

public enum OrderDirection {

ASCENDING,
DESCENDING;

public static OrderDirection fromOrderDirectionType(OrderDirectionType orderDirectionType) {
switch (orderDirectionType) {
case DESCENDING: return DESCENDING;
case ASCENDING:
default:
return ASCENDING;
}
}

}
Expand Up @@ -3318,9 +3318,55 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="pagedSearches" minOccurs="0" type="tns:ResourcePagedSearchConfigurationType">
<xsd:annotation>
<xsd:documentation>
How to handle paged search for this account type.
Although deals primarily with GUI issues, may be useful for other kinds of access as well.
EXPERIMENTAL.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>


<xsd:complexType name="ResourcePagedSearchConfigurationType">
<xsd:annotation>
<xsd:documentation>
How to handle paged search for this account type.
Although deals primarily with GUI issues, may be useful for other kinds of access as well.

If not present, paged searches are not enabled for this resource/kind/intent/objectClass.

EXPERIMENTAL.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="enabled" type="xsd:boolean" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Is the paged search enabled? Default is TRUE (if the pagedSearchConfiguration is present).
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="defaultSortField" type="xsd:QName" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
If no sorting is specified by client (higher levels), use this field as the default.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="defaultSortDirection" type="q:OrderDirectionType" minOccurs="0" >
<xsd:annotation>
<xsd:documentation>
If the defaultSortField is used, this element specified the sorting order
(ascending is the default).
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="ResourceConsistencyType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Expand Up @@ -236,7 +236,7 @@ public boolean handle(PrismObject<ShadowType> entitlementShadow) {
LOGGER.trace("Processed entitlement-to-subject association for account {}: query {}",
ShadowUtil.getHumanReadableName(resourceObject), query);
}
connector.search(entitlementDef, query, handler, attributesToReturn, parentResult);
connector.search(entitlementDef, query, handler, attributesToReturn, null, parentResult);
} catch (TunnelException e) {
throw (SchemaException)e.getCause();
}
Expand Down Expand Up @@ -432,7 +432,7 @@ public boolean handle(PrismObject<ShadowType> entitlementShadow) {
};
try {
LOGGER.trace("Searching for associations in deleted shadow, query: {}", query);
connector.search(entitlementOcDef, query, handler, attributesToReturn, parentResult);
connector.search(entitlementOcDef, query, handler, attributesToReturn, null, parentResult);
} catch (TunnelException e) {
throw (SchemaException)e.getCause();
} catch (GenericFrameworkException e) {
Expand Down

0 comments on commit d2e4890

Please sign in to comment.