具有显式finalName的Maven将无法正常工作

时间:2016-10-31 05:09:53

标签: java maven stack-overflow maven-package

1。背景

我的maven项目有很多模块和子模块jarswars,一切正常。我也可以在服务器上部署它而没有任何问题。

我决定关注this maven naming conversion,我正在使用project.nameproject.build.finalName进行一些测试以获得适当的名称。

我为根工件创建project.name所定义的模式是company-${project.artifactId},模块和子模块的模式是${project.parent.name}-${project.artifactId}

  • 公司任何伪影,任何-模块1
  • 公司任何伪影,任何-模块2-任何-submodule1
  • 公司任何伪影,任何-模块2-任何-submodule2

project.build.finalName的模式是${project.name}-${project.version}

  • 公司任何伪影,任何-module1-1.0.jar
  • 公司任何伪影,任何-模块2-任何-submodule1-2.0.jar
  • 公司任何伪影,任何-模块2-任何-submodule2-3.0.war

但maven没有生成这些文件,而是给了我StackOverflowError

2。重现错误的示例

您可以从github克隆此示例:https://github.com/pauloleitemoreira/company-any-artifact

在github中,有master分支,它将重现此错误。还有only-modules分支,这是一个使用${project.parent.name}生成jar finalName的工作示例。

让我们考虑一个带有一个根pom工件,一个pom模块和一个子模块的maven项目。

-any-artifact
     |
     |-any-module      
           |
           |-any-submodule

2.1 any-artifact

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.company</groupId>
    <artifactId>any-artifact</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <name>company-${project.artifactId}</name>

    <modules>
        <module>any-module</module>
    </modules>

    <!-- if remove finalName, maven will not throw StackOverflow error -->
    <build>
        <finalName>${project.name}-${project.version}</finalName>
    </build>
</project>

2.2 any-module

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>any-artifact</artifactId>
        <groupId>com.company</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.company.any-artifact</groupId>
    <artifactId>any-module</artifactId>
    <packaging>pom</packaging>

    <name>${project.parent.name}-${project.artifactId}</name>

    <modules>
        <module>any-submodule</module>
    </modules>
</project>

2.3 any-submodule

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>any-module</artifactId>
        <groupId>com.company.any-artifact</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.company.any-artifact.any-module</groupId>
    <artifactId>any-submodule</artifactId>

    <name>${project.parent.name}-${project.artifactId}</name>
</project>

第3。问题

尝试mvn clean install时,maven会给我一个StackOverflowError

Exception in thread "main" java.lang.StackOverflowError
    at org.codehaus.plexus.util.StringUtils.isEmpty(StringUtils.java:177)
    at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:194)
    at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:163)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:266)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)

重要的是要知道只有在我们使用子模块时才会发生错误。如果我们使用根POM工件和jar模块创建项目,则不会发生错误。

4。问题

为什么只有在使用子模块时才会出现此错误?

有什么建议可以解决我的问题吗?我应该忘记它并按照我想要的模式手动为每个项目设置project.nameproject.build.fileName吗?

重要更新:

有些答案只是说使用 &{parent.name} ,但是它不起作用。这是一个带有赏金的问题,请在回答此问题之前考虑使用Maven version 3.3.9测试您的解决方案。

Maven版本3.3.9

编辑 - 在错误发生时添加阶段问题的详细信息,工作正常,直到prepare-package阶段,但StackOverflow发生在该项目的maven生命周期的package阶段。

5 个答案:

答案 0 :(得分:8)

对您的问题的严格回答是${project.parent.name}作为模型插值过程的一部分进行解析。反过来,你有一个StackOverflowError,在一个完全不同的代码位置,即当...构建项目的最终JAR时。

第1部分:构建的模型错误

这里发生了什么。当您在项目上启动Maven命令时,首先要做的是创建项目的有效模型。这意味着读取您的POM文件,使用激活的配置文件进行推理,应用继承,对属性执行插值...所有这些都为您的项目构建最终的Maven模型。这项工作由Maven Model Builder组件完成。

构建模型的过程非常复杂,许多步骤可能分为两个阶段,但我们在model interpolation部分对此感兴趣的部分。这是Maven将在模型中用${...}表示的所有标记替换计算值。它在注入配置文件后发生,并执行继承。在那个时间点,由MavenProject对象表示的Maven项目尚未存在,只有Model正在构建。只有在你拥有一个完整的模型之后,才可以从中开始构建Maven项目。

因此,当插值完成时,它仅根据POM文件中存在的信息进行推理,并且唯一有效的值是mentioned in the model reference。 (如果您想查看源代码,则此替换由StringSearchModelInterpolator类执行。)值得注意的是,您会注意到模型中的<parent>元素包含父模型的名称。 Maven中的类Model实际上是使用来自源.mdo文件的Modello生成的,并且该来源仅定义了groupId, artifactId, version and relativePath(以及自定义id<parent>元素。这也是可见的in the documentation

所有这一切的结果是,在执行模型插值后,将不会替换令牌${project.parent.name}。而且,从中构造的MavenProject将包含一个包含${project.parent.name}未替换的名称。您可以在日志中看到这一点,在您的示例项目中,我们有

[INFO] Reactor Build Order:
[INFO] 
[INFO] company-any-artifact
[INFO] ${project.parent.name}-any-module
[INFO] ${project.parent.name}-any-submodule

意味着Maven将项目any-module的实际名称视为${project.parent.name}-any-module

第2部分:奇怪的事情开始

我们现在正处于反应堆中的所有项目都已正确创建甚至编译的时刻。实际上,理论上一切都应该工作得很好,但项目本身只有完全不知名的名字。但是你有一个奇怪的例子,它在用maven-jar-plugin创建JAR时失败了。使用以下日志在您的示例中构建失败:

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ any-submodule ---
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] company-any-artifact ............................... SUCCESS [  0.171 s]
[INFO] ${project.parent.name}-any-module .................. SUCCESS [  0.002 s]
[INFO] ${project.parent.name}-any-submodule ............... FAILURE [  0.987 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

意味着在模型建立之后出现了问题。原因是插件injects the name of the project as a parameter


/**
 * Name of the generated JAR.
 *
 * @parameter alias="jarName" expression="${jar.finalName}" default-value="${project.build.finalName}"
 * @required
 */
private String finalName;

注意project.build.finalName作为子模块生成的JAR名称的默认值。此注入和变量的插值由另一个名为PluginParameterExpressionEvaluator的类完成。

所以会发生什么:

  • any-submodule上的JAR插件会注入项目的最终名称,名为${project.parent.name}-any-submodule
  • 感谢您继承父项目,以及最顶层POM项目中<finalName>的声明,它继承了<finalName>${project.name}-${project.version}</finalName>
  • Maven现在尝试为${project.name}插入any-submodule
  • 由于第1部分,这将解析为${project.parent.name}-any-submodule
  • Maven现在尝试为${project.parent.name}插入any-submodule。这样可以正常工作:构建MavenProject并在项目实例上调用getParent(),返回具体的Maven父项目。因此,${project.parent.name}会尝试解析any-module的名称,实际上是${project.parent.name}-any-module
  • Maven现在尝试插入${project.parent.name}-any-module但仍在any-submodule项目实例上。对于PluginParameterExpressionEvaluator,评估令牌的根"project"未发生变化。
  • Maven现在尝试在${project.parent.name}上插入any-submodule,再次正常工作并返回${project.parent.name}-any-module
  • Maven现在尝试在${project.parent.name}上插入any-submodule ...工作并返回${project.parent.name}-any-module,以便它尝试评估${project.parent.name} ...

你可以在这里看到无休止的递归,这导致你拥有StackOverflowError。这是PluginParameterExpressionEvaluator中的错误吗?这一点尚不清楚:它的原因是模型值首先没有被正确替换。从理论上讲,它可以处理评估${project.parent}的特殊情况,并创建一个处理此父项目的新PluginParameterExpressionEvaluator,而不是始终处理当前项目。如果您对此有强烈的感受,请随时创建a JIRA issue

第3部分:没有子模块的原因

通过以上所述,您现在可以推断出在这种情况下它的工作原理。让我们了解Maven需要做什么来评估最终名称,因为必须在Maven Jar插件中注入:

  • any-module上的JAR插件会注入项目的最终名称,名为${project.parent.name}-any-module
  • 感谢您继承父项目,以及最顶层POM项目中<finalName>的声明,它继承了<finalName>${project.name}-${project.version}</finalName>
  • Maven现在尝试为${project.name}插入any-module
  • 此解析为${project.parent.name}-any-module,与之前相同。
  • Maven现在尝试为${project.parent.name}插入any-module。就像以前一样,这可以正常工作:构建MavenProject并在项目实例上调用getParent(),返回具体的Maven父项目。因此,${project.parent.name}会尝试解析any-artifact的名称,实际上是company-any-artifact
  • 插值成功并停止。

你没有任何错误。

答案 1 :(得分:4)

正如我在回答Difference between project.parent.name and parent.name ans use of finalName in pom.xml

时所述

让我们先来看看基础知识:

POM Reference中所述:

  

finalName :这是捆绑项目最终构建时的名称(没有文件扩展名,例如:my-project-1.0.jar)。它默认为$ {artifactId} - $ {version}。

     

名称:除了artifactId之外,项目往往具有会话名称。

所以这两个有不同的用途。

  • name纯粹是信息性的,主要用于生成的文档和构建日志。它不是在任何其他地方继承或使用的。它是一个人类可读的字符串,因此可以包含任何字符,即文件名中不允许的空格或字符。所以,这是有效的:<name>My Turbo Project on Speed!</name>。这显然至少是一件神器的可疑文件名。

  • 如上所述,finalName是生成的工件的名称。 继承,因此它通常应该依赖于属性。只有两个非常有用的选项是默认${artifactId}-${version}和无版本${artifactId}。其他一切都会导致混淆(例如名为foo的项目创建工件bar.jar)。其实,我的涡轮增压项目!是有效的,因为这是一个有效的文件名,但实际上,像这样的文件名往往是无法使用的(例如,尝试使用bash中包含!的文件名)

那么,关于Stackoverflow发生的原因:

  • name未被继承
  • project.parent.name也不会在插值过程中进行评估,因为该名称是少数对儿童完全不可见的属性之一
  • parent.name实际上曾用于较旧的Maven版本,但更多是由于某个错误(已弃用来访问没有前导project的属性)。
  • 缺少属性未插入,即按原样保留在模型中
  • 因此,在any-submodule的有效pom中,finalName的值是(尝试使用mvn help:effective-pom)仍为:${project.parent.name}-any-submodule

到目前为止一直很糟糕。现在出现了StackOverflow的原因

Maven有一个名为后期插值的附加功能,可以在实际使用时评估插件参数中的值。这允许pluing使用不属于模型的属性,但是由生命周期早期的插件生成(这允许插件为最终名称提供git修订)。

所以会发生这样的事情:

编辑:明确错误的实际原因(见评论):

  • 评估jar插件的finalName: @Parameter( defaultValue = "${project.build.finalName}", readonly = true )
  • PluginParameterExpressionEvaluator启动并尝试评估最终名称(${project.parent.name}-any-submodule,其中包含属性表达式$ {project.parent.name}。
  • 评估者询问模型,模型又返回父项目的名称,即:${project.parent.name}-any-module
  • 因此,评估者尝试解决此问题,然后再次返回${project.parent.name}-any-module,因为属性始终针对当前项目解决,循环再次开始。
  • 抛出StackOverflowError。

如何解决这个问题

可悲的是,你不能。

您需要为每个项目明确指定name(以及artifactId)。没有解决方法。

然后,可以finalName依赖它。不过我会反对它(见我对Difference between project.parent.name and parent.name ans use of finalName in pom.xml的回答)

更改最终名称的问题是本地构建工件的名称和存储库中的名称会有所不同,因此本地工件名为any-artifact-any-module-any-submodule.jar,但存储库中的工件名称会仍然是any-submodule.jar

建议

  • 如果您确实需要区分那么好,请改为更改artifactId:<artifactId>artifact-anymodule-anysubmodule</artifactId>
  • 请勿使用破折号作为短名称来区分您的结构级别。
  • 提示:模块的路径仍然是anymodule,不需要是模块的实际artifactId!
  • 虽然我们在这里:使用name来实现它的目的,是人类可读的,所以你可能会考虑更具视觉吸引力的东西(因为这是构建日志中出现的名称):{ {1}}。
  • 使用非常短的groovy脚本自动创建名称条目实际上非常容易。
  • 您还可以编写强制规则来强制执行artifactIds的命名

答案 2 :(得分:2)

这是属性继承的问题。
尝试使用${parent.name}代替${project.parent.name}
请看:Project name declared in parent POM isn't expanded in a module filtered web.xml

--- --- UPDATE

Benjamin Bentmann(maven committier)说:&#34;一般来说,${project.parent.*}形式的表达是一种不好的做法,因为它们依赖于某种构建状态,并且通常不会在整个POM中工作,出现惊喜&#34;。

https://issues.apache.org/jira/browse/MNG-5126?jql=text%20~%20%22parent%20name%22

也许您应该考虑使用${project.parent.*}是一种好方法。

答案 3 :(得分:-1)

将company-any-artifact中的pom.xml更改为以下内容,它将起作用。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.company</groupId>
    <artifactId>any-artifact</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>${project.groupId}</name>


    <modules>
        <module>any-module</module>
    </modules>

    <!-- if remove finalName, maven will not throw StackOverflow error -->
    <build>
        <finalName>${project.groupId}-${project.version}</finalName>
    </build>
</project>

将子模块中的pom.xml更改为

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>any-artifact</artifactId>
        <groupId>com.company</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.company.any-artifact</groupId>
    <artifactId>any-module</artifactId>
    <packaging>pom</packaging>  

  <!--   <name>${project.parent.name}-${project.artifactId}</name>  --> 

    <modules>
        <module>any-submodule</module>
    </modules>  
     <build>
        <finalName>${project.parent.name}-${project.artifactId}</finalName>
    </build> 
</project>

将子模块pom.xml更改为

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>any-module</artifactId>
        <groupId>com.company.any-artifact</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.company.any-artifact.any-module</groupId>
    <artifactId>any-submodule</artifactId>
        <!-- <name>${project.parent.name}-${project.artifactId}-${project.version}</name>    -->
    <build>
        <finalName>company-${project.parent.name}-${project.artifactId}-${project.version}</finalName>
    </build>
</project>

然后输出为: company-any-module-any-submodule-1.0-SNAPSHOT

答案 4 :(得分:-2)

有趣!我开始克隆回购并重现错误。我将不胜感激任何可以从下面提到的帮助我调试问题的步骤中获取的任何线索 -

  1. Maven Life Cycle Phases 发生问题的阶段是生命周期的package阶段。含义mvn package表示您的项目存在问题。

  2. 在错误中浏览堆栈跟踪线。在失败的地方了解表达式评估 -

    @Override
    public Object evaluate( String expr ) throws ExpressionEvaluationException {
        return evaluate( expr, null ); // Line 143
    }
    
  3. 它也不是造成它的finalName属性。由于指定了相同的默认值 <finalName>${artifactId}-${version}</finalName> 使用相同的项目配置工作正常。

  4. 然后尝试将any-submodule的包装更改为

    <packaging>pom</packaging>
    

    并且错误消失了。包装为jarwar等时的含义表达式评估不同,导致溢出。

  5. 修改any-moduleany-submodule pom.xml内容我可以肯定地说,project.parent.name导致评估表达式的递归导致堆栈溢出(如何? - 是I am still looking for ...)。另外,改变

    <name>${project.parent.name}-${project.artifactId}</name>

    <name>${parent.name}-${project.artifactId}</name>

    对我来说是有用的,因为我没有得到错误,但生成的jar是类型 -

    ${parent.name}-any-module-any-submodule-1.0-SNAPSHOT.jar

    ${parent.name}-any-submodule-1.0-SNAPSHOT分别进行了更改。

  6. 根据要求寻找解决方案,我正在寻找你正在使用的递归的尾部。

  7. 注意 - 仍在努力寻找适合此问题的解决方案。