/* * Copyright (c) 2010-2016 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.web.component.data; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.Validate; import org.apache.wicket.Component; import org.apache.wicket.RestartResponseException; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.query.ObjectPaging; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; 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.web.component.util.SelectableBean; import com.evolveum.midpoint.web.page.error.PageError; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; /** * @author lazyman * @author semancik */ public class SelectableBeanObjectDataProvider<O extends ObjectType> extends BaseSortableDataProvider<SelectableBean<O>> { private static final long serialVersionUID = 1L; private static final Trace LOGGER = TraceManager.getTrace(SelectableBeanObjectDataProvider.class); private static final String DOT_CLASS = SelectableBeanObjectDataProvider.class.getName() + "."; private static final String OPERATION_SEARCH_OBJECTS = DOT_CLASS + "searchObjects"; private static final String OPERATION_COUNT_OBJECTS = DOT_CLASS + "countObjects"; private Set<? extends O> selected = new HashSet<>(); private boolean emptyListOnNullQuery = false; private boolean useObjectCounting = true; /** * The number of all objects that the query can return. Defaults to a really big number * if we cannot count the number of objects. * TODO: make this default more reasonable. -1 or something like that (MID-3339) */ // private int size = Integer.MAX_VALUE; private Class<? extends O> type; private Collection<SelectorOptions<GetOperationOptions>> options; public SelectableBeanObjectDataProvider(Component component, Class<? extends O> type, Set<? extends O> selected ) { super(component, true, true); Validate.notNull(type); if (selected != null) { this.selected = selected; } this.type = type; } public void clearSelectedObjects(){ selected.clear(); } public List<O> getSelectedData() { preprocessSelectedDataInternal(); for (SelectableBean<O> selectable : super.getAvailableData()) { if (selectable.isSelected() && selectable.getValue() != null) { ((Set)selected).add(selectable.getValue()); } } List<O> allSelected = new ArrayList<>(); allSelected.addAll(selected); return allSelected; } private void preprocessSelectedData() { preprocessSelectedDataInternal(); getAvailableData().clear(); } private void preprocessSelectedDataInternal() { for (SelectableBean<O> available : getAvailableData()) { if (available.isSelected() && available.getValue() != null) { ((Set)selected).add(available.getValue()); } } for (SelectableBean<O> available : getAvailableData()) { if (!available.isSelected()) { if (selected.contains(available.getValue())) { selected.remove(available.getValue()); } } } } @Override public Iterator<SelectableBean<O>> internalIterator(long offset, long pageSize) { LOGGER.trace("begin::iterator() offset {} pageSize {}.", new Object[]{offset, pageSize}); // if (pageSize > 1000000) { // // Failsafe. Do not even try this. This can have huge impact on the resource. (MID-3336) // throw new IllegalArgumentException("Requested huge page size: "+pageSize); // } preprocessSelectedData(); OperationResult result = new OperationResult(OPERATION_SEARCH_OBJECTS); try { ObjectPaging paging = createPaging(offset, pageSize); Task task = getPage().createSimpleTask(OPERATION_SEARCH_OBJECTS); ObjectQuery query = getQuery(); if (query == null){ if (emptyListOnNullQuery) { return new ArrayList<SelectableBean<O>>().iterator(); } query = new ObjectQuery(); } query.setPaging(paging); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Query {} with {}", type.getSimpleName(), query.debugDump()); } if (ResourceType.class.equals(type) && (options == null || options.isEmpty())){ options = SelectorOptions.createCollection(GetOperationOptions.createNoFetch()); } List<PrismObject<? extends O>> list = (List)getModel().searchObjects(type, query, options, task, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Query {} resulted in {} objects", type.getSimpleName(), list.size()); } for (PrismObject<? extends O> object : list) { getAvailableData().add(createDataObjectWrapper(object.asObjectable())); } // result.recordSuccess(); } catch (Exception ex) { result.recordFatalError("Couldn't list objects.", ex); LoggingUtils.logUnexpectedException(LOGGER, "Couldn't list objects", ex); return handleNotSuccessOrHandledErrorInIterator(result); } finally { result.computeStatusIfUnknown(); } LOGGER.trace("end::iterator() {}", result); return getAvailableData().iterator(); } protected Iterator<SelectableBean<O>> handleNotSuccessOrHandledErrorInIterator(OperationResult result) { LOGGER.trace("handling non-success result {}", result); // page.showResult() will not work here. We are too deep in the rendering now. // Also do NOT re-throw not redirect to to error page. That will break the page. // Just return a SelectableBean that indicates the error. List<SelectableBean<O>> errorList = new ArrayList<>(1); SelectableBean<O> bean = new SelectableBean<>(); bean.setResult(result); errorList.add(bean); return errorList.iterator(); } public SelectableBean<O> createDataObjectWrapper(O obj) { SelectableBean<O> selectable = new SelectableBean<O>(obj); if (!WebComponentUtil.isSuccessOrHandledError(obj.getFetchResult())){ selectable.setResult(obj.getFetchResult()); } for (O s : selected){ if (s.getOid().equals(obj.getOid())){ selectable.setSelected(true); } } return selectable; } @Override protected int internalSize() { LOGGER.trace("begin::internalSize()"); if (!isUseObjectCounting()) { return Integer.MAX_VALUE; } int count = 0; OperationResult result = new OperationResult(OPERATION_COUNT_OBJECTS); try { Task task = getPage().createSimpleTask(OPERATION_COUNT_OBJECTS); Integer counted = getModel().countObjects(type, getQuery(), options, task, result); count = counted == null ? 0 : counted.intValue(); } catch (Exception ex) { result.recordFatalError("Couldn't count objects.", ex); LoggingUtils.logUnexpectedException(LOGGER, "Couldn't count objects", ex); } finally { result.computeStatusIfUnknown(); } if (!WebComponentUtil.isSuccessOrHandledError(result)) { getPage().showResult(result); throw new RestartResponseException(PageError.class); } LOGGER.trace("end::internalSize(): {}", count); return count; } @Override protected CachedSize getCachedSize(Map<Serializable, CachedSize> cache) { return cache.get(new TypedCacheKey(getQuery(), type)); } @Override protected void addCachedSize(Map<Serializable, CachedSize> cache, CachedSize newSize) { cache.put(new TypedCacheKey(getQuery(), type), newSize); } public void setType(Class<O> type) { Validate.notNull(type, "Class must not be null."); this.type = type; clearCache(); } public boolean isUseObjectCounting(){ return useObjectCounting; } public void setUseObjectCounting(boolean useCounting) { this.useObjectCounting = useCounting; } public Collection<SelectorOptions<GetOperationOptions>> getOptions() { return options; } public void setOptions(Collection<SelectorOptions<GetOperationOptions>> options) { this.options = options; } public boolean isEmptyListOnNullQuery() { return emptyListOnNullQuery; } public void setEmptyListOnNullQuery(boolean emptyListOnNullQuery) { this.emptyListOnNullQuery = emptyListOnNullQuery; } // public int getSize() { // return size; // } // // public void setSize(int size) { // this.size = size; // } }