org.paxle.se.search.impl.SearchProviderManager.java Source code

Java tutorial

Introduction

Here is the source code for org.paxle.se.search.impl.SearchProviderManager.java

Source

/**
 * This file is part of the Paxle project.
 * Visit http://www.paxle.net for more information.
 * Copyright 2007-2010 the original author or authors.
 *
 * Licensed under the terms of the Common Public License 1.0 ("CPL 1.0").
 * Any use, reproduction or distribution of this program constitutes the recipient's acceptance of this agreement.
 * The full license text is available under http://www.opensource.org/licenses/cpl1.0.txt
 * or in the file LICENSE.txt in the root directory of the Paxle distribution.
 *
 * Unless required by applicable law or agreed to in writing, this software is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

package org.paxle.se.search.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.paxle.se.query.PaxleQueryParser;
import org.paxle.se.query.tokens.AToken;
import org.paxle.se.search.ISearchProvider;
import org.paxle.se.search.ISearchProviderManager;
import org.paxle.se.search.ISearchRequest;
import org.paxle.se.search.ISearchResult;
import org.paxle.se.search.ISearchResultCollector;
import org.paxle.se.search.SearchException;

@Component
@Service(ISearchProviderManager.class)
@Reference(name = "searchProviders", referenceInterface = ISearchProvider.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC, bind = "addProvider", unbind = "removeProvider", target = "(service.pid=*)")
public class SearchProviderManager implements ISearchProviderManager {
    private final AtomicInteger requestIdGenerator = new AtomicInteger(0);

    /**
     * Thread pool service
     */
    private ExecutorService execService;

    /**
     * The context of this component
     */
    protected ComponentContext ctx;

    /**
     * A list of all currently registered {@link ISearchProvider search-providers}
     */
    private final LinkedHashMap<String, ServiceReference> providersRefs = new LinkedHashMap<String, ServiceReference>();

    /**
     * for logging
     */
    private final Log logger = LogFactory.getLog(SearchProviderManager.class);

    @Activate
    protected void activate(ComponentContext context) {
        this.ctx = context;
        this.execService = Executors.newCachedThreadPool();
    }

    @Deactivate
    protected void deactivate(ComponentContext context) {
        this.logger.info("Search provider manager going to be deactivated now ...");
        this.logger.debug("Waiting for searches to finish");
        this.execService.shutdown();
        this.logger.debug("Searches finished, cleaning up...");
        this.logger.info("shutdown complete");
    }

    protected void addProvider(ServiceReference providerRef) {
        String providerPID = (String) providerRef.getProperty(Constants.SERVICE_PID);

        this.providersRefs.put(providerPID, providerRef);
        this.logger.info(String.format("Search provider with PID '%s' from bundle '%s' registered.", providerPID,
                providerRef.getBundle().getSymbolicName()));
    }

    protected void removeProvider(ServiceReference providerRef) {
        String providerPID = (String) providerRef.getProperty(Constants.SERVICE_PID);
        this.providersRefs.remove(providerPID);

        this.logger.info(String.format("Search provider with PID '%s' from bundle %s unregistered.", providerPID,
                providerRef.getBundle().getSymbolicName()));
    }

    /**
     * @see ISearchProviderManager#getSearchProviders()
     */
    public Map<String, ISearchProvider> getSearchProviders() {
        final HashMap<String, ISearchProvider> providers = new HashMap<String, ISearchProvider>();
        for (ServiceReference proproviderRef : this.providersRefs.values()) {
            final ISearchProvider provider = (ISearchProvider) this.ctx.locateService("searchProviders",
                    proproviderRef);
            final String providerPID = (String) proproviderRef.getProperty(Constants.SERVICE_PID);
            providers.put(providerPID, provider);
        }
        return providers;
    }

    public List<ISearchResult> search(ISearchRequest request)
            throws InterruptedException, ExecutionException, SearchException {
        final ListResultCollector collector = this.createResultCollector();
        this.search(request, collector);
        return collector;
    }

    public List<ISearchResult> search(String paxleQuery, int maxResults, long timeout)
            throws InterruptedException, ExecutionException, SearchException {
        final ListResultCollector collector = this.createResultCollector();
        this.search(paxleQuery, maxResults, timeout, collector);
        return collector;
    }

    public void search(String paxleQuery, int maxResults, long timeout, ISearchResultCollector results)
            throws InterruptedException, ExecutionException, SearchException {
        ISearchRequest request = this.createRequest(paxleQuery, maxResults, timeout);
        this.search(request, results);
    }

    public ISearchRequest createRequest(String paxleQuery, int maxResults, long timeout) throws SearchException {
        if (paxleQuery == null)
            throw new NullPointerException("The search-query was null");
        if (maxResults <= 0)
            throw new IllegalArgumentException("The maxResult value must be greater than zero.");
        if (timeout < 0)
            throw new IllegalArgumentException("The timeout must not be negative.");

        // parsing the query-string into tokens
        final AToken query = PaxleQueryParser.parse(paxleQuery);
        if (query == null)
            throw new SearchException(paxleQuery, "Illegal query (maybe too short?)");

        // creating the search request object
        int requestID = this.requestIdGenerator.incrementAndGet();
        ISearchRequest searchRequest = new SearchRequest(requestID, query, maxResults, timeout);
        return searchRequest;
    }

    private ListResultCollector createResultCollector() {
        return new ListResultCollector();
    }

    private void search(ISearchRequest request, ISearchResultCollector results)
            throws InterruptedException, ExecutionException, SearchException {
        if (request == null)
            throw new NullPointerException("The search-request object must not be null");

        final CompletionService<ISearchResult> execCompletionService = new ExecutorCompletionService<ISearchResult>(
                this.execService);

        // determining all search-providers that should be used for the query
        HashSet<String> allowedProviderPIDs = new HashSet<String>(request.getProviderIDs());

        // loop through all providers and pass the request to each one
        List<String> usedProviderPIDs = new ArrayList<String>();
        for (Entry<String, ServiceReference> providerEntry : this.providersRefs.entrySet()) {
            final String providerPID = providerEntry.getKey();
            final ServiceReference providerRef = providerEntry.getValue();

            if (allowedProviderPIDs.size() > 0 && !allowedProviderPIDs.contains(providerPID)) {
                this.logger.debug(String.format("SEProvider '%s' is skipped for search request '%d'.", providerPID,
                        Integer.valueOf(request.getRequestID())));
                continue;
            }

            usedProviderPIDs.add(providerPID);
            execCompletionService.submit(new SearchProviderCallable(this.ctx, providerRef, request));
        }

        if (allowedProviderPIDs.size() == 0) {
            // store the providers we have used to process the search-request
            request.setProviderIDs(usedProviderPIDs);
        }

        // loop through all providers and collect the results
        long searchTimeout = request.getTimeout();
        for (int i = 0; i < usedProviderPIDs.size(); ++i) {
            final long start = System.currentTimeMillis();

            // waiting for the next search result
            final Future<ISearchResult> future = execCompletionService.poll(searchTimeout, TimeUnit.MILLISECONDS);
            if (future != null) {
                final ISearchResult r = future.get();

                if (r != null) {
                    final String providerPID = r.getProviderID();
                    final int size = r.getSize();
                    this.logger
                            .debug(String.format("SEProvider '%s' returned '%d' results for search-request '%d'.",
                                    providerPID, Integer.valueOf(size), Integer.valueOf(request.getRequestID())));

                    results.collect(r);
                }
            }

            final long diff = System.currentTimeMillis() - start;
            if ((searchTimeout -= diff) <= 0)
                break;
        }
    }

    /**
     * @see ISearchProviderManager#disableProvider(String)
     */
    public void disableProvider(String providerName) {
        // TODO: needs to be re-implemented
    }

    /**
     * @see ISearchProviderManager#enableProvider(String)
     */
    public void enableProvider(String providerName) {
        // TODO: needs to be re-implemented
    }

    /**
     * @see ISearchProviderManager#disabledProviders()
     */
    public Set<String> disabledProviders() {
        return Collections.emptySet();
    }
}