7

I need to write Java code that downloads a given artifact (GAV) from a given Maven repository. In the past I used the REST interface of Nexus 2 (which is quite easy) but I would like to have something which is independent of the repository (Nexus 2, Nexus 3, Artifactory) used.

In Fetching Maven artifacts programmatically a similar question is asked, but the main answer is from 2014 and suggests a library that was not updated since 2014.

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

4 Answers4

9

One can do the following thing with Aether (Version 1.1.0):

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
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.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
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.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;


public class ArtifactDownload
{

  private static RepositorySystem newRepositorySystem()
  {
    DefaultServiceLocator locator = MavenRepositorySystemUtils
        .newServiceLocator();
    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,
      File localRepository)
  {
    DefaultRepositorySystemSession session = MavenRepositorySystemUtils
        .newSession();
    LocalRepository localRepo = new LocalRepository(localRepository.toString());
    session.setLocalRepositoryManager(system.newLocalRepositoryManager(session,
        localRepo));
    return session;
  }


  public static File getArtifactByAether(String groupId, String artifactId,
      String version, String classifier, String packaging, File localRepository)
      throws IOException
  {
    RepositorySystem repositorySystem = newRepositorySystem();
    RepositorySystemSession session = newSession(repositorySystem,
        localRepository);

    Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier,
        packaging, version);
    ArtifactRequest artifactRequest = new ArtifactRequest();
    artifactRequest.setArtifact(artifact);

    List<RemoteRepository> repositories = new ArrayList<>();

    RemoteRepository remoteRepository = new RemoteRepository.Builder("public",
        "default", "http://somerepo").build();

    repositories.add(remoteRepository);

    artifactRequest.setRepositories(repositories);
    File result;

    try
    {
      ArtifactResult artifactResult = repositorySystem.resolveArtifact(session,
          artifactRequest);
      artifact = artifactResult.getArtifact();
      if (artifact != null)
      {
        result = artifact.getFile();
      }
      else
      {
        result = null;
      }
    }
    catch (ArtifactResolutionException e)
    {
      throw new IOException("Artefakt " + groupId + ":" + artifactId + ":"
          + version + " konnte nicht heruntergeladen werden.");
    }

    return result;

  }
}

using the following artifacts

    <dependency>
      <groupId>org.eclipse.aether</groupId>
      <artifactId>aether-api</artifactId>
      <version>${aetherVersion}</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.aether</groupId>
      <artifactId>aether-spi</artifactId>
      <version>${aetherVersion}</version>
    </dependency>
    <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>

with

<aetherVersion>1.1.0</aetherVersion>
<mavenVersion>3.3.3</mavenVersion>
J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • Is there some documentation about this aether? This post is the only thing I found that resemble to a documentation – rascio Jul 02 '19 at 13:02
  • I know it is bit outdated, but have you tried it with wildcards? I am strugling to download all artifacts under same GAV only having different classifier, but getting ArtifactResolutionException. I am using `https://repo1.maven.org/maven2` and passing this as artifact instance `Artifact artifact = new DefaultArtifact( "args4j:args4j:jar:*:2.33" );` This shall target https://repo1.maven.org/maven2/args4j/args4j/2.33/ – Martin Aug 08 '19 at 14:22
  • I don't think that wildcards work. Instead, you probably need to use some Aether function that resolves the list of artifacts for the given coordinates. Or you can use REST to figure out the list. – J Fabian Meier Aug 08 '19 at 14:47
3

I recently found myself in the situation to have the requirement to download files from a maven repository. I stumbled over (at: https://www.hascode.com/2017/09/downloading-maven-artifacts-from-a-pom-file-programmatically-with-eclipse-aether/#comment-5602) following https://github.com/shrinkwrap/resolver library which is much easier to use than the Eclipse Aether project.

String artifactoryUrl = "https://your-repo:port/repository";
String[] requiredTypes = new String[] { "zip", "xml" };

for (String requiredType : requiredTypes) {
    MavenResolvedArtifact[] result = Maven.configureResolver()
            .withMavenCentralRepo(false)
            .withRemoteRepo("default", artifactoryUrl, "default")
            .useLegacyLocalRepo(true) // You can also set to ignore origin of artifacts present in local repository via useLegacyLocalRepo(true) method.
            .resolve(
                    group + ":" +
                    id + ":" +
                    requiredType + ":" +
                    version
            )
            .withTransitivity()
            .asResolvedArtifact();

    for (MavenResolvedArtifact mavenResolvedArtifact : result) {
        System.out.println(mavenResolvedArtifact);
        System.out.println(mavenResolvedArtifact.asFile().getAbsolutePath());
    }
 }
desertnaut
  • 57,590
  • 26
  • 140
  • 166
keocra
  • 613
  • 5
  • 10
2

You are describing the features of maven-dependency-plugin (get goal)

So there is Java code that does exactly what you want, in the context of a maven plugin.

I would simply

  • retrieve the source code of the get plugin from github
  • remove all the maven plugin stuff (@Component dependency injection, parameters)
  • integrate the gist of it where you need it (and add dependencies on the internal maven libraries that are required)
Arnaud Jeansen
  • 1,619
  • 15
  • 27
0

You can download maven artifacts using aether API which will download the jars in given POM.xml file

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.ModelBuildingResult;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
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.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.Proxy;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RemoteRepository.Builder;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.repository.DefaultProxySelector;

public class DownloadingArtifactsByPomExample {

public static final String TARGET_LOCAL_REPOSITORY = "target/local-repository";

public static void main(String[] args) throws Exception {
    File projectPomFile = new 
File("C:\\Users\\user\\Desktop\\TEST_Aether\\test\\pom.xml");
//Paths.get("","pom.xml").toAbsolutePath().toFile();

    System.out.printf("loading this sample project's Maven descriptor from %s\n", 
projectPomFile);
    System.out.printf("local Maven repository set to %s\n",
            Paths.get("", TARGET_LOCAL_REPOSITORY).toAbsolutePath());

    RepositorySystem repositorySystem = getRepositorySystem();
    RepositorySystemSession repositorySystemSession = 
getRepositorySystemSession(repositorySystem);

    final DefaultModelBuildingRequest modelBuildingRequest = new 
DefaultModelBuildingRequest()
            .setPomFile(projectPomFile);

    ModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
    ModelBuildingResult modelBuildingResult = 
modelBuilder.build(modelBuildingRequest);

    Model model = modelBuildingResult.getEffectiveModel();
    System.out.printf("Maven model resolved: %s, parsing its dependencies..\n", 
model);
    for (Dependency d : model.getDependencies()) {
        System.out.printf("processing dependency: %s\n", d);
        Artifact artifact = new DefaultArtifact(d.getGroupId(), d.getArtifactId(), 
d.getType(), d.getVersion());
        ArtifactRequest artifactRequest = new ArtifactRequest();
        artifactRequest.setArtifact(artifact);
        artifactRequest.setRepositories(getRepositories(repositorySystem, 
repositorySystemSession));

        try {
            ArtifactResult artifactResult = 
repositorySystem.resolveArtifact(repositorySystemSession,
                    artifactRequest);
            artifact = artifactResult.getArtifact();
            // to load the artifact in current classloader
            ClassLoader classLoader = new URLClassLoader(new URL[] { 
artifact.getFile().toURI().toURL() });
            URL u = artifact.getFile().toURI().toURL();
            URLClassLoader urlClassLoader = (URLClassLoader) 
ClassLoader.getSystemClassLoader();
            Class urlClass = URLClassLoader.class;
            Method method = urlClass.getDeclaredMethod("addURL", new Class[] { 
URL.class });
            method.setAccessible(true);
            method.invoke(urlClassLoader, new Object[] { u });
            System.out.printf("artifact %s resolved to %s\n", artifact, 
artifact.getFile());
        } catch (ArtifactResolutionException e) {
            System.err.printf("error resolving artifact: %s\n", e.getMessage());
        }

    }

}

public static RepositorySystem getRepositorySystem() {
    DefaultServiceLocator serviceLocator = 
MavenRepositorySystemUtils.newServiceLocator();
    serviceLocator.addService(RepositoryConnectorFactory.class, 
BasicRepositoryConnectorFactory.class);
    serviceLocator.addService(TransporterFactory.class, 
FileTransporterFactory.class);
    serviceLocator.addService(TransporterFactory.class, 
HttpTransporterFactory.class);

    serviceLocator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() {
        @Override
        public void serviceCreationFailed(Class<?> type, Class<?> impl, Throwable 
exception) {
            System.err.printf("error creating service: %s\n", 
exception.getMessage());
            exception.printStackTrace();
        }
    });

    return serviceLocator.getService(RepositorySystem.class);
}

public static DefaultRepositorySystemSession 
getRepositorySystemSession(RepositorySystem system) {
    DefaultRepositorySystemSession repositorySystemSession = 
MavenRepositorySystemUtils.newSession();

    LocalRepository localRepository = new LocalRepository(TARGET_LOCAL_REPOSITORY);
    repositorySystemSession

.setLocalRepositoryManager(system.newLocalRepositoryManager(repositorySystemSession, 
localRepository));

    repositorySystemSession.setRepositoryListener(new 
ConsoleRepositoryEventListener());
    repositorySystemSession.setProxySelector(new DefaultProxySelector()
            .add(new Proxy(Proxy.TYPE_HTTPS, "proxy.tcs.com", 8080), 
Arrays.asList("localhost", "127.0.0.1")));
    return repositorySystemSession;
}

public static List<RemoteRepository> getRepositories(RepositorySystem system, 
RepositorySystemSession session) {
    return Arrays.asList(getCentralMavenRepository());
}

private static RemoteRepository getCentralMavenRepository() {
    // In "http://central.maven.org we can provide local m2 repository location to
    // load the jars into local repo
    // like file://C:user/m2/repository
    RemoteRepository.Builder builder = new Builder("central", "default", " 
   http://central.maven.org/maven2/");
    // for proxy
    builder.setProxy(new Proxy("type", "host", 8080));// port 8080
    RemoteRepository central = builder.build();
    return central;
}

}

import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.RepositoryEvent;

public class ConsoleRepositoryEventListener extends AbstractRepositoryListener {

@Override
public void artifactInstalled(RepositoryEvent event) {
    System.out.printf("artifact %s installed to file %s\n, event.getArtifact()", 
event.getFile());
}

@Override
public void artifactInstalling(RepositoryEvent event) {
    System.out.printf("installing artifact %s to file %s\n, event.getArtifact()", 
event.getFile());
}

@Override
public void artifactResolved(RepositoryEvent event) {
    System.out.printf("artifact %s resolved from repository %s\n", event.getArtifact(), event.getRepository());
}

@Override
public void artifactDownloading(RepositoryEvent event) {
    System.out.printf("downloading artifact %s from repository %s\n", event.getArtifact(), event.getRepository());
}

@Override
public void artifactDownloaded(RepositoryEvent event) {
    System.out.printf("downloaded artifact %s from repository %s\n", event.getArtifact(), event.getRepository());
}

@Override
public void artifactResolving(RepositoryEvent event) {
    System.out.printf("resolving artifact %s\n", event.getArtifact());
}

}

<properties>
    <aetherVersion>1.1.0</aetherVersion>
    <mavenVersion>3.2.1</mavenVersion>
</properties>
<dependencies>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-api</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-spi</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.aether</groupId>
        <artifactId>aether-util</artifactId>
        <version>${aetherVersion}</version>
    </dependency>
    <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>
  • Could you explain the differences between your answer and mine? – J Fabian Meier Mar 22 '19 at 19:47
  • I try to enhance your answer JF Meier , 1 st difference is I used to give pom.xml location as input to get all the jars , you gave artifact id , group id and all and 2 nd difference I have added how to set proxy for downloading and I have give example for how to use local repo as maven central – R.Karthick May 07 '19 at 20:29
  • Probably, the reader would have profited more if you had added some comments to my answer, explaining weak points. – J Fabian Meier May 08 '19 at 07:56