diff --git a/pom.xml b/pom.xml index 4bb9610a..26641448 100644 --- a/pom.xml +++ b/pom.xml @@ -183,18 +183,6 @@ under the License. 1.4 - - org.apache.maven.shared - maven-artifact-transfer - 0.13.1 - - - org.apache.maven.shared - maven-common-artifact-filters - 3.2.0 - compile - - ${beanshell-groupId} ${beanshell-artifactId} @@ -411,7 +399,6 @@ under the License. ${project.build.directory}/local-repo src/it/settings.xml -Djava.io.tmpdir=${project.build.directory} - true ${maven.compiler.source} ${maven.compiler.target} diff --git a/src/it/staging-dependencies-test-scope/invoker.properties b/src/it/staging-dependencies-test-scope/invoker.properties new file mode 100644 index 00000000..353946b6 --- /dev/null +++ b/src/it/staging-dependencies-test-scope/invoker.properties @@ -0,0 +1,18 @@ +# 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. + +invoker.goals = verify diff --git a/src/it/staging-dependencies-test-scope/pom.xml b/src/it/staging-dependencies-test-scope/pom.xml new file mode 100644 index 00000000..b130f77a --- /dev/null +++ b/src/it/staging-dependencies-test-scope/pom.xml @@ -0,0 +1,71 @@ + + + + + + 4.0.0 + + test + staging-dependencies-test-scope + 1.0-SNAPSHOT + + + Test to check for staging of test scope dependencies. + + + + + + org.slf4j + slf4j-api + 1.7.36 + + + + + org.slf4j + slf4j-simple + 1.7.36 + test + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + @pom.version@ + + ${project.build.directory}/it-repo + test + + + + integration-test + + install + + + + + + + diff --git a/src/it/staging-dependencies-test-scope/verify.bsh b/src/it/staging-dependencies-test-scope/verify.bsh new file mode 100644 index 00000000..9b32cff2 --- /dev/null +++ b/src/it/staging-dependencies-test-scope/verify.bsh @@ -0,0 +1,65 @@ +/* + * 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. + */ + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import org.codehaus.plexus.util.*; +import org.codehaus.plexus.util.xml.*; + +try +{ + File itRepoDir = new File( basedir, "target/it-repo" ); + System.out.println( "Checking for existence of: " + itRepoDir ); + if ( !itRepoDir.isDirectory() ) + { + System.out.println( "FAILED!" ); + return false; + } + + String[] files = { + "org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar", + "org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.pom", + "org/slf4j/slf4j-api/maven-metadata-local.xml", + "org/slf4j/slf4j-parent/1.7.36/slf4j-parent-1.7.36.pom", + "org/slf4j/slf4j-parent/maven-metadata-local.xml", + "org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar", + "org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.pom", + "org/slf4j/slf4j-simple/maven-metadata-local.xml", + "test/staging-dependencies-test-scope/1.0-SNAPSHOT/staging-dependencies-test-scope-1.0-SNAPSHOT.pom", + "test/staging-dependencies-test-scope/1.0-SNAPSHOT/staging-dependencies-test-scope-1.0-SNAPSHOT.jar" + }; + for ( String file : files ) + { + File stagedFile = new File( itRepoDir, file ); + System.out.println( "Checking for existence of: " + stagedFile ); + if ( !stagedFile.isFile() ) + { + throw new IllegalStateException( "Missing: " + stagedFile ); + } + } +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/src/it/staging-dependencies/pom.xml b/src/it/staging-dependencies/pom.xml index 3aa0840a..31cca75c 100644 --- a/src/it/staging-dependencies/pom.xml +++ b/src/it/staging-dependencies/pom.xml @@ -62,6 +62,12 @@ under the License. dep 2.0 + + org.slf4j + slf4j-simple + 1.7.36 + test + diff --git a/src/it/staging-dependencies/verify.bsh b/src/it/staging-dependencies/verify.bsh index ceb7f022..7e43c8ba 100644 --- a/src/it/staging-dependencies/verify.bsh +++ b/src/it/staging-dependencies/verify.bsh @@ -56,6 +56,8 @@ try "org/apache/maven/its/dep/2.0/dep-2.0.pom", "org/apache/maven/its/dep/2.0/dep-2.0.jar", "org/apache/maven/its/dep/maven-metadata-local.xml", + "test/staging-dependencies/1.0-SNAPSHOT/staging-dependencies-1.0-SNAPSHOT.jar", + "test/staging-dependencies/1.0-SNAPSHOT/staging-dependencies-1.0-SNAPSHOT.pom" }; for ( String file : files ) { @@ -67,6 +69,12 @@ try } } + File stagedTestScope = new File(itRepoDir, "org/slf4j/slf4j-simple"); + if ( stagedTestScope.exists() ) + { + throw new IllegalStateException( "Test scope dependencies should not exists" ); + } + Reader reader = ReaderFactory.newXmlReader( new File( itRepoDir, "org/apache/maven/its/dep/maven-metadata-local.xml" ) ); Xpp3Dom dom = Xpp3DomBuilder.build( reader ); IOUtil.close( reader ); diff --git a/src/it/staging-pom/pom.xml b/src/it/staging-pom/pom.xml index 443a3fc6..e9582c67 100644 --- a/src/it/staging-pom/pom.xml +++ b/src/it/staging-pom/pom.xml @@ -22,6 +22,13 @@ under the License. 4.0.0 + + org.apache.maven.plugins + maven-plugins + 39 + + + test pom-packaging 1.0-SNAPSHOT @@ -31,6 +38,10 @@ under the License. Test to check for MINVOKER-51, i.e. NPE when packaging is pom. + + true + + diff --git a/src/it/staging-pom/verify.bsh b/src/it/staging-pom/verify.bsh index 8565933f..c49ef782 100644 --- a/src/it/staging-pom/verify.bsh +++ b/src/it/staging-pom/verify.bsh @@ -33,6 +33,9 @@ try String[] files = { "test/pom-packaging/1.0-SNAPSHOT/pom-packaging-1.0-SNAPSHOT.pom", + "org/apache/maven/plugins/maven-plugins/39/maven-plugins-39.pom", + "org/apache/maven/maven-parent/39/maven-parent-39.pom", + "org/apache/apache/29/apache-29.pom" }; for ( String file : files ) { diff --git a/src/it/staging-reactor/verify.bsh b/src/it/staging-reactor/verify.bsh index 19f6fa83..9a684fc7 100644 --- a/src/it/staging-reactor/verify.bsh +++ b/src/it/staging-reactor/verify.bsh @@ -44,7 +44,6 @@ try "test/mod2/1.0-SNAPSHOT/maven-metadata-local.xml", "test/mod2/1.0-SNAPSHOT/mod2-1.0-SNAPSHOT.pom", "test/mod2/1.0-SNAPSHOT/mod2-1.0-SNAPSHOT.jar", - "test/mod2/1.0-SNAPSHOT/mod2-1.0-SNAPSHOT-sources.jar", "test/mod2-parent/maven-metadata-local.xml", "test/mod2-parent/1.0-SNAPSHOT/maven-metadata-local.xml", "test/mod2-parent/1.0-SNAPSHOT/mod2-parent-1.0-SNAPSHOT.pom", diff --git a/src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java b/src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java index 1e482873..8c2000d4 100644 --- a/src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java @@ -19,18 +19,18 @@ package org.apache.maven.plugins.invoker; import java.io.File; -import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.factory.ArtifactFactory; -import org.apache.maven.artifact.repository.ArtifactRepository; +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; @@ -41,53 +41,60 @@ 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.ProjectBuildingRequest; -import org.apache.maven.shared.artifact.filter.resolve.PatternExclusionsFilter; -import org.apache.maven.shared.transfer.artifact.install.ArtifactInstaller; -import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate; -import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver; -import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException; -import org.apache.maven.shared.transfer.repository.RepositoryManager; -import org.codehaus.plexus.util.FileUtils; +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.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.ArtifactDescriptorException; +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.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. * - * @since 1.2 * @author Paul Gier * @author Benjamin Bentmann - * + * @since 1.2 */ -// CHECKSTYLE_OFF: LineLength @Mojo( name = "install", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST, - requiresDependencyResolution = ResolutionScope.RUNTIME, + requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true) -// CHECKSTYLE_ON: LineLength public class InstallMojo extends AbstractMojo { - /** - * Maven artifact install component to copy artifacts to the local repository. - */ - @Component - private ArtifactInstaller installer; + // components used in Mojo @Component - private RepositoryManager repositoryManager; + private RepositorySystem repositorySystem; - /** - * The component used to create artifacts. - */ - @Component - private ArtifactFactory artifactFactory; + @Parameter(defaultValue = "${session}", readonly = true, required = true) + private MavenSession session; - /** - */ - @Parameter(property = "localRepository", required = true, readonly = true) - private ArtifactRepository localRepository; + @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. @@ -101,21 +108,6 @@ public class InstallMojo extends AbstractMojo { required = true) private File localRepositoryPath; - /** - * The current Maven project. - */ - @Parameter(defaultValue = "${project}", readonly = true, required = true) - private MavenProject project; - - @Parameter(defaultValue = "${session}", readonly = true, required = true) - private MavenSession session; - - /** - * The set of Maven projects in the reactor build. - */ - @Parameter(defaultValue = "${reactorProjects}", readonly = true) - private Collection reactorProjects; - /** * A flag used to disable the installation procedure. This is primarily intended for usage from the command line to * occasionally adjust the build. @@ -126,32 +118,23 @@ public class InstallMojo extends AbstractMojo { private boolean skipInstallation; /** - * The identifiers of already installed artifacts, used to avoid multiple installation of the same artifact. - */ - private Collection installedArtifacts; - - /** - * The identifiers of already copied artifacts, used to avoid multiple installation of the same artifact. - */ - private Collection copiedArtifacts; - - /** - * Extra dependencies that need to be installed on the local repository.
+ * 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 */ @@ -159,19 +142,12 @@ public class InstallMojo extends AbstractMojo { private String[] extraArtifacts; /** + * Scope to resolve project artifacts. + * + * @since 3.5.0 */ - @Component - private DependencyResolver resolver; - - /** - * if the local repository is not used as test repo, the parameter can force get artifacts from local repo - * if available instead of download the artifacts again. - * @since 3.2.1 - */ - @Parameter(property = "invoker.useLocalRepository", defaultValue = "false") - private boolean useLocalRepository; - - private ProjectBuildingRequest projectBuildingRequest; + @Parameter(property = "invoker.install.scope", defaultValue = "runtime") + private String scope; /** * Performs this mojo's tasks. @@ -184,377 +160,246 @@ public void execute() throws MojoExecutionException { return; } - createTestRepository(); - - installedArtifacts = new HashSet<>(); - copiedArtifacts = new HashSet<>(); + Collection resolvedArtifacts = new ArrayList<>(); - installProjectDependencies(project, reactorProjects); - installProjectParents(project); - installProjectArtifacts(project); + try { - installExtraArtifacts(extraArtifacts); + resolveProjectArtifacts(resolvedArtifacts); + resolveProjectPoms(project, resolvedArtifacts); + resolveProjectDependencies(resolvedArtifacts); + resolveExtraArtifacts(resolvedArtifacts); + installArtifacts(resolvedArtifacts); + + } catch (DependencyResolutionException + | InstallationException + | ArtifactDescriptorException + | ArtifactResolutionException e) { + throw new MojoExecutionException(e.getMessage(), e); + } } - /** - * Creates the local repository for the integration tests. If the user specified a custom repository location, the - * custom repository will have the same identifier, layout and policies as the real local repository. That means - * apart from the location, the custom repository will be indistinguishable from the real repository such that its - * usage is transparent to the integration tests. - * - * @throws MojoExecutionException If the repository could not be created. - */ - private void createTestRepository() throws MojoExecutionException { + private void resolveProjectArtifacts(Collection resolvedArtifacts) { - if (!localRepositoryPath.exists() && !localRepositoryPath.mkdirs()) { - throw new MojoExecutionException("Failed to create directory: " + localRepositoryPath); + // pom packaging doesn't have a main artifact + if (project.getArtifact() != null && project.getArtifact().getFile() != null) { + resolvedArtifacts.add(RepositoryUtils.toArtifact(project.getArtifact())); } - projectBuildingRequest = - repositoryManager.setLocalRepositoryBasedir(session.getProjectBuildingRequest(), localRepositoryPath); + + resolvedArtifacts.addAll(project.getAttachedArtifacts().stream() + .map(RepositoryUtils::toArtifact) + .collect(Collectors.toList())); } - /** - * Installs the specified artifact to the local repository. Note: This method should only be used for artifacts that - * originate from the current (reactor) build. Artifacts that have been grabbed from the user's local repository - * should be installed to the test repository via {@link #copyArtifact(File, Artifact)}. - * - * @param file The file associated with the artifact, must not be null. This is in most cases the value - * of artifact.getFile() with the exception of the main artifact from a project with - * packaging "pom". Projects with packaging "pom" have no main artifact file. They have however artifact - * metadata (e.g. site descriptors) which needs to be installed. - * @param artifact The artifact to install, must not be null. - * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). - */ - private void installArtifact(File file, Artifact artifact) throws MojoExecutionException { - try { - if (file == null) { - throw new IllegalStateException("Artifact has no associated file: " + artifact.getId()); - } - if (!file.isFile()) { - throw new IllegalStateException("Artifact is not fully assembled: " + file); - } + private void resolveProjectPoms(MavenProject project, Collection resolvedArtifacts) + throws ArtifactResolutionException { - if (installedArtifacts.add(artifact.getId())) { - artifact.setFile(file); - installer.install(projectBuildingRequest, localRepositoryPath, Collections.singletonList(artifact)); - } else { - getLog().debug("Not re-installing " + artifact + ", " + file); - } - } catch (Exception e) { - throw new MojoExecutionException("Failed to install artifact: " + artifact, e); + if (project == null) { + return; + } + + Artifact projectPom = RepositoryUtils.toArtifact(new ProjectArtifact(project)); + if (projectPom.getFile() != null) { + resolvedArtifacts.add(projectPom); + } else { + Artifact artifact = resolveArtifact(projectPom, project.getRemoteProjectRepositories()); + resolvedArtifacts.add(artifact); } + resolveProjectPoms(project.getParent(), resolvedArtifacts); } - /** - * Installs the specified artifact to the local repository. This method serves basically the same purpose as - * {@link #installArtifact(File, Artifact)} but is meant for artifacts that have been resolved - * from the user's local repository (and not the current build outputs). The subtle difference here is that - * artifacts from the repository have already undergone transformations and these manipulations should not be redone - * by the artifact installer. For this reason, this method performs plain copy operations to install the artifacts. - * - * @param file The file associated with the artifact, must not be null. - * @param artifact The artifact to install, must not be null. - * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). - */ - private void copyArtifact(File file, Artifact artifact) throws MojoExecutionException { - try { - if (file == null) { - throw new IllegalStateException("Artifact has no associated file: " + artifact.getId()); - } - if (!file.isFile()) { - throw new IllegalStateException("Artifact is not fully assembled: " + file); - } + private void resolveProjectDependencies(Collection resolvedArtifacts) + throws ArtifactResolutionException, MojoExecutionException, DependencyResolutionException { - if (copiedArtifacts.add(artifact.getId())) { - File destination = new File( - localRepositoryPath, - repositoryManager.getPathForLocalArtifact(projectBuildingRequest, artifact)); + DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(scope); - getLog().debug("Installing " + file + " to " + destination); + ArtifactTypeRegistry artifactTypeRegistry = + session.getRepositorySession().getArtifactTypeRegistry(); - copyFileIfDifferent(file, destination); + List managedDependencies = Optional.ofNullable(project.getDependencyManagement()) + .map(DependencyManagement::getDependencies) + .orElseGet(Collections::emptyList) + .stream() + .map(d -> RepositoryUtils.toDependency(d, artifactTypeRegistry)) + .collect(Collectors.toList()); - MetadataUtils.createMetadata(destination, artifact); - } else { - getLog().debug("Not re-installing " + artifact + ", " + file); - } - } catch (Exception e) { - throw new MojoExecutionException("Failed to stage artifact: " + artifact, e); - } - } + List dependencies = project.getDependencies().stream() + .map(d -> RepositoryUtils.toDependency(d, artifactTypeRegistry)) + .collect(Collectors.toList()); - private void copyFileIfDifferent(File src, File dst) throws IOException { - if (src.lastModified() != dst.lastModified() || src.length() != dst.length()) { - FileUtils.copyFile(src, dst); - dst.setLastModified(src.lastModified()); - } - } + CollectRequest collectRequest = new CollectRequest(); + collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact())); + collectRequest.setDependencies(dependencies); + collectRequest.setManagedDependencies(managedDependencies); - /** - * Installs the main artifact and any attached artifacts of the specified project to the local repository. - * - * @param mvnProject The project whose artifacts should be installed, must not be null. - * @throws MojoExecutionException If any artifact could not be installed. - */ - private void installProjectArtifacts(MavenProject mvnProject) throws MojoExecutionException { - try { - // Install POM (usually attached as metadata but that happens only as a side effect of the Install Plugin) - installProjectPom(mvnProject); + collectRequest.setRepositories(project.getRemoteProjectRepositories()); - // Install the main project artifact (if the project has one, e.g. has no "pom" packaging) - Artifact mainArtifact = mvnProject.getArtifact(); - if (mainArtifact.getFile() != null) { - installArtifact(mainArtifact.getFile(), mainArtifact); - } + DependencyRequest request = new DependencyRequest(collectRequest, classpathFilter); - // Install any attached project artifacts - Collection attachedArtifacts = mvnProject.getAttachedArtifacts(); - for (Artifact attachedArtifact : attachedArtifacts) { - installArtifact(attachedArtifact.getFile(), attachedArtifact); - } - } catch (Exception e) { - throw new MojoExecutionException("Failed to install project artifacts: " + mvnProject, e); - } - } + DependencyResult dependencyResult = + repositorySystem.resolveDependencies(session.getRepositorySession(), request); - /** - * Installs the (locally reachable) parent POMs of the specified project to the local repository. The parent POMs - * from the reactor must be installed or the forked IT builds will fail when using a clean repository. - * - * @param mvnProject The project whose parent POMs should be installed, must not be null. - * @throws MojoExecutionException If any POM could not be installed. - */ - private void installProjectParents(MavenProject mvnProject) throws MojoExecutionException { - try { - for (MavenProject parent = mvnProject.getParent(); parent != null; parent = parent.getParent()) { - if (parent.getFile() == null) { - copyParentPoms(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); - break; - } - installProjectPom(parent); - } - } catch (Exception e) { - throw new MojoExecutionException("Failed to install project parents: " + mvnProject, e); - } - } + List artifacts = dependencyResult.getArtifactResults().stream() + .map(ArtifactResult::getArtifact) + .collect(Collectors.toList()); - /** - * Installs the POM of the specified project to the local repository. - * - * @param mvnProject The project whose POM should be installed, must not be null. - * @throws MojoExecutionException If the POM could not be installed. - */ - private void installProjectPom(MavenProject mvnProject) throws MojoExecutionException { - try { - Artifact pomArtifact = null; - if ("pom".equals(mvnProject.getPackaging())) { - pomArtifact = mvnProject.getArtifact(); - } - if (pomArtifact == null) { - pomArtifact = artifactFactory.createProjectArtifact( - mvnProject.getGroupId(), mvnProject.getArtifactId(), mvnProject.getVersion()); - } - installArtifact(mvnProject.getFile(), pomArtifact); - } catch (Exception e) { - throw new MojoExecutionException("Failed to install POM: " + mvnProject, e); - } + resolvedArtifacts.addAll(artifacts); + resolvePomsForArtifacts(artifacts, resolvedArtifacts, collectRequest.getRepositories()); } /** - * Installs the dependent projects from the reactor to the local repository. The dependencies on other modules from - * the reactor must be installed or the forked IT builds will fail when using a clean repository. + * Resolve extra artifacts. * - * @param mvnProject The project whose dependent projects should be installed, must not be null. - * @param reactorProjects The set of projects in the reactor build, must not be null. - * @throws MojoExecutionException If any dependency could not be installed. + * @return */ - private void installProjectDependencies(MavenProject mvnProject, Collection reactorProjects) - throws MojoExecutionException { - // ... into dependencies that were resolved from reactor projects ... - Collection dependencyProjects = new LinkedHashSet<>(); - collectAllProjectReferences(mvnProject, dependencyProjects); - - // index available reactor projects - Map projects = new HashMap<>(reactorProjects.size()); - for (MavenProject reactorProject : reactorProjects) { - String projectId = reactorProject.getGroupId() - + ':' - + reactorProject.getArtifactId() - + ':' - + reactorProject.getVersion(); - - projects.put(projectId, reactorProject); - } - - // group transitive dependencies (even those that don't contribute to the class path like POMs) ... - Collection artifacts = mvnProject.getArtifacts(); - // ... and those that were resolved from the (local) repo - Collection dependencyArtifacts = new LinkedHashSet<>(); + private void resolveExtraArtifacts(Collection resolvedArtifacts) + throws MojoExecutionException, DependencyResolutionException, ArtifactDescriptorException, + ArtifactResolutionException { - for (Artifact artifact : artifacts) { - // workaround for MNG-2961 to ensure the base version does not contain a timestamp - artifact.isSnapshot(); + if (extraArtifacts == null) { + return; + } - String projectId = artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion(); + DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME); - if (!projects.containsKey(projectId)) { - dependencyArtifacts.add(artifact); + for (String extraArtifact : extraArtifacts) { + String[] gav = extraArtifact.split(":"); + if (gav.length < 3 || gav.length > 5) { + throw new MojoExecutionException("Invalid artifact " + extraArtifact); } - } - // install dependencies - try { - // copy dependencies that where resolved from the local repo - for (Artifact artifact : dependencyArtifacts) { - copyArtifact(artifact); - } + String groupId = gav[0]; + String artifactId = gav[1]; + String version = gav[2]; - // install dependencies that were resolved from the reactor - for (String projectId : dependencyProjects) { - MavenProject dependencyProject = projects.get(projectId); - if (dependencyProject == null) { - getLog().warn("skip dependencyProject null for projectId=" + projectId); - continue; - } - installProjectArtifacts(dependencyProject); - installProjectParents(dependencyProject); + String type = "jar"; + if (gav.length > 3) { + type = gav[3]; } - } catch (Exception e) { - throw new MojoExecutionException("Failed to install project dependencies: " + mvnProject, e); - } - } - protected void collectAllProjectReferences(MavenProject project, Collection dependencyProjects) { - for (MavenProject reactorProject : project.getProjectReferences().values()) { - String projectId = reactorProject.getGroupId() - + ':' - + reactorProject.getArtifactId() - + ':' - + reactorProject.getVersion(); - if (dependencyProjects.add(projectId)) { - collectAllProjectReferences(reactorProject, dependencyProjects); + String classifier = null; + if (gav.length == 5) { + classifier = gav[4]; } - } - } - private void copyArtifact(Artifact artifact) throws MojoExecutionException { - copyPoms(artifact); + ArtifactType artifactType = + session.getRepositorySession().getArtifactTypeRegistry().get(type); - Artifact depArtifact = artifactFactory.createArtifactWithClassifier( - artifact.getGroupId(), - artifact.getArtifactId(), - artifact.getBaseVersion(), - artifact.getType(), - artifact.getClassifier()); + List remoteRepositories = + artifactType != null && "maven-plugin".equals(artifactType.getId()) + ? project.getRemotePluginRepositories() + : project.getRemoteProjectRepositories(); - File artifactFile = artifact.getFile(); + Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, null, version, artifactType); - copyArtifact(artifactFile, depArtifact); - } + resolvePomsForArtifacts(Collections.singletonList(artifact), resolvedArtifacts, remoteRepositories); + + CollectRequest collectRequest = new CollectRequest(); + Dependency root = new Dependency(artifact, JavaScopes.COMPILE); + collectRequest.setRoot(root); + collectRequest.setRepositories(remoteRepositories); - private void copyPoms(Artifact artifact) throws MojoExecutionException { - Artifact pomArtifact = artifactFactory.createProjectArtifact( - artifact.getGroupId(), artifact.getArtifactId(), artifact.getBaseVersion()); + DependencyRequest request = new DependencyRequest(collectRequest, classpathFilter); + DependencyResult dependencyResult = + repositorySystem.resolveDependencies(session.getRepositorySession(), request); - File pomFile = new File(localRepository.getBasedir(), localRepository.pathOf(pomArtifact)); + List artifacts = dependencyResult.getArtifactResults().stream() + .map(ArtifactResult::getArtifact) + .collect(Collectors.toList()); - if (pomFile.isFile()) { - copyArtifact(pomFile, pomArtifact); - copyParentPoms(pomFile); + resolvedArtifacts.addAll(artifacts); + resolvePomsForArtifacts(artifacts, resolvedArtifacts, collectRequest.getRepositories()); } } - /** - * Installs all parent POMs of the specified POM file that are available in the local repository. - * - * @param pomFile The path to the POM file whose parents should be installed, must not be null. - * @throws MojoExecutionException If any (existing) parent POM could not be installed. - */ - private void copyParentPoms(File pomFile) throws MojoExecutionException { - Model model = PomUtils.loadPom(pomFile); - Parent parent = model.getParent(); - if (parent != null) { - copyParentPoms(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); + private void resolvePomsForArtifacts( + List artifacts, Collection resolvedArtifacts, List remoteRepositories) + throws ArtifactResolutionException, MojoExecutionException { + + for (Artifact a : artifacts) { + Artifact artifactResult = resolveArtifact(new SubArtifact(a, "", "pom"), remoteRepositories); + resolvePomWithParents(artifactResult, resolvedArtifacts, remoteRepositories); } } - /** - * Installs the specified POM and all its parent POMs to the local repository. - * - * @param groupId The group id of the POM which should be installed, must not be null. - * @param artifactId The artifact id of the POM which should be installed, must not be null. - * @param version The version of the POM which should be installed, must not be null. - * @throws MojoExecutionException If any (existing) parent POM could not be installed. - */ - private void copyParentPoms(String groupId, String artifactId, String version) throws MojoExecutionException { - Artifact pomArtifact = artifactFactory.createProjectArtifact(groupId, artifactId, version); + private void resolvePomWithParents( + Artifact artifact, Collection resolvedArtifacts, List remoteRepositories) + throws MojoExecutionException, ArtifactResolutionException { - if (installedArtifacts.contains(pomArtifact.getId()) || copiedArtifacts.contains(pomArtifact.getId())) { - getLog().debug("Not re-installing " + pomArtifact); + if (resolvedArtifacts.contains(artifact)) { return; } - File pomFile = new File(localRepository.getBasedir(), localRepository.pathOf(pomArtifact)); - if (pomFile.isFile()) { - copyArtifact(pomFile, pomArtifact); - copyParentPoms(pomFile); + 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.add(artifact); } - private void installExtraArtifacts(String[] extraArtifacts) throws MojoExecutionException { - if (extraArtifacts == null) { - return; - } + private Artifact resolveArtifact(Artifact artifact, List remoteRepositories) + throws ArtifactResolutionException { - for (String extraArtifact : extraArtifacts) { - String[] gav = extraArtifact.split(":"); - if (gav.length < 3 || gav.length > 5) { - throw new MojoExecutionException("Invalid artifact " + extraArtifact); - } + ArtifactRequest request = new ArtifactRequest(); + request.setArtifact(artifact); + request.setRepositories(remoteRepositories); + ArtifactResult artifactResult = repositorySystem.resolveArtifact(session.getRepositorySession(), request); + return artifactResult.getArtifact(); + } - String groupId = gav[0]; - String artifactId = gav[1]; - String version = gav[2]; + /** + * Install list of artifacts into local repository. + */ + private void installArtifacts(Collection resolvedArtifacts) throws InstallationException { - String type = "jar"; - if (gav.length > 3) { - type = gav[3]; - } + // 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 - String classifier = null; - if (gav.length == 5) { - classifier = gav[4]; - } + Map> collect = resolvedArtifacts.stream() + .collect(Collectors.groupingBy( + a -> String.format("%s:%s:%s", a.getGroupId(), a.getArtifactId(), a.getVersion()), + LinkedHashMap::new, + Collectors.toList())); + + RepositorySystemSession systemSessionForLocalRepo = createSystemSessionForLocalRepo(); + + for (List artifacts : collect.values()) { + InstallRequest request = new InstallRequest(); + request.setArtifacts(artifacts); + repositorySystem.install(systemSessionForLocalRepo, request); + } + } - DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate(); - try { - coordinate.setGroupId(groupId); - coordinate.setArtifactId(artifactId); - coordinate.setVersion(version); - coordinate.setType(type); - coordinate.setClassifier(classifier); - - if (!localRepository.getBasedir().equals(localRepositoryPath.getPath()) && useLocalRepository) { - String previousId = localRepository.getId(); - try { - // using another request with the correct target repo - ProjectBuildingRequest projectBuildingRequest = repositoryManager.setLocalRepositoryBasedir( - session.getProjectBuildingRequest(), localRepositoryPath); - projectBuildingRequest.setRemoteRepositories(Collections.singletonList(localRepository)); - resolver.resolveDependencies( - projectBuildingRequest, - coordinate, - new PatternExclusionsFilter(Collections.emptyList())); - } finally { - localRepository.setId(previousId); - } - } else { - resolver.resolveDependencies( - projectBuildingRequest, coordinate, new PatternExclusionsFilter(Collections.emptyList())); - } - } catch (DependencyResolverException e) { - throw new MojoExecutionException("Unable to resolve dependencies for: " + coordinate, 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; } } diff --git a/src/main/java/org/apache/maven/plugins/invoker/MetadataUtils.java b/src/main/java/org/apache/maven/plugins/invoker/MetadataUtils.java deleted file mode 100644 index 13d2c4a4..00000000 --- a/src/main/java/org/apache/maven/plugins/invoker/MetadataUtils.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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.Reader; -import java.io.Writer; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TimeZone; - -import org.apache.maven.artifact.Artifact; -import org.codehaus.plexus.util.ReaderFactory; -import org.codehaus.plexus.util.WriterFactory; -import org.codehaus.plexus.util.xml.Xpp3Dom; -import org.codehaus.plexus.util.xml.Xpp3DomBuilder; -import org.codehaus.plexus.util.xml.Xpp3DomUtils; -import org.codehaus.plexus.util.xml.Xpp3DomWriter; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; - -/** - * Provides utility methods for artifact metadata processing. - * - * @author Benjamin Bentmann - */ -class MetadataUtils { - - /** - * Creates local metadata files for the specified artifact. The goal is to simulate the installation of the artifact - * by a local build, thereby decoupling the forked builds from the inderministic collection of remote repositories - * that are available to the main build and from which the artifact was originally resolved. - * - * @param file The artifact's file in the local test repository, must not be null. - * @param artifact The artifact to create metadata for, must not be null. - * @throws IOException If the metadata could not be created. - */ - public static void createMetadata(File file, Artifact artifact) throws IOException { - TimeZone tz = java.util.TimeZone.getTimeZone("UTC"); - SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss"); - fmt.setTimeZone(tz); - String timestamp = fmt.format(new Date()); - - if (artifact.isSnapshot()) { - File metadataFile = new File(file.getParentFile(), "maven-metadata-local.xml"); - - Xpp3Dom metadata = new Xpp3Dom("metadata"); - addChild(metadata, "groupId", artifact.getGroupId()); - addChild(metadata, "artifactId", artifact.getArtifactId()); - addChild(metadata, "version", artifact.getBaseVersion()); - Xpp3Dom versioning = new Xpp3Dom("versioning"); - versioning.addChild(addChild(new Xpp3Dom("snapshot"), "localCopy", "true")); - addChild(versioning, "lastUpdated", timestamp); - metadata.addChild(versioning); - - writeMetadata(metadataFile, metadata); - } - - File metadataFile = new File(file.getParentFile().getParentFile(), "maven-metadata-local.xml"); - - Set allVersions = new LinkedHashSet<>(); - - Xpp3Dom metadata = readMetadata(metadataFile); - - if (metadata != null) { - Xpp3Dom versioning = metadata.getChild("versioning"); - if (versioning != null) { - Xpp3Dom versions = versioning.getChild("versions"); - if (versions != null) { - - Xpp3Dom[] children = versions.getChildren("version"); - for (Xpp3Dom aChildren : children) { - allVersions.add(aChildren.getValue()); - } - } - } - } - - allVersions.add(artifact.getBaseVersion()); - - metadata = new Xpp3Dom("metadata"); - addChild(metadata, "groupId", artifact.getGroupId()); - addChild(metadata, "artifactId", artifact.getArtifactId()); - Xpp3Dom versioning = new Xpp3Dom("versioning"); - versioning.addChild(addChildren(new Xpp3Dom("versions"), "version", allVersions)); - addChild(versioning, "lastUpdated", timestamp); - metadata.addChild(versioning); - - metadata = Xpp3DomUtils.mergeXpp3Dom(metadata, readMetadata(metadataFile)); - - writeMetadata(metadataFile, metadata); - } - - private static Xpp3Dom addChild(Xpp3Dom parent, String childName, String childValue) { - Xpp3Dom child = new Xpp3Dom(childName); - child.setValue(childValue); - parent.addChild(child); - return parent; - } - - private static Xpp3Dom addChildren(Xpp3Dom parent, String childName, Collection childValues) { - for (String childValue : childValues) { - addChild(parent, childName, childValue); - } - return parent; - } - - private static Xpp3Dom readMetadata(File metadataFile) throws IOException { - if (!metadataFile.isFile()) { - return null; - } - - try (Reader reader = ReaderFactory.newXmlReader(metadataFile)) { - return Xpp3DomBuilder.build(reader); - } catch (XmlPullParserException e) { - throw new IOException(e.getMessage(), e); - } - } - - private static void writeMetadata(File metadataFile, Xpp3Dom metadata) throws IOException { - metadataFile.getParentFile().mkdirs(); - - try (Writer writer = WriterFactory.newXmlWriter(metadataFile)) { - Xpp3DomWriter.write(writer, metadata); - } - } -}