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}
onany-submodule
, который, опять же, работает правильно и возвращает${project.parent.name}-any-module
. - Maven теперь пытается интерполировать
${project.parent.name}
onany-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.*}
- это хороший способ.
интересные! Я начал клонировать РЕПО и воспроизводить ошибку. Я был бы признателен за любые выводы, которые могут быть сделаны из любого из шагов, упомянутых ниже, которые помогли мне отладить проблему -
Фазы Жизненного Цикла 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
as<packaging>pom</packaging>
и ошибка ушла. Значение при упаковке как
jar
,war
etc выражение оценка отличается и приводит к переполнению. -
модификации
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
соответственно с изменением. ища решение в соответствии с требованием, я ищу хвост рекурсии, которую вы используете.
Примечание - все еще работают над поиском подходящего решения этой проблемы.
изменить 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-снимок