Java controller to farm out concurrent tasks I've recently given a coding interview on a Java concurrency task and unfortunately didn't get the job.  The worst part is I've given my best but now I'm not even sure where went wrong.  Can anyone help give me some ideas about things I can improve on below code?

The question is pretty vague.  Given 4 generic interfaces which on a high level divides a task into small pieces, work on each piece and combine the partial result into final result. I'm asked to implement the central controller piece of the interface.  The only requirement is to use concurrency in the partial result processing and "code must be production quality".

My code is as below (the interfaces were given).  I did put in a lot of comment to explain my assumptions which are removed here.

// adding V,W in order to use in private fields types
public class ControllerImpl&lt;T, U, V, W&gt; implements Controller&lt;T, U&gt; {

    private static Logger logger = LoggerFactory.getLogger(ControllerImpl.class);

    private static int BATCH_SIZE = 100;

    private Preprocessor&lt;T, V&gt; preprocessor;
    private Processor&lt;V, W&gt; processor;
    private Postprocessor&lt;U, W&gt; postprocessor;

    public ControllerImpl() {
        this.preprocessor = new PreprocessorImpl&lt;&gt;();
        this.processor = new ProcessorImpl&lt;&gt;();
        this.postprocessor = new PostprocessorImpl&lt;&gt;();
    }

    public ControllerImpl(Preprocessor preprocessor, Processor processor, Postprocessor postprocessor) {
        this.preprocessor = preprocessor;
        this.processor = processor;
        this.postprocessor = postprocessor;
    }

    @Override
    public U process(T arg) {
        if (arg == null) return null;

        final V[] parts = preprocessor.split(arg);
        final W[] partResult = (W[]) new Object[parts.length];

        final int poolSize = Runtime.getRuntime().availableProcessors();  
        final ExecutorService executor = getExecutor(poolSize);

        int i = 0;
        while (i &lt; parts.length) {
            final List&lt;Callable&lt;W&gt;&gt; tasks = IntStream.range(i, i + BATCH_SIZE)
                .filter(e -&gt; e &lt; parts.length) 
                .mapToObj(e -&gt; (Callable&lt;W&gt;) () -&gt; partResult[e] = processor.processPart(parts[e])) 
                .collect(Collectors.toList());
            i += tasks.size();

            try {
                logger.info("invoking batch of {} tasks to workers", tasks.size());
                long start = System.currentTimeMillis();
                final List&lt;Future&lt;W&gt;&gt; futures = executor.invokeAll(tasks); 
                long end = System.currentTimeMillis();
                logger.info("done batch processing took {} ms", end - start);
                for (Future future : futures) {
                    future.get();
                }
            } catch (InterruptedException e) {
                logger.error("{}", e);// have comments to explain better handling according to real business requirement 
            } catch (ExecutionException e) {
                logger.error("error: ", e);
            }
        }

        MoreExecutors.shutdownAndAwaitTermination(executor, 60, TimeUnit.SECONDS);

        return postprocessor.aggregate(partResult);
    }

    private ExecutorService getExecutor(int poolSize) {
        final ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("Processor-%d")
            .setDaemon(true)
            .build();
        return new ThreadPoolExecutor(poolSize, poolSize, 60, TimeUnit.SECONDS, new LinkedBlockingDeque&lt;&gt;(), threadFactory);
    }
}
Hints

I would have named the generic types properly.

Maybe I would have divided Logging from processing with help of a listener pattern.

I would have expected the input parameter to never be null, so returning null is obsolete.

I would have extracted some methods to have parts of code be named.

Code

On a very high level view I would have expected exactly what you described what the code should do. Splitting an input in parts, processing these parts in parallel, collect the results and aggregate them. This should have been expressed in the "process"-method.

public class Controller&lt;INPUT, RESULT, PARTIALINPUT, PARTIALRESULT&gt; {

    ...

    public RESULT process(INPUT input) {

        PARTIALINPUT[] partialInputArray = preprocessor.split(input);

        List&lt;Future&lt;PARTIALRESULT&gt;&gt; partialResultFutures = processAsynchronously(partialInputArray);
        List&lt;PARTIALRESULT&gt; partialResults = collectPartialResults(partialResultFutures);

        RESULT result = postprocessor.aggregate(partialResults);

        return result;
    }

    private List&lt;PARTIALRESULT&gt; collectPartialResults(List&lt;Future&lt;PARTIALRESULT&gt;&gt; partialResultFutures)
            throws InterruptedException, ExecutionException {

        List&lt;PARTIALRESULT&gt; partialResults = new ArrayList&lt;&gt;();

        for (Future&lt;PARTIALRESULT&gt; future : partialResultFutures) {
            partialResults.add(future.get());
        }

        return partialResults;
    }

    private List&lt;Future&lt;PARTIALRESULT&gt;&gt; processAsynchronously(PARTIALINPUT[] partialInputArray) {

        List&lt;Future&lt;PARTIALRESULT&gt;&gt; partialResultFutures = new ArrayList&lt;&gt;();

        for (PARTIALINPUT partialinput : partialInputArray) {
            Future&lt;PARTIALRESULT&gt; future = processAsyncronously(partialInput);
            partialResultFutures.add(future);
        }

        return partialResultFutures;
    }

    ...

}