7

This is similar to other questions (like this), but I want to be able to do this with the latest API's. The maven-dependency-plugin:tree verbose option has been deprecated and does nothing in the latest (2.5.1) code, so there is no good example of how to do it.

Community
  • 1
  • 1
Ben
  • 4,785
  • 3
  • 27
  • 39
  • Currently the new Aether which is used in this does not provide such information. Only the older versions of the maven-dependency-plugin does provide such information (http://maven.40175.n5.nabble.com/maven-dependency-plugin-Questions-td5729997.html). – khmarbaise Nov 14 '12 at 14:06
  • I guess that means the short answer is that it is not possible at the moment. At least until Hervé might be a solution into the maven-core. – Ben Nov 20 '12 at 14:30

2 Answers2

5

I believe Aether utility class from jcabi-aether can help you to get a list of all dependencies of any Maven artifact, for example:

File repo = this.session.getLocalRepository().getBasedir();
Collection<Artifact> deps = new Aether(this.getProject(), repo).resolve(
  new DefaultArtifact("junit", "junit-dep", "", "jar", "4.10"),
  JavaScopes.RUNTIME
);

If you're outside of Maven plugin:

File repo = new File("/tmp/local-repository");
MavenProject project = new MavenProject();
project.setRemoteProjectRepositories(
  Arrays.asList(
    new RemoteRepository(
      "maven-central",
      "default",
      "http://repo1.maven.org/maven2/"
    )
  )
);
Collection<Artifact> deps = new Aether(project, repo).resolve(
  new DefaultArtifact("junit", "junit-dep", "", "jar", "4.10"),
  "runtime"
);

The only dependency you need is:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-aether</artifactId>
  <version>0.7.5</version>
</dependency>
yegor256
  • 102,010
  • 123
  • 446
  • 597
  • Okay... And how would this work if you would like to use it on a `MavenProject.getArtifact`? Is there a way to do it? (I have asked this here: http://stackoverflow.com/questions/16480314/get-all-the-dependencies-of-a-mavenproject-including-transitive-ones-using-aet ). – carlspring May 21 '13 at 10:01
  • Try to use an utility class [`Classpath`](http://www.jcabi.com/jcabi-aether/apidocs-0.7.19/com/jcabi/aether/Classpath.html) from [`jcabi-aether`](http://www.jcabi.com/jcabi-aether/example-classpath.html) (I answered your other question as well) – yegor256 May 21 '13 at 10:32
  • WARNING: the jcabi-aether project appears to be abandoned, (their page was last updated some 8 years ago,) and nothing from it works with modern versions of maven and/or aether. Do not waste your time with it. An answer that works today is https://stackoverflow.com/a/40820480/773113 – Mike Nakis May 06 '22 at 16:44
0

Including my approach here, as the additional steps may become part of your actual use case, esp. if working on a composite or multi-module project.

(Maven 3, my runtime was 3.6; no direct dependency on Aether)

In my case I wanted to resolve the dependency tree of a specific artifact foo-runtime from inside my plugin; however,

  • some of the dependency versions were only available in its parent's foo-parent POM (i.e. absent in the foo-runtime's own POM).
  • The parent POM also had additional details, such as exclusions for some of foo-runtime's dependencies - via dependencyManagement.

So I had to:

  • explicitly load the parent's model,
  • link the child model to it,
  • fill in the missing version numbers of the child model (still not sure why Maven didn't automatically resolve these after linking the parent), and then
  • run dependency resolution for the child.

To avoid model building from scratch, I derived the model of of foo-runtime using an existing artifact foo-api (which in my case is always guaranteed to be present in the Maven project being built). All these artifacts share the same groupId.

    @Component
    public LifecycleDependencyResolver resolver;

    // ...

    // `artifacts` contains all artifacts of current/reactor `MavenProject` obtained via `project.getArtifacts()`
    private Set<Artifact> resolveRuntimeDeps(Set<Artifact> artifacts) throws MojoExecutionException {

        // foo-api will always be present; use it to derive coordinates for foo-runtime
        Artifact fooApi = artifacts.stream().filter(artifact -> "foo-api".equals(artifact.getArtifactId()))
                .findFirst().orElseThrow(() -> new MojoExecutionException("Unable to find foo-api"));

        Collection<String> scopes = Arrays.asList("compile", "runtime");

        MavenProject fooRoot = deriveProject(fooApi, "foo-parent");
        Model fooRootPom = fooRoot.getModel();

        MavenProject fooSrv = deriveProject(fooApi, "foo-runtime");
        fooSrv.setParent(fooRoot);

        // some foo-runtime deps depend on versions declared on parent pom; merge them
        Map<String, Artifact> depMgt = fooRootPom.getDependencyManagement().getDependencies().stream()
                .collect(Collectors.toMap(dep -> dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getType(), this::toArtifact));
        for (Dependency d : fooSrv.getDependencies()) {
            if (d.getVersion() == null) {
                Artifact managed = depMgt.get(d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getType());
                if (managed != null) {
                    d.setVersion(managed.getVersion());
                }
            }
        }

        try {
            resolver.resolveProjectDependencies(fooSrv, scopes, scopes, session, false, Collections.emptySet());
            return fooSrv.getArtifacts();
        } catch (LifecycleExecutionException e) {
            throw new MojoExecutionException("Error resolving foo-runtime dependencies", e);
        }
    }

    // load POM for another artifact based on foo-api JAR available in current project
    private MavenProject deriveProject(Artifact fooApi, String artifactId) throws MojoExecutionException {
        Model pom;
        String pomPath = fooApi.getFile().getAbsolutePath().replaceAll("foo-api", artifactId).replaceAll("\\.jar$", ".pom");
        try (InputStream fooRootPomData = new FileInputStream(pomPath)) {
            pom = new MavenXpp3Reader().read(fooRootPomData);
            pom.setPomFile(new File(pomPath));
        } catch (IOException | XmlPullParserException e) {
            throw new MojoExecutionException("Error loading " + artifactId + " metadata", e);
        }

        // set these params to avoid skips/errors during resolution
        MavenProject proj = new MavenProject(pom);
        proj.setArtifact(toArtifact(pom));
        proj.setArtifactFilter(Objects::nonNull);
        proj.setRemoteArtifactRepositories(Collections.emptyList());
        return proj;
    }

    private Artifact toArtifact(Model model) {
        return new DefaultArtifact(
                Optional.ofNullable(model.getGroupId()).orElseGet(() -> model.getParent().getGroupId()), model.getArtifactId(),
                Optional.ofNullable(model.getVersion()).orElseGet(() -> model.getParent().getVersion()), "compile", model.getPackaging(), null,
                project.getArtifact().getArtifactHandler());
    }

    private Artifact toArtifact(Dependency dep) {
        return new DefaultArtifact(dep.getGroupId(), dep.getArtifactId(), dep.getVersion(), dep.getScope(), dep.getType(), dep.getClassifier(),
                project.getArtifact().getArtifactHandler());
    }

(I tried almost all the other suggested approaches, however all of them ended up with some error or another. Now, looking back, I suspect many of those errors might have been due to the fact that my leaf POM was missing version numbers for some artifacts. It seems (acceptably so) the "model enrichment" phase - propagating parent versions etc. - is carried out by some earlier component in Maven's flow; and the caller has to take care of this, at least partially, when invoking the dependency resolver from scratch.)

Janaka Bandara
  • 1,024
  • 1
  • 12
  • 27