CloudManagerAllPackageMojo.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2020 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.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
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.apache.maven.project.MavenProjectHelper;
import io.wcm.devops.conga.plugins.aem.maven.allpackage.AllPackageBuilder;
import io.wcm.devops.conga.plugins.aem.maven.model.InstallableFile;
import io.wcm.devops.conga.plugins.aem.maven.model.ModelParser;
/**
* Builds an "all" content package dedicated for deployment via Adobe Cloud Manager
* for the given environment and node(s).
* <p>
* By default, it builds one "all" package per environment and node without adding any Cloud Manager
* environment-specific run mode suffixes to the folders. By defining a parameter <code>cloudManager.target</code>
* (contains a list of string values) in the CONGA environment it is possible:
* </p>
* <ul>
* <li>If it contains <code>none</code> no "all" package is build.</li>
* <li>If set to one or multiple environment names (normally dev/stage/prod) one "all" package for each of
* them is defined, and the environment name is added as runmode suffix to all config and install folders.</li>
* </ul>
*/
@Mojo(name = "cloudmanager-all-package", threadSafe = true)
public final class CloudManagerAllPackageMojo extends AbstractCloudManagerMojo {
/**
* Package name for the "all" content package.
*/
@Parameter(property = "conga.cloudManager.allPackage.name", defaultValue = "all")
private String name;
/**
* Group name for the "all" content package.
*/
@Parameter(property = "conga.cloudManager.allPackage.group", required = true)
private String group;
/**
* Build one single content package for all environments and nodes.
*/
@Parameter(property = "conga.cloudManager.allPackage.singlePackage", defaultValue = "false")
private boolean singlePackage;
/**
* Attach "all" content package(s) as artifacts to maven build lifecycle.
* The given package name will be used as classifier.
*/
@Parameter(property = "conga.cloudManager.allPackage.attachArtifact", defaultValue = "false")
private boolean attachArtifact;
/**
* Automatically generate dependencies between content packages based on file order in CONGA configuration.
* <p>
* Possible options see
* <a href="apidocs/io/wcm/devops/conga/plugins/aem/maven/AutoDependenciesMode.html">AutoDependenciesMode</a>.
* </p>
*/
@Parameter(property = "conga.cloudManager.allPackage.autoDependenciesMode")
private AutoDependenciesMode autoDependenciesMode;
/**
* How to optimize author/publish run modes in resulting "all" package.
* <p>
* Possible options see
* <a href="apidocs/io/wcm/devops/conga/plugins/aem/maven/RunModeOptimization.html">RunModeOptimization</a>.
* </p>
*/
@Parameter(property = "conga.cloudManager.allPackage.runModeOptimization", defaultValue = "OFF")
private RunModeOptimization runModeOptimization;
/**
* How to validate package types to be included in "all" package.
* <p>
* Possible options see
* <a href="apidocs/io/wcm/devops/conga/plugins/aem/maven/PackageTypeValidation.html">PackageTypeValidation</a>.
* </p>
*/
@Parameter(property = "conga.cloudManager.allPackage.packageTypeValidation", defaultValue = "STRICT")
private PackageTypeValidation packageTypeValidation;
/**
* How to handle versions of packages and sub-packages inside "all" package.
* <p>
* Possible options see
* <a href="apidocs/io/wcm/devops/conga/plugins/aem/maven/PackageVersionMode.html">PackageVersionMode</a>.
* </p>
*/
@Parameter(property = "conga.cloudManager.allPackage.packageVersionMode", defaultValue = "DEFAULT")
private PackageVersionMode packageVersionMode;
/**
* Automatically generate dependencies between content packages based on file order in CONGA configuration.
* @deprecated Please use autoDependenciesMode instead.
*/
@Deprecated(forRemoval = true)
@Parameter(property = "conga.cloudManager.allPackage.autoDependencies", defaultValue = "true")
private boolean autoDependencies;
/**
* Use separate dependency chains for mutable and immutable packages.
* @deprecated Please use autoDependenciesMode instead.
*/
@Deprecated(forRemoval = true)
@Parameter(property = "conga.cloudManager.allPackage.autoDependenciesSeparateMutable", defaultValue = "false")
private boolean autoDependenciesSeparateMutable;
/**
* Specifies additional properties to be set in the properties.xml file.
*/
@Parameter
private Map<String, String> properties;
/**
* Set this to "true" to skip installing packages to CRX although configured in the POM.
*/
@Parameter(property = "conga.cloudManager.allPackage.skip", defaultValue = "false")
private boolean skip;
@Parameter(defaultValue = "${project.build.outputTimestamp}")
private String outputTimestamp;
@Parameter(readonly = true, defaultValue = "${project}")
private MavenProject project;
@Component
private MavenProjectHelper projectHelper;
private static final String CLOUDMANAGER_TARGET_NONE = "none";
@Override
@SuppressWarnings("java:S5738") // use deprecated for backward-compatibility
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
return;
}
if (this.autoDependenciesMode == null) {
if (this.autoDependencies) {
if (this.autoDependenciesSeparateMutable) {
this.autoDependenciesMode = AutoDependenciesMode.IMMUTABLE_MUTABLE_SEPARATE;
}
else {
this.autoDependenciesMode = AutoDependenciesMode.IMMUTABLE_MUTABLE_COMBINED;
}
}
else {
this.autoDependenciesMode = AutoDependenciesMode.OFF;
}
}
if (singlePackage) {
buildSingleAllPackage();
}
else if (runModeOptimization == RunModeOptimization.ELIMINATE_DUPLICATES) {
buildAllPackagesPerEnvironment();
}
else {
buildAllPackagesPerEnvironmentAndNode();
}
}
/**
* Build an "all" package for each environment and node.
*/
private void buildAllPackagesPerEnvironmentAndNode() throws MojoExecutionException, MojoFailureException {
visitEnvironmentsNodes((environmentDir, nodeDir, cloudManagerTarget, files) -> {
String packageName = environmentDir.getName() + "." + nodeDir.getName() + "." + this.name;
AllPackageBuilder builder = createBuilder(packageName);
try {
builder.add(files, cloudManagerTarget);
}
catch (IllegalArgumentException ex) {
throw new MojoFailureException(ex.getMessage(), ex);
}
buildAllPackage(builder);
});
}
/**
* Build an "all" package for each environment, including all nodes of that environment in a single file.
*/
private void buildAllPackagesPerEnvironment() throws MojoExecutionException, MojoFailureException {
SortedMap<String, AllPackageBuilder> builderPerEnvironment = new TreeMap<>();
visitEnvironmentsNodes((environmentDir, nodeDir, cloudManagerTarget, files) -> {
String packageName = environmentDir.getName() + "." + this.name;
AllPackageBuilder builder = builderPerEnvironment.computeIfAbsent(packageName, this::createBuilder);
try {
builder.add(files, cloudManagerTarget);
}
catch (IllegalArgumentException ex) {
throw new MojoFailureException(ex.getMessage(), ex);
}
});
for (AllPackageBuilder builder : builderPerEnvironment.values()) {
buildAllPackage(builder);
}
}
/**
* Build a single "all" package containing packages from all environments and nodes.
*/
private void buildSingleAllPackage() throws MojoExecutionException, MojoFailureException {
String packageName = this.name;
AllPackageBuilder builder = createBuilder(packageName);
visitEnvironmentsNodes((environmentDir, nodeDir, cloudManagerTarget, files) -> {
try {
builder.add(files, cloudManagerTarget);
}
catch (IllegalArgumentException ex) {
throw new MojoFailureException(ex.getMessage(), ex);
}
});
buildAllPackage(builder);
}
private AllPackageBuilder createBuilder(String packageName) {
String fileName;
if (attachArtifact) {
fileName = project.getArtifactId() + "." + packageName + "-" + project.getVersion() + ".zip";
}
else {
fileName = packageName + ".zip";
}
File targetFile = new File(getTargetDir(), fileName);
return new AllPackageBuilder(targetFile, this.group, packageName)
.version(project.getVersion())
.autoDependenciesMode(this.autoDependenciesMode)
.runModeOptimization(this.runModeOptimization)
.packageTypeValidation(this.packageTypeValidation)
.packageVersionMode(this.packageVersionMode)
.logger(getLog())
.buildOutputTimestamp(new BuildOutputTimestamp(outputTimestamp));
}
private void buildAllPackage(AllPackageBuilder builder) throws MojoExecutionException {
try {
getLog().debug("Start generating " + getCanonicalPath(builder.getTargetFile()) + "...");
if (builder.build(properties)) {
getLog().info("Generated " + getCanonicalPath(builder.getTargetFile()));
if (attachArtifact) {
projectHelper.attachArtifact(this.project, "zip", builder.getPackageName(), builder.getTargetFile());
}
}
else {
getLog().debug("Skipped " + getCanonicalPath(builder.getTargetFile()) + " - no valid package.");
}
}
catch (IOException ex) {
throw new MojoExecutionException("Unable to generate " + getCanonicalPath(builder.getTargetFile()), ex);
}
}
private void visitEnvironmentsNodes(EnvironmentNodeVisitor visitor) throws MojoExecutionException, MojoFailureException {
List<File> environmentDirs = getEnvironmentDir();
for (File environmentDir : environmentDirs) {
List<File> nodeDirs = getNodeDirs(environmentDir);
for (File nodeDir : nodeDirs) {
ModelParser modelParser = new ModelParser(nodeDir);
Set<String> cloudManagerTarget = modelParser.getCloudManagerTarget();
boolean validNodeForAllPackage = false;
if (cloudManagerTarget.contains(CLOUDMANAGER_TARGET_NONE)) {
if (isEnvironmentConfiguredExplicitely(environmentDir.getName())) {
// cloud manager target is set to "none" - but environment is configured explicitly, so include it
validNodeForAllPackage = true;
cloudManagerTarget.remove(CLOUDMANAGER_TARGET_NONE);
}
}
else {
// cloud manager target is not set to "none" - include node
validNodeForAllPackage = true;
}
if (validNodeForAllPackage) {
List<InstallableFile> files = modelParser.getInstallableFilesForNode();
visitor.visit(environmentDir, nodeDir, cloudManagerTarget, files);
}
}
}
}
interface EnvironmentNodeVisitor {
void visit(File environmentDir, File nodeDir, Set<String> cloudManagerTarget,
List<InstallableFile> files) throws MojoExecutionException, MojoFailureException;
}
}