All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.galleon.maven.AbstractFeaturePackBuildMojo Maven / Gradle / Ivy

There is a newer version: 7.3.0.Final
Show newest version
/*
 * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * 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.
 */
package org.wildfly.galleon.maven;

import static java.lang.String.format;
import static org.wildfly.galleon.maven.FeatureSpecGeneratorInvoker.MODULE_PATH_SEGMENT;
import static org.wildfly.galleon.maven.Util.mkdirs;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.TreeSet;

import javax.xml.stream.XMLStreamException;

import nu.xom.ParsingException;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.shared.artifact.resolve.ArtifactResolver;
import org.apache.maven.shared.artifact.resolve.ArtifactResolverException;
import org.apache.maven.shared.artifact.resolve.ArtifactResult;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
import org.jboss.galleon.Constants;
import org.jboss.galleon.Errors;
import org.jboss.galleon.ProvisioningDescriptionException;
import org.jboss.galleon.ProvisioningException;
import org.jboss.galleon.Stability;
import org.jboss.galleon.config.ConfigModel;
import org.jboss.galleon.config.FeaturePackConfig;
import org.jboss.galleon.layout.FeaturePackDescriber;
import org.jboss.galleon.layout.FeaturePackDescription;
import org.jboss.galleon.maven.plugin.FpMavenErrors;
import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager;
import org.jboss.galleon.spec.ConfigLayerSpec;
import org.jboss.galleon.spec.FeaturePackPlugin;
import org.jboss.galleon.spec.FeaturePackSpec;
import org.jboss.galleon.spec.FeatureSpec;
import org.jboss.galleon.spec.PackageDependencySpec;
import org.jboss.galleon.spec.PackageSpec;
import org.jboss.galleon.universe.FeaturePackLocation;
import org.jboss.galleon.util.CollectionUtils;
import org.jboss.galleon.util.IoUtils;
import org.jboss.galleon.util.StringUtils;
import org.jboss.galleon.util.ZipUtils;
import org.jboss.galleon.xml.FeaturePackXmlWriter;
import org.jboss.galleon.xml.FeatureSpecXmlParser;
import org.jboss.galleon.xml.PackageXmlParser;
import org.jboss.galleon.xml.PackageXmlWriter;
import org.wildfly.channel.ChannelManifest;
import org.wildfly.channel.ChannelManifestMapper;
import org.wildfly.channel.ManifestRequirement;
import org.wildfly.channel.MavenCoordinate;
import org.wildfly.galleon.maven.build.tasks.ResourcesTask;
import org.wildfly.galleon.plugin.ArtifactCoords;
import org.wildfly.galleon.plugin.WfConstants;
import org.wildfly.galleon.plugin.WildFlyChannelResolutionMode;

/**
 * This Maven mojo creates a WildFly style feature-pack archive from the
 * provided resources according to the feature-pack build configuration file and
 * attaches it to the current Maven project as an artifact.
 *
 * The content of the future feature-pack archive is first created in the
 * directory called `layout` under the module's build directory which is then
 * ZIPped to create the feature-pack artifact.
 *
 * @author Alexey Loubyansky
 */
public abstract class AbstractFeaturePackBuildMojo extends AbstractMojo {

    static final String ARTIFACT_LIST_CLASSIFIER = "artifact-list";
    static final String ARTIFACT_LIST_EXTENSION = "txt";

    static boolean isProvided(String module) {
        return module.startsWith("java.")
                || module.startsWith("jdk.")
                || module.equals("org.jboss.modules");
    }

    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
    protected RepositorySystemSession repoSession;

    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    protected MavenProject project;

    @Parameter(defaultValue = "${session}", readonly = true, required = true)
    protected MavenSession session;

    @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
    protected List repositories;

    /**
     * The name of the release the feature-pack represents which will be stored
     * in the feature-pack's `resources/wildfly/wildfly-tasks.properties` as
     * `product.release.name` property.
     */
    @Parameter(alias = "release-name", defaultValue = "${product.release.name}")
    private String releaseName;

    /**
     * Path to a properties file content of which will be added to
     * feature-pack's `resources/wildfly/wildfly-tasks.properties` file that is
     * used as the source of properties during file copying tasks with property
     * replacement.
     */
    @Parameter(alias = "task-properties-file", required = false)
    private File taskPropsFile;

    /**
     * Various properties that will be added to feature-pack's
     * `resources/wildfly/wildfly-tasks.properties`.
* NOTE: values of this parameter will overwrite the corresponding values * from task-properties-file parameter, in case it's also set.
*/ @Parameter(alias = "task-properties", required = false) protected Map taskProps = Collections.emptyMap(); /** * Generates a channel manifest YAML definition when the feature-pack is produced. * Any dependency from the feature pack is declared as a stream in the channel manifest. */ @Parameter(alias = "generate-channel-manifest", required = false, defaultValue = "false", property = "wildfly.feature.pack.generate-channel-manifest") protected boolean generateChannelManifest; /** * Add any feature-pack dependency as a required manifest in the manifest YAML definition. * This parameter has no effect if "generate-channel-manifest" is false. */ @Parameter(alias = "add-feature-packs-as-required-manifests", required = false, defaultValue = "true") protected boolean addFeaturePacksAsRequiredManifests; /** * Feature-pack WildFly channel resolution mode when WildFly channels are configured in the provisioning tooling used to * provision this feature-pack. * "NOT_REQUIRED" means that the feature-pack and artifacts can be resolved without WildFly channels. * "REQUIRED" means that the feature-pack and all its artifacts must be only resolved from WildFly channels. * "REQUIRED_FP_ONLY" means that only the feature-pack must be only resolved from WildFly channels. * Referenced artifacts can be resolved outside of configured WildFly channels. */ @Parameter(alias = "wildfly-channel-resolution-mode", required = false, defaultValue = "NOT_REQUIRED", property = "wildfly.feature.pack.require.channel.resolution") protected WildFlyChannelResolutionMode wildflyChannelResolutionMode; @Parameter(alias = "deploy-channel-manifest", required = false, defaultValue = "true", property = "wildfly.feature.pack.deploy-channel-manifest") protected boolean deployChannelManifest; @Component protected RepositorySystem repoSystem; @Component protected ArtifactResolver artifactResolver; @Component private MavenProjectHelper projectHelper; /** * The minimum stability level of the WildFly processes used to generate feature specs and include packages. * Set this if you need to generate feature specs for features with a lower stability level * than the default level of the WildFly process being used for feature-spec generation. * It overrides the value set in the wildfly-feature-pack-build.xml file. */ @Parameter(alias = "minimum-stability-level", required = false) protected String minimumStabilityLevel; /** * The default stability level used at provisioning time to generate * configuration and provision packages. Can't be used when {@code config-stability-level} or {@code package-stability-level} is set. * It overrides the value set in the wildfly-feature-pack-build.xml file. */ @Parameter(alias = "stability-level", required = false) protected String stabilityLevel; /** * The default stability level used at provisioning time to generate * configuration. Can't be used when {@code stability-level} is set. * It overrides the value set in the wildfly-feature-pack-build.xml file. */ @Parameter(alias = "config-stability-level", required = false) protected String configStabilityLevel; /** * Enforce that no package at a lower stability level than the minimum-stability-level is referenced from Galleon constructs. */ @Parameter(alias = "forbid-lower-stability-level-package-reference", required = false, defaultValue = "false") protected boolean forbidLowerStatibilityLevelPackageReference; /** * The default stability level used at provisioning time when installing packages/JBoss Modules modules. * Can't be used when {@code stability-level} is set. * If both the {@code config-stability-level} and the {@code package-stability-level} options are set, * the level of the {@code package-stability-level} option must imply the level of the {@code config-stability-level} option. * It overrides the value set in the wildfly-feature-pack-build.xml file. */ @Parameter(alias = "package-stability-level", required = false) protected String packageStabilityLevel; private MavenProjectArtifactVersions artifactVersions; private Map fpDependencies = Collections.emptyMap(); private Path workDir; private Path fpDir; private Path fpPackagesDir; private Path resourcesWildFly; private Path fpResourcesDir; private Path resourcesDir; private Stability buildTimestabilityLevel; private Stability defaultConfigStabilityLevel; private Stability defaultPackageStabilityLevel; private final Set lowerStabilityPackages = new HashSet<>(); @Override public void execute() throws MojoExecutionException, MojoFailureException { try { artifactVersions = MavenProjectArtifactVersions.getInstance(project); doExecute(); } catch (RuntimeException | Error | MojoExecutionException | MojoFailureException e) { throw e; } } // Stability options override the values in the buildConfig protected void setStability(WildFlyFeaturePackBuild buildConfig) throws MojoExecutionException { if (minimumStabilityLevel == null) { minimumStabilityLevel = buildConfig.getMinimumStabilityLevel(); } buildTimestabilityLevel = minimumStabilityLevel == null ? null : Stability.fromString(minimumStabilityLevel); if (stabilityLevel == null) { stabilityLevel = buildConfig.getStabilityLevel(); } if (configStabilityLevel == null) { configStabilityLevel = buildConfig.getConfigStabilityLevel(); } if (packageStabilityLevel == null) { packageStabilityLevel = buildConfig.getPackageStabilityLevel(); } if (stabilityLevel == null) { defaultConfigStabilityLevel = configStabilityLevel == null ? null : Stability.fromString(configStabilityLevel); defaultPackageStabilityLevel = packageStabilityLevel == null ? null : Stability.fromString(packageStabilityLevel); } else { if (configStabilityLevel != null) { throw new MojoExecutionException("stability option can't be set when config-stability-level option is set"); } if (packageStabilityLevel != null) { throw new MojoExecutionException("stability option can't be set when package-stability-level option is set"); } defaultConfigStabilityLevel = Stability.fromString(stabilityLevel); defaultPackageStabilityLevel = Stability.fromString(stabilityLevel); } // Check that the minimum Stability level enables the stability level checkStabilityLevels(buildTimestabilityLevel, defaultConfigStabilityLevel, defaultPackageStabilityLevel); } protected Stability getMinimumStabilityLevel() { return buildTimestabilityLevel; } protected Stability getPackageStabilityLevel() { return defaultPackageStabilityLevel; } protected Stability getConfigStabilityLevel() { return defaultConfigStabilityLevel; } private static void checkStabilityLevels(Stability min, Stability config, Stability pkg) throws MojoExecutionException { min = min == null ? Stability.DEFAULT : min; config = config == null ? Stability.DEFAULT : config; pkg = pkg == null ? Stability.DEFAULT : pkg; if (!min.enables(config)) { throw new MojoExecutionException("The minimum stability " + min + " doesn't enable the config stability " + config); } if (!min.enables(pkg)) { throw new MojoExecutionException("The minimum stability " + min + " doesn't enable the package stability " + pkg); } if (!pkg.enables(config)) { throw new MojoExecutionException("The package stability " + pkg + " doesn't enable the config stability " + config); } } protected Map getFpDependencies() { return fpDependencies; } protected MavenProjectArtifactVersions getArtifactVersions() { return artifactVersions; } protected abstract void doExecute() throws MojoExecutionException, MojoFailureException; protected void setupDirs(String buildName, String fpArtifactId, String layoutDir, Path resourcesDir) { if (workDir == null) { workDir = Paths.get(buildName, layoutDir); IoUtils.recursiveDelete(workDir); fpDir = workDir.resolve(project.getGroupId()).resolve(fpArtifactId).resolve(project.getVersion()); fpPackagesDir = fpDir.resolve(Constants.PACKAGES); fpResourcesDir = fpDir.resolve(Constants.RESOURCES); resourcesWildFly = fpResourcesDir.resolve(WfConstants.WILDFLY); this.resourcesDir = resourcesDir; } } protected Path getWorkDir() { Objects.requireNonNull(workDir); return workDir; } protected Path getWildFlyResourcesDir() { return resourcesWildFly; } protected Path getFpDir() { Objects.requireNonNull(fpDir); return fpDir; } protected Path getPackagesDir() { Objects.requireNonNull(fpPackagesDir); return fpPackagesDir; } public String resolveVersion(final String coordsWoVersion) throws MojoExecutionException { final String resolved = artifactVersions.getVersion(coordsWoVersion); if (resolved == null) { throw new MojoExecutionException("The project is missing dependency on " + coordsWoVersion); } return resolved; } protected void buildFeaturePack(FeaturePackDescription.Builder fpBuilder, WildFlyFeaturePackBuild buildConfig) throws MojoExecutionException { if (buildConfig.hasConfigs()) { for (ConfigModel config : buildConfig.getConfigs()) { try { fpBuilder.getSpecBuilder().addConfig(config); } catch (ProvisioningDescriptionException e) { throw new MojoExecutionException("Failed to add config to the feature-pack", e); } } } if (buildConfig.hasPlugins()) { addPlugins(fpBuilder.getSpecBuilder(), buildConfig.getPlugins()); } Util.mkdirs(resourcesWildFly); if (buildConfig.hasResourcesTasks()) { for (ResourcesTask task : buildConfig.getResourcesTasks()) { task.execute(this, fpResourcesDir); } } // properties try (OutputStream out = Files.newOutputStream(resourcesWildFly.resolve(WfConstants.WILDFLY_TASKS_PROPS))) { getFPConfigProperties().store(out, "WildFly feature-pack properties"); } catch (IOException e) { throw new MojoExecutionException("Failed to store feature-pack properties", e); } // artifact versions for (ArtifactCoords.Gav gav : buildConfig.getDependencies().keySet()) { getArtifactVersions().remove(gav.getGroupId(), gav.getArtifactId()); } // WildFly channels configuration try (OutputStream out = Files.newOutputStream(resourcesWildFly.resolve(WfConstants.WILDFLY_CHANNEL_PROPS))) { getWildFlyChannelProperties().store(out, "WildFly channel properties"); } catch (IOException e) { throw new MojoExecutionException("Failed to store WildFly channel properties", e); } // Copy resources from src. try { Path srcArtifacts = resourcesDir.resolve(Constants.RESOURCES); if (Files.exists(srcArtifacts)) { IoUtils.copy(srcArtifacts, fpResourcesDir); } getArtifactVersions().store(resourcesWildFly.resolve(WfConstants.ARTIFACT_VERSIONS_PROPS)); } catch (ProvisioningException | IOException e) { throw new MojoExecutionException("Failed to store artifact versions", e); } // Build a maven artifact resolver that looks into the local maven repo, not in the project // built artifacts. DefaultRepositorySystemSession noWorkspaceSession = new DefaultRepositorySystemSession(repoSession); noWorkspaceSession.setWorkspaceReader(null); noWorkspaceSession.setOffline(true); // Using maven 3.9, having the remote repositories is required for the SNAPSHOT deployed artifacts // to have their version properly resolved. We set the session to be offline, so no remote resolution. ArtifactListBuilder builder = new ArtifactListBuilder(new MavenArtifactRepositoryManager(repoSystem, noWorkspaceSession, repositories), repoSession.getLocalRepository().getBasedir().toPath(), getLog()); buildArtifactList(builder); addConfigPackages(resourcesDir.resolve(Constants.PACKAGES), fpDir.resolve(Constants.PACKAGES), fpBuilder); Util.copyIfExists(resourcesDir, fpDir, Constants.LAYERS); Util.copyIfExists(resourcesDir, fpDir, Constants.CONFIGS); addFeatures(resourcesDir.resolve(Constants.FEATURES), fpDir.resolve(Constants.FEATURES)); Util.copyDirIfExists(resourcesDir.resolve(Constants.FEATURE_GROUPS), fpDir.resolve(Constants.FEATURE_GROUPS)); final Path resourcesWildFly = getWildFlyResourcesDir(); if (buildConfig.hasStandaloneExtensions()) { persistExtensions(resourcesWildFly, WfConstants.EXTENSIONS_STANDALONE, buildConfig.getStandaloneExtensions()); } if (buildConfig.hasDomainExtensions()) { persistExtensions(resourcesWildFly, WfConstants.EXTENSIONS_DOMAIN, buildConfig.getDomainExtensions()); } if (buildConfig.hasHostExtensions()) { persistExtensions(resourcesWildFly, WfConstants.EXTENSIONS_HOST, buildConfig.getHostExtensions()); } // scripts final Path scriptsDir = resourcesDir.resolve(WfConstants.SCRIPTS); if (Files.exists(scriptsDir)) { if (!Files.isDirectory(scriptsDir)) { throw new MojoExecutionException(WfConstants.SCRIPTS + " is not a directory"); } try { IoUtils.copy(scriptsDir, resourcesWildFly.resolve(WfConstants.SCRIPTS)); } catch (IOException e) { throw new MojoExecutionException(Errors.copyFile(scriptsDir, resourcesWildFly.resolve(WfConstants.SCRIPTS)), e); } } final FeaturePackDescription fpLayout; try { fpBuilder.getSpecBuilder().setConfigStability(defaultConfigStabilityLevel); fpBuilder.getSpecBuilder().setPackageStability(defaultPackageStabilityLevel); fpLayout = fpBuilder.build(); FeaturePackXmlWriter.getInstance().write(fpLayout.getSpec(), getFpDir().resolve(Constants.FEATURE_PACK_XML)); } catch (XMLStreamException | IOException | ProvisioningDescriptionException e) { throw new MojoExecutionException(Errors.writeFile(getFpDir().resolve(Constants.FEATURE_PACK_XML)), e); } // build feature-packs from the layout and attach as project artifacts try (DirectoryStream wdStream = Files.newDirectoryStream(getWorkDir(), entry -> Files.isDirectory(entry))) { for (Path groupDir : wdStream) { try (DirectoryStream groupStream = Files.newDirectoryStream(groupDir)) { for (Path artifactDir : groupStream) { final String artifactId = artifactDir.getFileName().toString(); try (DirectoryStream artifactStream = Files.newDirectoryStream(artifactDir)) { for (Path versionDir : artifactStream) { final Path target = Paths.get(project.getBuild().getDirectory()).resolve(artifactId + '-' + versionDir.getFileName() + ".zip"); if (Files.exists(target)) { IoUtils.recursiveDelete(target); } try { //Validate that we can parse this feature-pack... FeaturePackDescription desc = FeaturePackDescriber.describeFeaturePack(versionDir, "UTF-8"); checkFeaturePackContentStability(forbidLowerStatibilityLevelPackageReference, lowerStabilityPackages, desc.getPackages(), desc.getLayers(), desc.getFeatures(), desc.getConfigs(), getLog()); } catch (Exception ex) { throw new RuntimeException(ex); } ZipUtils.zip(versionDir, target); debug("Attaching feature-pack %s as a project artifact", target); projectHelper.attachArtifact(project, "zip", target.toFile()); final Path offLinerTarget = Paths.get(project.getBuild().getDirectory()).resolve(artifactId + '-' + versionDir.getFileName() + "-" + ARTIFACT_LIST_CLASSIFIER + "." + ARTIFACT_LIST_EXTENSION); debug("Attaching feature-pack artifact list %s as a project artifact", offLinerTarget); Files.write(offLinerTarget, builder.build().getBytes()); projectHelper.attachArtifact(project, ARTIFACT_LIST_EXTENSION, ARTIFACT_LIST_CLASSIFIER, offLinerTarget.toFile()); if (generateChannelManifest) { final Path channelManifestTarget = Paths.get(project.getBuild().getDirectory()).resolve(artifactId + '-' + versionDir.getFileName() + "-" + ChannelManifest.CLASSIFIER + "." + ChannelManifest.EXTENSION); debug("Attaching channel manifest definition %s as a project artifact", channelManifestTarget); String channelManifest = createYAMLChannelManifest(buildConfig); Files.write(channelManifestTarget, channelManifest.getBytes()); if (deployChannelManifest) { projectHelper.attachArtifact(project, ChannelManifest.EXTENSION, ChannelManifest.CLASSIFIER, channelManifestTarget.toFile()); } } } } } } } } catch (IOException e) { throw new MojoExecutionException("Failed to create a feature-pack archives from the layout", e); } } static void checkFeaturePackContentStability(boolean forbidLowerStatibilityLevelPackageReference, Set lowerStabilityPackages, Collection packages, Collection layers, Collection features, Map> configs, Log log) throws Exception { // Validate that no packages at a lower stability level are referenced for (PackageSpec spec : packages) { if (spec.hasLocalPackageDeps()) { for (PackageDependencySpec pds : spec.getLocalPackageDeps()) { if (lowerStabilityPackages.contains(pds.getName())) { String message = "package " + spec.getName() + " references the package " + pds.getName() + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; if (forbidLowerStatibilityLevelPackageReference) { throw new Exception(message); } else { log.warn(message); } } } } } for (ConfigLayerSpec spec : layers) { if (spec.hasLocalPackageDeps()) { for (PackageDependencySpec pds : spec.getLocalPackageDeps()) { if (lowerStabilityPackages.contains(pds.getName())) { String message = "package " + spec.getName() + " references the package " + pds.getName() + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; if (forbidLowerStatibilityLevelPackageReference) { throw new Exception(message); } else { log.warn(message); } } } } } for (FeatureSpec spec : features) { if (spec.hasLocalPackageDeps()) { for (PackageDependencySpec pds : spec.getLocalPackageDeps()) { if (lowerStabilityPackages.contains(pds.getName())) { String message = "feature " + spec.getName() + " references the package " + pds.getName() + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; if (forbidLowerStatibilityLevelPackageReference) { throw new Exception(message); } else { log.warn(message); } } } } } for (Entry> configModel : configs.entrySet()) { String modelName = configModel.getKey(); for (Entry cm : configModel.getValue().entrySet()) { ConfigModel config = cm.getValue(); String configName = cm.getKey(); if (config.hasLocalPackageDeps()) { for (PackageDependencySpec pds : config.getLocalPackageDeps()) { if (lowerStabilityPackages.contains(pds.getName())) { String message = "config " + modelName + "/" + configName + " references the package " + pds.getName() + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; if (forbidLowerStatibilityLevelPackageReference) { throw new Exception(message); } else { log.warn(message); } } } } } } } /** * Create a YAML Channel Manifest that defines streams for all the feature-pack * dependencies. The feature-pack itself is added to the channel's streams. */ String createYAMLChannelManifest(WildFlyFeaturePackBuild buildConfig) throws IOException { ArrayList manifestRequirements = new ArrayList<>(); if (addFeaturePacksAsRequiredManifests && !buildConfig.getDependencies().isEmpty()) { for (ArtifactCoords.Gav gav : new TreeSet<>(buildConfig.getDependencies().keySet())) { project.getDependencies().stream() .filter(dep -> gav.getGroupId().equals(dep.getGroupId()) && gav.getArtifactId().equals(dep.getArtifactId()) && "zip".equals(dep.getType())) .findFirst() .ifPresent( fp -> manifestRequirements.add(new ManifestRequirement(fp.getGroupId() +":"+fp.getArtifactId(), new MavenCoordinate(fp.getGroupId(), fp.getArtifactId(), fp.getVersion())))); } } // append all feature-pack artifacts dependencies as streams (except zip and pom dependencies) List streams = MavenProjectArtifactVersions.getFilteredArtifacts(project, buildConfig).stream() .filter(a -> !"zip".equals(a.getType()) && !"pom".equals(a.getType())) .map(artifact -> new org.wildfly.channel.Stream(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion())) .collect(Collectors.toList()); // add a stream for this feature pack streams.add(new org.wildfly.channel.Stream(project.getGroupId(), project.getArtifactId(), project.getVersion())); ChannelManifest channelManifest = new ChannelManifest(format("Manifest for %s feature pack.", project.getArtifact()), project.getGroupId() + ":" + project.getArtifactId(), format("Generated by org.wildfly.galleon-plugins:wildfly-galleon-maven-plugin at %s", Clock.systemUTC().instant()), manifestRequirements, streams); return ChannelManifestMapper.toYaml(channelManifest); } private void addConfigPackages(final Path configDir, final Path packagesDir, final FeaturePackDescription.Builder fpBuilder) throws MojoExecutionException { if (!Files.exists(configDir)) { return; } try (DirectoryStream stream = Files.newDirectoryStream(configDir)) { for (Path configPackage : stream) { final Path packageDir = packagesDir.resolve(configPackage.getFileName()); final Path packageFile = packageDir.resolve(Constants.PACKAGE_XML); final Path packageXml = configPackage.resolve(Constants.PACKAGE_XML); if (Files.exists(packageXml)) { final PackageSpec pkgSpec; try (BufferedReader reader = Files.newBufferedReader(packageXml)) { try { pkgSpec = PackageXmlParser.getInstance().parse(reader); } catch (XMLStreamException e) { throw new MojoExecutionException("Failed to parse " + packageXml, e); } } Stability packageStability = pkgSpec.getStability(); if (packageStability != null) { if (buildTimestabilityLevel != null && !buildTimestabilityLevel.enables(packageStability)) { getLog().warn("Package " + pkgSpec.getName() + " is not included in the feature-pack. " + "Package stability '" + packageStability + "' is not enabled by the '" + buildTimestabilityLevel + "' stability level that is the feature-pack minimum stability level."); lowerStabilityPackages.add(pkgSpec.getName()); continue; } } fpBuilder.addPackage(pkgSpec); } if (Files.exists(packageFile) && Files.exists(packageXml)) { warn("File " + packageFile + " already exists, replacing with " + packageXml); } if (!Files.exists(packageDir)) { Util.mkdirs(packageDir); } IoUtils.copy(configPackage, packageDir); } } catch (IOException e) { throw new MojoExecutionException("Failed to process config packages", e); } } private void addFeatures(final Path configDir, final Path featuresDir) throws MojoExecutionException { if (!Files.exists(configDir)) { return; } try (DirectoryStream stream = Files.newDirectoryStream(configDir)) { for (Path configFeature : stream) { final Path featureDir = featuresDir.resolve(configFeature.getFileName()); final Path featureXml = configFeature.resolve(Constants.SPEC_XML); if (!Files.exists(featureXml)) { throw new MojoExecutionException("Feature spec " + featureXml + " doesn't exist "); } final FeatureSpec featureSpec; try (BufferedReader reader = Files.newBufferedReader(featureXml)) { try { featureSpec = FeatureSpecXmlParser.getInstance().parse(reader); } catch (ProvisioningDescriptionException | XMLStreamException e) { throw new MojoExecutionException("Failed to parse " + featureXml, e); } } Stability featureStability = featureSpec.getStability(); if (featureStability != null) { if (buildTimestabilityLevel != null && !buildTimestabilityLevel.enables(featureStability)) { getLog().warn("Feature " + featureSpec.getName() + " is not included in the feature-pack. " + "Feature stability '" + featureStability + "' is not enabled by the '" + buildTimestabilityLevel + "' stability level that is the feature-pack minimum stability level."); continue; } } if (!Files.exists(featureDir)) { Util.mkdirs(featureDir); } IoUtils.copy(configFeature, featureDir); } } catch (IOException e) { throw new MojoExecutionException("Failed to process feature spec", e); } } private void persistExtensions(final Path resourcesWildFly, String name, List extensions) throws MojoExecutionException { try { Files.write(resourcesWildFly.resolve(name), extensions); } catch (IOException e) { throw new MojoExecutionException("Failed to persist " + name, e); } } private Properties getFPConfigProperties() throws MojoExecutionException { final Properties properties = new Properties(); properties.put("project.version", project.getVersion()); properties.put("version", project.getVersion()); // needed for licenses.xsl if (releaseName != null) { properties.put("product.release.name", releaseName); } if (taskPropsFile != null) { final Path p = taskPropsFile.toPath(); if (!Files.exists(p)) { throw new MojoExecutionException(Errors.pathDoesNotExist(p)); } try (Reader reader = Files.newBufferedReader(p)) { properties.load(reader); } catch (IOException e) { throw new MojoExecutionException(Errors.readFile(p), e); } } if (!taskProps.isEmpty()) { properties.putAll(taskProps); } return properties; } private Properties getWildFlyChannelProperties() throws MojoExecutionException { final Properties properties = new Properties(); properties.put(WfConstants.WILDFLY_CHANNEL_RESOLUTION_PROP, wildflyChannelResolutionMode.toString()); return properties; } private void addPlugins(FeaturePackSpec.Builder fpBuilder, Map plugins) throws MojoExecutionException { for (Map.Entry entry : plugins.entrySet()) { ArtifactCoords coords = entry.getValue(); if (coords.getVersion() == null) { coords = ArtifactCoordsUtil.fromJBossModules( resolveVersion(coords.getGroupId() + ':' + coords.getArtifactId()), "jar"); } try { resolveArtifact(ArtifactCoords.newInstance(coords.getGroupId(), coords.getArtifactId(), coords.getVersion(), coords.getExtension())); } catch (ProvisioningException e) { throw new MojoExecutionException("Failed to resolve feature-pack plugin " + coords, e); } final StringBuilder buf = new StringBuilder(128); buf.append(coords.getGroupId()).append(':') .append(coords.getArtifactId()).append(':'); final String classifier = coords.getClassifier(); if (classifier != null && !classifier.isEmpty()) { buf.append(classifier).append(':'); } buf.append(coords.getExtension()).append(':').append(coords.getVersion()); fpBuilder.addPlugin(FeaturePackPlugin.getInstance(entry.getKey(), buf.toString())); } } protected void processFeaturePackDependencies(WildFlyFeaturePackBuild buildConfig, final FeaturePackSpec.Builder fpBuilder) throws Exception { if (buildConfig.getDependencies().isEmpty()) { return; } fpDependencies = new LinkedHashMap<>(buildConfig.getDependencies().size()); for (Map.Entry depEntry : buildConfig.getDependencies().entrySet()) { ArtifactCoords depCoords = depEntry.getKey().toArtifactCoords(); if (depCoords.getVersion() == null) { final String coordsStr = artifactVersions.getVersion(depCoords.getGroupId() + ':' + depCoords.getArtifactId()); if (coordsStr == null) { throw new MojoExecutionException("Failed resolve artifact version for " + depCoords); } depCoords = ArtifactCoordsUtil.fromJBossModules(coordsStr, "zip"); if (depCoords.getExtension().equals("pom")) { depCoords = new ArtifactCoords(depCoords.getGroupId(), depCoords.getArtifactId(), depCoords.getVersion(), depCoords.getClassifier(), "zip"); } } final Path depZip = resolveArtifact(depCoords); final FeaturePackDependencySpec depSpec = depEntry.getValue(); final FeaturePackConfig depConfig = depSpec.getTarget(); final FeaturePackDescription fpDescr = FeaturePackDescriber.describeFeaturePackZip(depZip); // here we need to determine which format to use to persist the dependency location: // Maven coordinates or the FPL. The format is actually set in the feature-pack build parser. // However, the parser can't set the actual FPL value, since it does not have enough information. // So here we check whether the parser used the Galleon 1 FPL format and if so, replace it with // the proper FPL. FeaturePackLocation fpl = depEntry.getValue().getTarget().getLocation(); if(!fpl.isMavenCoordinates()) { fpl = fpDescr.getFPID().getLocation(); } else if(org.apache.commons.lang3.StringUtils.isEmpty(fpl.getBuild())) { fpl = fpl.replaceBuild(depCoords.getVersion()); } fpBuilder.addFeaturePackDep(depSpec.getName(), FeaturePackConfig.builder(fpl).init(depConfig).build()); fpDependencies.put(depSpec.getName(), fpDescr); } } public Path resolveArtifact(ArtifactCoords coords) throws ProvisioningException { final ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); buildingRequest.setLocalRepository(session.getLocalRepository()); buildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories()); try { final ArtifactResult result = artifactResolver.resolveArtifact(buildingRequest, new DefaultArtifact(coords.getGroupId(), coords.getArtifactId(), coords.getVersion(), "provided", coords.getExtension(), coords.getClassifier(), new DefaultArtifactHandler(coords.getExtension()))); return result.getArtifact().getFile().toPath(); } catch (ArtifactResolverException e) { throw new ProvisioningException(FpMavenErrors.artifactResolution(coords.toString()), e); } } protected void handleLayers(final Path srcModulesDir, final FeaturePackDescription.Builder fpBuilder, final Path targetResources, final PackageSpec.Builder modulesAll) throws MojoExecutionException { final Path layersDir = srcModulesDir.resolve(WfConstants.SYSTEM).resolve(WfConstants.LAYERS); if (Files.exists(layersDir)) { try (Stream layers = Files.list(layersDir)) { final Map moduleXmlByPkgName = new HashMap<>(); final Iterator i = layers.iterator(); while (i.hasNext()) { final Path layerDir = i.next(); Util.findModules(layerDir, moduleXmlByPkgName); if (moduleXmlByPkgName.isEmpty()) { throw new MojoExecutionException("Modules not found in " + layerDir); } } packageModules(fpBuilder, targetResources, moduleXmlByPkgName, modulesAll); } catch (IOException e) { throw new MojoExecutionException("Failed to process modules content", e); } } final Path layersConf = srcModulesDir.resolve(WfConstants.LAYERS_CONF); if (!Files.exists(layersConf)) { return; } final Path targetPath = getPackagesDir().resolve(WfConstants.LAYERS_CONF).resolve(Constants.CONTENT).resolve(WfConstants.MODULES).resolve(WfConstants.LAYERS_CONF); try { Files.createDirectories(targetPath.getParent()); IoUtils.copy(layersConf, targetPath); } catch (IOException e) { throw new MojoExecutionException(Errors.copyFile(layersConf, targetPath), e); } final PackageSpec.Builder pkgBuilder = PackageSpec.builder(WfConstants.LAYERS_CONF); addPackage(getPackagesDir(), fpBuilder, pkgBuilder); fpBuilder.getSpecBuilder().addDefaultPackage(WfConstants.LAYERS_CONF); } protected void handleAddOns(final Path srcModulesDir, final FeaturePackDescription.Builder fpBuilder, final Path targetResources, final PackageSpec.Builder modulesAll) throws MojoExecutionException { final Map moduleXmlByPkgName = new HashMap<>(); final Path addOnsDir = srcModulesDir.resolve(WfConstants.SYSTEM).resolve(WfConstants.ADD_ONS); if (Files.exists(addOnsDir)) { try (Stream addOn = Files.list(addOnsDir)) { final Iterator i = addOn.iterator(); while (i.hasNext()) { final Path addOnDir = i.next(); Util.findModules(addOnDir, moduleXmlByPkgName); if (moduleXmlByPkgName.isEmpty()) { throw new MojoExecutionException("Modules not found in " + addOnDir); } } packageModules(fpBuilder, targetResources, moduleXmlByPkgName, modulesAll); } catch (IOException e) { throw new MojoExecutionException("Failed to process modules content", e); } } } protected void handleModules(final Path srcModulesDir, final FeaturePackDescription.Builder fpBuilder, final Path targetResources, final PackageSpec.Builder modulesAll) throws MojoExecutionException { try { final Map moduleXmlByPkgName = new HashMap<>(); Util.findModules(srcModulesDir, moduleXmlByPkgName); packageModules(fpBuilder, targetResources, moduleXmlByPkgName, modulesAll); } catch (IOException e) { throw new MojoExecutionException("Failed to process modules content", e); } } protected PackageSpec addPackage(final Path fpPackagesDir, final FeaturePackDescription.Builder fpBuilder, final PackageSpec.Builder pkgBuilder) throws MojoExecutionException { final PackageSpec pkg = pkgBuilder.build(); fpBuilder.addPackage(pkg); writeXml(pkg, fpPackagesDir.resolve(pkg.getName())); return pkg; } private void writeXml(PackageSpec pkgSpec, Path dir) throws MojoExecutionException { try { Util.mkdirs(dir); PackageXmlWriter.getInstance().write(pkgSpec, dir.resolve(Constants.PACKAGE_XML)); } catch (XMLStreamException | IOException e) { throw new MojoExecutionException(Errors.writeFile(dir.resolve(Constants.PACKAGE_XML)), e); } } private void packageModules(FeaturePackDescription.Builder fpBuilder, Path resourcesDir, Map moduleXmlByPkgName, PackageSpec.Builder modulesAll) throws IOException, MojoExecutionException { Map> targetToAlias = new HashMap<>(); for (Map.Entry module : moduleXmlByPkgName.entrySet()) { try { ModuleXmlParser.populateAlias(module.getValue(), WfConstants.UTF8, targetToAlias); } catch (ParsingException e) { throw new IOException(Errors.parseXml(module.getValue()), e); } } for (Map.Entry module : moduleXmlByPkgName.entrySet()) { final String packageName = module.getKey(); final Path moduleXml = module.getValue(); final Path packageDir = getPackagesDir().resolve(packageName); final PackageSpec.Builder pkgSpecBuilder = PackageSpec.builder(packageName); final ModuleParseResult parsedModule; try { parsedModule = ModuleXmlParser.parse(moduleXml, WfConstants.UTF8, targetToAlias); String packageStability = parsedModule.getProperty(WfConstants.JBOSS_STABILITY); if (packageStability != null) { Stability stab = Stability.fromString(packageStability); if (buildTimestabilityLevel != null && !buildTimestabilityLevel.enables(stab)) { getLog().warn("JBoss Modules module " + parsedModule.getIdentifier() + " is not included in the feature-pack. " + "Package stability '" + packageStability + "' is not enabled by the '" + buildTimestabilityLevel + "' stability level that is the feature-pack minimum stability level."); lowerStabilityPackages.add(packageName); continue; } pkgSpecBuilder.setStability(stab); } final Path targetXml = packageDir.resolve(WfConstants.PM).resolve(WfConstants.WILDFLY).resolve(WfConstants.MODULE).resolve(resourcesDir.relativize(moduleXml)); mkdirs(targetXml.getParent()); IoUtils.copy(moduleXml.getParent(), targetXml.getParent()); if (!parsedModule.dependencies.isEmpty()) { for (ModuleParseResult.ModuleDependency moduleDep : parsedModule.dependencies) { final ModuleIdentifier moduleId = moduleDep.getModuleId(); String depName = moduleId.getName(); if (!moduleId.getSlot().equals("main")) { depName += '.' + moduleId.getSlot(); } if (moduleXmlByPkgName.containsKey(depName)) { PackageDependencySpec spec = getPackageDepSpec(packageName, moduleXml, moduleDep, depName); if (!spec.isOptional()) { pkgSpecBuilder.addPackageDep(spec); } continue; } Map.Entry depSrc = null; if (!fpDependencies.isEmpty()) { Set alternativeSrc = Collections.emptySet(); for (Map.Entry depEntry : fpDependencies.entrySet()) { if (depEntry.getValue().hasPackage(depName)) { if (depSrc != null) { alternativeSrc = CollectionUtils.add(alternativeSrc, depSrc.getKey()); } depSrc = depEntry; } } if (!alternativeSrc.isEmpty()) { final StringBuilder warn = new StringBuilder(); warn.append("Package ").append(depName).append(" from ").append(depSrc.getKey()) .append(" picked as dependency of ").append(packageName).append(" although ") .append(depName).append(" also exists in "); StringUtils.append(warn, alternativeSrc); getLog().warn(warn); } } if (depSrc != null) { PackageDependencySpec spec = getPackageDepSpec(packageName, moduleXml, moduleDep, depName); if (!spec.isOptional()) { pkgSpecBuilder.addPackageDep(depSrc.getKey(), spec); } } else if (moduleDep.isOptional() || isProvided(depName)) { // getLog().warn("UNSATISFIED EXTERNAL OPTIONAL DEPENDENCY " + packageName + " -> " + depName); } else { throw new MojoExecutionException( "Package " + packageName + " has unsatisifed external dependency on package " + depName); } } } } catch (ParsingException e) { throw new IOException(Errors.parseXml(moduleXml), e); } final PackageSpec pkgSpec = pkgSpecBuilder .build(); try { PackageXmlWriter.getInstance().write(pkgSpec, packageDir.resolve(Constants.PACKAGE_XML)); } catch (XMLStreamException e) { throw new IOException(Errors.writeFile(packageDir.resolve(Constants.PACKAGE_XML)), e); } if (modulesAll != null) { modulesAll.addPackageDep(packageName, true); } fpBuilder.addPackage(pkgSpec); } } private static PackageDependencySpec getPackageDepSpec(final String packageName, final Path moduleXml, ModuleParseResult.ModuleDependency moduleDep, String depName) throws ParsingException { final String passiveValue = moduleDep.getProperty(WfConstants.GALLEON_PASSIVE); final PackageDependencySpec depSpec; if (passiveValue != null && Boolean.parseBoolean(passiveValue)) { if (!moduleDep.isOptional()) { throw new ParsingException("Required dependency on module " + packageName + " cannot be annotated as galleon.passive in " + moduleXml); } depSpec = PackageDependencySpec.passive(depName); } else if (moduleDep.isOptional()) { depSpec = PackageDependencySpec.optional(depName); } else { depSpec = PackageDependencySpec.required(depName); } return depSpec; } protected void debug(String msg, Object... args) { if (getLog().isDebugEnabled()) { getLog().debug(format(msg, args)); } } protected void warn(String msg, Object... args) { if (getLog().isWarnEnabled()) { getLog().warn(format(msg, args)); } } private void buildArtifactList(ArtifactListBuilder builder) throws MojoExecutionException { try { final MavenProjectArtifactVersions projectArtifacts = MavenProjectArtifactVersions.getInstance(project); Set allArtifacts = new TreeSet<>(); addHardCodedArtifacts(allArtifacts); allArtifacts.addAll(projectArtifacts.getArtifacts().values()); // Generate the offliner file for dependencies and hardcoded. for (String artifact : allArtifacts) { ArtifactCoords coords = ArtifactCoordsUtil.fromJBossModules(artifact, null); builder.add(coords); } } catch (ProvisioningException | IOException | ArtifactDescriptorException ex) { throw new MojoExecutionException(ex.getMessage(), ex); } } private void addHardCodedArtifacts(Set all) throws IOException { Path packages = resourcesDir.resolve(Constants.PACKAGES); if (Files.exists(packages)) { processPackages(fpDir, all); } final Path projectModules = resourcesDir.resolve(WfConstants.MODULES); if (Files.exists(projectModules)) { addHardCodedArtifacts(projectModules, all); } } private void processPackages(Path fpDirectory, Set all) throws IOException { Files.walkFileTree(fpDirectory, new SimpleFileVisitor() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { if (dir.endsWith(MODULE_PATH_SEGMENT)) { addHardCodedArtifacts(dir, all); return FileVisitResult.SKIP_SUBTREE; } return FileVisitResult.CONTINUE; } }); } private void addHardCodedArtifacts(final Path source, Set all) throws IOException { Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (WfConstants.MODULE_XML.equals(file.getFileName().toString())) { try { ModuleXmlVersionResolver.addHardCodedArtifacts(file, all); } catch (XMLStreamException ex) { throw new IOException("Error while reading " + file, ex); } } return FileVisitResult.CONTINUE; } }); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy