edu.vt.middleware.ldap.search.SearchExecutor.java Source code

Java tutorial

Introduction

Here is the source code for edu.vt.middleware.ldap.search.SearchExecutor.java

Source

/*
  $Id$
    
  Copyright (C) 2003-2009 Virginia Tech.
  All rights reserved.
    
  SEE LICENSE FOR MORE INFORMATION
    
  Author:  Middleware Services
  Email:   middleware@vt.edu
  Version: $Revision$
  Updated: $Date$
*/
package edu.vt.middleware.ldap.search;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import javax.naming.NamingException;
import javax.naming.directory.SearchResult;
import edu.vt.middleware.ldap.Ldap;
import edu.vt.middleware.ldap.pool.LdapPool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <code>SearchExecuter</code> contains the basic methods necessary for
 * performing a Ldap query.
 *
 * @author  Middleware Services
 * @version  $Revision$ $Date$
 */

public class SearchExecutor {

    /** Identifier in search string that should be replaced with query data. */
    public static final String REGEX_QUERY = "@@@QUERY_1@@@";

    /** Identifier in search string that should be replaced with initial data. */
    public static final String REGEX_INITIAL = "@@@INITIAL_1@@@";

    /** Log for this class. */
    private static final Log LOG = LogFactory.getLog(SearchExecutor.class);

    /** Appended to every search to restrict result sets. */
    private String searchRestrictions;

    /**
     * Whether a query should use results from all searches or just the results
     * from the first match.
     */
    private boolean additive;

    /** Post processers for search results. */
    private List<PostProcessor> postProcessors = new ArrayList<PostProcessor>();

    /** Number of query terms for this search module. */
    private int termCount;

    /** Search strings for this search module. */
    private Map<Integer, String> queryTemplates = new HashMap<Integer, String>();

    /** Default constructor. */
    public SearchExecutor() {
    }

    /**
     * This creates a new <code>Search</code>.
     *
     * @param  tc  <code>int</code> number to query terms for this search
     */
    public SearchExecutor(final int tc) {
        this.termCount = tc;
    }

    /**
     * This returns the string to use for search restrictions.
     *
     * @return  <code>String</code>
     */
    public String getSearchRestrictions() {
        return this.searchRestrictions;
    }

    /**
     * This sets the string to use for search restrictions.
     *
     * @param  s  <code>String</code>
     */
    public void setSearchRestrictions(final String s) {
        this.searchRestrictions = s;
    }

    /**
     * Returns whether searches are additive.
     *
     * @return  <code>boolean</code>
     */
    public boolean isAdditive() {
        return this.additive;
    }

    /**
     * Sets whether searches should be additive.
     *
     * @param  b  whether searches are additive
     */
    public void setAdditive(final boolean b) {
        this.additive = b;
    }

    /**
     * This returns the post processors to run on search results.
     *
     * @return  <code>List</code> of post processors
     */
    public List<PostProcessor> getPostProcessors() {
        return this.postProcessors;
    }

    /**
     * This sets the post processors to run on search results.
     *
     * @param  l  list of post processors
     */
    public void setPostProcessors(final List<PostProcessor> l) {
        this.postProcessors = l;
    }

    /**
     * Returns the term count for this search.
     *
     * @return  <code>int</code>
     */
    public int getTermCount() {
        return this.termCount;
    }

    /**
     * Sets the term count for this search.
     *
     * @param  i  term count
     */
    public void setTermCount(final int i) {
        this.termCount = i;
    }

    /**
     * Returns the searches for this search.
     *
     * @return  <code>List</code> of attempt number to search string
     */
    public Map<Integer, String> getQueryTemplates() {
        return this.queryTemplates;
    }

    /**
     * Sets the searches for this search.
     *
     * @param  m  map of attempt number to search string
     */
    public void setQueryTemplates(final Map<Integer, String> m) {
        this.queryTemplates = m;
    }

    /**
     * This performs a Ldap search with the supplied <code>Query</code>.
     *
     * @param  ldapPool  <code>LdapPool</code> to use for searching
     * @param  query  <code>Query</code> to search for
     *
     * @return  <code>Iterator</code> - of search results
     *
     * @throws  PeopleSearchException  if an error occurs searching the Ldap
     */
    public Iterator<SearchResult> executeSearch(final LdapPool<Ldap> ldapPool, final Query query)
            throws PeopleSearchException {
        final SortedMap<Integer, SearchResult> m = new TreeMap<Integer, SearchResult>();
        final List<String> l = new ArrayList<String>();

        if (this.queryTemplates != null && this.queryTemplates.size() > 0) {
            final SearchThread[] threads = new SearchThread[this.queryTemplates.size()];
            for (int i = 1; i <= this.queryTemplates.size(); i++) {
                final String ldapQuery = this.buildLdapQuery(this.getQueryTemplate(query.getQueryParameters(), i),
                        query.getSearchRestrictions());
                if (ldapQuery != null) {
                    threads[i - 1] = new SearchThread(ldapPool, ldapQuery, query.getQueryAttributes());
                } else {
                    threads[i - 1] = null;
                }
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Performing search with " + threads.length + " threads");
            }

            int count = 0;
            boolean loop = true;
            while (loop && count < threads.length) {
                List<QueryResult> results = null;
                if (additive) {
                    results = this.doAdditiveSearch(threads);
                } else {
                    results = this.doIterativeSearch(threads[count]);
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Loop " + count + " found " + results.size() + " results");
                }
                for (int i = 0; i < results.size(); i++) {
                    int j = m.size();
                    final QueryResult qr = results.get(i);
                    if (qr != null) {
                        final SearchResult sr = qr.getSearchResult();
                        if (sr != null) {
                            if (!l.contains(sr.getName())) {
                                if (additive) {
                                    qr.setSearchIteration(i);
                                    qr.setTermCount(this.termCount);
                                    this.processResults(qr);
                                } else {
                                    qr.setSearchIteration(count);
                                    qr.setTermCount(this.termCount);
                                    this.processResults(qr);
                                }
                                // store for results
                                m.put(new Integer(j++), sr);
                                l.add(sr.getName());
                                if (LOG.isDebugEnabled()) {
                                    if (LOG.isDebugEnabled()) {
                                        LOG.debug("Query " + (additive ? i : count) + " found: " + sr.getName());
                                    }
                                }
                            }
                        }
                    }
                }
                if (additive || !m.isEmpty()) {
                    loop = false;
                } else {
                    count++;
                }
            }
        } else {
            if (LOG.isWarnEnabled()) {
                LOG.warn("No searches configured");
            }
        }

        Iterator<SearchResult> i = null;
        if (query.getFromResult() != null) {
            if (query.getToResult() != null) {
                if (query.getFromResult().intValue() <= query.getToResult().intValue()) {
                    i = m.subMap(query.getFromResult(), query.getToResult()).values().iterator();
                }
            } else {
                i = m.tailMap(query.getFromResult()).values().iterator();
            }
        } else if (query.getToResult() != null) {
            i = m.headMap(query.getToResult()).values().iterator();
        } else {
            i = m.values().iterator();
        }
        return i;
    }

    /**
     * This performs an additive Ldap search.
     *
     * @param  threads  <code>SearchThread[]</code> to run search with
     *
     * @return  <code>List</code> of <code>QueryResult</code>
     */
    private List<QueryResult> doAdditiveSearch(final SearchThread[] threads) {
        final List<QueryResult> results = new ArrayList<QueryResult>(threads.length);
        for (SearchThread st : threads) {
            if (st != null) {
                st.startSearch();
            }
        }
        for (SearchThread st : threads) {
            if (st != null) {
                try {
                    st.join();
                } catch (InterruptedException e) {
                    if (LOG.isErrorEnabled()) {
                        LOG.error("Interrupted waiting for search thread", e);
                    }
                }
            }
        }
        for (SearchThread st : threads) {
            if (st != null) {
                results.addAll(st.getResults());
            }
        }
        return results;
    }

    /**
     * This performs an iterative Ldap search.
     *
     * @param  thread  <code>SearchThread</code> to run search with
     *
     * @return  <code>List</code> of <code>QueryResult</code>
     */
    private List<QueryResult> doIterativeSearch(final SearchThread thread) {
        final List<QueryResult> results = new ArrayList<QueryResult>(1);
        if (thread != null) {
            thread.startSearch();
            try {
                thread.join();
            } catch (InterruptedException e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Interrupted waiting for search thread", e);
                }
            }
            results.addAll(thread.getResults());
        }
        return results;
    }

    /**
     * This performs the processing of all search results. Post processers are run
     * and top results is used if configured.
     *
     * @param  queryResult  <code>QueryResult</code> to process
     *
     * @throws  PeopleSearchException  if an error occurs processing results
     */
    private void processResults(final QueryResult queryResult) throws PeopleSearchException {
        // perform any post processing
        if (queryResult != null) {
            for (PostProcessor p : postProcessors) {
                try {
                    p.processResult(queryResult);
                } catch (NamingException e) {
                    if (LOG.isErrorEnabled()) {
                        LOG.error("Error occured during post processing", e);
                    }
                    throw new PeopleSearchException(e);
                }
            }
        }
    }

    /**
     * This returns a ldap search string for the supply list of query parameters.
     * count represents the number of queries which have been attempted, thus far.
     *
     * @param  queryParams  <code>String[]</code> to search for
     * @param  count  <code>int</code> number of queries performed
     *
     * @return  <code>String</code> - ldap search string
     */
    private String getQueryTemplate(final String[] queryParams, final int count) {
        String query = null;

        if (queryParams != null && queryParams.length > 0 && count <= this.queryTemplates.size()) {
            query = this.queryTemplates.get(new Integer(count));
            if (query != null) {

                // clone the params array, it may need to be changed for this search
                final String[] qp = (String[]) queryParams.clone();

                // replace all the initial parameters
                final String regexInitial = REGEX_INITIAL;
                final String[] qi = this.getInitials(qp);
                if (qi != null) {
                    for (int i = 1; i <= qi.length; i++) {
                        if (qi[i - 1] != null) {
                            query = query.replaceAll(regexInitial.replaceAll("1", Integer.toString(i)),
                                    Matcher.quoteReplacement(qi[i - 1]));
                        }
                    }
                }

                // replace all the query parameters
                final String regexQuery = REGEX_QUERY;
                for (int i = 1; i <= qp.length; i++) {
                    if (qp[i - 1] != null) {
                        query = query.replaceAll(regexQuery.replaceAll("1", Integer.toString(i)),
                                Matcher.quoteReplacement(qp[i - 1]));
                    }
                }
            }
        }

        return query;
    }

    /**
     * This converts an array of names into an array of initials.
     *
     * @param  queryParams  <code>String[]</code>
     *
     * @return  <code>String[]</code> - of initials
     */
    private String[] getInitials(final String[] queryParams) {
        final String[] initials = new String[queryParams.length];

        for (int i = 0; i < initials.length; i++) {
            try {
                if (queryParams[i] != null && !"".equals(queryParams[i])) {
                    initials[i] = queryParams[i].substring(0, 1);
                } else {
                    initials[i] = null;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                initials[i] = null;
            }
        }

        return initials;
    }

    /**
     * This attaches the search restrictions to the supply query.
     *
     * @param  query  <code>String</code> to search for
     * @param  dynamicRestrictions  <code>String</code> to limit search
     *
     * @return  <code>String</code> - modified query string
     */
    private String buildLdapQuery(final String query, final String dynamicRestrictions) {
        String search = null;
        if (query != null) {
            final StringBuffer s = new StringBuffer();
            if (dynamicRestrictions != null && searchRestrictions != null) {
                s.append("(&").append(searchRestrictions);
                s.append(dynamicRestrictions);
                s.append(query);
                s.append(")");
            } else if (dynamicRestrictions != null) {
                s.append("(&").append(dynamicRestrictions);
                s.append(query);
                s.append(")");
            } else if (searchRestrictions != null) {
                s.append("(&").append(searchRestrictions);
                s.append(query);
                s.append(")");
            } else {
                s.append(query);
            }
            search = s.toString();
        }
        return search;
    }
}