@Override
 public Set<NodeId> getAvailableNodes(NodeType type) {
   Set<NodeId> result = Sets.newHashSet();
   ZNode typeNode = rootNode.child(CoordinationUtil.nodeNameOf(type));
   for (ZNode node : typeNode.children()) {
     if (node.hasChild(CoordinationUtil.AVAILABLE_NODE_NAME)) {
       result.add(NodeId.of(type, node.getShortPath()));
     }
   }
   return result;
 }
  @Override
  public void initialize() {
    log.debug("Going to initialize required znode structure in zookeeper");
    for (NodeType type : NodeType.values()) {
      String child = CoordinationUtil.nodeNameOf(type);
      rootNode.createChild(znode().withPath(child));
      log.debug("Created node {}", child);
    }

    rootNode.createChild(znode().withPath(CoordinationUtil.STATUSES_NODE_NAME));
    log.debug("Created node {}", CoordinationUtil.STATUSES_NODE_NAME);
    log.debug("Successfully initialized");
  }
  @Override
  public boolean canExecuteCommands(NodeId nodeId, Set<Qualifier<?>> qualifiers) {
    ZNode typeNode = rootNode.child(CoordinationUtil.nodeNameOf(nodeId.getType()));
    String identifier = nodeId.getIdentifier();
    if (!typeNode.hasChild(identifier)) {
      throw new CoordinatorException("Node with id " + nodeId + " is not found");
    }

    ZNode node = typeNode.child(identifier);

    if (!node.hasChild(CoordinationUtil.AVAILABLE_NODE_NAME)) {
      return false;
    }
    for (Qualifier<?> qualifier : qualifiers) {
      if (!node.hasChild(nodeNameOf(qualifier))) {
        return false;
      }
    }
    return true;
  }
  @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));
  }