1. Background
My maven project has a lot of modules and submodules with jars
and wars
and everything works. I also can deploy it on server without any problem.
I decided to follow this maven naming conversion, I am making some tests with project.name
and project.build.finalName
to have an appropriate name.
The pattern I defined to create project.name
for the root artifact is company-${project.artifactId}
and for the modules and sub-modules is ${project.parent.name}-${project.artifactId}
:
The pattern for project.build.finalName
is ${project.name}-${project.version}
:
But instead of producing these files, maven gives me a StackOverflowError
.
2. The example to reproduce the error
You can clone this example from github: https://github.com/pauloleitemoreira/company-any-artifact
In github, there is the master
branch, that will reproduce this error. And there is only-modules
branch, that is a working example that uses ${project.parent.name}
to generate the jar finalName
as I want.
Let's consider a maven project with one root pom artifact, one pom module and one submodule.
-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. Problem
When try to mvn clean install
, maven gives me a 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)
It is important to know that the error occurs only when we are working with submodules. If we create a project with a root POM artifact and a jar module, the error don't occur.
4. The question
Why this error occurs only when we are using submodules?
Any suggestion to solve my problem? Should I forget it and set project.name
and project.build.fileName
manually for each project, following the pattern I want?
IMPORTANT UPDATED:
Some answers just say to use &{parent.name}
, but it does not work. Please, it is a question with a bounty, consider test your solution with Maven version 3.3.9
, before answering this question.
Maven version 3.3.9
Edit - Adding details to the question with the phase when the error occurs, things are working fine until the prepare-package
phase, but the StackOverflow occurs at the package
phase on maven lifecycle for the project.
The strict answer to your question is that ${project.parent.name}
will not be resolved as part the model interpolation process. And in turn, you have a StackOverflowError
, in a completely different place of the code, namely when... building the final JAR of your project.
Here's what happens. When you're launching a Maven command on a project, the first action it takes is creating the effective model of the project. This means reading your POM file, reasoning with activated profiles, applying inheritance, performing interpolation on properties... all of this to build the final Maven model for your project. This work is done by the Maven Model Builder component.
The process of building the model is quite complicated, with a lot of steps divided in possibly 2 phases, but the part we're interested in here in the model interpolation part. This is when Maven will replace in the model all tokens denoted by ${...}
with a calculated value. It happens after profiles are injected, and inheritance is performed. At that point in time, the Maven project, as represented by a MavenProject
object, doesn't exist yet, only its Model
is being built. And it is only after you have a full model that you can start constructing the Maven project from it.
As such, when interpolation is done, it only reasons in terms of the information present in the POM file, and the only valid values are the ones mentioned in the model reference. (This replacement is performed by the StringSearchModelInterpolator
class, if you want to look at the source code.) Quite notably, you will notice that the <parent>
element in the model does not contain the name of the parent model. The class Model
in Maven is actually generated with Modello from a source .mdo
file, and that source only defines groupId
, artifactId
, version
and relativePath
(along with a custom id
) for the <parent>
element. This is also visible in the documentation.
The consequence of all that, is that after model interpolation is performed, the token ${project.parent.name}
will not be replaced. And, further, the MavenProject
constructed from it will have a name containing ${project.parent.name}
unreplaced. You can see this in the logs, in your sample project, we have
[INFO] Reactor Build Order:
[INFO]
[INFO] company-any-artifact
[INFO] ${project.parent.name}-any-module
[INFO] ${project.parent.name}-any-submodule
Meaning that Maven consider the actual name of the project any-module
to be ${project.parent.name}-any-module
.
We're now at a time when all of the projects in the reactor were correctly created and even compiled. Actually, everything should theoretically work just fine, but with only completely borked names for the projects themselves. But you have a strange case, where it fails at the creation of the JAR with the maven-jar-plugin
. The build fails in your example with the following logs:
[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] ------------------------------------------------------------------------
meaning that something went wrong well after the model was built. And the reason is that the plugin 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;
Notice project.build.finalName
as the default value of the generated JAR name for the submodule. This injection, and the interpolation of the variables are done by another class called PluginParameterExpressionEvaluator
.
So what happens in this:
any-submodule
injects the final name of the project, named ${project.parent.name}-any-submodule
.<finalName>
in your top-most POM project, it inherits <finalName>${project.name}-${project.version}</finalName>
.${project.name}
for any-submodule
.${project.parent.name}-any-submodule
, due to Part 1.${project.parent.name}
for any-submodule
. This works correctly: the MavenProject
is built and getParent()
will be called on the project instance, returning the concrete Maven parent project. As such, ${project.parent.name}
will try to resolve the name of any-module
, which is actually ${project.parent.name}-any-module
.${project.parent.name}-any-module
, but still on the any-submodule
project instance. For PluginParameterExpressionEvaluator
, the root "project"
on which to evaluate tokens hasn't changed.${project.parent.name}
on any-submodule
, which, again, works correctly and returns ${project.parent.name}-any-module
.${project.parent.name}
on any-submodule
... which works and returns ${project.parent.name}-any-module
so it tries to evaluate ${project.parent.name}
...And you can see the endless recursion happening here, which results in the StackOverflowError
you have. Is this a bug in PluginParameterExpressionEvaluator
? This is unclear: it reasons on model values that were not correctly replaced in the first place. In theory, it could handle the special case of evaluating ${project.parent}
and create a new PluginParameterExpressionEvaluator
working on this parent project, instead of always working on the current project. If you feel strongly about this, feel free to create a JIRA issue.
With what has been said above, you could now deduce why it works in this case. Let's reason with what Maven needs to do to evaluate the final name, as has to be injected in the Maven Jar Plugin:
any-module
injects the final name of the project, named ${project.parent.name}-any-module
.<finalName>
in your top-most POM project, it inherits <finalName>${project.name}-${project.version}</finalName>
.${project.name}
for any-module
.${project.parent.name}-any-module
, same as before.${project.parent.name}
for any-module
. Just like before, this works correctly: the MavenProject
is built and getParent()
will be called on the project instance, returning the concrete Maven parent project. As such, ${project.parent.name}
will try to resolve the name of any-artifact
, which is actually company-any-artifact
.And you don't have any errors.