package org.xbib.pipeline; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; /** * A simple pipeline executor. * @param <R> the pipeline request type * @param <P> the pipeline type */ public class SimplePipelineExecutor<R extends PipelineRequest, P extends Pipeline<R>> implements PipelineExecutor<R,P> { private final ExecutorService executorService; private BlockingQueue<R> queue; private Collection<Pipeline<R>> pipelines; private Collection<Future<R>> futures; private PipelineProvider<P> provider; private PipelineSink<R> sink; private List<Throwable> exceptions; private int concurrency; public SimplePipelineExecutor(ExecutorService executorService) { this.executorService = executorService; } @Override public SimplePipelineExecutor<R,P> setConcurrency(int concurrency) { this.concurrency = concurrency; return this; } @Override public SimplePipelineExecutor<R,P> setPipelineProvider(PipelineProvider<P> provider) { this.provider = provider; return this; } @Override public SimplePipelineExecutor<R,P> setQueue(BlockingQueue<R> queue) { if (queue == null) { throw new IllegalArgumentException("null queue is not accepted"); } this.queue = queue; return this; } @Override public SimplePipelineExecutor<R,P> setSink(PipelineSink<R> sink) { this.sink = sink; return this; } @Override public SimplePipelineExecutor<R,P> prepare() { if (provider == null) { throw new IllegalStateException("no provider set"); } if (queue == null) { throw new IllegalStateException("no queue set"); } this.pipelines = new LinkedList<>(); if (concurrency < 1) { concurrency = 1; } for (int i = 0; i < Math.min(concurrency, 256); i++) { pipelines.add(provider.get().setQueue(queue)); } return this; } /** * Execute pipelines * @return this executor */ @Override public SimplePipelineExecutor<R,P> execute() { if (pipelines == null) { prepare(); } if (pipelines.isEmpty()) { throw new IllegalStateException("pipelines empty"); } futures = new LinkedList<>(); for (Callable<R> pipeline : pipelines) { futures.add(executorService.submit(pipeline)); } return this; } /** * Wait for all results of the executions. * * @return this pipeline executor * @throws InterruptedException * @throws ExecutionException */ @Override public SimplePipelineExecutor<R,P> waitFor() throws InterruptedException, ExecutionException { if (executorService == null || pipelines == null || futures == null || futures.isEmpty()) { return this; } exceptions = new LinkedList<>(); for (Future<R> future : futures) { R r = future.get(); if (sink != null && !future.isCancelled()) { try { sink.sink(r); } catch (IOException e) { exceptions.add(e); } } } return this; } @Override public void shutdown() throws InterruptedException, IOException { } /** * Get the pipelines of this executor. * @return the pipelines */ @Override public Collection<Pipeline<R>> getPipelines() { return pipelines; } /** * Get the collected I/O exceptions that were thrown by the pipelines. * @return list of exceptions */ public List<Throwable> getExceptions() { return exceptions; } }