class OperationRunner implements Runnable {

    public void run()
    {
        // TODO: should this perform any retries?
        try
        {
            logger.debug("OperationWrapper [" + operationWrapper.getOperation().getName() + "]");
            if (executorContext.getJsonContext() == null)
            {
                logger.warn("Executor context is null");
            }
            else
            {
                logger.debug("Executor Context [" + executorContext.getJsonContext().toString() + "], Executor ProgressReporter [" +
                             executorContext.getAgendaProgressReporter() + "], Executor Reporter [" + executorContext.getReporter() + "]");
            }
            logger.debug("Executing start of Pod");
            BaseOperationExecutor executor = executorContext.getOperationExecutorFactory().generateOperationExecutor(executorContext, operationWrapper);
            // register the executor as a provider of operation progress
            executorContext.getAgendaProgressReporter().registerOperationProgressProvider(executor);
            String outputPayload = executor.execute(operationWrapper.getInputPayload());
            // get the last progress
            OperationProgress operationProgress = executor.retrieveOperationProgress();
            if(operationProgress == null)
            {
                setFailureOnOperationWrapper(
                    operationWrapper,
                    ExecutorMessages.OPERATION_EXECUTION_INCOMPLETE_NO_PROGRESS.getMessage(getOperationName(operationWrapper)),
                    null);
            }
            else
            {
                if(operationProgress.getProcessingState() == ProcessingState.COMPLETE)
                {
                    evaluateCompletedOperation(operationWrapper, operationProgress, outputPayload);
                }
                else
                {
                    setFailureOnOperationWrapper(
                        operationWrapper,
                        ExecutorMessages.OPERATION_EXECUTION_INCOMPLETE.getMessage(getOperationName(operationWrapper), operationProgress.getProcessingState()),
                        null);
                }
            }
        }
        catch(Throwable t)
        {
            setFailureOnOperationWrapper(
                operationWrapper,
                ExecutorMessages.OPERATION_EXECUTION_ERROR.getMessage(getOperationName(operationWrapper)),
                t);
        }
        // always call the onComplete (critical for the operation conductor)
        if(onOperationCompleteListener != null) onOperationCompleteListener.onComplete(operationWrapper);
    }

    public  OperationRunner(OperationWrapper operationWrapper, ExecutorContext executorContext, OnOperationCompleteListener onOperationCompleteListener);

}