InstallPackagesMojo.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2017 wcm.io
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package io.wcm.devops.conga.plugins.aem.maven;

import static io.wcm.devops.conga.generator.util.FileUtil.getCanonicalPath;

import java.io.File;
import java.util.List;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
import org.codehaus.plexus.util.xml.Xpp3Dom;

import io.wcm.devops.conga.plugins.aem.maven.model.BundleFile;
import io.wcm.devops.conga.plugins.aem.maven.model.InstallableFile;
import io.wcm.devops.conga.plugins.aem.maven.model.ModelContentPackageFile;
import io.wcm.devops.conga.plugins.aem.maven.model.ModelParser;
import io.wcm.tooling.commons.packmgr.install.PackageFile;
import io.wcm.tooling.commons.packmgr.install.PackageInstaller;

/**
 * Installs all AEM content packages and OSGi bundles to AEM which are referenced in a model.yaml
 * generated by CONGA for a node.
 */
@Mojo(name = "package-install", threadSafe = true, requiresProject = false)
public final class InstallPackagesMojo extends AbstractContentPackageMojo {

  /**
   * Directory with the generated CONGA configuration containing the model.yaml.
   */
  @Parameter(required = true, property = "conga.nodeDirectory")
  private File nodeDirectory;

  /**
   * Whether to install (unpack) the uploaded package automatically or not.
   */
  @Parameter(property = "vault.install", defaultValue = "true")
  private boolean install;

  /**
   * Force upload and install of content package. If set to:
   * <ul>
   * <li><code>true</code>: Package is always installed, even if it was already uploaded before.</li>
   * <li><code>false</code>: Package is only installed if it was not already uploade before.</li>
   * <li>nothing (default): Force is applied to packages with the string "-SNAPSHOT" in it's filename.</li>
   * </ul>
   */
  @Parameter(property = "vault.force")
  private Boolean force;

  /**
   * If set to true nested packages get installed as well.
   */
  @Parameter(property = "vault.recursive", defaultValue = "true")
  private boolean recursive;

  /**
   * Delay further steps after package installation by this amount of seconds
   */
  @Parameter(property = "vault.delayAfterInstallSec")
  private Integer delayAfterInstallSec;

  /**
   * Replicate package(s) to publish instance after upload.
   */
  @Parameter(property = "vault.replicatePackage")
  private boolean replicate;

  /**
   * Version of Sling plugin
   */
  @Parameter(property = "sling.plugin.version", required = true, defaultValue = "2.4.2")
  private String slingPluginVersion;

  @Parameter(defaultValue = "${project}", readonly = true)
  private MavenProject project;
  @Parameter(defaultValue = "${session}", readonly = true)
  private MavenSession session;
  @Component(role = MavenPluginManager.class)
  private MavenPluginManager pluginManager;
  @Component(role = BuildPluginManager.class)
  private BuildPluginManager buildPluginManager;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    if (isSkip()) {
      return;
    }

    if (!nodeDirectory.exists() || !nodeDirectory.isDirectory()) {
      throw new MojoFailureException("Node directory not found: " + getCanonicalPath(nodeDirectory));
    }

    getLog().info("Get AEM content packages from " + getCanonicalPath(nodeDirectory));

    // collect files to install
    ModelParser modelParser = new ModelParser(nodeDirectory);
    List<InstallableFile> items = modelParser.getInstallableFilesForNode();

    // ensure any file exist
    if (items.isEmpty()) {
      getLog().warn("No file found for installing.");
      return;
    }

    // install files
    PackageInstaller installer = new PackageInstaller(getPackageManagerProperties());
    installer.setReplicate(this.replicate);

    for (InstallableFile item : items) {
      if (item instanceof ModelContentPackageFile) {
        PackageFile packageFile = toPackageFile((ModelContentPackageFile)item);
        installer.installFile(packageFile);
      }
      else if (item instanceof BundleFile) {
        BundleFile bundleFile = (BundleFile)item;
        if (bundleFile.getInstall() == null || bundleFile.getInstall()) {
          installBundleViaSlingPlugin(bundleFile.getFile());
        }
      }
      else {
        getLog().warn("Unsupported file: " + getCanonicalPath(item.getFile()));
      }
    }
  }

  private PackageFile toPackageFile(ModelContentPackageFile item) {
    PackageFile output = new PackageFile();

    output.setFile(item.getFile());
    Boolean installParam = item.getInstall();
    if (installParam != null) {
      output.setInstall(installParam);
    }
    else {
      output.setInstall(this.install);
    }
    Boolean forcePAram = item.getForce();
    if (forcePAram != null) {
      output.setForce(forcePAram);
    }
    else {
      output.setForce(this.force);
    }
    Boolean recursiveParam = item.getRecursive();
    if (recursiveParam != null) {
      output.setRecursive(recursiveParam);
    }
    else {
      output.setRecursive(this.recursive);
    }
    Integer delayAfterInstallSecParam = item.getDelayAfterInstallSec();
    if (delayAfterInstallSecParam != null) {
      output.setDelayAfterInstallSec(delayAfterInstallSecParam);
    }
    else if (this.delayAfterInstallSec != null) {
      output.setDelayAfterInstallSec(this.delayAfterInstallSec);
    }
    else {
      output.setDelayAfterInstallSecAutoDetect();
    }
    output.setHttpSocketTimeoutSec(item.getHttpSocketTimeoutSec());

    return output;
  }

  /**
   * Executes the sling-maven-plugin directly from the current project to install OSGi bundles.
   */
  private void installBundleViaSlingPlugin(File file) throws MojoExecutionException {
    Plugin plugin = new Plugin();
    plugin.setGroupId("org.apache.sling");
    plugin.setArtifactId("sling-maven-plugin");
    plugin.setVersion(this.slingPluginVersion);

    try {
      PluginDescriptor pluginDescriptor = pluginManager.getPluginDescriptor(plugin,
          project.getRemotePluginRepositories(), session.getRepositorySession());
      MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo("install-file");
      MojoExecution mojoExecution = new MojoExecution(pluginDescriptor.getMojo("install-file"));

      Xpp3Dom config = convertConfiguration(mojoDescriptor.getMojoConfiguration());
      config.getChild("slingUrl").setValue(buildConsoleUrl());
      config.getChild("user").setValue(this.getConsoleUser());
      config.getChild("password").setValue(this.getConsolePassword());
      config.getChild("mountByFS").setValue("false");
      config.getChild("bundleFileName").setValue(file.getAbsolutePath());
      mojoExecution.setConfiguration(config);

      buildPluginManager.executeMojo(session, mojoExecution);
    }
    catch (Exception ex) {
      throw new MojoExecutionException("Faild to execute plugin: " + plugin, ex);
    }
  }

  private Xpp3Dom convertConfiguration(PlexusConfiguration plexusConfig) throws PlexusConfigurationException {
    Xpp3Dom config = new Xpp3Dom(plexusConfig.getName());
    config.setValue(plexusConfig.getValue());
    for (String attribute : plexusConfig.getAttributeNames()) {
      config.setAttribute(attribute, plexusConfig.getAttribute(attribute));
    }
    for (PlexusConfiguration child : plexusConfig.getChildren()) {
      config.addChild(convertConfiguration(child));
    }
    return config;
  }

}