【问题标题】:Invocation of MavenCli fails within a Maven plugin在 Maven 插件中调用 MavenCli 失败
【发布时间】:2017-03-28 00:39:51
【问题描述】:

我创建了一个小工具来包装MavenCli,它使用快速入门原型生成一个新的 Maven 项目。

Util 作为单元测试执行时,它运行良好(只是生成一个空的 Maven 项目)。

现在我想将这个小包装器集成到一个 Maven 插件中。但是当我执行 mojo(在第三个 Maven 项目中)时,MavenCli 的调用失败并出现异常:

[ERROR] Error executing Maven.
[ERROR] java.util.NoSuchElementException
  role: org.apache.maven.eventspy.internal.EventSpyDispatcher
  roleHint: 
[ERROR] Caused by: null

工具看起来像:

public void createProject() {
    final MavenCli cli = new MavenCli();
    System.setProperty("maven.multiModuleProjectDirectory", "/usr/share/maven");
    cli.doMain(new String[] { "archetype:generate", "-DgroupId=com.my.company",
            "-DartifactId=hello-world", "-DarchetypeArtifactId=maven-archetype-quickstart",
            "-DinteractiveMode=false" }, "/tmp", System.out, System.out);
}

util的相关依赖:

<dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-embedder</artifactId>
    <version>3.3.9</version>
</dependency>
<dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-core</artifactId>
    <version>3.3.9</version>
</dependency>

mojo 代码如下:

@Mojo(name = "custommojo", requiresProject = false)
public class CustomMojo extends AbstractMojo {

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        Util.createProject();
    }

}

mojo 的 POM 仅包含对相关 Maven 工件(plugin-api、plugin-annotation 等)和 util 的依赖项。

我提到的第三个项目是一个空的“maven-quickstart”项目,它依赖于 mojo-project 和 mojo 在编译阶段执行的配置。

我不知道为什么它在单元测试环境中有效,但在 mojo 环境中无效。

有人可以帮忙吗?

【问题讨论】:

    标签: java maven maven-plugin maven-embedder


    【解决方案1】:

    这是一个类加载问题。

    MavenCli 将尝试从当前线程的上下文类加载器加载类。在 Maven 插件内部,有a special, restricted, classloader,它可以访问:

    • 自己的类;
    • 在其依赖项块中使用的类;
    • 导出的类作为项目可能的构建扩展的一部分;
    • 从 Maven 核心和核心扩展中导出的类;
    • 并将引导类加载器作为父级。

    但是,特定类 org.apache.maven.eventspy.internal.EventSpyDispatcher 是 Maven 核心的一部分,但 it is not part of the exported APIs(包 org.apache.maven.eventspy 未列为 exportedPackage)。所以插件无法加载该类。这也是它在您的测试中起作用的原因:您不在插件内部,因此类加载器是不同的并且可以访问该类。

    您甚至无法为插件显式添加对maven-core 的依赖:它将被忽略,因为它应该已经提供了。

    这里有两种解决方案:

    • 不要在插件中使用 Maven Embedder API,而是使用 Invoker API。两者的区别在于 Invoker API 将在干净的环境中启动 Maven,与当前环境完全不同。由于它重新开始一切,因此您不会遇到任何类加载问题。
    • 使用mojo-executor 库,它提供了一种从Maven 插件中调用其他Mojo 的简单方法。您可以在这里使用它来调用archetype:generate Mojo。

    【讨论】:

    • 非常感谢 - 我按照建议切换到 Invoker API - 它运行良好。注意:在我使用 Embedder API 之前,我已经尝试过使用 mojo-executor,但没有成功,因为很难执行整个生命周期(例如“安装”) - mojo-executor 正在为“archetype:generate”工作,但是尝试为另一个项目调用 mojo 时也很难配置(尤其是当这个另一个项目完全超出当前构建的范围时)
    • 实际上可以处理类加载问题并使用MavenCli,请参阅我的答案。
    【解决方案2】:

    这适用于我在自定义 maven 插件中(使用 Maven 3.5.0):

    ClassRealm classRealm = (ClassRealm) Thread.currentThread().getContextClassLoader();
    MavenCli cli = new MavenCli(classRealm.getWorld());
    cli.doMain( ... );
    

    plexus Launcher sets the context class loader 到它的ClassRealm,它可以访问“全局”ClassWorld

    不确定该解决方案的稳定性如何,但目前看起来不错。

    使用过的进口:

    import org.codehaus.plexus.classworlds.ClassWorld;
    import org.codehaus.plexus.classworlds.realm.ClassRealm;
    import org.apache.maven.cli.MavenCli;
    

    【讨论】:

    • 我在尝试这个时遇到了转换错误,但它让我朝着正确的方向前进:' MavenCli cli = new MavenCli(new ClassWorld("maven",Thread.currentThread().getContextClassLoader())) ; '
    • 我也有同样的问题!
    【解决方案3】:

    @亚历山大:

    你的回答让我朝着正确的方向前进,我在随后尝试运行 doMain maven 命令时遇到了上述错误(一个命令会成功)。

    但是你的代码给了我一个 ClassCast 异常:

    Exception in thread "main" java.lang.ClassCastException: sun.misc.Launcher$AppClassLoader cannot be cast to org.codehaus.plexus.classworlds.realm.ClassRealm
        at com.misys.tools.integration.codegen.cli.cmd.InstallApi.mavenInstallMchApi(InstallApi.java:58)
        at com.misys.tools.integration.codegen.cli.cmd.InstallApi.run(InstallApi.java:50)
        at com.misys.tools.integration.codegen.cli.OpenApiGen.main(OpenApiGen.java:26)
    

    我设法将代码重写为:

    MavenCli cli = new MavenCli(new ClassWorld("maven",Thread.currentThread().getContextClassLoader()));
    

    然后嵌入式 maven 的 2 个后续 doMain 调用成功!

    【讨论】:

    • 也许你的代码由于某种原因没有在 plexus Launcher 中运行,它将线程上下文类加载器设置为ClassRealm(至少在我处理这个时)。也许启动器和您的插件之间的其他东西正在添加不同的sun.misc.Launcher$AppClassLoader 作为上下文类加载器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 2020-04-21
    • 2020-03-16
    相关资源
    最近更新 更多