diff --git a/pom.xml b/pom.xml
index e3a43c00c..b7cd698b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,9 @@ under the License.
Maarten Mulders
+
+ Pim Moerenhout
+
@@ -243,7 +246,7 @@ under the License.
org.apache.maven.shared
maven-artifact-transfer
- 0.11.0
+ 0.13.0
org.apache.maven.shared
@@ -254,7 +257,7 @@ under the License.
org.apache.commons
commons-lang3
- 3.6
+ 3.8.1
@@ -317,7 +320,7 @@ under the License.
org.apache.maven.wagon
wagon-http-lightweight
- 3.3.4
+ 3.4.0
provided
diff --git a/src/it/projects/list-repositories-verbose/invoker.properties b/src/it/projects/list-repositories-verbose/invoker.properties
new file mode 100644
index 000000000..48fda226e
--- /dev/null
+++ b/src/it/projects/list-repositories-verbose/invoker.properties
@@ -0,0 +1,19 @@
+# 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 = ${project.groupId}:${project.artifactId}:${project.version}:list-repositories
+invoker.debug = true
\ No newline at end of file
diff --git a/src/it/projects/list-repositories-verbose/pom.xml b/src/it/projects/list-repositories-verbose/pom.xml
new file mode 100644
index 000000000..908248587
--- /dev/null
+++ b/src/it/projects/list-repositories-verbose/pom.xml
@@ -0,0 +1,47 @@
+
+
+
+
+ 4.0.0
+
+ org.apache.maven.its.dependency
+ test
+ 1.0-SNAPSHOT
+
+ Test
+
+ Test dependency:list-repositories
+
+
+
+ UTF-8
+
+
+
+
+ org.apache.maven
+ maven-project
+ 2.0.6
+
+
+
+
diff --git a/src/it/projects/list-repositories-verbose/verify.groovy b/src/it/projects/list-repositories-verbose/verify.groovy
new file mode 100644
index 000000000..68bdb6a73
--- /dev/null
+++ b/src/it/projects/list-repositories-verbose/verify.groovy
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+File file = new File( basedir, "build.log" );
+assert file.exists();
+
+String buildLog = file.getText( "UTF-8" );
+assert buildLog.contains( 'location: Maven settings (user/global)' );
+
+return true;
diff --git a/src/it/projects/list-repositories/invoker.properties b/src/it/projects/list-repositories/invoker.properties
index 6973bb1e3..e79fcffec 100644
--- a/src/it/projects/list-repositories/invoker.properties
+++ b/src/it/projects/list-repositories/invoker.properties
@@ -16,3 +16,4 @@
# under the License.
invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:list-repositories
+invoker.debug = false
\ No newline at end of file
diff --git a/src/it/projects/list-repositories/verify.groovy b/src/it/projects/list-repositories/verify.groovy
new file mode 100644
index 000000000..cb109383c
--- /dev/null
+++ b/src/it/projects/list-repositories/verify.groovy
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+File file = new File( basedir, "build.log" );
+assert file.exists();
+
+String buildLog = file.getText( "UTF-8" );
+assert !buildLog.contains( 'location:' );
+
+return true;
diff --git a/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java b/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java
index deed8d34d..f2fb18ffc 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java
@@ -19,15 +19,36 @@
* under the License.
*/
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Repository;
+import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.plugins.dependency.AbstractDependencyMojo;
-import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
-import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector;
-import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
+import org.apache.maven.project.DefaultProjectBuildingRequest;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.settings.Mirror;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.shared.transfer.artifact.ArtifactCoordinate;
+import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
+import org.apache.maven.shared.transfer.collection.CollectResult;
+import org.apache.maven.shared.transfer.collection.DependencyCollectionException;
+import org.apache.maven.shared.transfer.collection.DependencyCollector;
+import org.apache.maven.shared.transfer.graph.DependencyNode;
+import org.apache.maven.shared.transfer.graph.DependencyVisitor;
/**
* Goal that resolves all project dependencies and then lists the repositories used by the build and by the transitive
@@ -40,12 +61,46 @@
public class ListRepositoriesMojo
extends AbstractDependencyMojo
{
+ /**
+ * Maven Project Builder component.
+ */
+ @Component
+ private ProjectBuilder projectBuilder;
+
/**
* Dependency collector, needed to resolve dependencies.
*/
@Component( role = DependencyCollector.class )
private DependencyCollector dependencyCollector;
+ /**
+ * Component used to resolve artifacts and download their files from remote repositories.
+ */
+ @Component
+ private ArtifactResolver artifactResolver;
+
+ /**
+ * The system settings for Maven. This is the instance resulting from
+ * merging global and user-level settings files.
+ */
+ @Parameter( defaultValue = "${settings}", readonly = true, required = true )
+ private Settings settings;
+
+ /**
+ * Remote repositories used for the project.
+ */
+ @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true )
+ private List remoteRepositories;
+
+ /**
+ * Sets whether the plugin runs in verbose mode. As of plugin version 2.3, the default value is derived from Maven's
+ * global debug flag (compare command line switch -X).
+ *
+ * @since 3.1.2
+ */
+ @Parameter( property = "verbose" )
+ private boolean verbose;
+
/**
* Displays a list of the repositories used by this build.
*
@@ -55,22 +110,276 @@ public class ListRepositoriesMojo
protected void doExecute()
throws MojoExecutionException
{
+
+ for ( ArtifactRepository artifactRepository : remoteRepositories )
+ {
+ verbose( "Maven remote repositories: " + repositoryAsString( artifactRepository ) );
+ }
+
+ final Set repositories = new HashSet<>();
+ final Set artifacts = new HashSet<>();
+
+ DependencyVisitor visitor = new DependencyVisitor()
+ {
+ @Override
+ public boolean visitEnter( DependencyNode dependencyNode )
+ {
+ repositories.addAll( dependencyNode.getRemoteRepositories() );
+ artifacts.add( dependencyNode.getArtifact() );
+ return true;
+ }
+
+ @Override
+ public boolean visitLeave( DependencyNode dependencyNode )
+ {
+ return true;
+ }
+ };
+
try
{
- CollectorResult collectResult =
- dependencyCollector.collectDependencies( session.getProjectBuildingRequest(), getProject().getModel() );
+ ProjectBuildingRequest projectBuildingRequest = session.getProjectBuildingRequest();
+
+ CollectResult collectResult =
+ dependencyCollector.collectDependencies( projectBuildingRequest, getProject().getModel() );
+
+ for ( Exception e : collectResult.getExceptions() )
+ {
+ throw new MojoExecutionException( "Collect dependencies failed", e );
+ }
+
+ collectResult.getRoot().accept( visitor );
+
+ verbose( "Artifacts used by the build of " + collectResult.getRoot().getArtifact() + ":" );
+ for ( Artifact artifact : artifacts )
+ {
+ verbose( " " + artifact.toString() );
+ }
this.getLog().info( "Repositories used by this build:" );
+ for ( ArtifactRepository repo : repositories )
+ {
+ if ( isVerbose() )
+ {
+ Set locations = new HashSet();
+ for ( Mirror mirror : settings.getMirrors() )
+ {
+ if ( mirror.getId().equals( repo.getId() )
+ && ( mirror.getUrl().equals( repo.getUrl() ) ) )
+ {
+ locations.add( "Maven settings (user/global)" );
+ }
+ }
+
+ Artifact projectArtifact = getProject().getArtifact();
+ MavenProject project = getMavenProject( ArtifactUtils.key( projectArtifact ) );
+ traversePom( repo, projectArtifact, project, locations );
+
+ for ( Artifact artifact : artifacts )
+ {
+ MavenProject artifactProject = getMavenProject( ArtifactUtils.key( artifact ) );
+ traversePom( repo, artifact, artifactProject, locations );
+ }
+ writeRepository( repo, locations );
+ }
+ else
+ {
+ this.getLog().info( repo.toString() );
+ }
+ }
+ }
+ catch ( DependencyCollectionException e )
+ {
+ throw new MojoExecutionException( "Unable to collect", e );
+ }
+ }
+
+ private void writeRepository( ArtifactRepository artifactRepository, Set locations )
+ {
+ StringBuilder sb = new StringBuilder( 256 );
+ sb.append( artifactRepository.toString() );
+ for ( String location : locations )
+ {
+ sb.append( " location: " ).append( location ).append( System.lineSeparator() );
+ }
+ this.getLog().info( sb.toString() );
+ }
+
+ /**
+ * Parses the given String into GAV artifact coordinate information, adding the given type.
+ *
+ * @param artifactString should respect the format groupId:artifactId[:version]
+ * @param type The extension for the artifact, must not be null.
+ * @return the Artifact object for the artifactString parameter.
+ * @throws MojoExecutionException if the artifactString doesn't respect the format.
+ */
+ private ArtifactCoordinate getArtifactCoordinate( String artifactString, String type )
+ throws MojoExecutionException
+ {
+ if ( org.codehaus.plexus.util.StringUtils.isEmpty( artifactString ) )
+ {
+ throw new IllegalArgumentException( "artifact parameter could not be empty" );
+ }
+
+ String groupId; // required
+ String artifactId; // required
+ String version; // optional
+
+ String[] artifactParts = artifactString.split( ":" );
+ switch ( artifactParts.length )
+ {
+ case 2:
+ groupId = artifactParts[0];
+ artifactId = artifactParts[1];
+ version = Artifact.LATEST_VERSION;
+ break;
+ case 3:
+ groupId = artifactParts[0];
+ artifactId = artifactParts[1];
+ version = artifactParts[2];
+ break;
+ default:
+ throw new MojoExecutionException( "The artifact parameter '" + artifactString
+ + "' should be conform to: " + "'groupId:artifactId[:version]'." );
+ }
+ return getArtifactCoordinate( groupId, artifactId, version, type );
+ }
+
+ private ArtifactCoordinate getArtifactCoordinate( String groupId, String artifactId, String version, String type )
+ {
+ DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
+ coordinate.setGroupId( groupId );
+ coordinate.setArtifactId( artifactId );
+ coordinate.setVersion( version );
+ coordinate.setExtension( type );
+ return coordinate;
+ }
+
+ /**
+ * Retrieves the Maven Project associated with the given artifact String, in the form of
+ * groupId:artifactId[:version]. This resolves the POM artifact at those coordinates and then builds
+ * the Maven project from it.
+ *
+ * @param artifactString Coordinates of the Maven project to get.
+ * @return New Maven project.
+ * @throws MojoExecutionException If there was an error while getting the Maven project.
+ */
+ private MavenProject getMavenProject( String artifactString )
+ throws MojoExecutionException
+ {
+ ArtifactCoordinate coordinate = getArtifactCoordinate( artifactString, "pom" );
+ try
+ {
+ ProjectBuildingRequest pbr = new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
+ pbr.setRemoteRepositories( remoteRepositories );
+ pbr.setProject( null );
+ pbr.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
+ pbr.setResolveDependencies( false );
+ Artifact artifact = artifactResolver.resolveArtifact( pbr, coordinate ).getArtifact();
+ return projectBuilder.build( artifact.getFile(), pbr ).getProject();
+ }
+ catch ( Exception e )
+ {
+ throw new MojoExecutionException( "Unable to get the POM for the artifact '" + artifactString
+ + "'. Verify the artifact parameter.", e );
+ }
+ }
+
+ private void traversePom( ArtifactRepository artifactRepository,
+ Artifact artifact, MavenProject mavenProject, Set locations )
+ throws MojoExecutionException
+ {
+ getLog().debug( "Looking for locations of repository " + repositoryAsString( artifactRepository )
+ + " for " + artifact );
+ if ( mavenProject != null )
+ {
+ for ( Repository repository : mavenProject.getOriginalModel().getRepositories() )
+ {
+ getLog().debug( "Found repository: " + repositoryAsString( repository )
+ + " @ " + artifact + ":" + mavenProject.getOriginalModel().getPomFile() );
+ if ( isRepositoryEqual( repository, artifactRepository ) )
+ {
+ locations.add( mavenProject.getModel().getPomFile().toString() );
+ }
+ }
+
+ traverseParentPom( artifactRepository, mavenProject, locations );
+ }
+ else
+ {
+ throw new MojoExecutionException( "No POM for the artifact '" + artifact + "'" );
+ }
+ return;
+ }
- for ( ArtifactRepository repo : collectResult.getRemoteRepositories() )
+ private void traverseParentPom( ArtifactRepository artifactRepository,
+ MavenProject mavenProject, Set locations )
+ throws MojoExecutionException
+ {
+ MavenProject parent = mavenProject.getParent();
+ if ( parent != null )
+ {
+ Model originalModel = parent.getOriginalModel();
+ if ( originalModel.getRepositories().size() != 0
+ || originalModel.getPluginRepositories().size() != 0 )
{
- this.getLog().info( repo.toString() );
+ String artifactKey =
+ ArtifactUtils.key( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
+ MavenProject parentPom = getMavenProject( artifactKey );
+
+ for ( Repository repository : originalModel.getRepositories() )
+ {
+ getLog().debug( "Found parent repository " + repositoryAsString( repository )
+ + " @ " + parentPom.getArtifact() + ":" + parentPom.getFile() );
+ if ( isRepositoryEqual( repository, artifactRepository ) )
+ {
+ locations.add( parentPom.getFile().toString() );
+ }
+ }
}
+ traverseParentPom( artifactRepository, parent, locations );
}
- catch ( DependencyCollectorException e )
+ return;
+ }
+
+ private String repositoryAsString( Repository repository )
+ {
+ StringBuilder sb = new StringBuilder( 32 );
+ sb.append( repository.getId() );
+ sb.append( " (" );
+ sb.append( repository.getUrl() );
+ sb.append( ")" );
+ return sb.toString();
+ }
+
+ private String repositoryAsString( ArtifactRepository repository )
+ {
+ StringBuilder sb = new StringBuilder( 32 );
+ sb.append( repository.getId() );
+ sb.append( " (" );
+ sb.append( repository.getUrl() );
+ sb.append( ")" );
+ return sb.toString();
+ }
+
+ private boolean isVerbose()
+ {
+ return ( verbose || getLog().isDebugEnabled() );
+ }
+
+ private void verbose( String message )
+ {
+ if ( isVerbose() )
{
- throw new MojoExecutionException( "Unable to resolve artifacts", e );
+ getLog().info( message );
}
}
+ private boolean isRepositoryEqual( Repository repository, ArtifactRepository artifactRepository )
+ {
+ // TODO: Use org.apache.maven.RepositoryUtils in Maven or check also snapshots, etc.
+ return repository.getId().equals( artifactRepository.getId() )
+ && repository.getUrl().equals( artifactRepository.getUrl() );
+ }
+
}
diff --git a/src/site/apt/usage.apt.vm b/src/site/apt/usage.apt.vm
index f14c46f78..6ec181c9f 100644
--- a/src/site/apt/usage.apt.vm
+++ b/src/site/apt/usage.apt.vm
@@ -687,6 +687,17 @@ mvn dependency:build-classpath -Dmdep.outputFile=cp.txt
This goal is used to list all the repositories that this build depends upon. It will show repositories defined in your settings,
poms and declared in transitive dependency poms.
+ This goal can be executed from the command line:
+
++-----+
+mvn dependency:list-repositories
++-----+
+
+ Optionally, in verbose or debug mode it will display the location of the listed repository:
+
++-----+
+mvn dependency:list-repositories -Dverbose
++-----+
* <<>>