@Override
  public void execute(final OperationContext context, final ModelNode operation)
      throws OperationFailedException {

    if (context.hasFailureDescription()) {
      // abort
      context.setRollbackOnly();
      context.completeStep();
      return;
    }

    final Set<String> outstanding = new HashSet<String>(hostProxies.keySet());
    final List<
            TransactionalProtocolClient.PreparedOperation<HostControllerUpdateTask.ProxyOperation>>
        results =
            new ArrayList<
                TransactionalProtocolClient.PreparedOperation<
                    HostControllerUpdateTask.ProxyOperation>>();
    final Map<String, HostControllerUpdateTask.ExecutedHostRequest> finalResults =
        new HashMap<String, HostControllerUpdateTask.ExecutedHostRequest>();
    final HostControllerUpdateTask.ProxyOperationListener listener =
        new HostControllerUpdateTask.ProxyOperationListener();
    for (Map.Entry<String, ProxyController> entry : hostProxies.entrySet()) {
      // Create the proxy task
      final String host = entry.getKey();
      final TransformingProxyController proxyController =
          (TransformingProxyController) entry.getValue();
      List<DomainOperationTransformer> transformers =
          context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS);
      ModelNode op = operation;
      if (transformers != null) {
        for (final DomainOperationTransformer transformer : transformers) {
          op = transformer.transform(context, op);
        }
      }
      final HostControllerUpdateTask task =
          new HostControllerUpdateTask(host, op.clone(), context, proxyController);
      // Execute the operation on the remote host
      final HostControllerUpdateTask.ExecutedHostRequest finalResult = task.execute(listener);
      finalResults.put(host, finalResult);
    }

    // Wait for all hosts to reach the prepared state
    boolean interrupted = false;
    try {
      try {
        while (outstanding.size() > 0) {
          final TransactionalProtocolClient.PreparedOperation<
                  HostControllerUpdateTask.ProxyOperation>
              prepared = listener.retrievePreparedOperation();
          final String hostName = prepared.getOperation().getName();
          if (!outstanding.remove(hostName)) {
            continue;
          }
          final ModelNode preparedResult = prepared.getPreparedResult();
          if (HOST_CONTROLLER_LOGGER.isTraceEnabled()) {
            HOST_CONTROLLER_LOGGER.tracef(
                "Preliminary result for remote host %s is %s", hostName, preparedResult);
          }
          domainOperationContext.addHostControllerResult(hostName, preparedResult);
          results.add(prepared);
        }
      } catch (InterruptedException ie) {
        interrupted = true;
        // Set rollback only
        domainOperationContext.setFailureReported(true);
        // Rollback all HCs
        for (final HostControllerUpdateTask.ExecutedHostRequest finalResult :
            finalResults.values()) {
          finalResult.asyncCancel();
        }
        // Wait that all hosts are rolled back!?
        for (final Map.Entry<String, HostControllerUpdateTask.ExecutedHostRequest> entry :
            finalResults.entrySet()) {
          final String hostName = entry.getKey();
          try {
            final HostControllerUpdateTask.ExecutedHostRequest request = entry.getValue();
            final ModelNode result = request.getFinalResult().get();
            final ModelNode transformedResult = request.transformResult(result);
            domainOperationContext.addHostControllerResult(hostName, transformedResult);
          } catch (Exception e) {
            final ModelNode result = new ModelNode();
            result.get(OUTCOME).set(FAILED);
            if (e instanceof InterruptedException) {
              result
                  .get(FAILURE_DESCRIPTION)
                  .set(MESSAGES.interruptedAwaitingResultFromHost(entry.getKey()));
              interrupted = true;
            } else {
              result
                  .get(FAILURE_DESCRIPTION)
                  .set(MESSAGES.exceptionAwaitingResultFromHost(entry.getKey(), e.getMessage()));
            }
            domainOperationContext.addHostControllerResult(hostName, result);
          }
        }
      }

      context.completeStep();

    } finally {
      try {
        // Inform the remote hosts whether to commit or roll back their updates
        // Do this in parallel
        boolean rollback = domainOperationContext.isCompleteRollback();
        for (final TransactionalProtocolClient.PreparedOperation<
                HostControllerUpdateTask.ProxyOperation>
            prepared : results) {
          if (prepared.isDone()) {
            continue;
          }
          if (!rollback) {
            prepared.commit();
          } else {
            prepared.rollback();
          }
        }
        // Now get the final results from the hosts
        for (final TransactionalProtocolClient.PreparedOperation<
                HostControllerUpdateTask.ProxyOperation>
            prepared : results) {
          final String hostName = prepared.getOperation().getName();
          try {
            final ModelNode finalResult = prepared.getFinalResult().get();
            domainOperationContext.addHostControllerResult(hostName, finalResult);

            if (HOST_CONTROLLER_LOGGER.isTraceEnabled()) {
              HOST_CONTROLLER_LOGGER.tracef(
                  "Final result for remote host %s is %s", hostName, finalResult);
            }
          } catch (InterruptedException e) {
            interrupted = true;
            CONTROLLER_LOGGER.interruptedAwaitingFinalResponse(hostName);
          } catch (ExecutionException e) {
            CONTROLLER_LOGGER.caughtExceptionAwaitingFinalResponse(e.getCause(), hostName);
          }
        }
      } finally {
        if (interrupted) {
          Thread.currentThread().interrupt();
        }
      }
    }
  }