@Override protected void work() throws Throwable { try { executor = new MyExecutorService(ExecutorType.CACHED_THREAD_POOL, getLogger()); final AbortHandler abortHandler = new AbortHandler() { @Override public void notify(Throwable t) { stop(); } }; WorkerSynchronizer synchronizer = new WorkerSynchronizer(); backPressure = new Semaphore(numWorkers); final BlockingQueue<CompletionService<Job>> ecsQueue = new LinkedBlockingQueue<CompletionService<Job>>(); distributor = new WorkerLauncher( new WorkerInstantiator() { @Override public WorkerBase instantiate() { LpnDistributor d = new LpnDistributor( abortHandler, inTray, executor.getThreadGroup(), numWorkers, jobProcessorFactory, backPressure); ecsQueue.add(d.getEcs()); return d; } }, abortHandler); distributor.start(executor, synchronizer); final CompletionService<Job> ecs = ecsQueue.take(); gatherer = new WorkerLauncher( new WorkerInstantiator() { @Override public WorkerBase instantiate() { return new LpnGatherer(abortHandler, ecs, processedJobs, backPressure); } }, abortHandler); gatherer.start(executor, synchronizer); synchronizer.waitForAny(); } finally { info("Sending kill signal to workers"); if (distributor != null) distributor.stop(); if (gatherer != null) gatherer.stop(); if (executor != null) executor.shutdownAndAwaitTermination(); } }