blob: 48985a1ae7cdf0ac1e9bc6922af06fd895b2e366 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.sling.cpconverter.maven.mojos;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
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.ProjectBuildingRequest;
import org.apache.maven.shared.transfer.artifact.install.ArtifactInstallerException;
import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
import org.apache.sling.feature.cpconverter.accesscontrol.DefaultAclManager;
import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
import org.apache.sling.feature.cpconverter.filtering.RegexBasedResourceFilter;
import org.apache.sling.feature.cpconverter.filtering.ResourceFilter;
import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.PACKAGE_CLASSIFIER;
import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.ZIP_TYPE;
/**
* Converts the given Content Packages into Feature Models
* and places the converted Content Package into the local
* Maven Repository
*/
@Mojo(
name = "convert-cp",
requiresProject = true,
threadSafe = true
)
public class ConvertCPMojo
extends AbstractBaseMojo
{
public static final String CFG_STRICT_VALIDATION = "strictValidation";
public static final String CFG_MERGE_CONFIGURATIONS = "mergeConfigurations";
public static final String CFG_BUNDLE_START_ORDER = "bundleStartOrder";
public static final String CFG_ARTIFACT_ID_OVERRIDE = "artifactIdOverride";
public static final String CFG_INSTALL_CONVERTED_PACKAGE = "installConvertedContentPackage";
public static final String CFG_FM_OUTPUT_DIRECTORY = "featureModelsOutputDirectory";
public static final String CFG_CONVERTED_CP_OUTPUT_DIRECTORY = "convertedContentPackageOutputDirectory";
public static final String CFG_CONVERTED_CP_FM_PREFIX = "fmPrefix";
public static final String CFG_SYSTEM_PROPERTIES = "cpSystemProperties";
public static final String CFG_CONTENT_PACKAGES = "packages";
public static final String CFG_IS_CONTENT_PACKAGE = "isContentPackage";
public static final String CFG_API_REGIONS = "apiRegions";
public static final String CFG_EXPORT_TO_API_REGION = "exportToApiRegion";
public static final String CFG_FILTER_PATTERNS = "filterPatterns";
public static final boolean DEFAULT_STRING_VALIDATION = false;
public static final boolean DEFAULT_MERGE_CONFIGURATIONS = false;
public static final int DEFAULT_BUNDLE_START_ORDER = 20;
public static final boolean DEFAULT_INSTALL_CONVERTED_PACKAGE = true;
public static final String DEFAULT_CONVERTED_CP_OUTPUT_DIRECTORY = "${project.build.directory}/cp-conversion";
public static final String DEFAULT_FM_OUTPUT_DIRECTORY = DEFAULT_CONVERTED_CP_OUTPUT_DIRECTORY + "/fm.out";
public static final boolean DEFAULT_IS_CONTENT_PACKAGE = false;
/**
* If set to {@code true} the Content Package is strictly validated.
*/
@Parameter(property = CFG_STRICT_VALIDATION, defaultValue = DEFAULT_STRING_VALIDATION + "")
private boolean strictValidation;
//AS TODO: Is that applicable to a single CP ?
/**
* If set to {@code true} the OSGi Configurations with same PID are merged.
*/
@Parameter(property = CFG_MERGE_CONFIGURATIONS, defaultValue = DEFAULT_MERGE_CONFIGURATIONS + "")
private boolean mergeConfigurations;
/**
* If set to {@code true} the Content Package is strictly validated.
*/
@Parameter(property = CFG_BUNDLE_START_ORDER, defaultValue = DEFAULT_BUNDLE_START_ORDER + "")
private int bundleStartOrder;
/**
* If set to {@code true} the Content Package is strictly validated.
*/
@Parameter(property = CFG_ARTIFACT_ID_OVERRIDE)
private String artifactIdOverride;
/**
* Target directory for the converted Content Package Feature Model file
*/
@Parameter(property = CFG_FM_OUTPUT_DIRECTORY, defaultValue = DEFAULT_FM_OUTPUT_DIRECTORY)
private File fmOutput;
/**
* Target directory for the Converted Content Package file
*/
@Parameter(property = CFG_CONVERTED_CP_OUTPUT_DIRECTORY, defaultValue = DEFAULT_CONVERTED_CP_OUTPUT_DIRECTORY)
private File convertedCPOutput;
/**
* If set to {@code true} the converted Content Package will be installed in the local Maven Repository
*/
@Parameter(property = CFG_INSTALL_CONVERTED_PACKAGE, defaultValue = DEFAULT_INSTALL_CONVERTED_PACKAGE + "")
private boolean installConvertedCP;
/**
* System Properties to hand over to the Content Package Converter
*/
@Parameter(property = CFG_CONVERTED_CP_FM_PREFIX)
private String fmPrefix;
/**
* System Properties to hand over to the Content Package Converter
*/
@Parameter(property = CFG_SYSTEM_PROPERTIES)
private List<String> systemProperties;
/**
* List of Content Packages to be converted
*/
@Parameter(property = CFG_CONTENT_PACKAGES)
private List<ContentPackage> contentPackages;
/**
* If set to {@code true} the module is handled as Content Package
*/
@Parameter(property = CFG_IS_CONTENT_PACKAGE, defaultValue = DEFAULT_IS_CONTENT_PACKAGE + "")
private boolean isContentPackage;
/**
* Specify the API Regions that the generated feature is made part of
*/
@Parameter(property = CFG_API_REGIONS)
private List<String> apiRegions;
/**
* Specify the API Region to export all exported packages to, if not specified
* packages will not be added to the api-regions extension.
*/
@Parameter(property = CFG_EXPORT_TO_API_REGION)
private String exportToApiRegion;
/**
* Regex based pattern(s) to reject content-package archive entries.
*/
@Parameter(property = CFG_FILTER_PATTERNS)
private List<String> filterPatterns;
@Parameter(defaultValue="${repositorySystemSession}")
private RepositorySystemSession repoSession;
@Component
private RepositorySystem repoSystem;
@Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true)
private List<RemoteRepository> remoteRepos;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
// Un-encode a given Artifact Override Id
if(artifactIdOverride != null) {
String old = artifactIdOverride;
artifactIdOverride = artifactIdOverride.replaceAll("\\$\\{\\{", "\\$\\{");
artifactIdOverride = artifactIdOverride.replaceAll("}}", "}");
getLog().info("Replaced Old Artifact Id Override: '" + old + "', with new one: '" + artifactIdOverride + "'");
}
// Parse the System Properties if provided
Map<String,String> properties = new HashMap<>();
if(systemProperties != null) {
for(String systemProperty: systemProperties) {
if(systemProperty != null) {
int index = systemProperty.indexOf("=");
if(index > 0 && index < systemProperty.length() - 1) {
String key = systemProperty.substring(0, index);
String value = systemProperty.substring(index + 1);
properties.put(key, value);
}
}
}
}
try {
DefaultFeaturesManager featuresManager = new DefaultFeaturesManager(
mergeConfigurations,
bundleStartOrder,
fmOutput,
artifactIdOverride,
fmPrefix,
properties
);
if (!apiRegions.isEmpty())
featuresManager.setAPIRegions(apiRegions);
if (exportToApiRegion != null)
featuresManager.setExportToAPIRegion(exportToApiRegion);
ContentPackage2FeatureModelConverter converter = new ContentPackage2FeatureModelConverter(strictValidation)
.setFeaturesManager(
featuresManager
)
.setBundlesDeployer(
new DefaultArtifactsDeployer(
convertedCPOutput
)
)
.setEntryHandlersManager(
new DefaultEntryHandlersManager()
)
.setAclManager(
new DefaultAclManager()
)
.setEmitter(DefaultPackagesEventsEmitter.open(fmOutput))
.setResourceFilter(getResourceFilter());
if(contentPackages == null || contentPackages.isEmpty()) {
getLog().info("Project Artifact File: " + project.getArtifact());
String targetPath = project.getModel().getBuild().getDirectory() + "/"
+ project.getModel().getBuild().getFinalName()
+ "." + ZIP_TYPE;
File targetFile = new File(targetPath);
if (targetFile.exists() && targetFile.isFile() && targetFile.canRead()) {
converter.convert(project.getArtifact().getFile());
} else {
getLog().error("Artifact is not found: " + targetPath);
}
} else {
for(ContentPackage contentPackage: contentPackages) {
contentPackage.setExcludeTransitive(true);
contentPackage.setModuleIsContentPackage(isContentPackage);
getLog().info("Content Package Artifact File: " + contentPackage.toString() + ", is module CP: " + isContentPackage);
final Collection<Artifact> artifacts =
contentPackage.getMatchingArtifacts(project, repoSystem, repoSession, remoteRepos);
if (artifacts.isEmpty()) {
getLog().warn("No matching artifacts for " + contentPackage);
continue;
}
getLog().info("Target Convert CP of --- " + contentPackage + " ---");
for (final Artifact artifact : artifacts) {
final File source = artifact.getFile();
getLog().info("Artifact: '" + artifact + "', source file: '" + source + "'");
if (source != null && source.exists() && source.isFile() && source.canRead()) {
converter.convert(source);
Artifact convertedPackage = new DefaultArtifact(
artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
"compile", ZIP_TYPE, PACKAGE_CLASSIFIER, artifactHandlerManager.getArtifactHandler(ZIP_TYPE)
);
// installConvertedCP(convertedPackage);
} else {
getLog().error("Artifact is not found: " + artifact);
}
}
}
}
installGeneratedArtifacts();
} catch (Throwable t) {
throw new MojoExecutionException("Content Package Converter Exception", t);
}
}
private ResourceFilter getResourceFilter() {
if (filterPatterns == null || filterPatterns.size() == 0)
return null;
RegexBasedResourceFilter filter = new RegexBasedResourceFilter();
for (String filterPattern : filterPatterns) {
filter.addFilteringPattern(filterPattern);
}
return filter;
}
/**
* For now this is a hack to update the local Maven Repo (.m2/repository) correctly
* bypassing the Maven Installer as this is giving us grief
*/
private void installGeneratedArtifacts() {
if(installConvertedCP) {
File userHome = new File(System.getProperty("user.home"));
if(userHome.isDirectory()) {
File destFolder = new File(userHome, ".m2/repository");
if(destFolder.isDirectory()) {
copyFiles(convertedCPOutput, destFolder);
if(isContentPackage) {
installFMDescriptor(project.getArtifact());
}
}
}
}
}
/**
* Copies the local generated Maven Artifacts into the local Maven Repo
* recursively one folder at the time.
* Folder are created on the local Maven Repo if not found, missing files
* are copied over but only CP2FM Converted CP files are replaced on the
* target folders
*
* @param source Folder with the source files / folders
* @param destination Destination folder (inside the local Maven repo). This is the corresponding folder to the source
*/
private void copyFiles(File source, File destination) {
for(File file: source.listFiles()) {
String name = file.getName();
if(file.isDirectory()) {
File newDest = new File(destination, name);
if(!newDest.exists()) {
newDest.mkdirs();
}
if(newDest.isDirectory()) {
copyFiles(file, newDest);
} else {
getLog().warn("Source File: '" + file.getAbsolutePath() + "' is a folder but its counterpart is a file: " + newDest.getAbsolutePath());
}
} else {
File newDest = new File(destination, name);
if(!newDest.exists()) {
// Copy File over
try {
Files.copy(file.toPath(), newDest.toPath(), StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
getLog().warn("Failed to copy File: '" + file.getAbsolutePath() + "' to File: " + newDest.getAbsolutePath(), e);
}
} else {
// We only overwrite converted files
if(name.endsWith(PACKAGE_CLASSIFIER + "." + ZIP_TYPE)) {
// Copy File over
try {
Files.copy(file.toPath(), newDest.toPath(), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
getLog().warn("Failed to copy generated File: '" + file.getAbsolutePath() + "' to File: " + newDest.getAbsolutePath(), e);
}
} else {
getLog().info("Ignore File: '" + file.getAbsolutePath());
}
}
}
}
}
private void installFMDescriptor(Artifact artifact) {
if(installConvertedCP) {
Collection<Artifact> artifacts = Collections.synchronizedCollection(new ArrayList<>());
// Source FM Descriptor File Path
String fmDescriptorFilePath = fmPrefix + artifact.getArtifactId() + ".json";
File fmDescriptorFile = new File(fmOutput, fmDescriptorFilePath);
if(fmDescriptorFile.exists() && fmDescriptorFile.canRead()) {
// Need to create a new Artifact Handler for the different extension and an Artifact to not
// change the module artifact
DefaultArtifactHandler fmArtifactHandler = new DefaultArtifactHandler("slingosgifeature");
DefaultArtifact fmArtifact = new DefaultArtifact(
artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
artifact.getScope(), "slingosgifeature", artifact.getArtifactId(), fmArtifactHandler
);
fmArtifact.setFile(fmDescriptorFile);
artifacts.add(fmArtifact);
try {
installArtifact(mavenSession.getProjectBuildingRequest(), artifacts);
} catch (MojoFailureException | MojoExecutionException e) {
getLog().error("Failed to install FM Descriptor", e);
}
} else {
getLog().error("Could not find FM Descriptor File: " + fmDescriptorFile);
}
}
}
private void installArtifact(ProjectBuildingRequest pbr, Collection<Artifact> artifacts )
throws MojoFailureException, MojoExecutionException
{
try
{
installer.install(pbr, artifacts);
}
catch ( ArtifactInstallerException e )
{
throw new MojoExecutionException( "ArtifactInstallerException", e );
}
}
}