/* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.concurrent; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; 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 org.ldaptive.Connection; import org.ldaptive.LdapException; import org.ldaptive.Operation; import org.ldaptive.Request; import org.ldaptive.Response; import org.ldaptive.SearchFilter; import org.ldaptive.SearchOperation; import org.ldaptive.SearchRequest; import org.ldaptive.SearchResult; import org.ldaptive.handler.SearchEntryHandler; import org.ldaptive.pool.PooledConnectionFactory; /** * Executes a list of search filters in parallel, each search is performed on a separate connection in the pool. If you * need to execute all searches on the same connection see {@link ParallelSearchExecutor}. A cached thread pool is used * by default. * * @author Middleware Services */ public class ParallelPooledSearchExecutor extends AbstractParallelSearchExecutor<PooledConnectionFactory> { /** Default constructor. */ public ParallelPooledSearchExecutor() { this(Executors.newCachedThreadPool()); } /** * Creates a new parallel pooled search executor. * * @param es executor service */ public ParallelPooledSearchExecutor(final ExecutorService es) { super(es); } @Override public Collection<Response<SearchResult>> search( final PooledConnectionFactory factory, final SearchFilter[] filters, final String[] attrs, final SearchEntryHandler... handlers) throws LdapException { final CompletionService<Response<SearchResult>> searches = new ExecutorCompletionService<>(getExecutorService()); final List<Future<Response<SearchResult>>> futures = new ArrayList<>(filters.length); for (SearchFilter filter : filters) { final SearchRequest sr = newSearchRequest(this); if (filter != null) { sr.setSearchFilter(filter); } if (attrs != null) { sr.setReturnAttributes(attrs); } if (handlers != null) { sr.setSearchEntryHandlers(handlers); } final Connection conn = factory.getConnection(); final SearchOperation op = createSearchOperation(conn); futures.add(searches.submit(createCallable(conn, op, sr))); } final List<Response<SearchResult>> responses = new ArrayList<>(filters.length); for (Future<Response<SearchResult>> future : futures) { try { responses.add(future.get()); } catch (ExecutionException e) { logger.debug("ExecutionException thrown, ignoring", e); } catch (InterruptedException e) { logger.warn("InterruptedException thrown, ignoring", e); } } return responses; } /** * Returns a {@link Callable} that executes the supplied request with the supplied operation in a try-finally block * that opens and closes the connection. * * @param <Q> type of ldap request * @param <S> type of ldap response * @param conn connection that the operation will execute on * @param operation to execute * @param request to pass to the operation * * @return callable for the supplied operation and request */ protected static <Q extends Request, S> Callable<Response<S>> createCallable( final Connection conn, final Operation<Q, S> operation, final Q request) { return () -> { try { conn.open(); return operation.execute(request); } finally { conn.close(); } }; } }