Подтвердить что ты не робот

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

1. Фон

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

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

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

  • компания-любой-артефакт любой-module1
  • компания-любой-артефакт любой-module2-любой-submodule1
  • компания-любой-артефакт любой-module2-любой-submodule2

Шаблон для project.build.finalName равен ${project.name}-${project.version}:

  • компания-любой-артефакт любой-module1-1.0.jar
  • компания-любой-артефакт любой-module2-любой-submodule1-2.0.jar
  • компания-любой-артефакт любой-module2-любой-submodule2-3.0.war

Но вместо создания этих файлов maven дает мне StackOverflowError.

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

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

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

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

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

<?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}, но он не работает. Пожалуйста, вопрос с щедростью, перед тем, как ответить на этот вопрос, попробуйте проверить свое решение с помощью Maven version 3.3.9.

Версия Maven 3.3.9

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

4b9b3361

Ответ 1

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

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

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

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

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

Следствием всего этого является то, что после выполнения интерполяции модели токен ${project.parent.name} не будет заменен. И, кроме того, построенный из него MavenProject будет иметь имя, содержащее ${project.parent.name} unreplaced. Вы можете увидеть это в журналах, в вашем примере проекта, мы

[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: начинается странность

Мы сейчас в то время, когда все проекты в реакторе были правильно созданы и даже скомпилированы. На самом деле, все должно теоретически работать отлично, но с полностью разрешенными именами для самих проектов. Но у вас странный случай, когда он терпит неудачу при создании JAR с помощью 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> в самом верхнем 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, , но все же на экземпляр проекта 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, работающий над этим родительским проектом, вместо того, чтобы всегда работать над текущим проектом. Если вы решительно настроены на это, не стесняйтесь создавать проблему JIRA.

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

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

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

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

Ответ 2

Как я сказал в своем ответе на Разницу между project.parent.name и parent.name ans использование finalName в pom.xml

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

как указано в Ссылка POM:

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).
  • недостающее свойство не интерполируется, т.е. остается в модели как
  • Поэтому в вашем эффективном pom для any-submodule значение finalName равно (попробуйте с mvn help:effective-pom): ${project.parent.name}-any-submodule

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

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

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

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

  • Определяется окончательное имя для плагина jar: @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 полагаться на него. Я бы посоветовал ему (см. Мой ответ Разница между project.parent.name и parent.name ans использование finalName в pom.xml)

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

Предложение

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

Ответ 3

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

--- UPDATE ---

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

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

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

Ответ 4

измените pom.xml в company-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>${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>

тогда выход был: компания-any-module-any-submodule-1.0-SNAPSHOT

Ответ 5

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

  • Фазы жизненного цикла Maven Фазой, где возникла проблема, была фаза package жизненного цикла. Значение mvn package воспроизводит проблему с вашим проектом.

  • Прошел через строки трассировки стека в ошибке. Знакомство с оценкой выражения там, где это происходит -

    @Override
    public Object evaluate( String expr ) throws ExpressionEvaluationException {
        return evaluate( expr, null ); // Line 143
    }
    
  • Это также не атрибут finalName, который вызывал его. Поскольку указание значения по умолчанию того же <finalName>${artifactId}-${version}</finalName> отлично работает с теми же конфигурациями проекта.

  • Затем попытался изменить упаковку any-submodule как

    <packaging>pom</packaging>
    

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

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

    <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 соответственно с изменением.

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

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