java - tutorial - mvn run
Maven con un finalName explícito no funcionará correctamente (5)
1. Antecedentes
Mi proyecto de Maven tiene muchos módulos y submódulos con jars
y wars
y todo funciona. También puedo implementarlo en el servidor sin ningún problema.
Decidí seguir esta conversión de nombramiento de Maven , estoy haciendo algunas pruebas con project.name
y project.build.finalName
para tener un nombre apropiado.
El patrón que definí para crear project.name
para el artefacto raíz es company-${project.artifactId}
y para los módulos y submódulos es ${project.parent.name}-${project.artifactId}
:
- empresa-cualquier-artefacto-cualquier-módulo1
- empresa-cualquier-artefacto-cualquier-módulo2-cualquier-submódulo1
- empresa-cualquier-artefacto-cualquier-módulo2-cualquier-submódulo2
El patrón para project.build.finalName
es ${project.name}-${project.version}
:
- empresa-cualquier-artefacto-cualquier-módulo1-1.0.jar
- company-any-artifact-any-module2-any-submodule1-2.0.jar
- empresa-cualquier-artefacto-cualquier-módulo2-cualquiera-submódulo2-3.0.war
Pero en lugar de producir estos archivos, Maven me da un StackOverflowError
.
2. El ejemplo para reproducir el error.
Puede clonar este ejemplo desde github: https://github.com/pauloleitemoreira/company-any-artifact
En github, está la rama master
, que reproducirá este error. Y hay only-modules
rama de only-modules
, que es un ejemplo funcional que usa ${project.parent.name}
para generar el jar nombre finalName
como quiero.
Consideremos un proyecto de Maven con un artefacto pom raíz, un módulo pom y un submódulo.
-any-artifact
|
|-any-module
|
|-any-submodule
2.1 cualquier artefacto
<?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 cualquier módulo
<?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 cualquier-submódulo
<?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. problema
Cuando trato de mvn clean install
, Maven me da un 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)
Es importante saber que el error se produce solo cuando estamos trabajando con submódulos. Si creamos un proyecto con un artefacto POM raíz y un módulo jar, no se produce el error.
4. la pregunta
¿Por qué este error se produce solo cuando estamos utilizando submódulos?
¿Alguna sugerencia para resolver mi problema? ¿Debería olvidarlo y establecer project.name
y project.build.fileName
manualmente para cada proyecto, siguiendo el patrón que quiero?
IMPORTANTE ACTUALIZADO:
Algunas respuestas solo dicen usar &{parent.name}
, pero no funciona . Por favor, es una pregunta con una recompensa, considere probar su solución con la Maven version 3.3.9
antes de responder esta pregunta.
Maven versión 3.3.9
Editar : agregando detalles a la pregunta con la fase cuando se produce el error, las cosas funcionan bien hasta la fase de prepare-package
, pero StackOverflow se produce en la fase del package
en el ciclo de vida de Maven para el proyecto.
¡Interesante! Comencé clonando el repositorio y reproduciendo el error. Apreciaría cualquier pista que pueda tomarse de cualquiera de los pasos mencionados a continuación que me ayudaron a solucionar el problema.
Fases del ciclo de vida de Maven La fase en la que se produjo el problema fue la fase del
package
del ciclo de vida.mvn package
Meaning reproduce el problema con su proyecto.Pasó por las líneas de seguimiento de la pila en el error. Conocer la evaluación de la expresión donde está fallando.
@Override public Object evaluate( String expr ) throws ExpressionEvaluationException { return evaluate( expr, null ); // Line 143 }
Tampoco es el atributo
finalName
que lo estaba causando. Desde que se especifica el valor predeterminado del mismo<finalName>${artifactId}-${version}</finalName>
funciona bien con las mismas configuraciones de proyecto.Luego intenté cambiar el empaque del
any-submodule
como<packaging>pom</packaging>
y el error se fue. Significa que, mientras se empaqueta como
jar
,war
, etc., la evaluación de la expresión es diferente y produce un desbordamiento.Modificando
any-module
o el contenido deany-submodule
pom.xml
, puedo decir con cierta confianza que es elproject.parent.name
que está causando una recursión al evaluar la expresión y el desbordamiento de la pila (¿Cómo? - Es algo que soy sigue buscando ..). Además, cambiando<name>${project.parent.name}-${project.artifactId}</name>
a
<name>${parent.name}-${project.artifactId}</name>
funciona para mí en el sentido de que no recibo un error, pero el tarro generado es de tipo -
${parent.name}-any-module-any-submodule-1.0-SNAPSHOT.jar
y${parent.name}-any-submodule-1.0-SNAPSHOT
respectivamente con el cambio.Buscando la solución de acuerdo con el requisito, estoy buscando una cola para la recursión que está utilizando.
Nota : Sigo trabajando para encontrar una solución adecuada para este problema.
Como dije en mi respuesta a Diferencia entre project.parent.name y parent.name y uso de finalName en pom.xml
Veamos primero lo básico:
como se indica en la referencia de POM :
finalName : este es el nombre del proyecto empaquetado cuando finalmente se construye (sin la extensión del archivo, por ejemplo: my-project-1.0.jar). El valor predeterminado es $ {artifactId} - $ {version}.
nombre : los proyectos tienden a tener nombres de conversación, más allá del artefacto.
Así que estos dos tienen diferentes usos.
name
es meramente informativo y se utiliza principalmente para la documentación generada y en los registros de compilación. No se hereda ni se utiliza en ningún otro lugar. Es una cadena legible por humanos y, por lo tanto, puede contener cualquier carácter, es decir, espacios o caracteres no permitidos en los nombres de archivo. Entonces, esto sería válido:<name>My Turbo Project on Speed!</name>
. Que es claramente al menos un nombre de archivo cuestionable para un artefacto.como se indicó anteriormente,
finalName
es el nombre del artefacto generado. Se hereda, por lo que generalmente debe depender de las propiedades. Las únicas dos opciones realmente útiles son el${artifactId}-${version}
predeterminado y el${artifactId}-${version}
. Todo lo demás genera confusión (como un proyecto llamadofoo
crea un artefactobar.jar
). En realidad, mi proyecto turbo! sería válido, ya que este es un nombre de archivo válido, pero en realidad, los nombres de archivo como ese tienden a ser bastante inutilizables (por ejemplo, intente direccionar un nombre de archivo que contenga! de un bash)
Entonces, en cuanto a por qué sucede el :
-
name
no se hereda -
project.parent.name
tampoco se evalúa durante la interpolación, ya que el nombre es una de las pocas propiedades que son completamente invisibles para los niños -
parent.name
realidad solía funcionar en versiones anteriores de Maven, pero más debido a un error (también está obsoleto para acceder a propiedades sin elproject
principal). - una propiedad faltante no se interpola, es decir, permanece en el modelo como está
- Por lo tanto, en su pom efectivo para
any-submodule
, el valor parafinalName
es (pruébelo conmvn help:effective-pom
) aún:${project.parent.name}-any-submodule
Hasta aquí todo mal. Ahora viene la razón para el
Maven tiene una característica adicional llamada interpolación tardía que evalúa los valores en los parámetros de los complementos cuando se usan realmente. Esto permite utilizar propiedades que no forman parte del modelo, pero que son generadas por complementos anteriormente en el ciclo de vida (esto permite, por ejemplo, que los complementos contribuyan con una revisión de git al nombre final).
Entonces lo que pasa es esto:
edición : aclaró el motivo real del error (ver comentarios):
- El nombre final para el complemento jar se evalúa:
@Parameter( defaultValue = "${project.build.finalName}", readonly = true )
- El
PluginParameterExpressionEvaluator
e intenta evaluar el nombre final (${project.parent.name}-any-submodule
, que contiene una expresión de propiedad $ {project.parent.name}. - El evaluador pregunta al modelo, que a su vez devuelve el nombre del proyecto principal, que es:
${project.parent.name}-any-module
. - Entonces el evaluador intenta resolver esto, que devuelve
${project.parent.name}-any-module
(nuevamente), ya que una propiedad siempre se resuelve contra el proyecto actual , el ciclo comienza nuevamente. - Se lanza un Error.
Como resolver esto
Lamentablemente, no puedes.
Debe especificar explícitamente el name
(así como artifactId
) para cada proyecto. No hay solución.
Entonces, podrías dejar que finalName
confíe en él. Sin embargo, no lo recomendaría (vea mi respuesta a la diferencia entre project.parent.name y parent.name y el uso de finalName en pom.xml )
El problema al cambiar el nombre final de esta manera es que el nombre del artefacto de compilación local y el del repositorio serían diferentes, por lo que localmente su artefacto se llama any-artifact-any-module-any-submodule.jar
, pero el artefacto El nombre en su repositorio aún sería any-submodule.jar
Sugerencia
- Si realmente necesita diferenciar esa multa, cambie el artifactId en su lugar:
<artifactId>artifact-anymodule-anysubmodule</artifactId>
. - No use guiones para el nombre corto para diferenciar los niveles de su estructura.
- Sugerencia: la ruta del módulo puede seguir siendo cualquier
anymodule
, no es necesario que sea el elemento de artefacto real del módulo. - Mientras estamos en eso: use el
name
para lo que fue diseñado, para que pueda ser leído por humanos, por lo que podría considerar algo más llamativo visualmente (ya que este es el nombre que aparece en el registro de compilación):<name>Artifact :: AnyModule :: AnySubModule</name>
. - En realidad, es muy fácil crear las entradas de nombre automáticamente usando un script groovy muy corto.
- También puede escribir una regla de cumplimiento para imponer la denominación de los artifactIds
Este es un problema con la herencia de atributos.
Intente usar ${parent.name}
lugar de ${project.parent.name}
.
Mire: El nombre del proyecto declarado en el POM principal no se expande en un módulo web.xml filtrado .
---ACTUALIZAR---
Benjamin Bentmann (comisario experto) dijo: "En general, sin embargo, las expresiones de la forma ${project.parent.*}
Son una mala práctica ya que se basan en un estado de construcción determinado y generalmente no funcionan en todo el POM, dando lugar a sorpresas. ".
https://issues.apache.org/jira/browse/MNG-5126?jql=text%20~%20%22parent%20name%22
Quizás deberías considerar si usar ${project.parent.*}
Es una buena manera.
La respuesta estricta a su pregunta es que ${project.parent.name}
no se resolverá como parte del proceso de interpolación del modelo. Y a su vez, tiene un Error
, en un lugar del código completamente diferente, a saber, cuando ... construye el JAR final de su proyecto.
Parte 1: El modelo construido es incorrecto
Esto es lo que pasa. Cuando está lanzando un comando de Maven en un proyecto, la primera acción que realiza es crear el modelo efectivo del proyecto. Esto significa leer su archivo POM, razonar con perfiles activados, aplicar herencia, realizar interpolación en propiedades ... todo esto para construir el modelo final de Maven para su proyecto. Este trabajo es realizado por el componente Maven Model Builder .
El proceso de construcción del modelo es bastante complicado, con muchos pasos divididos en posiblemente 2 fases, pero la parte que nos interesa aquí es la parte de interpolación del modelo . Esto es cuando Maven reemplazará en el modelo todos los tokens indicados por ${...}
con un valor calculado. Ocurre después de que se inyectan los perfiles y se realiza la herencia. En ese momento, el proyecto Maven, representado por un objeto MavenProject
, aún no existe, solo se está construyendo su Model
. Y solo después de tener un modelo completo puede comenzar a construir el proyecto Maven a partir de él.
Como tal, cuando se realiza la interpolación, solo se basa en la información presente en el archivo POM, y los únicos valores válidos son los que se mencionan en la referencia del modelo . (Este reemplazo lo realiza la clase StringSearchModelInterpolator
, si desea consultar el código fuente). En particular, notará que el elemento <parent>
en el modelo no contiene el nombre del modelo principal. La clase Model
en Maven se genera realmente con Modello partir de un archivo .mdo
origen, y esa fuente solo define groupId
, artifactId
, version
y relativePath
(junto con una id
personalizada) para el elemento <parent>
. Esto también es visible en la documentación .
La consecuencia de todo eso, es que después de que se realiza la interpolación del modelo, el token ${project.parent.name}
no será reemplazado. Y, además, el MavenProject
construido a partir de él tendrá un nombre que contiene ${project.parent.name}
reemplazar. Puede ver esto en los registros, en su proyecto de ejemplo, tenemos
[INFO] Reactor Build Order:
[INFO]
[INFO] company-any-artifact
[INFO] ${project.parent.name}-any-module
[INFO] ${project.parent.name}-any-submodule
Lo que significa que Maven considera que el nombre real del proyecto any-module
es ${project.parent.name}-any-module
.
Parte 2: La rareza comienza.
Ahora estamos en un momento en que todos los proyectos en el reactor se crearon correctamente e incluso se compilaron. En realidad, todo debería funcionar teóricamente bien, pero solo con nombres completos para los proyectos en sí. Pero tienes un caso extraño, donde falla en la creación del JAR con el maven-jar-plugin
. La compilación falla en su ejemplo con los siguientes registros:
[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] ------------------------------------------------------------------------
lo que significa que algo salió mal después de que el modelo fue construido. Y la razón es que el complemento inyecta el nombre del proyecto como un parámetro :
/** * Name of the generated JAR. * * @parameter alias="jarName" expression="${jar.finalName}" default-value="${project.build.finalName}" * @required */ private String finalName;
Observe project.build.finalName
como el valor predeterminado del nombre JAR generado para el submódulo. Esta inyección y la interpolación de las variables se realizan mediante otra clase llamada PluginParameterExpressionEvaluator
.
Entonces, ¿qué pasa en esto?
- El complemento JAR en el
any-submodule
inyecta el nombre final del proyecto, denominado${project.parent.name}-any-submodule
. - Gracias por la herencia de los proyectos principales y la declaración de
<finalName>
en su proyecto POM más importante, hereda<finalName>${project.name}-${project.version}</finalName>
. - Maven ahora intenta interpolar
${project.name}
paraany-submodule
. - Esto se resuelve en
${project.parent.name}-any-submodule
, debido a la Parte 1. - Maven intenta ahora interpolar
${project.parent.name}
paraany-submodule
. Esto funciona correctamente: elMavenProject
está construido y segetParent()
en la instancia del proyecto, devolviendo el proyecto principal concreto de Maven. Como tal,${project.parent.name}
intentará resolver el nombre deany-module
, que en realidad es${project.parent.name}-any-module
. - Maven ahora intenta interpolar
${project.parent.name}-any-module
, pero aún en la instancia de proyectoany-submodule
. ParaPluginParameterExpressionEvaluator
, el"project"
raíz en el que evaluar tokens no ha cambiado. - Maven ahora intenta interpolar
${project.parent.name}
enany-submodule
, que, de nuevo, funciona correctamente y devuelve${project.parent.name}-any-module
. - Maven ahora intenta interpolar
${project.parent.name}
enany-submodule
... que funciona y devuelve${project.parent.name}-any-module
así que intenta evaluar${project.parent.name}
. ..
Y puedes ver la recursión interminable que ocurre aquí, lo que resulta en el Error
que tienes. ¿Es este un error en PluginParameterExpressionEvaluator
? Esto no está claro: se basa en valores de modelo que no se reemplazaron correctamente en primer lugar. En teoría, podría manejar el caso especial de evaluar ${project.parent}
y crear un nuevo PluginParameterExpressionEvaluator
trabajando en este proyecto principal, en lugar de trabajar siempre en el proyecto actual. Si te sientes convencido al respecto, siéntete libre de crear un problema de JIRA .
Parte 3: Por qué funciona sin el módulo secundario
Con lo que se ha dicho anteriormente, ahora podría deducir por qué funciona en este caso. Razonemos con lo que Maven necesita hacer para evaluar el nombre final, como se debe inyectar en el Plugin Maven Jar:
- El complemento JAR en
any-module
inyecta el nombre final del proyecto, denominado${project.parent.name}-any-module
. - Gracias por la herencia del proyecto principal y la declaración de
<finalName>
en su proyecto POM más importante, hereda<finalName>${project.name}-${project.version}</finalName>
. - Maven ahora intenta interpolar
${project.name}
paraany-module
. - Esto se resuelve en
${project.parent.name}-any-module
, igual que antes. - Maven intenta ahora interpolar
${project.parent.name}
paraany-module
. Al igual que antes, esto funciona correctamente: elMavenProject
está construido y segetParent()
en la instancia del proyecto, devolviendo el proyecto principal concreto de Maven. Como tal,${project.parent.name}
intentará resolver el nombre deany-artifact
, que en realidad escompany-any-artifact
. - La interpolación ha tenido éxito y se detiene.
Y no tienes ningún error.
cambie pom.xml en company-any-artifact a continuación y funcionará.
<?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 error -->
<build>
<finalName>${project.groupId}-${project.version}</finalName>
</build>
</project>
cambiar pom.xml en submódulo a continuación
<?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>
cambiar el submódulo pom.xml a continuación
<?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>
entonces la salida fue: company-any-module-any-submodule-1.0-SNAPSHOT