@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();
    }
  }