| /* |
| * 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.acl.DefaultAclManager; |
| import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer; |
| import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager; |
| 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 CFP_EXPORT_TO_API_REGION = "exportToApiRegion"; |
| |
| 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 = CFP_EXPORT_TO_API_REGION) |
| private String exportToApiRegion; |
| |
| @Parameter(defaultValue="${repositorySystemSession}") |
| private RepositorySystemSession repoSession; |
| |
| @Component |
| private RepositorySystem repoSystem; |
| |
| @Parameter(defaultValue = "${project.remotePluginRepositories}", 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)); |
| |
| 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); |
| } |
| |
| } |
| |
| /** |
| * 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 ); |
| } |
| } |
| |
| } |