public DispatcherResult dispatch(
      final ExecutionContext context, final ExecutionItem item, final Dispatchable toDispatch)
      throws DispatcherException {
    final NodesSelector nodesSelector = context.getNodeSelector();
    INodeSet nodes = null;
    try {
      nodes =
          framework.filterAuthorizedNodes(
              context.getFrameworkProject(),
              new HashSet<String>(Arrays.asList("read", "run")),
              framework.filterNodeSet(
                  nodesSelector, context.getFrameworkProject(), context.getNodesFile()));
    } catch (NodeFileParserException e) {
      throw new DispatcherException(e);
    }
    if (nodes.getNodes().size() < 1) {
      throw new DispatcherException("No nodes matched");
    }
    boolean keepgoing = context.isKeepgoing();

    context
        .getExecutionListener()
        .log(4, "preparing for sequential execution on " + nodes.getNodes().size() + " nodes");
    final HashSet<String> nodeNames = new HashSet<String>(nodes.getNodeNames());
    final HashMap<String, Object> failures = new HashMap<String, Object>();
    FailedNodesListener failedListener = context.getExecutionListener().getFailedNodesListener();
    if (null != failedListener) {
      failedListener.matchedNodes(nodeNames);
    }
    boolean interrupted = false;
    final Thread thread = Thread.currentThread();
    boolean success = true;
    final HashMap<String, StatusResult> resultMap = new HashMap<String, StatusResult>();
    final Collection<INodeEntry> nodes1 = nodes.getNodes();
    // reorder based on configured rank property and order
    final String rankProperty =
        null != context.getNodeRankAttribute() ? context.getNodeRankAttribute() : "nodename";
    final boolean rankAscending = context.isNodeRankOrderAscending();
    final INodeEntryComparator comparator = new INodeEntryComparator(rankProperty);
    final TreeSet<INodeEntry> orderedNodes =
        new TreeSet<INodeEntry>(rankAscending ? comparator : Collections.reverseOrder(comparator));
    orderedNodes.addAll(nodes1);
    for (final Object node1 : orderedNodes) {
      if (thread.isInterrupted()
          || thread instanceof ExecutionServiceThread
              && ((ExecutionServiceThread) thread).isAborted()) {
        interrupted = true;
        break;
      }
      final INodeEntry node = (INodeEntry) node1;
      context
          .getExecutionListener()
          .log(
              Constants.DEBUG_LEVEL,
              "Executing command on node: " + node.getNodename() + ", " + node.toString());
      try {

        if (thread.isInterrupted()
            || thread instanceof ExecutionServiceThread
                && ((ExecutionServiceThread) thread).isAborted()) {
          interrupted = true;
          break;
        }
        final StatusResult result;
        final ExecutionContext interimcontext =
            new ExecutionContextImpl.Builder(context)
                .nodeSelector(SelectorUtils.singleNode(node.getNodename()))
                .build();
        if (null != item) {
          result = framework.getExecutionService().interpretCommand(interimcontext, item, node);
        } else {
          result = toDispatch.dispatch(interimcontext, node);
        }
        if (null != result) {
          resultMap.put(node.getNodename(), result);
        }
        if (null == result || !result.isSuccess()) {
          success = false;
          //                    context.getExecutionListener().log(Constants.ERR_LEVEL,
          //                        "Failed execution for node " + node.getNodename() + ": " +
          // result);
          if (null != result) {
            failures.put(node.getNodename(), result);
          } else {
            failures.put(node.getNodename(), "Failed execution, result was null");
          }
          if (!keepgoing) {
            break;
          }
        } else {
          nodeNames.remove(node.getNodename());
        }
      } catch (Throwable e) {
        success = false;
        failures.put(
            node.getNodename(), "Error dispatching command to the node: " + e.getMessage());
        context
            .getExecutionListener()
            .log(
                Constants.ERR_LEVEL,
                "Failed dispatching to node " + node.getNodename() + ": " + e.getMessage());

        final StringWriter stringWriter = new StringWriter();
        e.printStackTrace(new PrintWriter(stringWriter));
        context
            .getExecutionListener()
            .log(
                Constants.DEBUG_LEVEL,
                "Failed dispatching to node "
                    + node.getNodename()
                    + ": "
                    + stringWriter.toString());

        if (!keepgoing) {
          if (failures.size() > 0 && null != failedListener) {
            // tell listener of failed node list
            failedListener.nodesFailed(failures);
          }
          throw new DispatcherException(
              "Failed dispatching to node " + node.getNodename() + ": " + e.getMessage(), e, node);
        }
      }
    }
    if (keepgoing && nodeNames.size() > 0) {
      if (null != failedListener) {
        // tell listener of failed node list
        failedListener.nodesFailed(failures);
      }
      // now fail
      // XXX: needs to change from exception
      throw new NodesetFailureException(failures);
    } else if (null != failedListener && failures.isEmpty() && !interrupted) {
      failedListener.nodesSucceeded();
    }
    if (interrupted) {
      throw new DispatcherException("Node dispatch interrupted");
    }

    final boolean status = success;
    return new DispatcherResult() {
      public Map<String, ? extends StatusResult> getResults() {
        return resultMap;
      }

      public boolean isSuccess() {
        return status;
      }

      @Override
      public String toString() {
        return "DispatcherResult{"
            + "status="
            + isSuccess()
            + ", "
            + "results="
            + getResults()
            + "}";
      }
    };
  }