Maven с явным finalName не будет работать должным образом

1. Фон

мой проект maven имеет много модулей и подмодулей с jars и wars и все работает. Я также могу развернуть его на сервере без каких-либо проблем.

я решил последовать это преобразование имен maven, я делаю некоторые тесты с project.name и project.build.finalName чтобы иметь соответствующее имя.

шаблон, который я определил для создания project.name для корневого артефакта это company-${project.artifactId} и для модули и подмодули-это ${project.parent.name}-${project.artifactId}:

  • компания-любой-артефакт-любой-модуль1
  • company-any-artifact-any-module2-any-submodule1
  • компания-любой-артефакт-любой-module2-любой-submodule2

шаблон project.build.finalName is ${project.name}-${project.version}:

  • компания-любой-артефакт-любой-модуль1-1.0.Джар
  • company-any-artifact-any-module2-any-submodule1-2.0.Джар
  • компания-любой-артефакт-любой-module2-любой-submodule2-3.0.война!--41-->

но вместо того, чтобы создавать эти файлы, maven дает мне StackOverflowError.

2. Пример для воспроизведения ошибки

вы можете клонировать этот пример из github: https://github.com/pauloleitemoreira/company-any-artifact

в github есть master ветвь, которая будет воспроизводить эту ошибку. И есть only-modules ветвь, это рабочий пример, который использует ${project.parent.name} для создания банки finalName как я хочу.

рассмотрим проект maven с одним корневым артефактом pom, одним модулем pom и одним подмодулем.

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

2.1 любой артефакт

<?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 любые-модуль

<?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 любой подмодуль

<?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.name и project.build.fileName вручную для каждого проекта, следуя шаблону, который я хочу?

ВАЖНОЕ ОБНОВЛЕНИЕ:

некоторые ответы просто говорят, чтобы использовать &{parent.name}, а не работает. Пожалуйста, это вопрос с bounty, рассмотрим проверить ваше решение с Maven version 3.3.9, прежде чем ответить на этот вопрос.

Maven версии 3.3.9

Edit - добавление деталей к вопросу с фазой, когда происходит ошибка, все работает нормально, пока prepare-package фаза, но StackOverflow происходит в package фаза жизненного цикла maven для проекта.

5 ответов


строгий ответ на ваш вопрос заключается в том, что ${project.parent.name} будет не разрешаться как часть процесса интерполяции модели. И, в свою очередь, у вас есть StackOverflowError, в совершенно другом месте кода, а именно когда... создание последней банки вашего проекта.

Часть 1: построенная модель неверна

вот что происходит. При запуске команды Maven в проекте Первым действием, которое она выполняет, является создание эффективной модели проекта. Это означает чтение файла POM, рассуждение с активированными профилями, применение наследования, выполнение интерполяции свойств... все это для создания окончательной модели Maven для вашего проекта. Эта работа выполняется Maven Model Builder компонент.

процесс построения модели довольно сложный, с большим количеством шагов, разделенных, возможно, на 2 фазы, но та часть, которая нас интересует здесь, в модель интерполяции часть. Это когда Maven заменит в модели все маркеры, обозначенные ${...} с вычисленным значением. Это происходит после того, как профили вводятся, и выполняется наследование. В этот момент времени проект Maven, представленный MavenProject объект, еще не существует, только его Model строится. И только после того, как у вас есть полная модель, вы можете начать строить проект Maven из нее.

как таковой, при интерполяции, то только в плане информации присутствует в файле POM, и единственными допустимыми значениями являются те упоминается в справочной модели. (Эта замена выполняется с помощью StringSearchModelInterpolator класс, если вы хотите посмотреть исходный код.) Примечательно, что вы заметите, что <parent> элемент в модели не содержит имя родительской модели. Класс!--7--> в Maven фактически генерируется с Modello источник .mdo файл, и только этот источник определяет groupId, artifactId, version и relativePath (вместе с id) для <parent> элемент. Это также видно документации.

следствием всего этого является то, что после интерполяции модели выполняется токен ${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. Ошибка сборки в вашем примере со следующими журналами:

[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] ------------------------------------------------------------------------

означает, что что-то пошло не так после того, как модель была построена. И причина в том, что плагин вводит имя проекта в качестве параметра:


/**
 * 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.

Итак, что происходит в этом:

  • плагин JAR на any-submodule вводит конечное имя проекта с именем ${project.parent.name}-any-submodule.
  • Спасибо за наследование от родительских проектов и объявление <finalName> в вашем проекте top-most POM он наследует <finalName>${project.name}-${project.version}</finalName>.
  • Maven теперь пытается интерполировать ${project.name} на any-submodule.
  • разрешается ${project.parent.name}-any-submodule, в силу части 1.
  • 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, но все равно экземпляр. Для PluginParameterExpressionEvaluator, корневой "project" на котором оценка токенов не изменилась.
  • Maven теперь пытается интерполировать ${project.parent.name} on any-submodule, который, опять же, работает правильно и возвращает ${project.parent.name}-any-module.
  • Maven теперь пытается интерполировать ${project.parent.name} on any-submodule... который работает и возвращает ${project.parent.name}-any-module поэтому он пытается оценить ${project.parent.name}...

и вы можете увидеть бесконечную рекурсию, происходящую здесь, что приводит к StackOverflowError у вас есть. Это ошибка в PluginParameterExpressionEvaluator? Это непонятно: это причины по модели значения, которые были неправильно заменены в первую очередь. Теоретически он мог бы справиться с частным случаем оценки ${project.parent} и создать новый PluginParameterExpressionEvaluator работа над этим родительским проектом, вместо того, чтобы всегда работать над текущим проектом. Если вы чувствуете себя сильно об этом, не стесняйтесь создавать проблема JIRA.

Часть 3: Почему он работает без субмодуля

с тем, что было сказано выше, теперь вы можете вывести, почему это работает в этом случае. Давайте рассуждать с тем, что Maven нужно сделать, чтобы оценить окончательное имя, как должно быть введено в плагин Maven Jar:

  • плагин JAR на any-module вводит конечное имя проекта с именем ${project.parent.name}-any-module.
  • Спасибо за наследование от родительского проекта и объявление <finalName> в вашем проекте top-most POM он наследует <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.
  • интерполяция прошла успешно и останавливается.

и у вас их нет ошибки.


как я заявил в своем ответе разница между проектом.родитель.имя и родитель.имя и использование finalName в pom.в XML

давайте сначала рассмотрим основы:

как говорится в пом ссылки:

finalName: это имя проекта в комплекте, когда он, наконец, построен (без расширения файла, например: my-project-1.0.сосуд.) По умолчанию используется значение ${artifactId} - ${version}.

имя: проекты, как правило, имеют разговорные имена, за пределами artifactId.

таким образом, эти два имеют разное использование.

  • name является чисто информационным и в основном используется для сгенерированной документации и в журналах сборки. Он не наследуется и не используется нигде. Это читаемая человеком строка и может содержать любой символ, т. е. пробелы или символы, не разрешенные в именах файлов. Итак, это будет действительным: <name>My Turbo Project on Speed!</name>. Что явно, по крайней мере, сомнительное имя файла для артефакта.

  • как указано выше, finalName - имя сгенерированного артефакта. Это is наследуется, поэтому обычно следует полагаться на свойства. Единственные два действительно полезных параметра-по умолчанию ${artifactId}-${version} и versionless ${artifactId}. Все остальное приводит к путанице (например, проект под названием foo создать артефакт bar.jar). Вообще-то, мой турбо проект! будет действительным, так как это допустимое имя файла, но на самом деле такие имена файлов, как правило, довольно непригодны для использования (попробуйте адресовать имя файла ! из Баша, например)


Итак, почему происходит Stackoverflow:

  • name не наследуется
  • project.parent.name также не оценивается во время интерполяции, так как имя является одним из немногих свойств, которые полностью невидимы для детей
  • parent.name на самом деле привык работать в старых версиях Maven, но больше из-за ошибки (это устаревший для доступа к свойствам без ведущего project).
  • отсутствующее свойство не интерполируется, т. е. остается в модели как есть
  • поэтому в вашем эффективном pom для any-submodule значение finalName is (попробуйте с mvn help:effective-pom) все-таки: ${project.parent.name}-any-submodule

пока все плохо. Теперь приходит причина StackOverflow

Maven имеет дополнительная функция называется поздней интерполяцией, которая оценивает значения в параметрах плагина, когда они фактически используются. Это позволяет pluing использовать свойства, которые не являются частью модели, но генерируются плагинами ранее в жизненном цикле (это позволяет, например, плагинам вносить изменения Git в конечное имя).

Итак, что происходит:

редактировать: сделал фактическую причину ошибки более ясной (см. комментарии):

  • вычисляется конечное имя для плагина jar: @Parameter( defaultValue = "${project.build.finalName}", readonly = true )
  • на PluginParameterExpressionEvaluator пинает и пытается оценить окончательное имя (${project.parent.name}-any-submodule, который содержит выражение свойства ${project.родитель.имя}.
  • оценщик запрашивает модель, которая в свою очередь возвращает имя родительского проекта, которое: ${project.parent.name}-any-module.
  • таким образом, оценщик пытается решить эту проблему, которая возвращает ${project.parent.name}-any-module (опять же), так как свойство всегда решено против текущего проекта цикл начинается заново.
  • выбрасывается StackOverflowError.

как это решить

к сожалению, вы не можете.

нужно явно указать name (а также artifactId) для каждого проекта. Обходного пути нет.

затем мог бы пусть finalName полагаться на него. Однако я бы не советовал (см. Мой ответ на разница между проектом.родитель.имя и родитель.имя и использование finalName в pom.в XML)

проблема в изменении конечного имени таким образом заключается в том, что имя локального артефакта сборки и имя в репозитории будут отличаться, поэтому локально ваш артефакт называется any-artifact-any-module-any-submodule.jar, но имя артефакта в вашем репозитории будет по-прежнему any-submodule.jar

предложение

  • Если вам действительно нужно дифференцировать этот штраф, измените artifactId вместо этого: <artifactId>artifact-anymodule-anysubmodule</artifactId>.
  • не используйте тире для shortname, чтобы различать уровни вашей структуры.
  • подсказка:путь модуля может быть еще anymodule, is не должен быть фактическим artifactId модуля!
  • пока мы на нем: используйте name для того, что было предназначено, чтобы быть читаемым человеком, поэтому вы можете рассмотреть что-то более визуально привлекательное (так как это имя появляется в журнале сборки): <name>Artifact :: AnyModule :: AnySubModule</name>.
  • на самом деле очень легко просто автоматически создавать записи имен с помощью очень короткого скрипта groovy.
  • вы также можете написать правило принудительного исполнения для принудительного именования artifactIds

это проблема с наследованием атрибутов.
Попробуйте использовать ${parent.name} вместо ${project.parent.name}.
Посмотрите на: имя проекта, объявленное в Родительском POM, не расширяется в отфильтрованном модуле web.в XML.

---обновление---

Бенджамин Бентман (maven committier) сказал: "В общем, хотя, выражения формы ${project.parent.*} плохая практика, поскольку они полагаются на определенное состояние сборки и обычно не работают во всем POM, что приводит к сюрпризы."

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

Возможно, вам следует рассмотреть использование ${project.parent.*} - это хороший способ.


интересные! Я начал клонировать РЕПО и воспроизводить ошибку. Я был бы признателен за любые выводы, которые могут быть сделаны из любого из шагов, упомянутых ниже, которые помогли мне отладить проблему -

  1. Фазы Жизненного Цикла Maven Фаза, на которой возникла проблема, была 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 as

    <packaging>pom</packaging>
    

    и ошибка ушла. Значение при упаковке как jar , war etc выражение оценка отличается и приводит к переполнению.

  5. модификации any-module или any-submodule pom.xml контент я могу сказать с некоторой уверенностью, что это project.parent.name это вызывает рекурсию при оценке выражения и вызывает переполнение стека (как? - это что-то я все еще ищу..). Кроме того, изменение

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

    to

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

    работает для меня в смысле что я не получаю ошибку, но созданный банку типа

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

    ${parent.name}-any-submodule-1.0-SNAPSHOT соответственно с изменением.

  6. ища решение в соответствии с требованием, я ищу хвост рекурсии, которую вы используете.

Примечание - все еще работают над поиском подходящего решения этой проблемы.


изменить 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>

затем вывод был: компания-любой-модуль-любой-подмодуль-1.0-снимок