/** Locates the internal address of the node on which a deployment is deployed. */
 private void findDeploymentAddress(
     final String deploymentID, Handler<AsyncResult<String>> resultHandler) {
   context.execute(
       new Action<String>() {
         @Override
         public String perform() {
           synchronized (deployments) {
             JsonObject locatedInfo = null;
             Collection<String> sdeploymentsInfo = deployments.get(group);
             for (String sdeploymentInfo : sdeploymentsInfo) {
               JsonObject deploymentInfo = new JsonObject(sdeploymentInfo);
               if (deploymentInfo.getString("id").equals(deploymentID)) {
                 locatedInfo = deploymentInfo;
                 break;
               }
             }
             if (locatedInfo != null) {
               return locatedInfo.getString("address");
             }
             return null;
           }
         }
       },
       resultHandler);
 }
 /** Removes a deployment from the deployments map and returns the real deploymentID. */
 private void removeDeployment(
     final String deploymentID, Handler<AsyncResult<String>> doneHandler) {
   context.execute(
       new Action<String>() {
         @Override
         public String perform() {
           Collection<String> groupDeployments = deployments.get(group);
           if (groupDeployments != null) {
             String deployment = null;
             for (String sdeployment : groupDeployments) {
               JsonObject info = new JsonObject(sdeployment);
               if (info.getString("id").equals(deploymentID)) {
                 deployment = sdeployment;
                 break;
               }
             }
             if (deployment != null) {
               deployments.remove(group, deployment);
               return new JsonObject(deployment).getString("realID");
             }
           }
           return null;
         }
       },
       doneHandler);
 }
 /** Lists nodes in the group. */
 private void doListNode(final Message<JsonObject> message) {
   context.execute(
       new Action<Collection<String>>() {
         @Override
         public Collection<String> perform() {
           return groups.get(group);
         }
       },
       new Handler<AsyncResult<Collection<String>>>() {
         @Override
         public void handle(AsyncResult<Collection<String>> result) {
           if (result.failed()) {
             message.reply(
                 new JsonObject()
                     .putString("status", "error")
                     .putString("message", result.cause().getMessage()));
           } else if (result.result() == null) {
             message.reply(
                 new JsonObject().putString("status", "ok").putArray("result", new JsonArray()));
           } else {
             message.reply(
                 new JsonObject()
                     .putString("status", "ok")
                     .putArray(
                         "result",
                         new JsonArray(
                             result.result().toArray(new String[result.result().size()]))));
           }
         }
       });
 }
 /** Called when a node leaves the cluster. */
 private synchronized void doNodeLeft(final String nodeID) {
   log.info(String.format("%s - %s left the cluster", this, nodeID));
   context.run(
       new Runnable() {
         @Override
         public void run() {
           // Redeploy any failed deployments.
           synchronized (deployments) {
             Collection<String> sdeploymentsInfo = deployments.get(group);
             for (final String sdeploymentInfo : sdeploymentsInfo) {
               final JsonObject deploymentInfo = new JsonObject(sdeploymentInfo);
               // If the deployment node is equal to the node that left the cluster then
               // remove the deployment from the deployments list and attempt to redeploy it.
               if (deploymentInfo.getString("node").equals(nodeID)) {
                 // If the deployment is an HA deployment then attempt to redeploy it on this node.
                 if (deployments.remove(group, sdeploymentInfo)
                     && deploymentInfo.getBoolean("ha", false)) {
                   doRedeploy(deploymentInfo);
                 }
               }
             }
           }
         }
       });
 }
 @Override
 public void stop(final Handler<AsyncResult<Void>> doneHandler) {
   synchronized (registry) {
     registry.remove(local);
   }
   context.execute(
       new Action<Void>() {
         @Override
         public Void perform() {
           groups.remove(group);
           return null;
         }
       },
       new Handler<AsyncResult<Void>>() {
         @Override
         public void handle(AsyncResult<Void> result) {
           vertx.eventBus().unregisterHandler(group, messageHandler, doneHandler);
           listener.unregisterJoinHandler(null);
           listener.unregisterLeaveHandler(null);
           final CountingCompletionHandler<Void> counter =
               new CountingCompletionHandler<Void>(3)
                   .setHandler(
                       new Handler<AsyncResult<Void>>() {
                         @Override
                         public void handle(AsyncResult<Void> result) {
                           clearDeployments(doneHandler);
                         }
                       });
           vertx.eventBus().unregisterHandler(local, messageHandler, counter);
           vertx.eventBus().unregisterHandler(internal, internalHandler, counter);
           vertx.eventBus().unregisterHandler(group, messageHandler, counter);
         }
       });
 }
 /** Selects a node in the group. */
 private void doSelectNode(final Message<JsonObject> message) {
   final Object key = message.body().getValue("key");
   if (key == null) {
     message.reply(
         new JsonObject().putString("status", "error").putString("message", "No key specified."));
   } else {
     context.execute(
         new Action<String>() {
           @Override
           public String perform() {
             String address = nodeSelectors.get(key);
             if (address != null) {
               return address;
             }
             Collection<String> nodes = groups.get(group);
             int index = new Random().nextInt(nodes.size());
             int i = 0;
             for (String node : nodes) {
               if (i == index) {
                 nodeSelectors.put(key, node);
                 return node;
               }
               i++;
             }
             return null;
           }
         },
         new Handler<AsyncResult<String>>() {
           @Override
           public void handle(AsyncResult<String> result) {
             if (result.failed()) {
               message.reply(
                   new JsonObject()
                       .putString("status", "error")
                       .putString("message", result.cause().getMessage()));
             } else if (result.result() == null) {
               message.reply(
                   new JsonObject()
                       .putString("status", "error")
                       .putString("message", "No nodes to select."));
             } else {
               message.reply(
                   new JsonObject()
                       .putString("status", "ok")
                       .putString("result", result.result()));
             }
           }
         });
   }
 }
 /** Adds a changed deployment to the deployments map. */
 private void addMappedDeployment(
     final String deploymentID,
     final JsonObject deploymentInfo,
     Handler<AsyncResult<String>> doneHandler) {
   context.execute(
       new Action<String>() {
         @Override
         public String perform() {
           deploymentInfo.putString("realID", deploymentID);
           deploymentInfo.putString("node", listener.nodeId());
           deploymentInfo.putString("address", internal);
           deployments.put(group, deploymentInfo.encode());
           return deploymentID;
         }
       },
       doneHandler);
 }
 /**
  * When the cluster is shutdown properly we need to remove deployments from the deployments map in
  * order to ensure that deployments aren't redeployed if this node leaves the cluster.
  */
 private void clearDeployments(final Handler<AsyncResult<Void>> doneHandler) {
   context.execute(
       new Action<Void>() {
         @Override
         public Void perform() {
           Collection<String> sdeploymentsInfo = deployments.get(group);
           for (String sdeploymentInfo : sdeploymentsInfo) {
             JsonObject deploymentInfo = new JsonObject(sdeploymentInfo);
             if (deploymentInfo.getString("address").equals(internal)) {
               deployments.remove(group, sdeploymentInfo);
             }
           }
           return null;
         }
       },
       doneHandler);
 }
  /** Finds a node in the group. */
  private void doFindNode(final Message<JsonObject> message) {
    String nodeName = message.body().getString("node");
    if (nodeName == null) {
      message.reply(
          new JsonObject().putString("status", "error").putString("message", "No node specified."));
      return;
    }

    final String address = String.format("%s.%s", group, nodeName);
    context.execute(
        new Action<Boolean>() {
          @Override
          public Boolean perform() {
            return groups.containsEntry(group, address);
          }
        },
        new Handler<AsyncResult<Boolean>>() {
          @Override
          public void handle(AsyncResult<Boolean> result) {
            if (result.failed()) {
              message.reply(
                  new JsonObject()
                      .putString("status", "error")
                      .putString("message", result.cause().getMessage()));
            } else if (!result.result()) {
              message.reply(
                  new JsonObject()
                      .putString("status", "error")
                      .putString("message", "Invalid node."));
            } else {
              message.reply(
                  new JsonObject().putString("status", "ok").putString("result", address));
            }
          }
        });
  }