/** {@inheritDoc} */
  @Override
  public void execute() throws MojoExecutionException {

    // only ever interested in war and ear projects. silently ignore other projects.
    final String ext = project.getPackaging();
    if ("war".equals(ext) || "ear".equals(ext)) {

      pushFields();

      final Wagon wagon = getWagon();
      if (null != wagon) {

        try {
          executeServerDeploy(wagon);

        } finally {
          try {
            wagon.disconnect();

          } catch (ConnectionException ex) {
            getLog().error(ex);
            throw new MojoExecutionException("repository wagon not disconnected", ex);
          }
        }

      } else {

        executeLocalDeploy();
      }
    }
  }
  private void updateVersionFile(final Wagon wagon)
      throws IOException, TransferFailedException, ResourceDoesNotExistException,
          AuthorizationException {

    // update the version.txt accordingly
    final File versionOldFile = File.createTempFile("version-old", "txt");
    final File versionNewFile = File.createTempFile("version-new", "txt");
    wagon.get("version.txt", versionOldFile);

    boolean updated = false;

    // look for our artifactId entry
    final BufferedReader reader = new BufferedReader(new FileReader(versionOldFile));
    final BufferedWriter writer = new BufferedWriter(new FileWriter(versionNewFile));
    for (String line = reader.readLine(); null != line; line = reader.readLine()) {
      if (line.equals(project.getArtifactId())) {
        updateArtifactEntry(reader, writer);
        updated = true;
      } else {
        // line remains the same
        writer.write(line);
        writer.newLine();
      }
    }
    if (!updated) {
      writer.newLine();
      updateArtifactEntry(reader, writer);
    }
    reader.close();
    writer.close();
    wagon.put(versionNewFile, "version.txt");
  }
  /**
   * @return the wagon (already connected) to use against the profile's serverDeployLocation or null
   *     if in development profile
   * @throws org.apache.maven.plugin.MojoExecutionException
   */
  private Wagon getWagon() throws MojoExecutionException {

    loadProfile();

    try {

      Wagon wagon = null;

      if (null != profile) {

        final String serverDeployLocation =
            System.getProperty(
                SERVER_DEPLOY_LOCATION,
                project.getProperties().getProperty("serverDeployLocation"));

        final String protocol =
            serverDeployLocation.substring(0, serverDeployLocation.indexOf(':'));

        final Repository wagonRepository = new Repository();

        wagonRepository.setUrl(serverDeployLocation);

        final RepositoryPermissions permissions = new RepositoryPermissions();
        permissions.setFileMode("g+w");
        wagonRepository.setPermissions(permissions);

        wagon = (Wagon) container.lookup(Wagon.ROLE, protocol);
        wagon.connect(wagonRepository);
      }
      return wagon;

    } catch (ConnectionException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("repository wagon not connected", ex);
    } catch (AuthenticationException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("repository wagon not authenticated", ex);
    } catch (ComponentLookupException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("repository wagon not found", ex);
    }
  }
  protected List<String> scanForArtifactPaths(ArtifactRepository repository) {
    List<String> collected;
    try {
      Wagon wagon = wagonManager.getWagon(repository.getProtocol());
      Repository artifactRepository = new Repository(repository.getId(), repository.getUrl());
      wagon.connect(artifactRepository);
      collected = new ArrayList<String>();
      scan(wagon, "/", collected);
      wagon.disconnect();

      return collected;

    } catch (UnsupportedProtocolException e) {
      throw new RuntimeException(e);
    } catch (ConnectionException e) {
      throw new RuntimeException(e);
    } catch (AuthenticationException e) {
      throw new RuntimeException(e);
    }
  }
  private void scan(Wagon wagon, String basePath, List<String> collected) {
    try {
      List<String> files = wagon.getFileList(basePath);

      if (files.isEmpty()) {
        collected.add(basePath);
      } else {
        basePath = basePath + "/";
        for (String file : files) {
          logger.info("Found file in the source repository: " + file);
          scan(wagon, basePath + file, collected);
        }
      }
    } catch (TransferFailedException e) {
      throw new RuntimeException(e);
    } catch (ResourceDoesNotExistException e) {
      // is thrown when calling getFileList on a file
      collected.add(basePath);
    } catch (AuthorizationException e) {
      throw new RuntimeException(e);
    }
  }
  private void executeServerDeploy(final Wagon wagon) throws MojoExecutionException {

    // alpha|nuclei|beta|electron|gamma|production deployment goes through scpexe
    try {
      if (ensureNoLocalModifications()) {

        @SuppressWarnings("unchecked")
        final List<ArtifactItem> theArtifactItems = getProcessedArtifactItems(stripVersion);

        for (ArtifactItem item : theArtifactItems) {

          final Artifact artifact =
              factory.createArtifactWithClassifier(
                  item.getGroupId(),
                  item.getArtifactId(),
                  item.getVersion(),
                  item.getType(),
                  profile);

          resolver.resolve(artifact, getRemoteRepos(), getLocal());

          final String sesamSite = project.getProperties().getProperty("sesam.site");
          final String destName = null != sesamSite ? sesamSite : project.getBuild().getFinalName();

          // tag the code
          final boolean tagOnDeploy =
              Boolean.parseBoolean(
                  System.getProperty(
                      TAG_ON_DEPLOY, project.getProperties().getProperty("tag.on.deploy")));

          if (tagOnDeploy && !Boolean.getBoolean(DRY_RUN)) {
            tagDeploy();
          }

          // do the upload
          getLog()
              .info(
                  "Uploading "
                      + artifact.getFile().getAbsolutePath()
                      + " to "
                      + wagon.getRepository().getUrl()
                      + '/'
                      + destName
                      + ".war");

          if (!Boolean.getBoolean(DRY_RUN)) {
            wagon.put(artifact.getFile(), destName + ".war");
          }

          // update the version.txt
          getLog().info("Updating " + wagon.getRepository().getUrl() + "/version.txt");

          if (Boolean.getBoolean(DRY_RUN)) {

            final StringWriter sb = new StringWriter();
            final BufferedWriter w = new BufferedWriter(sb);
            updateArtifactEntry(new BufferedReader(new StringReader("")), w);
            w.flush();
            getLog().info("version.txt entry will be \n" + sb.toString());

          } else {

            updateVersionFile(wagon);
          }
        }
      }

    } catch (TransferFailedException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("transfer failed", ex);
    } catch (ResourceDoesNotExistException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("resource does not exist", ex);
    } catch (AuthorizationException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("authorisation exception", ex);
    } catch (ArtifactNotFoundException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("artifact not found", ex);
    } catch (ArtifactResolutionException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("artifact resolution exception", ex);
    } catch (ScmException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("scm exception", ex);
    } catch (ComponentLookupException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("failed to lookup ScmManager", ex);
    } catch (IOException ex) {
      getLog().error(ex);
      throw new MojoExecutionException("IOException", ex);
    }
  }
  public void copy(Repository sourceRepository, Repository targetRepository, String version)
      throws WagonException, IOException {
    String prefix = "staging-plugin";

    String fileName = prefix + "-" + version + ".zip";

    String tempdir = System.getProperty("java.io.tmpdir");

    logger.debug("Writing all output to " + tempdir);

    // Create the renameScript script

    String renameScriptName = prefix + "-" + version + "-rename.sh";

    File renameScript = new File(tempdir, renameScriptName);

    // Work directory

    File basedir = new File(tempdir, prefix + "-" + version);

    FileUtils.deleteDirectory(basedir);

    basedir.mkdirs();

    Wagon sourceWagon = wagonManager.getWagon(sourceRepository);
    AuthenticationInfo sourceAuth = wagonManager.getAuthenticationInfo(sourceRepository.getId());

    sourceWagon.connect(sourceRepository, sourceAuth);

    logger.info("Looking for files in the source repository.");

    List<String> files = new ArrayList<String>();

    scan(sourceWagon, "", files);

    logger.info("Downloading files from the source repository to: " + basedir);

    for (String s : files) {

      if (s.contains(".svn")) {
        continue;
      }

      File f = new File(basedir, s);

      FileUtils.mkdir(f.getParentFile().getAbsolutePath());

      logger.info("Downloading file from the source repository: " + s);

      sourceWagon.get(s, f);
    }

    // ----------------------------------------------------------------------------
    // Now all the files are present locally and now we are going to grab the
    // metadata files from the targetRepositoryUrl and pull those down locally
    // so that we can merge the metadata.
    // ----------------------------------------------------------------------------

    logger.info("Downloading metadata from the target repository.");

    Wagon targetWagon = wagonManager.getWagon(targetRepository);

    if (!(targetWagon instanceof CommandExecutor)) {
      throw new CommandExecutionException(
          "Wagon class '"
              + targetWagon.getClass().getName()
              + "' in use for target repository is not a CommandExecutor");
    }

    AuthenticationInfo targetAuth = wagonManager.getAuthenticationInfo(targetRepository.getId());

    targetWagon.connect(targetRepository, targetAuth);

    PrintWriter rw = new PrintWriter(new FileWriter(renameScript));

    File archive = new File(tempdir, fileName);

    for (String s : files) {

      if (s.startsWith("/")) {
        s = s.substring(1);
      }

      if (s.endsWith(MAVEN_METADATA)) {
        File emf = new File(basedir, s + IN_PROCESS_MARKER);

        try {
          targetWagon.get(s, emf);
        } catch (ResourceDoesNotExistException e) {
          // We don't have an equivalent on the targetRepositoryUrl side because we have something
          // new on the sourceRepositoryUrl side so just skip the metadata merging.

          continue;
        }

        try {
          mergeMetadata(emf);
        } catch (XmlPullParserException e) {
          throw new IOException("Metadata file is corrupt " + s + " Reason: " + e.getMessage());
        }
      }
    }

    Set moveCommands = new TreeSet();

    // ----------------------------------------------------------------------------
    // Create the Zip file that we will deploy to the targetRepositoryUrl stage
    // ----------------------------------------------------------------------------

    logger.info("Creating zip file.");

    OutputStream os = new FileOutputStream(archive);

    ZipOutputStream zos = new ZipOutputStream(os);

    scanDirectory(basedir, basedir, zos, version, moveCommands);

    // ----------------------------------------------------------------------------
    // Create the renameScript script. This is as atomic as we can
    // ----------------------------------------------------------------------------

    logger.info("Creating rename script.");

    for (Object moveCommand : moveCommands) {
      String s = (String) moveCommand;

      // We use an explicit unix '\n' line-ending here instead of using the println() method.
      // Using println() will cause files and folders to have a '\r' at the end if the plugin is run
      // on Windows.
      rw.print(s + "\n");
    }

    IOUtil.close(rw);

    ZipEntry e = new ZipEntry(renameScript.getName());

    zos.putNextEntry(e);

    InputStream is = new FileInputStream(renameScript);

    IOUtil.copy(is, zos);

    IOUtil.close(is);

    IOUtil.close(zos);

    sourceWagon.disconnect();

    // Push the Zip to the target system

    logger.info("Uploading zip file to the target repository.");

    targetWagon.put(archive, fileName);

    logger.info("Unpacking zip file on the target machine.");

    String targetRepoBaseDirectory = targetRepository.getBasedir();

    // We use the super quiet option here as all the noise seems to kill/stall the connection

    String command =
        "unzip -o -qq -d "
            + targetRepoBaseDirectory
            + " "
            + targetRepoBaseDirectory
            + "/"
            + fileName;

    ((CommandExecutor) targetWagon).executeCommand(command);

    logger.info("Deleting zip file from the target repository.");

    command = "rm -f " + targetRepoBaseDirectory + "/" + fileName;

    ((CommandExecutor) targetWagon).executeCommand(command);

    logger.info("Running rename script on the target machine.");

    command = "cd " + targetRepoBaseDirectory + "; sh " + renameScriptName;

    ((CommandExecutor) targetWagon).executeCommand(command);

    logger.info("Deleting rename script from the target repository.");

    command = "rm -f " + targetRepoBaseDirectory + "/" + renameScriptName;

    ((CommandExecutor) targetWagon).executeCommand(command);

    targetWagon.disconnect();
  }