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

org.wildfly.channelplugin.UpgradeComponentsMojo Maven / Gradle / Ivy

Go to download

This maven plugin overrides dependencies versions in a Maven project according to Wildfly channel definition.

There is a newer version: 1.0.19
Show newest version
package org.wildfly.channelplugin;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
import org.apache.maven.shared.dependency.graph.DependencyNode;
import org.apache.maven.shared.dependency.graph.traversal.CollectingDependencyNodeVisitor;
import org.commonjava.maven.atlas.ident.ref.ArtifactRef;
import org.commonjava.maven.atlas.ident.ref.ProjectRef;
import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef;
import org.commonjava.maven.atlas.ident.ref.SimpleArtifactRef;
import org.commonjava.maven.atlas.ident.ref.SimpleProjectRef;
import org.commonjava.maven.atlas.ident.ref.SimpleProjectVersionRef;
import org.commonjava.maven.ext.common.ManipulationException;
import org.commonjava.maven.ext.common.model.Project;
import org.commonjava.maven.ext.core.ManipulationSession;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.RemoteRepository;
import org.wildfly.channel.Channel;
import org.wildfly.channel.ChannelManifestCoordinate;
import org.wildfly.channel.ChannelMapper;
import org.wildfly.channel.ChannelSession;
import org.wildfly.channel.Repository;
import org.wildfly.channel.UnresolvedMavenArtifactException;
import org.wildfly.channel.VersionResult;
import org.wildfly.channel.maven.ChannelCoordinate;
import org.wildfly.channel.maven.VersionResolverFactory;
import org.wildfly.channelplugin.manipulation.PomManipulator;
import org.wildfly.channelplugin.utils.IOUtils;
import org.wildfly.channeltools.util.VersionUtils;

import javax.inject.Inject;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import static org.wildfly.channeltools.util.ConversionUtils.toArtifactRef;
import static org.wildfly.channeltools.util.ConversionUtils.toProjectRefs;

/**
 * This tasks overrides dependencies versions according to provided channel file.
 * 

* One of following properties needs to be set: *

  • `channelFile` to use a channel file located on a local file system,
  • *
  • `channelGAV` to lookup an artifact containing the channel file.
  • */ @Mojo(name = "upgrade", requiresProject = true, requiresDirectInvocation = true) public class UpgradeComponentsMojo extends AbstractChannelMojo { /** * Path to the channel definition file on a local filesystem. *

    * Exactly one of 'channelFile', 'manifestFile', 'channelGAV', 'manifestGAV' can be set. */ @Parameter(property = "channelFile") String channelFile; /** * Path to the channel manifest file on a local filesystem. *

    * Exactly one of 'channelFile', 'manifestFile', 'channelGAV', 'manifestGAV' can be set. */ @Parameter(property = "manifestFile") String manifestFile; /** * GAV of an artifact than contains the channel file. *

    * Exactly one of 'channelFile', 'manifestFile', 'channelGAV', 'manifestGAV' can be set. */ @Parameter(property = "channelGAV") String channelGAV; /** * GAV of an artifact than contains the channel file. *

    * Exactly one of 'channelFile', 'manifestFile', 'channelGAV', 'manifestGAV' can be set. */ @Parameter(property = "manifestGAV") String manifestGAV; /** * Comma separated list of remote repositories URLs, that should be used to resolve artifacts. */ @Parameter(property = "remoteRepositories") List remoteRepositories; /** * Local repository path. */ @Parameter(property = "localRepository") String localRepositoryPath; /** * Comma separated list of dependency G:As that should not be upgraded. */ @Parameter(property = "ignoreStreams") List ignoreStreams; /** * Takes precedence over the ignoreStreams settings. One can use it to set "-DignoreStreams=org.wildfly.core:*" and * "-DdontIgnoreStreams=org.wildfly.core:wildfly-core-parent" which would together ignore all org.wildfly.core:* * streams except for org.wildfly.core:wildfly-core-parent. */ @Parameter(property = "dontIgnoreStreams") List dontIgnoreStreams; /** * Comma separated list of module G:As that should not be processed. */ @Parameter(property = "ignoreModules") List ignoreModules; /** * Comma separated list of property names. Project properties that match one of these names will not get * overridden. */ @Parameter(property = "ignoreProperties") List ignoreProperties; /** * Comma separated list of property names prefixes. Project properties that match one of these prefixes will not get * overridden. */ @Parameter(property = "ignorePropertiesPrefixedWith") List ignorePropertiesPrefixedWith; /** * Comma separated list of propertyName=propertyValue. Given properties will be overridden to given values. *

    * This setting takes precedence over channel streams. */ @Parameter(property = "overrideProperties") List overrideProperties; /** * Comma separated list of groupId:artifactId:version triplets. All existing dependencies in the project with given * groupId and artifactId will be set to given version. *

    * This setting takes precedence over channel streams. */ @Parameter(property = "overrideDependencies") List overrideDependencies; /** * Replace property reference in dependency version element with inlined version string, when it's not possible to * override property value due to conflicting versions for different dependencies that use the property. */ @Parameter(property = "inlineVersionOnConflict", defaultValue = "true") boolean inlineVersionOnConflict; /** * If true, transitive dependencies of the project that are also declared in the channel will be injected into root * POM's dependencyManagement section. */ @Parameter(property = "injectTransitiveDependencies", defaultValue = "true") boolean injectTransitiveDependencies; /** * When injecting new dependency elements (to override transitive dependency versions), add exclussions according to given * project submodule (given in G:A format). *

    * Effectively, this means that when a transitive dependency G:A:V is going to be injected to override the dependency version, * the plugin will try to find this same dependency in a dependency tree of a given submodule and copy exclusions it finds * there to the injected dependency element. *

    * If this parameter is unset, the dependency exclusions are taken from managed dependencies section of the root module * effective POM. */ @Parameter(property = "copyExclusionsFrom") String copyExclusionsFrom; @Parameter(property = "ignoreTestDependencies", defaultValue = "true") boolean ignoreTestDependencies; /** * When a dependency is defined with version string referencing a property, and that property is defined in a parent * pom outside the project, the property would be injected into a pom where the dependency is defined, if this * parameter is set to true (default). */ @Parameter(property = "injectExternalProperties", defaultValue = "true") boolean injectExternalProperties; /** * Should the remote maven repositories (specified via -DremoteRepositories or in input channels) be injected into * the parent project POM, to ensure that project is buildable? */ @Parameter(property = "injectRepositories", defaultValue = "true") boolean injectRepositories; @Inject DependencyGraphBuilder dependencyGraphBuilder; @Inject MavenSession mavenSession; @Inject ManipulationSession manipulationSession; @Inject RepositorySystem repositorySystem; private List channels = new ArrayList<>(); private ChannelSession channelSession; private final List ignoredStreams = new ArrayList<>(); private final List unignoredStreams = new ArrayList<>(); private final List ignoredModules = new ArrayList<>(); private Set projectGavs; private final HashMap, PomManipulator> manipulators = new HashMap<>(); private final HashMap, String> upgradedProperties = new HashMap<>(); private final Set declaredDependencies = new HashSet<>(); /** * This includes pre-processing of input parameters. */ private void init() throws MojoExecutionException { if (StringUtils.isBlank(localRepositoryPath)) { try { localRepositoryPath = IOUtils.createTemporaryCache(); } catch (IOException e) { throw new MojoExecutionException("Cannot create local maven cache", e); } } final DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils.newSession(); final LocalRepository localRepository = new LocalRepository(localRepositoryPath); final LocalRepositoryManager localRepoManager = repositorySystem.newLocalRepositoryManager(repositorySystemSession, localRepository); repositorySystemSession.setLocalRepositoryManager(localRepoManager); initChannel(); if (!remoteRepositories.isEmpty()) { // TODO: Include remote repositories defined in the project pom.xml? channels = overrideRemoteRepositories(channels, remoteRepositories); } channelSession = new ChannelSession(channels, new VersionResolverFactory(repositorySystem, repositorySystemSession)); ignoreStreams.forEach(ga -> ignoredStreams.add(SimpleProjectRef.parse(ga))); dontIgnoreStreams.forEach(ga -> unignoredStreams.add(SimpleProjectRef.parse(ga))); ignoreModules.forEach(ga -> ignoredModules.add(SimpleProjectRef.parse(ga))); } /** * This updates the configuration according to a config file living in the project root. Should be called before the * {@link #init()} method. */ private void reconfigure() throws MojoExecutionException { File configFile = new File(mavenSession.getExecutionRootDirectory(), MojoConfigurator.DEFAULT_CONFIGURATION_FILE); try { MojoConfigurator configurator = new MojoConfigurator(configFile); configurator.configureProperties(this); } catch (IOException e) { throw new MojoExecutionException("Unable to read plugin configuration from " + MojoConfigurator.DEFAULT_CONFIGURATION_FILE, e); } } @Override public void execute() throws MojoExecutionException { if (!mavenSession.getCurrentProject().isExecutionRoot()) { // do not perform any work in submodules return; } reconfigure(); init(); try { List pmeProjects = parsePmeProjects(); // collect GAVs of in-project modules, these are not going to be upgraded projectGavs = pmeProjects.stream() .map(p -> new SimpleProjectVersionRef(p.getGroupId(), p.getArtifactId(), p.getVersion())) .collect(Collectors.toSet()); // process project modules for (Project project: pmeProjects) { ProjectRef moduleGA = project.getKey().asProjectRef(); if (ignoredModules.contains(moduleGA)) { getLog().info(String.format("Skipping module %s:%s", project.getGroupId(), project.getArtifactId())); continue; } getLog().info(String.format("Processing module %s:%s", project.getGroupId(), project.getArtifactId())); // create manipulator for given module PomManipulator manipulator = new PomManipulator(project); manipulators.put(Pair.of(project.getGroupId(), project.getArtifactId()), manipulator); processModule(project, manipulator); } if (injectTransitiveDependencies) { injectTransitiveDependencies(); } // if channel was given as an input, insert channel repositories into the parent pom if (injectRepositories) { Project rootProject = findRootProject(pmeProjects); PomManipulator rootManipulator = manipulators.get(Pair.of(rootProject.getGroupId(), rootProject.getArtifactId())); InjectRepositoriesMojo.insertRepositories(rootProject, rootManipulator, channels); } // override modified poms for (PomManipulator manipulator: manipulators.values()) { manipulator.writePom(); } } catch (ManipulationException | XMLStreamException e) { throw new MojoExecutionException("Project parsing failed", e); } catch (DependencyGraphBuilderException e) { throw new MojoExecutionException("Dependency collector error", e); } } /** * Processes single project module: *

  • collects all declared dependencies,
  • *
  • performs hard overrides of properties and dependency versions in the module,
  • *
  • upgrades dependencies according to channel definition.
  • */ private void processModule(Project pmeProject, PomManipulator manipulator) throws ManipulationException, XMLStreamException { Map resolvedProjectDependencies = collectResolvedProjectDependencies(pmeProject); resolvedProjectDependencies.keySet().forEach(a -> declaredDependencies.add(a.asProjectRef())); List overriddenProperties = performHardPropertyOverrides(manipulator); List overriddenDependencies = performHardDependencyOverrides(resolvedProjectDependencies, manipulator); List> dependenciesToUpgrade = findDependenciesToUpgrade(resolvedProjectDependencies); for (Pair upgrade: dependenciesToUpgrade) { String newVersion = upgrade.getRight(); Dependency locatedDependency = upgrade.getLeft(); @SuppressWarnings("UnnecessaryLocalVariable") Dependency d = locatedDependency; if (overriddenDependencies.contains(locatedDependency)) { // if there was a hard version override, the dependency is not processed again continue; } if (VersionUtils.isProperty(locatedDependency.getVersion())) { // dependency version is set from a property String originalVersionString = locatedDependency.getVersion(); String versionPropertyName = VersionUtils.extractPropertyName(originalVersionString); if (overriddenProperties.contains(versionPropertyName)) { // this property has been overridden based on `overrideProperties` parameter, do not process again continue; } Pair projectProperty = followProperties(pmeProject, versionPropertyName); if (projectProperty == null) { Pair externalProperty = resolveExternalProperty(mavenProject, versionPropertyName); if (externalProperty != null) { projectProperty = Pair.of(null, externalProperty.getLeft()); } } if (projectProperty == null) { ChannelPluginLogger.LOGGER.errorf( "Unable to upgrade %s:%s:%s to '%s', can't locate property '%s' in the project", d.getGroupId(), d.getArtifactId(), d.getVersion(), newVersion, versionPropertyName); continue; } Project targetProject = projectProperty.getLeft(); String targetPropertyName = projectProperty.getRight(); if (isIgnoredProperty(targetPropertyName)) { getLog().info(String.format("Ignoring property '%s'", targetPropertyName)); continue; } if (upgradedProperties.containsKey(projectProperty)) { if (!upgradedProperties.get(projectProperty).equals(newVersion)) { // property has already been changed to different value String propertyName = projectProperty.getRight(); String currentPropertyValue = upgradedProperties.get(projectProperty); if (inlineVersionOnConflict) { getLog().warn(String.format("Inlining version string for %s:%s:%s, new version '%s'. " + "The original version property '%s' has already been modified to '%s'.", d.getGroupId(), d.getArtifactId(), d.getVersion(), newVersion, propertyName, currentPropertyValue)); manipulator.overrideDependencyVersion(d.getGroupId(), d.getArtifactId(), originalVersionString, newVersion); } else { getLog().warn(String.format( "Can't upgrade %s:%s:%s to '%s', property '%s' was already upgraded to '%s'.", d.getGroupId(), d.getArtifactId(), d.getVersion(), newVersion, propertyName, currentPropertyValue)); } continue; // do not override the property again } } // get manipulator for the module where the target property is located upgradedProperties.put(projectProperty, newVersion); if (targetProject != null) { // property has been located in some project module // => override the located property in the module where it has been located PomManipulator targetManipulator = manipulators.get( Pair.of(targetProject.getGroupId(), targetProject.getArtifactId())); targetManipulator.overrideProperty(targetPropertyName, newVersion); } else if (injectExternalProperties) { // property has been located in external parent pom // => inject the property into current module PomManipulator targetManipulator = manipulators.get( Pair.of(pmeProject.getGroupId(), pmeProject.getArtifactId())); targetManipulator.injectProperty(targetPropertyName, newVersion); } else { getLog().warn(String.format("Can't upgrade %s:%s:%s to %s, property %s is not defined in the " + "scope of the project (consider enabling the injectExternalProperties parameter).", d.getGroupId(), d.getArtifactId(), d.getVersion(), newVersion, targetPropertyName)); } } else { // dependency version is inlined in version element, can be directly overwritten manipulator.overrideDependencyVersion(toArtifactRef(locatedDependency), newVersion); } } } /** * Injects all transitive dependencies that are present in channels into parent POM's dependencyManagement section. *

    * Call this method after all modules has been processed via the `processModule()` method. */ private void injectTransitiveDependencies() throws DependencyGraphBuilderException, XMLStreamException { PomManipulator rootManipulator = manipulators.get( Pair.of(mavenProject.getGroupId(), mavenProject.getArtifactId())); // (dependency => exclusions list) map of undeclared dependencies Map> undeclaredDependencies = collectUndeclaredDependencies(); List>> dependenciesToInject = undeclaredDependencies.entrySet().stream() .sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey())) // filter only deps that have a stream defined in the channel .filter(entry -> { // verify if the artifact from the project dependency tree is modified by the channel ArtifactRef a = entry.getKey(); try { VersionResult versionResult = channelSession.findLatestMavenArtifactVersion(a.getGroupId(), a.getArtifactId(), a.getType(), a.getClassifier(), a.getVersionString()); return !versionResult.getVersion().equals(a.getVersionString()); } catch (UnresolvedMavenArtifactException e) { // no stream found -> no change return false; } }) .filter(entry -> { ArtifactRef a = entry.getKey(); boolean isIgnored = ignoredStreams.contains(a.asProjectRef()) || ignoredStreams.contains(new SimpleProjectRef(a.getGroupId(), "*")); boolean isUnignored = unignoredStreams.contains(a.asProjectRef()); return isUnignored || !isIgnored; }) .collect(Collectors.toList()); for (Map.Entry> entry : dependenciesToInject) { ArtifactRef a = entry.getKey(); try { VersionResult versionResult = channelSession.findLatestMavenArtifactVersion(a.getGroupId(), a.getArtifactId(), a.getType(), a.getClassifier(), a.getVersionString()); String newVersion = versionResult.getVersion(); if (!newVersion.equals(a.getVersionString())) { SimpleArtifactRef newDependency = new SimpleArtifactRef(a.getGroupId(), a.getArtifactId(), newVersion, a.getType(), a.getClassifier()); getLog().info(String.format("Injecting undeclared dependency: %s (original version was %s)", newDependency, a.getVersionString())); rootManipulator.injectManagedDependency(newDependency, entry.getValue()); } } catch (UnresolvedMavenArtifactException e) { getLog().error(String.format("Unable to resolve dependency %s", a)); } } } /** * Performs hard property overrides based on the `overrideProperties` parameter input. * * @param manipulator manipulator for current module * @return list of overridden properties */ private List performHardPropertyOverrides(PomManipulator manipulator) throws XMLStreamException { ArrayList overriddenProperties = new ArrayList<>(); for (String nameValue: overrideProperties) { String[] split = nameValue.split("="); if (split.length != 2) { getLog().error(String.format("Can't interpret property to override settings: '%s'", nameValue)); continue; } String propertyName = split[0]; String propertyValue = split[1]; if (manipulator.overrideProperty(propertyName, propertyValue)) { getLog().info(String.format("Property '%s' overridden to '%s'", propertyName, propertyValue)); overriddenProperties.add(propertyName); } } return overriddenProperties; } /** * Performs hard dependency versions overrides based on the `overrideDependencies` parameter input. These versions * are inlined into the version elements, version properties are not followed. * * @param resolvedProjectDependencies collection of all resolved dependencies in the module * @param manipulator manipulator for current module * @return list of updated dependencies */ private List performHardDependencyOverrides(Map resolvedProjectDependencies, PomManipulator manipulator) throws XMLStreamException { List overriddenDependencies = new ArrayList<>(); for (Dependency dependency: resolvedProjectDependencies.values()) { Optional overridenVersion = findOverridenVersion(dependency); if (overridenVersion.isPresent()) { manipulator.overrideDependencyVersion(toArtifactRef(dependency), overridenVersion.get()); overriddenDependencies.add(dependency); } } return overriddenDependencies; } private Optional findOverridenVersion(Dependency dependency) { for (String gav: overrideDependencies) { String[] split = gav.split(":"); if (split.length != 3) { continue; } String g = split[0]; String a = split[1]; String v = split[2]; if (dependency.getGroupId().equals(g) && dependency.getArtifactId().equals(a)) { return Optional.of(v); } } return Optional.empty(); } private boolean isIgnoredProperty(String propertyName) { if (ignoreProperties.contains(propertyName)) { return true; } for (String prefix: ignorePropertiesPrefixedWith) { if (propertyName.startsWith(prefix)) { return true; } } return false; } private Map collectResolvedProjectDependencies(Project pmeProject) throws ManipulationException { Map projectDependencies = new HashMap<>(); projectDependencies.putAll(pmeProject.getResolvedManagedDependencies(manipulationSession)); projectDependencies.putAll(pmeProject.getResolvedDependencies(manipulationSession)); if (projectDependencies.isEmpty()) { getLog().debug("No dependencies found in " + pmeProject.getArtifactId()); } return projectDependencies; } private List> findDependenciesToUpgrade( Map resolvedProjectDependencies) { List> dependenciesToUpgrade = new ArrayList<>(); for (Map.Entry entry : resolvedProjectDependencies.entrySet()) { ArtifactRef artifactRef = entry.getKey(); Dependency dependency = entry.getValue(); Objects.requireNonNull(artifactRef); Objects.requireNonNull(dependency); if (projectGavs.contains(artifactRef.asProjectVersionRef())) { getLog().debug("Ignoring in-project dependency: " + artifactRef.asProjectVersionRef().toString()); continue; } if (!unignoredStreams.contains(artifactRef.asProjectRef())) { if (ignoredStreams.contains(artifactRef.asProjectRef())) { getLog().info("Skipping dependency (ignored stream): " + artifactRef.asProjectVersionRef().toString()); continue; } ProjectRef wildCardIgnoredProjectRef = new SimpleProjectRef(artifactRef.getGroupId(), "*"); if (ignoredStreams.contains(wildCardIgnoredProjectRef)) { getLog().info("Skipping dependency (ignored stream): " + artifactRef.asProjectVersionRef().toString()); continue; } } if (artifactRef.getVersionString() == null) { // this is not expected to happen getLog().error("Resolved dependency has null version: " + artifactRef); continue; } if (VersionUtils.isProperty(artifactRef.getVersionString())) { // hack: PME doesn't seem to resolve properties from external parent poms Pair externalProperty = resolveExternalProperty(mavenProject, VersionUtils.extractPropertyName(artifactRef.getVersionString())); if (externalProperty != null) { artifactRef = new SimpleArtifactRef(artifactRef.getGroupId(), artifactRef.getArtifactId(), externalProperty.getRight(), artifactRef.getType(), artifactRef.getClassifier()); } else { // didn't manage to resolve dependency version, this is not expected to happen getLog().error("Resolved dependency has version with property: " + artifactRef); continue; } } if ("test".equals(dependency.getScope()) && ignoreTestDependencies) { getLog().info("Skipping dependency (ignored scope): " + artifactRef.asProjectVersionRef().toString()); continue; } try { VersionResult versionResult = channelSession.findLatestMavenArtifactVersion(artifactRef.getGroupId(), artifactRef.getArtifactId(), artifactRef.getType(), artifactRef.getClassifier(), artifactRef.getVersionString()); String channelVersion = versionResult.getVersion(); if (!channelVersion.equals(artifactRef.getVersionString())) { getLog().info("Updating dependency " + artifactRef.getGroupId() + ":" + artifactRef.getArtifactId() + ":" + artifactRef.getVersionString() + " to version " + channelVersion); } dependenciesToUpgrade.add(Pair.of(dependency, channelVersion)); } catch (UnresolvedMavenArtifactException e) { // this produces a lot of noise due to many of e.g. test artifacts not being managed by channels, so keep it // at the debug level getLog().debug("Can't resolve artifact: " + artifactRef, e); } } return dependenciesToUpgrade; } private void initChannel() throws MojoExecutionException { int numberOfSources = 0; if (StringUtils.isNotBlank(channelFile)) numberOfSources++; if (StringUtils.isNotBlank(channelGAV)) numberOfSources++; if (StringUtils.isNotBlank(manifestFile)) numberOfSources++; if (StringUtils.isNotBlank(manifestGAV)) numberOfSources++; if (numberOfSources > 1) { throw new MojoExecutionException("Exactly one of [channelFile, channelGAV, manifestFile, manifestGAV] has to be given."); } // Do not enforce this for now, repositories are also read from project pom.xml currently. /*if ((StringUtils.isNotBlank(manifestFile) || StringUtils.isNotBlank(manifestGAV)) && remoteRepositories.isEmpty()) { throw new MojoExecutionException("The remoteRepositories property is mandatory when manifest is given."); }*/ try { if (StringUtils.isNotBlank(channelFile)) { String[] paths = channelFile.split(","); for (String path: paths) { Path channelFilePath = Path.of(path); if (!channelFilePath.isAbsolute()) { channelFilePath = Path.of(mavenSession.getExecutionRootDirectory()).resolve(channelFilePath); } getLog().info("Reading channel file " + channelFilePath); channels.add(ChannelMapper.from(channelFilePath.toUri().toURL())); } } else if (StringUtils.isNotBlank(channelGAV)) { String[] gavs = channelGAV.split(","); for (String gav: gavs) { channels.addAll(resolveChannelsFromGav(gav)); } } else if (StringUtils.isNotBlank(manifestFile)) { String[] paths = manifestFile.split(","); for (String path: paths) { URL manifestUrl = Path.of(path).toUri().toURL(); ChannelManifestCoordinate coordinate = new ChannelManifestCoordinate(manifestUrl); channels.add(new Channel("a-channel", null, null, null, coordinate, null, null)); } } else if (StringUtils.isNotBlank(manifestGAV)) { String[] gavs = manifestGAV.split(","); for (String gav: gavs) { ChannelManifestCoordinate coordinate = toManifestCoordinate(gav); // Compose list of repositories to look for the manifest as a union of the remoteRepositories property // and repositories from the project pom.xml. List repositories = mavenProject.getRemoteProjectRepositories().stream() .map(rr -> new Repository(rr.getId(), rr.getUrl())) .collect(Collectors.toList()); repositories.addAll(createRepositories(remoteRepositories)); channels.add(new Channel("a-channel", null, null, repositories, coordinate, null, null)); } } else { throw new MojoExecutionException("No channel or manifest specified."); } } catch (MalformedURLException e) { throw new MojoExecutionException("Can't parse the channel or manifest file path", e); } } private List resolveChannelsFromGav(String gavString) { ChannelCoordinate channelCoordinate = toChannelCoordinate(gavString); RepositorySystemSession repoSession = mavenSession.getRepositorySession(); // Collect repositories that should be used to locate the channel. Probably this should be a union of the // repositories given in the `remoteRepositories` property and the repositories defined in the project pom.xml. final List channelRepos = new ArrayList<>(mavenProject.getRemoteProjectRepositories()); int repoNumber = 0; if (remoteRepositories != null && !remoteRepositories.isEmpty()) { for (String repoUrl: remoteRepositories) { RemoteRepository repo = new RemoteRepository.Builder("repo-" + repoNumber++, "default", repoUrl).build(); channelRepos.add(repo); } } try (VersionResolverFactory versionResolverFactory = new VersionResolverFactory(repositorySystem, repoSession)) { return versionResolverFactory.resolveChannels(List.of(channelCoordinate), channelRepos); } catch (MalformedURLException e) { // This should not happen here, URL coordinates are not supposed to be present. throw new IllegalStateException("Couldn't resolve channel GAV", e); } } private static ChannelCoordinate toChannelCoordinate(String gavString) { String[] gavSegments = gavString.split(":"); ChannelCoordinate coordinate; if (gavSegments.length == 2) { coordinate = new ChannelCoordinate(gavSegments[0], gavSegments[1]); } else if (gavSegments.length == 3) { coordinate = new ChannelCoordinate(gavSegments[0], gavSegments[1], gavSegments[2]); } else { throw new IllegalArgumentException("Invalid GAV string, channel GAV has to have two or three segments separated with ':'. Given value was: " + gavString); } return coordinate; } private static ChannelManifestCoordinate toManifestCoordinate(String gavString) { String[] gavSegments = gavString.split(":"); ChannelManifestCoordinate coordinate; if (gavSegments.length == 2) { coordinate = new ChannelManifestCoordinate(gavSegments[0], gavSegments[1]); } else if (gavSegments.length == 3) { coordinate = new ChannelManifestCoordinate(gavSegments[0], gavSegments[1], gavSegments[2]); } else { throw new IllegalArgumentException("Invalid GAV string, manifest GAV has to have two or three segments separated with ':'. Given value was: " + gavString); } return coordinate; } private static List overrideRemoteRepositories(List channels, List repositories) { List updatedChannels = new ArrayList<>(channels.size()); for (Channel channel: channels) { updatedChannels.add(new Channel(channel.getName(), channel.getDescription(), channel.getVendor(), createRepositories(repositories), channel.getManifestCoordinate(), channel.getBlocklistCoordinate(), channel.getNoStreamStrategy())); } return updatedChannels; } private static List createRepositories(List userRepositories) { HashMap result = new HashMap<>(); int idx = 0; for (String input: userRepositories) { String[] segments = input.split("::"); Repository previous; String id; if (segments.length == 1) { id = "repo-" + idx++; previous = result.put(id, new Repository(id, segments[0])); } else if (segments.length == 2) { id = segments[0]; previous = result.put(id, new Repository(id, segments[1])); } else { throw new IllegalArgumentException("Invalid remote repository entry: " + input); } if (previous != null) { throw new IllegalArgumentException("Duplicate remote repository key: '" + id + "'"); } } return new ArrayList<>(result.values()); } /** * Collects transitive dependencies from all project's modules, which are not declared in the project. *

    * This has to be called after all submodules has been processed (so that all declared dependencies has been * collected). * * @return a map containing a dependencies as keys and list of exclusions as values. */ private Map> collectUndeclaredDependencies() throws DependencyGraphBuilderException { Map> artifactExclusions = new HashMap<>(); // First of all, if `copyExclusionsFrom` module has been set, we are going to remember exclusions from all dependencies // of this module. These exclusions will be used for the newly injected dependency elements. // First of all, we want to collect information about exclusions for the dependencies that are eventually going to be // injected into the project. There are two possible strategies: if (copyExclusionsFrom != null) { // Either collect the exclusions from a dependency tree of a project submodule that a user specified. ProjectRef exclusionsModule = SimpleProjectRef.parse(copyExclusionsFrom); Optional exclusionsProject = mavenProject.getCollectedProjects().stream() .filter(p -> exclusionsModule.getGroupId().equals(p.getGroupId()) && exclusionsModule.getArtifactId().equals(p.getArtifactId())) .findFirst(); if (exclusionsProject.isPresent()) { ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(mavenSession.getProjectBuildingRequest()); buildingRequest.setProject(exclusionsProject.get()); DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, null); CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(); rootNode.accept(visitor); visitor.getNodes().forEach(node -> { HashSet exclusionSet = new HashSet<>(toProjectRefs(node.getExclusions())); Collection previousExclusions = artifactExclusions.put(toArtifactRef(node.getArtifact()), exclusionSet); if (previousExclusions != null) { exclusionSet.addAll(previousExclusions); } }); } } else { // Or else collect the exclusions from a dependency management section of an effective POM of the root module. List managedDependencies = mavenProject.getModel().getDependencyManagement().getDependencies(); managedDependencies.forEach(d -> artifactExclusions.put(toArtifactRef(d), toProjectRefs(d.getExclusions()))); } final List projectGAs = projectGavs.stream().map(ProjectRef::asProjectRef) .collect(Collectors.toList()); // This performs a traversal of a dependency tree of all submodules in the project. All discovered dependencies // that are not directly declared in the project are considered transitive dependencies. Map> undeclaredDependencies = new HashMap<>(); for (MavenProject module: mavenProject.getCollectedProjects()) { ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(mavenSession.getProjectBuildingRequest()); buildingRequest.setProject(module); DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, null); CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(); rootNode.accept(visitor); visitor.getNodes().forEach(node -> { ArtifactRef artifact = toArtifactRef(node.getArtifact()); Collection exclusions = artifactExclusions.get(artifact); // Project modules should not be counted into undeclared dependencies. if (projectGAs.contains(artifact.asProjectRef())) { return; } // Declared project dependencies should not be counted as undeclared. if (declaredDependencies.contains(artifact.asProjectRef())) { return; } // Ignore test scope dependencies. if ("test".equals(node.getArtifact().getScope()) && ignoreTestDependencies) { // Ignore test scope undeclared dependencies entirely. return; } undeclaredDependencies.put(artifact, exclusions); }); } return undeclaredDependencies; } /** * If a property references another property (possibly recursively), this method returns the final referenced * property name and the module where the property is defined. *

    * This method doesn't support cases when a property value is a composition of multiple properties, or a composition * of properties and strings. */ static Pair followProperties(Project pmeProject, String propertyName) { Properties properties = pmeProject.getModel().getProperties(); if (!properties.containsKey(propertyName)) { // property not present in current module, look into parent module Project parentProject = pmeProject.getProjectParent(); if (parentProject == null) { return null; } else { return followProperties(parentProject, propertyName); } } else { // property is defined in this module String propertyValue = (String) properties.get(propertyName); if (VersionUtils.isProperty(propertyValue)) { // the property value is also a property reference -> follow the chain String newPropertyName = VersionUtils.extractPropertyName(propertyValue); Pair targetProperty = followProperties(pmeProject, newPropertyName); if (targetProperty != null) { return targetProperty; } } return Pair.of(pmeProject, propertyName); } } /** * Resolves a property from external parent pom. If given property references another property, this method * tries to traverse the property chain. * * @return pair [property, value], where the property is the last property name in the traversal chain */ static Pair resolveExternalProperty(MavenProject mavenProject, String propertyName) { if (mavenProject == null) { return null; } Properties properties = mavenProject.getModel().getProperties(); if (!properties.containsKey(propertyName)) { return resolveExternalProperty(mavenProject.getParent(), propertyName); } else { // property is defined in this module String propertyValue = (String) properties.get(propertyName); if (VersionUtils.isProperty(propertyValue)) { // the property value is also a property reference -> follow the chain String newPropertyName = VersionUtils.extractPropertyName(propertyValue); Pair targetProperty = resolveExternalProperty(mavenProject, newPropertyName); if (targetProperty != null) { return targetProperty; } } return Pair.of(propertyName, propertyValue); } } }





    © 2015 - 2024 Weber Informatics LLC | Privacy Policy