/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2010-2014 ForgeRock AS. */ package org.identityconnectors.framework.impl.api.local.operations; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.identityconnectors.common.Assertions; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.api.ResultsHandlerConfiguration; import org.identityconnectors.framework.api.operations.SearchApiOp; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.SearchResult; import org.identityconnectors.framework.common.objects.filter.Filter; import org.identityconnectors.framework.common.objects.filter.FilterTranslator; import org.identityconnectors.framework.spi.Connector; import org.identityconnectors.framework.spi.SearchResultsHandler; import org.identityconnectors.framework.spi.operations.SearchOp; public class SearchImpl extends ConnectorAPIOperationRunner implements SearchApiOp { private static final Log logger = Log.getLog(SearchImpl.class); /** * Initializes the operation works. */ public SearchImpl(final ConnectorOperationalContext context, final Connector connector) { super(context, connector); } /** * Call the SPI search routines to return the results to the * {@link ResultsHandler}. * * @see SearchOp#executeQuery(org.identityconnectors.framework.common.objects.ObjectClass, * Object, * org.identityconnectors.framework.common.objects.ResultsHandler, * org.identityconnectors.framework.common.objects.OperationOptions) */ public SearchResult search(ObjectClass objectClass, Filter originalFilter, ResultsHandler handler, OperationOptions options) { Assertions.nullCheck(objectClass, "objectClass"); if (ObjectClass.ALL.equals(objectClass)) { throw new UnsupportedOperationException( "Operation is not allowed on __ALL__ object class"); } Assertions.nullCheck(handler, "handler"); // cast null as empty if (options == null) { options = new OperationOptionsBuilder().build(); } SearchOp<?> search = ((SearchOp<?>) getConnector()); ResultsHandlerConfiguration hdlCfg = null != getOperationalContext() ? getOperationalContext() .getResultsHandlerConfiguration() : new ResultsHandlerConfiguration(); Filter actualFilter = originalFilter; // actualFilter is used for chaining filters - it points to the filter where new filters should be chained if (hdlCfg.isEnableFilteredResultsHandler() && hdlCfg.isEnableCaseInsensitiveFilter() && actualFilter != null) { logger.ok("Creating case insensitive filter"); ObjectNormalizerFacade normalizer = new ObjectNormalizerFacade(objectClass, new CaseNormalizer()); actualFilter = new NormalizingFilter(actualFilter, normalizer); } if (hdlCfg.isEnableNormalizingResultsHandler()) { final ObjectNormalizerFacade normalizer = getNormalizer(objectClass); // chain a normalizing handler (must come before // filter handler) NormalizingResultsHandler normalizingHandler = new NormalizingResultsHandler(handler, normalizer); // chain a filter handler.. if (hdlCfg.isEnableFilteredResultsHandler()) { // chain a filter handler.. final Filter normalizedFilter = normalizer.normalizeFilter(actualFilter); handler = new FilteredResultsHandler(normalizingHandler, normalizedFilter); actualFilter = normalizedFilter; } else { handler = normalizingHandler; } } else if (hdlCfg.isEnableFilteredResultsHandler()) { // chain a filter handler.. handler = new FilteredResultsHandler(handler, actualFilter); } // chain an attributes to get handler.. String[] attrsToGet = options.getAttributesToGet(); if (attrsToGet != null && attrsToGet.length > 0 && hdlCfg.isEnableAttributesToGetSearchResultsHandler()) { handler = getAttributesToGetResutlsHandler(handler, options); } final ResultsHandler handlerChain = handler; final AtomicReference<SearchResult> result = new AtomicReference<SearchResult>(null); rawSearch(search, objectClass, actualFilter, new SearchResultsHandler() { public void handleResult(final SearchResult searchResult) { result.set(searchResult); } public boolean handle(ConnectorObject connectorObject) { return handlerChain.handle(connectorObject); } }, options); return result.get(); } /** * Public because it is used by TestHelpersImpl. Raw, SPI-level search. * * @param search * The underlying implementation of search (generally the * connector itself) * @param objectClass * The object class * @param filter * The filter * @param handler * The handler * @param options * The options */ public static void rawSearch(SearchOp<?> search, ObjectClass objectClass, Filter filter, SearchResultsHandler handler, OperationOptions options) { FilterTranslator<?> translator = search.createFilterTranslator(objectClass, options); List<?> queries = translator.translate(filter); if (queries.size() == 0) { search.executeQuery(objectClass, null, handler, options); } else { // eliminate dups if more than one boolean eliminateDups = queries.size() > 1; if (eliminateDups) { handler = new DuplicateFilteringResultsHandler(handler); } for (Object query : queries) { @SuppressWarnings("unchecked") SearchOp<Object> hack = (SearchOp<Object>) search; hack.executeQuery(objectClass, query, handler, options); // don't run any more queries if the consumer // has stopped if (handler instanceof DuplicateFilteringResultsHandler) { DuplicateFilteringResultsHandler h = (DuplicateFilteringResultsHandler) handler; if (!h.isStillHandling()) { break; } } } } } private ResultsHandler getAttributesToGetResutlsHandler(ResultsHandler handler, OperationOptions options) { ResultsHandler ret = handler; String[] attrsToGet = options.getAttributesToGet(); if (attrsToGet != null && attrsToGet.length > 0) { ret = new AttributesToGetSearchResultsHandler(handler, attrsToGet); } return ret; } /** * Simple results handler that can reduce attributes to only the set of * attribute to get. * */ public static class AttributesToGetSearchResultsHandler extends AttributesToGetResultsHandler implements ResultsHandler { private final ResultsHandler handler; public AttributesToGetSearchResultsHandler(final ResultsHandler handler, String[] attrsToGet) { super(attrsToGet); Assertions.nullCheck(handler, "handler"); this.handler = handler; } /** * Handle the object w/ reduced attributes. */ public boolean handle(ConnectorObject obj) { obj = reduceToAttrsToGet(obj); return handler.handle(obj); } } }