/**
  * Extracts the package name from an apk file.
  *
  * @param apkFile apk file to extract package name from.
  * @return the package name from inside the apk file.
  */
 protected String extractPackageNameFromApk(File apkFile) throws MojoExecutionException {
   CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
   executor.setLogger(this.getLog());
   List<String> commands = new ArrayList<String>();
   commands.add("dump");
   commands.add("xmltree");
   commands.add(apkFile.getAbsolutePath());
   commands.add("AndroidManifest.xml");
   getLog().info(getAndroidSdk().getPathForTool("aapt") + " " + commands.toString());
   try {
     executor.executeCommand(getAndroidSdk().getPathForTool("aapt"), commands, true);
     final String xmlTree = executor.getStandardOut();
     return extractPackageNameFromAndroidManifestXmlTree(xmlTree);
   } catch (ExecutionException e) {
     throw new MojoExecutionException(
         "Error while trying to figure out package name from inside apk file " + apkFile);
   } finally {
     getLog().error(executor.getStandardError());
   }
 }
  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);
              }
            }
          }
        });
  }
  private static <C extends Command<R>, R extends Serializable> void executeCommand(
      CommandExecutor<C, R> executor,
      ZNode executorNode,
      final QueueEntry<C, R> entry,
      final NodeContext nodeContext) {
    String relativePath = entry.getResultPath().substring(executorNode.getPath().length() + 1);
    final ZNode output = executorNode.child(relativePath);
    final NodeCommandExecutionListener<C> listener = entry.getListener();

    try {
      C command = entry.getCommand();
      listener.onCommandExecutionStarted(command, nodeContext);
      R result = executor.execute(command, nodeContext);
      log.debug("Command {} executed", command);
      listener.onCommandExecuted(command);
      output.setObject(CommandExecutionResult.success(result));
    } catch (Throwable throwable) {
      // todo add fail event
      log.error("error during task execution", throwable);
      output.setObject(CommandExecutionResult.fail(throwable));
    }
  }
 /**
  * Undeploys an apk, specified by package name, from a connected emulator or usb device.
  *
  * @param packageName the package name to undeploy.
  * @param deleteDataAndCacheDirectoriesOnDevice <code>true</code> to delete the application's data
  *     and cache directories on the device, <code>false</code> to keep them.
  * @return <code>true</code> if successfully undeployed, <code>false</code> otherwise.
  */
 protected boolean undeployApk(String packageName, boolean deleteDataAndCacheDirectoriesOnDevice)
     throws MojoExecutionException {
   CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
   executor.setLogger(this.getLog());
   List<String> commands = new ArrayList<String>();
   commands.add("uninstall");
   if (!deleteDataAndCacheDirectoriesOnDevice) {
     commands.add("-k"); // ('-k' means keep the data and cache directories)
   }
   commands.add(packageName);
   getLog().info(getAndroidSdk().getPathForTool("adb") + " " + commands.toString());
   try {
     executor.executeCommand(getAndroidSdk().getPathForTool("adb"), commands, false);
     getLog().debug(executor.getStandardOut());
     getLog().debug(executor.getStandardError());
     return true;
   } catch (ExecutionException e) {
     getLog().error(executor.getStandardOut());
     getLog().error(executor.getStandardError());
     return false;
   }
 }
  /**
   * Deploys an apk file to a connected emulator or usb device.
   *
   * @param apkFile the file to deploy
   * @throws MojoExecutionException If there is a problem deploying the apk file.
   */
  protected void deployApk(File apkFile) throws MojoExecutionException {
    CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
    executor.setLogger(this.getLog());
    List<String> commands = new ArrayList<String>();

    // Check if a specific device should be used
    if (StringUtils.isNotBlank(device)) {
      if ("usb".equals(device)) {
        commands.add("-d");
      } else if ("emulator".equals(device)) {
        commands.add("-e");
      } else {
        commands.add("-s");
        commands.add(device);
      }
    }

    commands.add("install");
    commands.add("-r");
    commands.add(apkFile.getAbsolutePath());
    getLog().info(getAndroidSdk().getPathForTool("adb") + " " + commands.toString());
    try {
      executor.executeCommand(getAndroidSdk().getPathForTool("adb"), commands, false);
      final String standardOut = executor.getStandardOut();
      if (standardOut != null && standardOut.contains("Failure")) {
        throw new MojoExecutionException(
            "Error deploying "
                + apkFile
                + " to device. You might want to add command line parameter -Dandroid.undeployBeforeDeploy=true or add plugin configuration tag <undeployBeforeDeploy>true</undeployBeforeDeploy>\n"
                + standardOut);
      }
    } catch (ExecutionException e) {
      getLog().error(executor.getStandardOut());
      getLog().error(executor.getStandardError());
      throw new MojoExecutionException("Error deploying " + apkFile + " to device.", e);
    }
  }
  @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));
  }