/** {@inheritDoc} */
  @Override
  public OperationResult execute(
      OperationContext context, ModelNode operation, ResultHandler resultHandler)
      throws OperationFailedException {

    validator.validate(operation);

    String name = operation.require(NAME).asString();
    String runtimeName =
        operation.hasDefined(RUNTIME_NAME) ? operation.get(RUNTIME_NAME).asString() : name;
    byte[] hash;
    if (operation.hasDefined(INPUT_STREAM_INDEX) && operation.hasDefined(HASH)) {
      throw new OperationFailedException(
          new ModelNode().set("Can't pass in both an input-stream-index and a hash"));
    } else if (operation.hasDefined(INPUT_STREAM_INDEX)) {
      InputStream in = getContents(context, operation);
      try {
        hash = deploymentRepository.addDeploymentContent(in);
      } catch (IOException e) {
        throw new OperationFailedException(new ModelNode().set(e.toString()));
      }
    } else if (operation.hasDefined(HASH)) {

      hash = operation.get(HASH).asBytes();
      if (!deploymentRepository.hasDeploymentContent(hash)) {
        throw new OperationFailedException(
            new ModelNode()
                .set(
                    String.format(
                        "No deployment content with hash %s is available in the deployment content repository.",
                        HashUtil.bytesToHexString(hash))));
      }
    } else {
      throw new OperationFailedException(
          new ModelNode().set("Neither an attachment or a hash were passed in"));
    }

    ModelNode rootModel = context.getSubModel();
    ModelNode deployments = rootModel.get(DEPLOYMENT);

    ModelNode replaceNode = deployments.hasDefined(name) ? deployments.get(name) : null;
    if (replaceNode == null) {
      throw new OperationFailedException(
          new ModelNode().set(String.format("No deployment with name %s found", name)));
    }

    boolean start = replaceNode.get(ENABLED).asBoolean();

    ModelNode deployNode = new ModelNode();
    deployNode.get(NAME).set(name);
    deployNode.get(RUNTIME_NAME).set(runtimeName);
    deployNode.get(HASH).set(hash);
    deployNode.get(ENABLED).set(start);

    deployments.get(name).set(deployNode);

    ModelNode compensatingOp = operation.clone();
    compensatingOp.get(RUNTIME_NAME).set(replaceNode.get(RUNTIME_NAME).asString());
    compensatingOp.get(HASH).set(replaceNode.get(HASH).asBytes());
    if (operation.hasDefined(INPUT_STREAM_INDEX)) {
      operation.remove(INPUT_STREAM_INDEX);
    }

    if (start) {
      DeploymentHandlerUtil.replace(deployNode, name, context, resultHandler);
    } else {
      resultHandler.handleResultComplete();
    }

    return new BasicOperationResult(compensatingOp);
  }
  /**
   * Scan the given directory for content changes.
   *
   * @param directory the directory to scan
   * @param updates the update list;
   * @param foundDeployed place to store marker files found in the directory; key is the name of the
   *     deployment, value is the marker file
   * @param newlyAdded place to store names of newly added content
   * @param registeredDeployments TODO
   * @return the builder the current builder following any changes
   */
  private void scanDirectory(
      File directory,
      final List<ModelNode> updates,
      Map<String, File> foundDeployed,
      Set<String> newlyAdded,
      Set<String> registeredDeployments) {

    // TODO externalize config of filter?
    File[] children = directory.listFiles(filter);
    if (children == null) {
      return;
    }

    for (File child : children) {

      String fileName = child.getName();

      if (fileName.endsWith(DEPLOYED)) {
        String origName = fileName.substring(0, fileName.length() - DEPLOYED.length());
        foundDeployed.put(origName, child);
      } else if (child.isDirectory()) {
        int idx = fileName.lastIndexOf('.');
        if (idx > -1 && ARCHIVES.contains(fileName.substring(idx))) {
          // FIXME handle exploded deployments
          log.warnf(
              "%s is an exploded deployment and exploded deployments are not currently handled by %s",
              child.getName(), getClass().getSimpleName());
        } else {
          // It's just a dir for organizing content. Recurse
          scanDirectory(child, updates, foundDeployed, newlyAdded, registeredDeployments);
        }
      } else {
        // Found a single non-marker file
        boolean uploaded = false;
        if (registeredDeployments.contains(fileName)) {

          byte[] hash = new byte[0];
          try {
            final InputStream inputStream = new FileInputStream(child);
            hash = deploymentRepository.addDeploymentContent(fileName, fileName, inputStream);
          } catch (IOException e) {
            log.error("Failed to add content to deployment repository for [" + fileName + "]", e);
            continue;
          }
          updates.add(getFullReplaceOperation(fileName, hash));
          uploaded = true;
        } else {
          byte[] hash = new byte[0];
          try {
            final InputStream inputStream = new FileInputStream(child);
            hash = deploymentRepository.addDeploymentContent(fileName, fileName, inputStream);
          } catch (IOException e) {
            log.error("Failed to add content to deployment repository for [" + fileName + "]", e);
            continue;
          }
          updates.add(getAddOperation(fileName, hash));
          updates.add(getDeployOperation(fileName));
          uploaded = true;
        }

        if (uploaded && replaceWithDeployedMarker(child)) {
          newlyAdded.add(fileName);
        }
      }
    }
  }