org.apache.maven.plugins.invoker.InstallMojo Maven / Gradle / Ivy
Show all versions of maven-invoker-plugin Show documentation
/*
* 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.maven.plugins.invoker;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.ProjectArtifact;
import org.eclipse.aether.DefaultRepositoryCache;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.ArtifactType;
import org.eclipse.aether.artifact.ArtifactTypeRegistry;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.DefaultDependencyNode;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.installation.InstallRequest;
import org.eclipse.aether.installation.InstallationException;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.artifact.SubArtifact;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
/**
* Installs the project artifacts of the main build into the local repository as a preparation to run the sub projects.
* More precisely, all artifacts of the project itself, all its locally reachable parent POMs and all its dependencies
* from the reactor will be installed to the local repository.
*
* @author Paul Gier
* @author Benjamin Bentmann
* @since 1.2
*/
@Mojo(
name = "install",
defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST,
requiresDependencyResolution = ResolutionScope.TEST,
threadSafe = true)
public class InstallMojo extends AbstractMojo {
// components used in Mojo
@Component
private RepositorySystem repositorySystem;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
/**
* The path to the local repository into which the project artifacts should be installed for the integration tests.
* If not set, the regular local repository will be used. To prevent soiling of your regular local repository with
* possibly broken artifacts, it is strongly recommended to use an isolated repository for the integration tests
* (e.g. ${project.build.directory}/it-repo
).
*/
@Parameter(
property = "invoker.localRepositoryPath",
defaultValue = "${session.localRepository.basedir}",
required = true)
private File localRepositoryPath;
/**
* A flag used to disable the installation procedure. This is primarily intended for usage from the command line to
* occasionally adjust the build.
*
* @since 1.4
*/
@Parameter(property = "invoker.skip", defaultValue = "false")
private boolean skipInstallation;
/**
* Extra dependencies that need to be installed on the local repository.
*
* Format:
*
* groupId:artifactId:version:type:classifier
*
*
* Examples:
*
* org.apache.maven.plugins:maven-clean-plugin:2.4:maven-plugin
* org.apache.maven.plugins:maven-clean-plugin:2.4:jar:javadoc
*
*
* If the type is 'maven-plugin' the plugin will try to resolve the artifact using plugin remote repositories,
* instead of using artifact remote repositories.
*
* NOTICE all dependencies will be resolved with transitive dependencies in runtime
scope.
*
* @since 1.6
*/
@Parameter
private String[] extraArtifacts;
/**
* Scope to resolve project artifacts.
*
* @since 3.5.0
*/
@Parameter(property = "invoker.install.scope", defaultValue = "runtime")
private String scope;
/**
* Performs this mojo's tasks.
*
* @throws MojoExecutionException If the artifacts could not be installed.
*/
public void execute() throws MojoExecutionException {
if (skipInstallation) {
getLog().info("Skipping artifact installation per configuration.");
return;
}
Map resolvedArtifacts = new LinkedHashMap<>();
try {
resolveProjectArtifacts(resolvedArtifacts);
resolveProjectPoms(project, resolvedArtifacts);
resolveProjectDependencies(resolvedArtifacts);
resolveExtraArtifacts(resolvedArtifacts);
installArtifacts(resolvedArtifacts);
} catch (DependencyResolutionException | InstallationException | ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
private void resolveProjectArtifacts(Map resolvedArtifacts) {
// pom packaging doesn't have a main artifact
if (project.getArtifact() != null && project.getArtifact().getFile() != null) {
Artifact artifact = RepositoryUtils.toArtifact(project.getArtifact());
resolvedArtifacts.put(ArtifactIdUtils.toId(artifact), artifact);
}
project.getAttachedArtifacts().stream()
.map(RepositoryUtils::toArtifact)
.forEach(a -> resolvedArtifacts.put(ArtifactIdUtils.toId(a), a));
}
private void resolveProjectPoms(MavenProject project, Map resolvedArtifacts)
throws ArtifactResolutionException {
if (project == null) {
return;
}
Artifact projectPom = RepositoryUtils.toArtifact(new ProjectArtifact(project));
if (projectPom.getFile() != null) {
resolvedArtifacts.put(projectPom.toString(), projectPom);
} else {
Artifact artifact = resolveArtifact(projectPom, project.getRemoteProjectRepositories());
resolvedArtifacts.put(ArtifactIdUtils.toId(artifact), artifact);
}
resolveProjectPoms(project.getParent(), resolvedArtifacts);
}
private void resolveProjectDependencies(Map resolvedArtifacts)
throws ArtifactResolutionException, MojoExecutionException, DependencyResolutionException {
DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(scope);
ArtifactTypeRegistry artifactTypeRegistry =
session.getRepositorySession().getArtifactTypeRegistry();
List managedDependencies = Optional.ofNullable(project.getDependencyManagement())
.map(DependencyManagement::getDependencies)
.orElseGet(Collections::emptyList)
.stream()
.map(d -> RepositoryUtils.toDependency(d, artifactTypeRegistry))
.collect(Collectors.toList());
List dependencies = project.getDependencies().stream()
.map(d -> RepositoryUtils.toDependency(d, artifactTypeRegistry))
.filter(d -> classpathFilter.accept(new DefaultDependencyNode(d), null))
.collect(Collectors.toList());
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
collectRequest.setDependencies(dependencies);
collectRequest.setManagedDependencies(managedDependencies);
collectRequest.setRepositories(project.getRemoteProjectRepositories());
DependencyRequest request = new DependencyRequest(collectRequest, classpathFilter);
DependencyResult dependencyResult =
repositorySystem.resolveDependencies(session.getRepositorySession(), request);
List artifacts = dependencyResult.getArtifactResults().stream()
.map(ArtifactResult::getArtifact)
.collect(Collectors.toList());
artifacts.forEach(a -> resolvedArtifacts.put(ArtifactIdUtils.toId(a), a));
resolvePomsForArtifacts(artifacts, resolvedArtifacts, collectRequest.getRepositories());
}
/**
* Resolve extra artifacts.
*/
private void resolveExtraArtifacts(Map resolvedArtifacts)
throws MojoExecutionException, DependencyResolutionException, ArtifactResolutionException {
if (extraArtifacts == null) {
return;
}
DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME);
for (String extraArtifact : extraArtifacts) {
String[] gav = extraArtifact.split(":");
if (gav.length < 3 || gav.length > 5) {
throw new MojoExecutionException("Invalid artifact " + extraArtifact);
}
String groupId = gav[0];
String artifactId = gav[1];
String version = gav[2];
String type = "jar";
if (gav.length > 3) {
type = gav[3];
}
String classifier = null;
if (gav.length == 5) {
classifier = gav[4];
}
ArtifactType artifactType =
session.getRepositorySession().getArtifactTypeRegistry().get(type);
List remoteRepositories =
artifactType != null && "maven-plugin".equals(artifactType.getId())
? project.getRemotePluginRepositories()
: project.getRemoteProjectRepositories();
Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, null, version, artifactType);
resolvePomsForArtifacts(Collections.singletonList(artifact), resolvedArtifacts, remoteRepositories);
CollectRequest collectRequest = new CollectRequest();
Dependency root = new Dependency(artifact, JavaScopes.COMPILE);
collectRequest.setRoot(root);
collectRequest.setRepositories(remoteRepositories);
DependencyRequest request = new DependencyRequest(collectRequest, classpathFilter);
DependencyResult dependencyResult =
repositorySystem.resolveDependencies(session.getRepositorySession(), request);
List artifacts = dependencyResult.getArtifactResults().stream()
.map(ArtifactResult::getArtifact)
.collect(Collectors.toList());
artifacts.forEach(a -> resolvedArtifacts.put(ArtifactIdUtils.toId(a), a));
resolvePomsForArtifacts(artifacts, resolvedArtifacts, collectRequest.getRepositories());
}
}
private void resolvePomsForArtifacts(
List artifacts,
Map resolvedArtifacts,
List remoteRepositories)
throws ArtifactResolutionException, MojoExecutionException {
for (Artifact a : artifacts) {
Artifact artifactResult = resolveArtifact(new SubArtifact(a, "", "pom"), remoteRepositories);
resolvePomWithParents(artifactResult, resolvedArtifacts, remoteRepositories);
}
}
private void resolvePomWithParents(
Artifact artifact, Map resolvedArtifacts, List remoteRepositories)
throws MojoExecutionException, ArtifactResolutionException {
if (resolvedArtifacts.containsKey(ArtifactIdUtils.toId(artifact))) {
return;
}
Model model = PomUtils.loadPom(artifact.getFile());
Parent parent = model.getParent();
if (parent != null) {
DefaultArtifact pom =
new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion());
Artifact resolvedPom = resolveArtifact(pom, remoteRepositories);
resolvePomWithParents(resolvedPom, resolvedArtifacts, remoteRepositories);
}
resolvedArtifacts.put(ArtifactIdUtils.toId(artifact), artifact);
}
private Artifact resolveArtifact(Artifact artifact, List remoteRepositories)
throws ArtifactResolutionException {
ArtifactRequest request = new ArtifactRequest();
request.setArtifact(artifact);
request.setRepositories(remoteRepositories);
ArtifactResult artifactResult = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
return artifactResult.getArtifact();
}
/**
* Install list of artifacts into local repository.
*/
private void installArtifacts(Map resolvedArtifacts) throws InstallationException {
RepositorySystemSession systemSessionForLocalRepo = createSystemSessionForLocalRepo();
// we can have on dependency two artifacts with the same groupId:artifactId
// with different version, in such case when we install both in one request
// metadata will contain only one version
Map> collect = resolvedArtifacts.values().stream()
.filter(a -> !hasTheSamePathAsTarget(a, systemSessionForLocalRepo))
.collect(Collectors.groupingBy(
a -> String.format("%s:%s:%s", a.getGroupId(), a.getArtifactId(), a.getVersion()),
LinkedHashMap::new,
Collectors.toList()));
for (List artifacts : collect.values()) {
InstallRequest request = new InstallRequest();
request.setArtifacts(artifacts);
repositorySystem.install(systemSessionForLocalRepo, request);
}
}
private boolean hasTheSamePathAsTarget(Artifact artifact, RepositorySystemSession systemSession) {
try {
LocalRepositoryManager lrm = systemSession.getLocalRepositoryManager();
File targetBasedir = lrm.getRepository().getBasedir();
if (targetBasedir == null) {
return false;
}
File targetFile = new File(targetBasedir, lrm.getPathForLocalArtifact(artifact)).getCanonicalFile();
File sourceFile = artifact.getFile().getCanonicalFile();
if (Objects.equals(targetFile, sourceFile)) {
getLog().debug("Skip install the same target " + sourceFile);
return true;
}
return false;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Create a new {@link RepositorySystemSession} connected with local repo.
*/
private RepositorySystemSession createSystemSessionForLocalRepo() {
RepositorySystemSession repositorySystemSession = session.getRepositorySession();
if (localRepositoryPath != null) {
// "clone" repository session and replace localRepository
DefaultRepositorySystemSession newSession =
new DefaultRepositorySystemSession(session.getRepositorySession());
// Clear cache, since we're using a new local repository
newSession.setCache(new DefaultRepositoryCache());
// keep same repositoryType
String contentType = newSession.getLocalRepository().getContentType();
if ("enhanced".equals(contentType)) {
contentType = "default";
}
LocalRepositoryManager localRepositoryManager = repositorySystem.newLocalRepositoryManager(
newSession, new LocalRepository(localRepositoryPath, contentType));
newSession.setLocalRepositoryManager(localRepositoryManager);
repositorySystemSession = newSession;
getLog().debug("localRepoPath: "
+ localRepositoryManager.getRepository().getBasedir());
}
return repositorySystemSession;
}
}