6

For an artifact on Maven central (or any other given Nexus repository), I want to make a list of all direct dependencies.

At first, I thought about just reading the pom.xml and gather all entries from the dependency section. But I noticed that these entries might have no version (supplied by dependency management) or that entries might come from parent poms.

My second idea was to build some kind of artificial Maven project and gather the dependencies with mvn dependency:tree. This might become complicated.

What would be the most direct (but also reliable) way?

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • Can you explain why you need only the direct dependencies ? What are you trying to accomplish here ? – user2189998 Sep 22 '16 at 12:01
  • what about an uber jar configured to skip transitive dependencies? – A_Di-Matteo Sep 22 '16 at 12:02
  • @user2189998 It is actually complicated - I need to construct poms for some artifacts which mimic the behaviour of Maven central jars. It seems strange (it really is) but after a long discussion this seems to be the only way to avoid problems we have with legacy projects. – J Fabian Meier Sep 22 '16 at 12:07
  • Do you want to do this in a Maven plugin or outside of a Maven plugin? Or is it a one-time thing? – Tunaki Sep 22 '16 at 13:32
  • I want to write a java program that does it for some hundred artifacts. – J Fabian Meier Sep 22 '16 at 13:34

2 Answers2

10

Outside of a Maven plugin, you can do this programmatically using Aether. There is a method readArtifactDescriptor that does exactly this:

Gets information about an artifact like its direct dependencies and potential relocations.

First, add the Aether dependencies to your POM:

<dependencies>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-impl</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-connector-basic</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-transport-file</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-transport-http</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-aether-provider</artifactId>
        <version>${mavenVersion}</version>
    </dependency>
</dependencies>
<properties>
    <aetherVersion>1.1.0</aetherVersion>
    <mavenVersion>3.3.9</mavenVersion>
</properties>

Then you can have:

public static void main(final String[] args) throws Exception {
    DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
    RepositorySystem system = newRepositorySystem(locator);
    RepositorySystemSession session = newSession(system);

    RemoteRepository central = new RemoteRepository.Builder("central", "default", "http://repo1.maven.org/maven2/").build();

    Artifact artifact = new DefaultArtifact("groupId:artifactId:version");
    ArtifactDescriptorRequest request = new ArtifactDescriptorRequest(artifact, Arrays.asList(central), null);
    ArtifactDescriptorResult result = system.readArtifactDescriptor(session, request);

    for (Dependency dependency : result.getDependencies()) {
        System.out.println(dependency);
    }
}

private static RepositorySystem newRepositorySystem(DefaultServiceLocator locator) {
    locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
    locator.addService(TransporterFactory.class, FileTransporterFactory.class);
    locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
    return locator.getService(RepositorySystem.class);
}

private static RepositorySystemSession newSession(RepositorySystem system) {
    DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
    LocalRepository localRepo = new LocalRepository("target/local-repo");
    session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
    // set possible proxies and mirrors
    session.setProxySelector(new DefaultProxySelector().add(new Proxy(Proxy.TYPE_HTTP, "host", 3625), Arrays.asList("localhost", "127.0.0.1")));
    session.setMirrorSelector(new DefaultMirrorSelector().add("my-mirror", "http://mirror", "default", false, "external:*", null));
    return session;
}

What this does is creating a Aether repository system and telling it to read the artifact descriptor of a given artifact. The artifact is constructed with the constructor new DefaultArtifact("...") giving it the wanted coordinates.

A request object is created with this artifact and the list of repositories to fetch it from. In the above sample, only Maven Central was added, but you could add more RemoteRepository by creating them with the RemoteRepository.Builder class. After calling readArtifactDescriptor, the result contains the list of direct dependencies, that can be retrieved with getDependencies().

Proxies and mirrors can be configured with the help of the DefaultProxySelector and DefaultMirrorSelector respectively. DefaultProxySelector.add takes a Proxy as argument, which can be created with its constructor by passing it its type (like "http"), host, port and optionally an Authentication (take a look at the AuthenticationBuilder class to create authentication objects), and a list of non-proxied host. In the same way, DefaultMirrorSelector.add takes its id, URL, type (which is "default" for Maven, but you could have P2 for example), whether it is a repository manager, the actual repository ids mirrored (according to the mirror specification), and the repository type not mirrored.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
4

The following workflow seems to be ok:

  1. Download the pom from Maven central, put it a separate directory and name it pom.xml.

  2. Apply mvn dependency:list -DexcludeTransitive and grab the console output.

  3. Filter the list of dependencies from the console output.

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142