2016-11-14 113 views
2

我創建了一個小實用程序來包裝MavenCli,它使用quickstart原型生成一個新的Maven項目。在Maven插件中調用MavenCli失敗

作爲單元測試執行Util時,它工作得很好(只是生成一個空的Maven項目)。

現在我想將這個小包裝器集成到Maven插件中。但是,當我執行的魔力(第三Maven項目內),的MavenCli調用失敗,出現異常:

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

的util的樣子:

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的情況下。

任何人都可以幫忙嗎?

回答

3

這是一個類加載問題。

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依賴關係:它將被忽略,因爲它已被提供。

有2個解決方案,在這裏:

  • 不要從一個插件內使用Maven嵌入API,但Invoker API。兩者之間的區別在於,Invoker API將在乾淨的環境中啓動Maven,與當前的完全不同。既然它重新開始一切,你將不會有任何類加載問題。
  • 使用mojo-executor庫,該庫提供了一種從Maven插件中調用其他Mojos的簡單方法。你可以在這裏用它來調用Mojo。
+0

謝謝了很多 - 我切換到調用API的建議 - 它工作得很好。注意:在我使用Embedder API之前,我已經嘗試過使用mojo-executor,但沒有成功,因爲執行整個生命週期很困難(例如「安裝」) - mojo-executor正在爲「archetype:generate」工作,但在嘗試調用另一個項目的mojo時也很難配置(特別是當這個其他項目完全超出當前構建的範圍時) –

+0

實際上可以處理類加載問題並使用'MavenCli',請參閱我的回答。 –

0

這對我的作品(使用Maven 3.5.0)的自定義Maven插件裏面:

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

叢啓動sets the context class loaderClassRealm,其先後獲得了 「全球」 ClassWorld

不知道解決方案有多穩定,但目前看起來不錯。

採用進口:

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