private <C extends Command<R>, R extends Serializable> void registerExecutor(
      final NodeContext nodeContext, final CommandExecutor<C, R> executor, ZNode node) {
    final ZNode executorNode =
        node.createChild(znode().withPath(nodeNameOf(executor.getQualifier())));
    final ZNode queueNode = executorNode.createChild(znode().withPath("queue"));
    executorNode.createChild(znode().withPath("result"));

    log.debug("Created znodes for executor {}", executorNode.getPath());

    queueNode.addChildrenWatcher(
        new Watcher() {
          @Override
          public void process(WatchedEvent event) {
            if (event.getType() != Event.EventType.NodeChildrenChanged) {
              return;
            }

            synchronized (lock) {
              if (log.isDebugEnabled()) {
                log.debug(
                    "Children changed {} event type {}", queueNode.getPath(), event.getType());
              }

              List<QueueEntry<C, R>> entries = getEntries(queueNode, this);

              for (final QueueEntry<C, R> entry : entries) {
                Runnable run =
                    new Runnable() {

                      @Override
                      public void run() {
                        executeCommand(executor, executorNode, entry, nodeContext);
                      }
                    };

                ZookeeperCoordinator.this.executor.execute(run);
              }
            }
          }
        });
  }
  @Override
  public void registerNode(
      NodeContext nodeContext, Set<Worker> workers, final StatusChangeListener listener)
      throws CoordinatorException {
    log.info("Going to register node {} with {} workers", nodeContext.getId(), workers.size());

    ZNode typeNode = rootNode.child(CoordinationUtil.nodeNameOf(nodeContext.getId().getType()));
    ZNode node = typeNode.createChild(znode().withPath(nodeContext.getId().getIdentifier()));

    Set<CommandExecutor<?, ?>> executors = Sets.newHashSet();
    Set<Qualifier<?>> qualifiers = Sets.newHashSet();

    for (Worker worker : workers) {
      for (CommandExecutor<?, ?> executor : worker.getExecutors()) {
        Qualifier<?> qualifier = executor.getQualifier();
        if (qualifiers.contains(qualifier)) {
          throw new CoordinatorException(
              "Executor for qualifier " + qualifier + " is already registered");
        }

        executors.add(executor);
      }
    }

    for (CommandExecutor<?, ?> executor : executors) {
      registerExecutor(nodeContext, executor, node);
    }

    rootNode.addNodeWatcher(
        new Watcher() {
          @Override
          public void process(WatchedEvent event) {
            if (event.getState() == Event.KeeperState.Disconnected) {
              listener.onCoordinatorDisconnected();
            }

            if (event.getState() == Event.KeeperState.SyncConnected) {
              listener.onCoordinatorConnected();
            }
          }
        });

    ZNode statuses = rootNode.child(CoordinationUtil.STATUSES_NODE_NAME);

    statuses.createChild(znode().ephemeralSequential().withDataObject(nodeContext.getId()));

    Lock lock = new ReentrantLock();

    lock.lock();
    try {
      Collection<NodeId> nodeIds = Sets.newHashSet();
      StatusWatcher statusWatcher = new StatusWatcher(statuses, lock, nodeIds, listener);
      List<ZNode> nodes = statuses.children(statusWatcher);
      for (ZNode zNode : nodes) {
        nodeIds.add(zNode.getObject(NodeId.class));
      }
    } finally {
      lock.unlock();
    }

    node.createChild(znode().withPath(CoordinationUtil.AVAILABLE_NODE_NAME));
  }