Java Spring Boot custom properties dont work in application.yml - java

I'm following a tutorial on Java Spring Boot and the current topic is that one can use both application.properties and application.yml
So my application.properties looked like this:
spring.profiles.active=EN, cat
custom.username=user
custom.password=pass
I deleted it and created an application.yml instead. IntelliJ even marks it with the little green start button icon. application.yml looks like this:
spring:
profiles:
active: EN, cat
custom:
username: user
password: pass
But when I do the custom properties don't get recognized any more. The IDE marks them red and shows this error: "Key 'custom' is not expected here"
I wasn't sure if that's correct so I tried what the IDE suggested when using autocomplete to write spring.profiles.active which was writing the list elements like this:
spring:
profiles:
active:
- EN
- cat
custom:
username: user
password: pass
But that also didn't help.
I'm not sure where to go from here. I've tried to research the issue, however the only hits I get that come somewhat close all just mention that it's possible to use yml instead of properties and some even use custom properties like I displayed above.
Is this maybe a version issue? My pom looks like this:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>myID</groupId>
<artifactId>myArtifact</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>myName</name>
<description>myDescription</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Update
To answer the question Mark B posed in the comments. This is the error message:
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2023-01-05T08:58:07.711+01:00 ERROR 5026 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in my.package.controllers.I18nController required a bean of type 'my.package.services.GreetingService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Qualifier("i18nService")
The following candidates were found but could not be injected:
- User-defined bean
- User-defined bean
- User-defined bean method 'primaryGreetingService' in 'GreetingServiceConfig'
- User-defined bean method 'propertyInjectedGreetingService' in 'GreetingServiceConfig'
- User-defined bean method 'setterInjectedGreetingService' in 'GreetingServiceConfig'
- User-defined bean
Action:
Consider revisiting the entries above or defining a bean of type 'my.package.services.GreetingService' in your configuration.
Process finished with exit code 1
At first glance this looks like maybe I messed up something within the definition or configuration of my beans. However, when I revert my repository back to the state where I used the application.properties file as described above, everything works as expected.
I retook the steps to isolate the problem as much as possible.
All I did was:
delete application.properties
create application.yml
I have changed nothing else.
git status (translated and simplified):
On Branch master
Your Branch is in the same state as 'origin/master'.
Staged changes:
deleted: src/main/resources/application.properties
new file: src/main/resources/application.yml
Unstaged changes::
changed: src/main/resources/application.yml
src/main/resources/application.yml is staged as new file and then has unstaged changes because the IDE staged the new file on creation. Just to clear out any confusion. Though this shouldn't affect the outcome.
Update 2
As suggested in the comments I tried to add #ConfigurationProperties(prefix = "custom"), but all that did was change the error slightly:
Error creating bean with name 'i18nController' defined in file
[/path/target/classes/my/package/controllers/I18nController.class]:
Unsatisfied dependency expressed through constructor parameter 0:
No qualifying bean of type 'my.package.services.GreetingService' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Qualifier("i18nService")}

As you're using spring-boot-starter SnakeYAML library should be on your classpath, but please double-check with mvn dependency:list, search the output for a line [INFO] org.yaml:snakeyaml:jar:1.33:compile -- module org.yaml.snakeyaml [auto] .
If it's missing, then please add SnakeYAML dependency: https://mvnrepository.com/artifact/org.yaml/snakeyaml
Eclipse IDE has auto-convertion mechanism which converts properties into yaml format. Just right click on the file and click "convert" it works like a charm. I'm using Eclipse STS edition.
I guess that you're not sharing full properties file and there is a syntax error or something is missing in your yaml configuration. Please give a try with plugins which are available for InteliJ IDEA: https://plugins.jetbrains.com/plugin/8000-properties-to-yaml-converter or online converters like: https://www.javainuse.com/app2yaml .
UPDATE:
I Decided to reproduce your example code on my machine with exactly the same version of SpringBoot and Java. First of all I've created a properties files and tested (it worked fine) and then migrated properties file into YAML format and it also works fine with exactly the same result.
When it comes to unexpected key custom it only says that this key if unknown (more like warning then error), and you can define it in META-INF like below.
So as other comments suggest I'm sure that it's connected to code which relays on properties file in some hardcoded way (for example #PropertySource annotation, see also my comment below).

Related

"Application failed to start" after spring-boot-starter-mail usage

Want to implement simple mail sender solution based on spring-boot-starter-mail
After adding dependency and set up properties I tried to run app and that error occurred.
I think that's worth noticing that I am not, and not about either to implement Spring Cloud. Just the mail service.
Doesn't understand why spring boot expects some services that are from these package.
Tried various version of dependency, defining some #Beans but don't think that's the issue
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
logs:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-10-23 00:07:05.752 ERROR 19576 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 1 of method mailNotifier in de.codecentric.boot.admin.server.config.AdminServerNotifierAutoConfiguration$MailNotifierConfiguration required a bean of type 'de.codecentric.boot.admin.server.domain.entities.InstanceRepository' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'instanceRepository' in 'AdminServerAutoConfiguration' not loaded because #ConditionalOnBean (types: de.codecentric.boot.admin.server.config.AdminServerMarkerConfiguration$Marker; SearchStrategy: all) did not find any beans of type de.codecentric.boot.admin.server.config.AdminServerMarkerConfiguration$Marker
Action:
Consider revisiting the entries above or defining a bean of type 'de.codecentric.boot.admin.server.domain.entities.InstanceRepository' in your configuration.
Just want to send email via smtp.gmail.com
Hope that spring-boot-starter-mail 'd be enough for that.
Thanks!
As LHCHIN commented, it was a matter of spring-boot-admin-starter-server and spring-boot-admin-dependencies in <dependencyManagement/>
I would always advice to avoid copy pasting the dependencies with their version. Most of the cases you don't need to specifiy the version and let Spring boot figure out based on mvn which version will be suitable for you.
For the spring boot starter mail, I just needed the following dependencies
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.5.6</version>
</dependency>
The source I used is : https://www.baeldung.com/spring-email and it worked for me with version and without.

Why Integrating Spring Cloud application with the AWS Parameter Store return no property from param store?

Intent : I am working on a POC which intends to use AWS Parameter store as a property store.This would store the confidential application properties in AWS SSM's Parameter store.I am using Java 8 with spring boot/cloud version 2.
Resource : I followed this ref guide from spring docs
and
also a comprehensive article Integrating the AWS Parameter Store with Spring Cloud .Hence was trying to utilize
spring-cloud-starter-aws-parameter-store-config.jar
and hence added required dependency in the build file.
Expected output :
Actual output :
Here is snapshot from AWS console I am trying to access below shown parameters from AWS parameter store
Below are my spring property files:
application.yml
bootstrap.yml
I am using maven with below dependencies in POM.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws-parameter-store-config</artifactId>
</dependency>
</dependencies>
Am I missing something here?? Please let me know if someone has already faced and resolved this issue.
I am able to put and get parameter from command line,its just not able to get this java lib working.
GitHub repo of the sample I am trying -
GitHub repo link
I checked your app, it didn't work as expected for me as I had ~.aws/config file which leads to misconfiguration of AWS credentials(cause by DefaultAWSCredentialsProviderChain, read more here ), so I removed it, and I tried again but it fails saying that spring can't find aws region in the env, so apparently those specified in application.yml won't be used until spring loads properties from AWS parameter store.
How I made it work
I added:
System.setProperty("aws.accessKeyId","My_Key");
System.setProperty("aws.secretKey","Secret");
System.setProperty("aws.region","us-east-1");//same region where all your params exist
before SpringApplication.run(DemoApplication.class, args); and then it worked.
when changing the aws.region to another one where there are no params value defined I got the exact same result as yours (empty values).
make sure there isn't any aws config on your machine or EC2 instance that will override those provided in your app.

SpringBoot "not an entity"

I am new to Hibernate and SpringBoot. My projects deals with a search engine that is composed of 2 independent modules + 1 base module common to both (where the IndexSetup class resides).
There is one module for indexing (JavaFx) and the other one for searching via the web browser (Spring Boot).
The indexing module involves an "IndexSetup" class that has the details on how / what should be indexed :
#Entity
#Table(name = "IndexSetups")
#Access(AccessType.PROPERTY)
public class IndexSetup {
private final SimpleIntegerProperty id = new SimpleIntegerProperty();
#Id
#GeneratedValue(strategy = GenerationType.AUTO) // For H2 AUTO is required to auto increment the id
public int getId() {
return id.get();
}
//... other properties, getters and setters
}
So it works great, the data is indexed and can be retrieved via a search method within the indexing module.
However when I run the Spring Boot server and do the same search I get
java.lang.IllegalArgumentException: Not an entity: class my.package.IndexSetup
By the way there is no build error, and before the modules were parts of a parent pom project, they were in the same project with the server class in a subfolder, and it worked. I decided to separate them for convenience during developpment and to offer two independent modules in production.
So why did it work when everything was under the same Netbeans project and now that the modules are in 2 different subfolders (but in the same group id package "my.package") I get this "Not an entity" and what should I do to solve this, where should I look at ?
Please note : I already tried this without success ("null pointer exception, cannot load the database").
Edit 1:
I also tried to add #EntityScan following this but I still get Not an entity: class my.package.IndexSetup :
#SpringBootApplication
#EntityScan( basePackages = {"my.package"} )
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
Edit 2 :
The architecture of the project is like :
- Parent project (my.package)
-Module Base (with IndexSetup class)
-Module Indexing (that depends on Base)
-Module Server (that also depends on Base)
The parent pom.xml reads like the following :
<?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>my.package</groupId>
<artifactId>MyApp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--According to https://stackoverflow.com/questions/10665936/maven-how-to-build-multiple-independent-maven-projects-from-one-project-->
<modules>
<module>Base</module> <!-- Common resources which is a dependency in Indexer and Server -->
<module>Indexer</module> <!-- Indexing part with JavaFx-->
<module>Server</module> <!-- Server (spring boot) part of -->
</modules>
<name>MyApp</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${java.home}/lib/jfxrt.jar</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
</plugins>
</build>
Edit 3:
The problem originates from when the table to look at is specified:
Root<IndexSetup> from = criteriaQuery.from(IndexSetup.class);
Looking at hibernate sources not an entity is thrown whenever entityType == null. So I don't gather why the entity type is null here whereas it works outside of SpringBoot ?
Edit 4:
If I remove SpringApplication.run(ServerApplication.class, args); from the Server class' main method then the same call which was causing the issue ie :
LocalDatabase.getInstance(false) // no GUI
.getAllIndexSetups();
now works picobello. Of course it does not solve anything since I still need SpringBoot for the search! So for me it means that Spring Boot does not understand the hibernate configuration. I opened a new question to introduce the problem more accurately.
Any help appreciated,
I think you should add to your #EntityScan annotation package of your entities within second project/module
As first, some checking :
Is all your configuration only built with annotations in ServerApplication or any Java class, or are there any others external configurations in XML / YML files ? Maybe look for conflicts. We do prefer to not mix XML with annotation configuration, if possible.
Try to remove #Serializable (not really mandatory).
Try to move your entity in your root package (just as a test).
Check if the package which export #Entity is correct.
Question : what are you calling "module", it is a subpackage or Maven module or another thing ? Could we have the configuration of package names about this ?
Edit :
In the case of a multi-modules project, did you follow recommendations from spring.io about multi-modules projets ? Did you import the Spring BOM (or starter) in your submodules and did you test the Spring Boot Maven Plugin ?
Can you provide your application.properties (or application.yml whatever) with your datasource configuration ? You should check if your datasource (and JPA, driver class, ...) is correctly defined ; see spring.io
So it happened that I did not use correctly SpringBoot capabilities. Here are the steps I followed. Please remember the architecture of the project :
- Parent maven project (my.package)
|-Module Base (with IndexSetup class and [initialy] hibernate.cfg.xml in /resources. It also had in the beginning LocalDatabase class to access to the local db via hibernate)
|-Module Indexing (that depends on Base)
|-Module Server (that also depends on Base)
|-Database file (myLocalDB)
1) First I removed hibernate.cfg.xml from Base and dropped it into resources of the Indexing module. I did it because SpringBoot has its own configuration mechanism. I also removed LocalDatabase class from Base (since it would not be needed by SpringBoot) and dropped it too in the Indexing Module (where it is used indeed).
2) Following [this](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html] I added spring-boot-starter-data-jpa to the Server module pom.xml.
3) Following this tutorial I created a JPA Repository IndexSetupRepository with barely a single line of code :
public interface IndexSetupRepository extends CrudRepository<IndexSetup, Integer> {
}
4) In the Server application.properties I added those lines :
# Embedded database configuration
# The embedded database file is one level above the Server folder (ie directly within the parent project)
# we use the auto server mode to be able to use the database simultaneously in the indexer and the server
spring.datasource.url=jdbc:h2:file:../myLocalDB;AUTO_SERVER=TRUE
spring.datasource.username=myName
# This parameter helped me discover that SpringBoot was not targetting the right table name.
spring.jpa.hibernate.ddl-auto=validate
5) As SpringBoot was telling me it could not find table named index_setup (see camel case converted to _), I had to add this line to the application.properties :
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
6) Now as I got "Entity not managed", I eventually added #EntityScan annotation to the Server main class as many of you advised me to do.
#EntityScan("my.package.Entities")
Please note that #EntityScan should point to the folder containing the entity class not the entity class itself ie #EntityScan("my.package.Entities.IndexSetup") did not work.

Maven with an explicit finalName won't work properly

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}:
company-any-artifact-any-module1
company-any-artifact-any-module2-any-submodule1
company-any-artifact-any-module2-any-submodule2
The pattern for project.build.finalName is ${project.name}-${project.version}:
company-any-artifact-any-module1-1.0.jar
company-any-artifact-any-module2-any-submodule1-2.0.jar
company-any-artifact-any-module2-any-submodule2-3.0.war
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.
Part 1: The Model built is wrong
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.
Part 2: The weirdness begins
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:
The JAR plugin on the any-submodule injects the final name of the project, named ${project.parent.name}-any-submodule.
Thanks for inheritance from the parent projects, and the declaration of <finalName> in your top-most POM project, it inherits <finalName>${project.name}-${project.version}</finalName>.
Maven now tries to interpolate ${project.name} for any-submodule.
This resolves to ${project.parent.name}-any-submodule, due to Part 1.
Maven tries now to interpolate ${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.
Maven now tries to interpolate ${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.
Maven now tries to interpolate ${project.parent.name} on any-submodule, which, again, works correctly and returns ${project.parent.name}-any-module.
Maven now tries to interpolate ${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.
Part 3: Why it works without the sub module
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:
The JAR plugin on the any-module injects the final name of the project, named ${project.parent.name}-any-module.
Thanks for inheritance from the parent project, and the declaration of <finalName> in your top-most POM project, it inherits <finalName>${project.name}-${project.version}</finalName>.
Maven now tries to interpolate ${project.name} for any-module.
This resolves to ${project.parent.name}-any-module, same as before.
Maven tries now to interpolate ${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.
Interpolation has succeeded and stops.
And you don't have any errors.
As I stated in my answer to Difference between project.parent.name and parent.name ans use of finalName in pom.xml
Let's first look at the basics:
as stated in POM Reference:
finalName: This is the name of the bundled project when it is finally built (sans the file extension, for example: my-project-1.0.jar). It defaults to ${artifactId}-${version}.
name: Projects tend to have conversational names, beyond the artifactId.
So these two have different uses.
name is purely informational and mainly used for generated documentation and in the build logs. It is not inherited nor used anywhere else. It is a human readable String and can thus contain any character, i.e. spaces or characters not allowed in filenames. So, this would be valid: <name>My Turbo Project on Speed!</name>. Which is clearly at least a questionable file name for an artifact.
as stated above, finalName is the name of the generated artifact. It is inherited, so it should usually rely on properties. The only two really useful options are the default ${artifactId}-${version} and the versionless ${artifactId}. Everything else leads to confusion (such as a project named foo creating an artifact bar.jar). Actually, My turbo Project! would be valid, since this is a valid filename, but in reality, filenames like that tend to be rather unusable (try adressing a filename containing ! from a bash, for example)
So, as to why the Stackoverflow happens:
name is not inherited
project.parent.name also is not evaluated during interpolation, since the name is one of the few properties which are completey invisible to the children
parent.name actually used to work in older Maven versions, but more due to a bug (also it is deprecated to access properties without the leading project).
a missing property is not interpolated, i.e. stays in the model as is
Therefore, in your effective pom for any-submodule, the value for finalName is (try it with mvn help:effective-pom) still: ${project.parent.name}-any-submodule
So far so bad. Now comes the reason for the StackOverflow
Maven has an addtional feature called late interpolation that evaluates values in plugin parameters when they are actually used. This allows a pluing to use properties that are not part of the model, but are generated by plugins earlier in the lifecycle (this allows, for instance plugins to contribute a git revision to the final name).
So what happens is this:
edit: made the actual reason for the error clearer (see comments):
The finalName for the jar plugin is evaluated:
#Parameter( defaultValue = "${project.build.finalName}", readonly = true )
The PluginParameterExpressionEvaluator kicks in and tries to evaluate the final name (${project.parent.name}-any-submodule, which contains a property expression ${project.parent.name}.
The evaluator asks the model, which in turn returns the name of the parent project, which is: ${project.parent.name}-any-module.
So the evaluator tries to resolve this, which return ${project.parent.name}-any-module (again), since a property is always resolved against the current project, the cycle begins again.
A StackOverflowError is thrown.
How to solve this
Sadly, you can't.
You need to explicitly specify name (as well as artifactId) for every project. There is no workaround.
Then, you could let finalName rely on it. I would however advise against it (see my answer to Difference between project.parent.name and parent.name ans use of finalName in pom.xml)
The problem in changing the final name that way is that the name of the locally build artifact and the one in the repository would differ, so locally your artifact is named any-artifact-any-module-any-submodule.jar, but the artifact name in your repository would be still any-submodule.jar
Suggestion
If you really need to differentiate that fine, change the artifactId instead: <artifactId>artifact-anymodule-anysubmodule</artifactId>.
Don't use dashes for the shortname to differentiate between levels of your structure.
hint: the path of the module can be still anymodule, is does not need to be the actual artifactId of the module!
While we are at it: use the name for what it was intended, to be human readable, so you might consider something more visually appealling (since this is the name that appears in the build log): <name>Artifact :: AnyModule :: AnySubModule</name>.
It is actually very easy to simply create the name entries automatically using a very short groovy script.
You could also write an enforce rule to enforce the naming of the artifactIds
This is issue with attribute inheritance.
Try to use ${parent.name} instead of ${project.parent.name}.
Look at: Project name declared in parent POM isn't expanded in a module filtered web.xml.
---UPDATE---
Benjamin Bentmann (maven committier) said: "In general though, expressions of the form ${project.parent.*} are a bad practice as they rely on a certain build state and do not generally work throughout the POM, giving rise to surprises".
https://issues.apache.org/jira/browse/MNG-5126?jql=text%20~%20%22parent%20name%22
Maybe you should consider is using ${project.parent.*} is a good way.
change pom.xml in company-any-artifact to below and it will work .
<?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>
change pom.xml in submodule to below
<?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>
change submodule pom.xml to below
<?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>
then the output was : company-any-module-any-submodule-1.0-SNAPSHOT
Interesting! I started off cloning the repo and reproducing the error. I would appreciate any leads that can be taken from any of the steps mentioned below that helped me debug the problem -
Maven Life Cycle Phases
The phase where the issue occurred was the package phase of the lifecycle. Meaning mvn package reproduces the issue with your project.
Went through the stack trace lines in the error. Getting to know its the expression evaluation where it's failing -
#Override
public Object evaluate( String expr ) throws ExpressionEvaluationException {
return evaluate( expr, null ); // Line 143
}
It's also not the finalName attribute which was causing it. Since specifying the default value of the same
<finalName>${artifactId}-${version}</finalName>
works fine with the same project configs.
Then tried changing the packaging of the any-submodule as
<packaging>pom</packaging>
and the error went away. Meaning while packaging as jar , war etc the expression evaluation is different and results in an overflow.
Modifying the any-module or the any-submodule pom.xml content I can say with some confidence that it's the project.parent.name that is causing a recursion in evaluating the expression and causing the stack overflow(How? - is something I am still looking for..). Also, changing
<name>${project.parent.name}-${project.artifactId}</name>
to
<name>${parent.name}-${project.artifactId}</name>
works for me in the sense that I do not get an error but the jar generated is of type -
${parent.name}-any-module-any-submodule-1.0-SNAPSHOT.jar and
${parent.name}-any-submodule-1.0-SNAPSHOT respectively with the change.
Looking for the solution according to the requirement, I am seeking a tail to the recursion that you are using.
Note - Still working on finding an appropriate solution to this problem.

Getting property from pom.xml within java

I have a maven project, and in the pom.xml I set properties as such:
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>myArtifact</artifactId>
<name>SomeProject</name>
<packaging>jar</packaging>
<properties>
<some-system-property>1.9.9</some-system-property>
</properties>
<...>
</project>
I want to pull the some-system-property value from within the java code, similar to
String someSystemPropery = System.getProperty("some-system-property");
But, this always returns null. Looking over StackOverflow, most of the answers seem to revolve around enhanced maven plugins which modify the code - something that's a nonstarter in my environment.
Is there a way to just get a property value from a pom.xml within the codebase? Alternatively, can one get the version of a dependency as described in the pom.xml (the 1.9.9 value below):
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
</dependency>
</dependencies>
from code? Either one would solve my needs
Those are Maven properties that apply during the build, not runtime system properties. One typical approach is to use Maven resource filtering to write the value into a properties file in the target directory.
Maven properties and not system properties.
Generally you should set the system property for a maven plugin that is triggering the execution:
surefire for unit tests,
exec for execution,
jetty or similar for starting a web container
There is also properties maven plugin than can set properties:
http://mojo.codehaus.org/properties-maven-plugin/set-system-properties-mojo.html
Property values are accessible anywhere within a POM by using the notation ${X}, where X is the property, not outside. All properties accessible via java.lang.System.getProperties() are available as POM properties, such as ${java.home}, but not the other way around. So for your java code, it will need to scan the pom.xml as a xml parsing use case, but not sure why you want to do it.

Categories