Invocation of MavenCli fails within a Maven plugin

最后都变了- 提交于 2021-02-07 08:47:11

问题


I've created small util to wrap MavenCli, which generates a new Maven project, using the quickstart archetype.

When executing the Util as a unit test, it is working quite well (just generating an empty Maven project).

Now I want to integrate this small wrapper into a Maven plugin. But when I execute the mojo (within a third Maven project), the invocation of MavenCli fails with exception:

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

The util looks like:

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

relevant dependency of the 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>

The mojo code looks like:

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

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

}

The POM of the mojo just includes dependencies to relevant Maven artifacts (plugin-api, plugin-annotation, etc.) and the util.

The third project I mentioned, is an empty "maven-quickstart" project which have dependency to the mojo-project and a configuration for the mojo to execute in compile phase.

I have no idea why it works in context of unit test, but not in context of a mojo.

Can anybody help?


回答1:


This is a class loading issue.

MavenCli will try to load classes from the context classloader of the current thread. Inside of a Maven plugin, there is a special, restricted, classloader, which has access to:

  • its own classes;
  • the classes used in its dependencies block;
  • exported classes as part of a possible build extension of the project;
  • exported classes from the Maven core and core extensions;
  • and has the bootstrap classloader as parent.

However, the specific class org.apache.maven.eventspy.internal.EventSpyDispatcher is part of Maven core, but it is not part of the exported APIs (the package org.apache.maven.eventspy is not listed as an exportedPackage). So the plugin cannot load that class. This is also why it works in your tests: you're not inside of a plugin, so the classloader is different and has access to that class.

You won't even be able to get away with adding explictly a dependency on maven-core for the plugin: it will be disgarded since it is supposedly already provided.

There are 2 solutions here:

  • Don't use the Maven Embedder API from within a plugin, but the Invoker API. The difference between the two is that the Invoker API will launch Maven in a clean environment, completely distinct with regard to the current one. Since it starts everything anew, you won't have any classloading issue.
  • Use the mojo-executor library, that provides an easy way to invoke other Mojos from within a Maven plugin. You could use it here to invoke the archetype:generate Mojo.



回答2:


This works for me inside a custom maven plugin (using Maven 3.5.0):

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

The plexus Launcher sets the context class loader to its ClassRealm, which has access to the "global" ClassWorld.

Not sure how stable that solution is, but so far looking good.

Used imports:

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



回答3:


@Alexander:

You answer put me in the right direction, I was getting the mentioned error when trying to run to doMain maven commands subsequently (one command would succeed).

However your code gives me a ClassCast exception :

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)

I managed to rewrite the code to:

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

And then 2 subsequent doMain invocations of embedded maven succeed!



来源:https://stackoverflow.com/questions/40587683/invocation-of-mavencli-fails-within-a-maven-plugin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!