Maven:以编程方式获取所有依赖项 [英] Maven: get all dependencies programmatically

查看:118
本文介绍了Maven:以编程方式获取所有依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何以编程方式在Maven执行环境之外获取Maven模块的所有依赖关系?

How can I programmatically get all dependencies of a Maven module outside a Maven execution environment?

到目前为止,我有:

通过maven-core:

via maven-core:

Path pomPath = ...;
MavenXpp3Reader reader = new MavenXpp3Reader();
try (InputStream is = Files.newInputStream(pomPath)) {
    Model model = reader.read(is);
    this.mavenProject = new MavenProject(model);
}

并通过jcabi-aether:

and via jcabi-aether:

File localRepo = Paths.get(System.getProperty("user.home"), ".m2").toFile();
new Classpath(mavenProject, localRepo, "runtime")

到目前为止,这通常是正确的吗?

Is this generally correct so far?

现在的问题是我收到了NullPointerException:

The issue now is that I'm getting a NullPointerException:

Caused by: java.lang.NullPointerException
    at com.jcabi.aether.Aether.mrepos(Aether.java:197)
    at com.jcabi.aether.Aether.<init>(Aether.java:140)
    at com.jcabi.aether.Classpath.<init>(Classpath.java:125)

因为mavenProject.getRemoteProjectRepositories()返回null.

since mavenProject.getRemoteProjectRepositories() returns null.

如何在考虑settings.xml文件(镜像,代理,存储库等)的情况下初始化MavenProject以包含配置的远程存储库?

How can I initialze the MavenProject to contain the configured remote repos taking the settings.xml file (mirrors, proxies, repositories etc.) into account as well?

推荐答案

在Maven插件之外,对工件进行操作的方法是通过Aether.该团队有一个示例项目来获取给定工件的传递依赖,该工件称为如此处所示),您可以简单地进行以下操作:

Outside of a Maven plugin, the way to operate on artifacts is through Aether. The team has a sample project to get the transitive dependencies of a given artifact called ResolveTransitiveDependencies. Once you have the Aether dependencies set up (like shown here), you can simply 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("group.id:artifact.id:version");

    CollectRequest collectRequest = new CollectRequest(new Dependency(artifact, JavaScopes.COMPILE), Arrays.asList(central));
    DependencyFilter filter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE);
    DependencyRequest request = new DependencyRequest(collectRequest, filter);
    DependencyResult result = system.resolveDependencies(session, request);

    for (ArtifactResult artifactResult : result.getArtifactResults()) {
        System.out.println(artifactResult.getArtifact().getFile());
    }
}

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));
    return session;
}

它将下载工件并将其放入"target/local-repo" .

It will download the artifacts and place them into "target/local-repo".

请注意,您可以在系统会话上使用 DefaultProxySelector DefaultMirrorSelector 配置代理和镜像.可以读取Maven设置文件并用它来填充会话,但是事情变得非常丑陋而且很快...

Note that you can configure proxy and mirrors with the DefaultProxySelector and DefaultMirrorSelector on the system session. It would be possible to read the Maven settings file and use it to populate the session, but things get really ugly really fast...

当您希望与Maven本身紧密结合,因为您可以访问POM进行处理并希望将设置考虑在内时,直接以编程方式调用Maven会容易得多.在这种情况下,您会对给定POM文件的每个依赖项(包括传递性依赖项)的路径感兴趣.为此, dependency:list 目标,以及设置 outputAbsoluteArtifactFilename 更改为 true ,将会(几乎)给出准确的结果.

When you want tight coupling with Maven itself because you have access to the POM to process and want to take the settings into account, it is a lot simpler to directly invoke Maven programmatically. In this case, you're interested in the path of each dependencies, including transitive dependencies, of a given POM file. For that the dependency:list goal, together with setting the outputAbsoluteArtifactFilename to true, will give (almost) exactly that.

要以编程方式调用Maven,可以使用 Invoker API .将依赖项添加到您的项目中:

To invoke Maven programmatically, it is possible to use the Invoker API. Adding the dependency to your project:

<dependency>
  <groupId>org.apache.maven.shared</groupId>
  <artifactId>maven-invoker</artifactId>
  <version>2.2</version>
</dependency>

您可以拥有:

InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(new File(pomPath));
request.setGoals(Arrays.asList("dependency:list"));
Properties properties = new Properties();
properties.setProperty("outputFile", "dependencies.txt"); // redirect output to a file
properties.setProperty("outputAbsoluteArtifactFilename", "true"); // with paths
properties.setProperty("includeScope", "runtime"); // only runtime (scope compile + runtime)
// if only interested in scope runtime, you may replace with excludeScope = compile
request.setProperties(properties);

Invoker invoker = new DefaultInvoker();
// the Maven home can be omitted if the "maven.home" system property is set
invoker.setMavenHome(new File("/path/to/maven/home"));
invoker.setOutputHandler(null); // not interested in Maven output itself
InvocationResult result = invoker.execute(request);
if (result.getExitCode() != 0) {
    throw new IllegalStateException("Build failed.");
}

Pattern pattern = Pattern.compile("(?:compile|runtime):(.*)");
try (BufferedReader reader = Files.newBufferedReader(Paths.get("dependencies.txt"))) {
    while (!"The following files have been resolved:".equals(reader.readLine()));
    String line;
    while ((line = reader.readLine()) != null && !line.isEmpty()) {
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
            // group 1 contains the path to the file
            System.out.println(matcher.group(1));
        }
    }
}

这将创建一个包含以下内容的调用请求:调用目标和系统属性,就像您在命令行上启动 mvndependency:list -Dprop = value 一样.要使用的设置的路径将默认为"$ {user.home}/settings.xml" 的标准位置,但是也可以使用指定设置的路径> request.setUserSettingsFile(...) request.setGlobalSettingsFile(...).调用程序需要设置为Maven主目录(即安装目录),但前提是未设置"maven.home" 系统属性.

This creates an invocation request which contains: the goals to invoke and the system properties, just like you would launch mvn dependency:list -Dprop=value on the command-line. The path to the settings to use will default to the standard location of "${user.home}/settings.xml", but it would also be possible to specify the path to the settings with request.setUserSettingsFile(...) and request.setGlobalSettingsFile(...). The invoker needs to be set the Maven home (i.e. installation directory), but only if the "maven.home" system property isn't set.

调用 dependency:list 的结果将重定向到文件,该文件随后将进行后处理.该目标的输出包括以下格式的依赖项列表(如果没有分类器,则分类器可能不存在):

The result of invoking dependency:list is redirected to a file, that is later post-processed. The output of that goal consists of the list of dependencies in the format (the classifier may be absent, if there are none):

group.id:artifact.id:type[:classifier]:version:scope:pathname

没有一种方法只能输出已解析工件的文件的路径,并且分类器可能不存在的事实使解析有点复杂(我们不能在:上拆分限制,因为路径可能包含: ...).首先,已解析的工件在输出文件中的以下文件已解析:" 行下,然后,因为所需作用域仅是 compile 运行时,我们可以使用简单的正则表达式获取工件文件的路径,该正则表达式接受 compile: runtime:之后的所有内容.该路径然后可以直接用作新文件.

There isn't a way to output only the path of the resolved artifact's file, and the fact that the classifier may be absent complicates the parsing a bit (we can't split on : with a limit, since the path could contain a :...). First, the resolved artifacts are below the line "The following files have been resolved:" in the output file, then, since the wanted scope are only compile or runtime, we can get the path of the artifact's file with a simple regular expression that takes everything which is after compile: or runtime:. That path can then directly be used as a new File.

如果后处理过程中的箍圈看起来太脆弱,我想您可以创建自己的插件来输出已解析工件的文件名.

If the hoops during post-processing look too fragile, I guess you could create your own plugin that just outputs the resolved artifact's filename.

这篇关于Maven:以编程方式获取所有依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆