如何以编程方式获取Maven模块在Maven执行环境之外的所有依赖项?
到目前为止,我有:
通过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:
File localRepo = Paths.get(System.getProperty("user.home"), ".m2").toFile();
new Classpath(mavenProject, localRepo, "runtime")
到目前为止这一般是正确的吗?
现在的问题是我收到了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。
如何初始化MavenProject以包含已配置的远程回购,同时考虑settings.xml文件(镜像,代理,存储库等)?
答案 0 :(得分:7)
在Maven插件之外,对工件进行操作的方法是通过Aether。该团队有一个示例项目,用于获取名为ResolveTransitiveDependencies
的给定工件的传递依赖性。一旦设置了Aether依赖项(like shown here),您就可以:
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"
。
请注意,您可以在系统会话中使用DefaultProxySelector
和DefaultMirrorSelector
配置代理和镜像。有可能读取Maven设置文件并使用它来填充会话,但事情变得非常难看......
如果您希望与Maven本身紧密耦合,因为您可以访问要处理的POM并想要考虑设置,那么以编程方式直接调用Maven要简单得多。在这种情况下,您对给定POM文件的每个依赖项的路径感兴趣,包括传递依赖项。为此,dependency:list
目标以及将outputAbsoluteArtifactFilename
设置为true
,将(几乎)完全相同。
要以编程方式调用Maven,可以使用Invoker API。将依赖项添加到项目中:
<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));
}
}
}
这会创建一个调用请求,其中包含:要调用的目标和系统属性,就像在命令行上启动mvn dependency:list -Dprop=value
一样。要使用的设置的路径默认为"${user.home}/settings.xml"
的标准位置,但也可以使用request.setUserSettingsFile(...)
和request.setGlobalSettingsFile(...)
指定设置的路径。调用者需要设置Maven主目录(即安装目录),但仅限于未设置"maven.home"
系统属性。
调用dependency:list
的结果将重定向到一个文件,稍后进行后期处理。该目标的输出包含格式中的依赖项列表(如果没有,则分类器可能不存在):
group.id:artifact.id:type[:classifier]:version:scope:pathname
没有办法只输出已解析工件文件的路径,并且分类器可能不存在这一事实使解析变得复杂(我们无法在:
上拆分限制,因为路径可能包含:
...)。首先,已解析的工件位于输出文件中的"The following files have been resolved:"
行下方,然后,由于所需范围仅为compile
或runtime
,因此我们可以获取工件文件的路径简单的正则表达式,它包含compile:
或runtime:
之后的所有内容。然后,该路径可以直接用作new File
。
如果后处理过程中的箍看起来太脆弱,我想你可以创建自己的插件,只输出已解析的工件的文件名。