Spring Boot 2 : How to load each application.yml files of different modules into a given web or batch runner - spring-boot

I create a Spring Boot Application, and I'm wondering if it is possible to load upmteens application.yml files of different modules.
I have this structure :
myProject
|__ moduleCommons
| |__ application.yml
|__ moduleWeb
| |__ application.yml
| |__ MyProjectWebApplication.java
|__ moduleBatch
|__ application.yml
|__ MyProjectBatchApplication.java
To launch the Spring Boot application of web module, I run the MyProjectWebApplication.java.
To launch a Spring Batch of batch module, I run the MyProjectBatchApplication.java.
Both of them need some commons properties, which are set in application.yml file of commons module (like database configuration, directory paths for upload files, etc.).
And some others properties are specific to each module, so set in application.yml file of web module (like servlet context path, jwt settings) or batch module (like mailer settings).
And, for example, MyProjectWebApplication class into which i would like to load web and commons application.yml files :
package com.myProject.web;
#SpringBootApplication(
scanBasePackages = {"com.myProject.web", "com.myProject.commons"},
exclude = {DataSourceAutoConfiguration.class, LiquibaseAutoConfiguration.class}
)
#EnableJpaRepositories(
basePackages = "com.myProject.commons.datasources.defaut.repository",
repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class
)
#ContextConfiguration(classes = {MyProjectDbConfig.class})
public class MyProjectWebApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyProjectWebApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyProjectWebApplication.class, args);
}
}
But it seems I load only web module application.yml file with this configuration...
Help ! :)

You could make it by setting application.yml in common module and /config/application.yml in the depending modules. If you need a third fallback for test you can use /config/application-test.yml with #ActiveProfiles("test") on every #Test.
That way your set up would be: test properties -> main properties -> commons properties
This works on a build-from-scratch testcase, but I am not sure of the consequences of having main properties under config when running on different environments (https://github.com/spring-projects/spring-boot/issues/9128, i.e, whether commons application.yml is guaranteed to precede other dependencies potential ones)

You can rename your application file in common module to application.yaml or application.propertes, and don't change it in other modules. It's a hack, but it's work. If you use same files in your target and common module, Spring will take only one, which in target module. So you need to add another default file or one more profile.

Related

How can I load additional properties file in a spring boot application packaged as a war?

I have a standard springboot web app. I want to load properties file that's not in the classpath. application.properties is in the classpath and is being read correctly.
I don't have an issue when I'm building a jar. I just put the .properties alongside the jar and it works. But when I package a war, I couldn't get the application to read the properties file .
You put the properties file parallel to application.properties file and load it in a class like this:
#PropertySource("classpath:foo.properties")
public class My class {
#Value( "${some.property}" )
String myProp;
}
You can using ClassPathResource. given Class for loading resources.
This is sample code for you
ClassPathResource resource = new ClassPathResource("/application/context/blabla.yml");
InputStream inputStream = resource.getInputStream();
File file = resource.getFile();
// using inputStream or file
ClassPathResource
You can use spring application.properties to have spring profiles and use the spring profiles to define separate properties for each environment as you like.You can even separate out the spring profiles in to different files like appication-dev.properties etc so that you can have each spring profile in different files.
You can read the properties by using #Configuration annotation :
#Configuration
#EnableConfigurationProperties(TestProperties.class)
public class MySampleConfiguration {
}
Here TestProperties.class is used to map the values from the property file or yaml .
Reference in detail : https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

File can not be loaded from src/test/resources while working on spring boot junit tests

I created one folder src/test/resources and added one file Test.json and trying to access in junit test like this
#RunWith(SpringRunner.class)
#WebMvcTest(value = ReadController.class, secure = false)
public class ReadControllerTest {
#Value("${classpath:Test.json}")
Resource testFile;
#Test
public void test() throws Exception{
File file = testFile.getFile();
System.out.println(file.exists());
}
}
When I run this unit test, it tries to find file in classpath but file is not present there.
How to add this file in classpath?
By Default, spring boot classpath will be pointed to src/main/resources but you are using src/test/resources.
Try to copy the JSON file to src/main/resources.

spring-boot application.properties are not overwriten

I have one class:
package com.example.propertyorder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#SpringBootApplication
#PropertySource(value="file:C:\\TEMP\\property-order.properties", ignoreResourceNotFound = true)
public class PropertyOrderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(PropertyOrderApplication.class, args);
Environment env = (Environment) run.getBean("environment");
System.out.println(env.getProperty("my.value"));
System.out.println(env.getProperty("my.value2"));
}
}
and I have one application.properties file:
my.value=application.properties
and I have my external properties file C:\\TEMP\\property-order.properties:
my.value=property-order.properties
my.value2=gotcha
but the result is always:
application.properties
gotcha
instead of:
property-order.properties
gotcha
So it looks like, that spring-boot's application.properties overrules all.
Is there a way to fix it.
The only solution that i found is to not use the application.properties, but a my-app.properties instead and place that one before my external file in the ProperySource-tree:
#PropertySource(value="classpath:my-app.properties")
#PropertySource(value="file:C:\\TEMP\\property-order.properties", ignoreResourceNotFound = true)
Is there a better way, so that I can stay with the application.properties ?
edit:
added missing value2 to the property file
The actual behavior is conform to the Spring Boot documentation :
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order:
....
14.Application properties outside of your packaged jar (application.properties and YAML variants).
15.Application properties packaged inside your jar (application.properties and YAML variants).
16.#PropertySource annotations on your #Configuration classes.
The application.properties (inside or outside the jar) have a higher priority (14 and 15 respectively) than any #PropertySource annotations added in #Configuration class (16).
Is there a better way, so that I can stay with the
application.properties ?
Of course you can use any of the 13 higher priority ways :
1.Devtools global settings properties on your home directory
(~/.spring-boot-devtools.properties when devtools is active).
2.#TestPropertySource annotations on your tests.
3.#SpringBootTest#properties annotation attribute on your tests. Command
line arguments.
4.Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an
environment variable or system property).
5.ServletConfig init parameters.
6.ServletContext init parameters.
7.JNDI attributes from java:comp/env.
8.Java System properties (System.getProperties()).
9.OS environment variables.
10.A RandomValuePropertySource that has properties only in random.*.
11.Profile-specific application properties outside of your packaged jar
(application-{profile}.properties and YAML variants).
12.Profile-specific application properties packaged inside your jar
(application-{profile}.properties and YAML variants).
13.Application properties outside of your packaged jar
(application.properties and YAML variants).
For example renaming property-order.properties to application-order.properties to make it a Spring Boot profile properties and run your application with order as active profile would give it a higher priority and so it should be enough :
#SpringBootApplication
public class PropertyOrderApplication {...}
If you want to override the property defined int the application.properties, you can use the below approach.
#PropertySources({
#PropertySource(value = "classpath:application.properties"),
#PropertySource(value = "file:/user/home/external.properties", ignoreResourceNotFound = true)
})
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
}

Spring boot on Tomcat with external configuration

I can't find an answer to this question on stackoverflow hence im asking here so I could get some ideas.
I have a Spring Boot application that I have deployed as a war package on Tomcat 8. I followed this guide Create a deployable war file which seems to work just fine.
However the issue I am currently having is being able to externalize the configuration so I can manage the configuration as puppet templates.
In the project what I have is,
src/main/resources
-- config/application.yml
-- config/application.dev.yml
-- config/application.prod.yml
-- logback-spring.yml
So how can I possibly load config/application.dev.yml and config/application.prod.yml externally and still keep config/application.yml ? (contains default properties including spring.application.name)
I have read that the configuration is load in this order,
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
Hence I tried to load the configuration files from /opt/apache-tomcat/lib to no avail.
What worked so far
Loading via export CATALINA_OPTS="-Dspring.config.location=/opt/apache-tomcat/lib/application.dev.yml"
however what I would like to know is,
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
And is there a better method to achieve this ?
You are correct about load order. According to Spring boot documentation
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).
[Note]
You can also use YAML ('.yml') files as an alternative to '.properties'.
This means that if you place your application.yml file to /opt/apache-tomcat/lib or /opt/apache-tomcat/lib/config it will get loaded.
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
However, if you place application.dev.yml to that path, it will not be loaded because application.dev.yml is not filename Spring is looking for. If you want Spring to read that file as well, you need to give it as option
--spring.config.name=application.dev or -Dspring.config.name=application.dev.
But I do not suggest this method.
And is there a better method to achieve this ?
Yes. Use Spring profile-specific properties. You can rename your files from application.dev.yml to application-dev.yml, and give -Dspring.profiles.active=dev option. Spring will read both application-dev.yml and application.yml files, and profile specific configuration will overwrite default configuration.
I would suggest adding -Dspring.profiles.active=dev (or prod) to CATALINA_OPTS on each corresponding server/tomcat instance.
I have finally simplified solution for reading custom properties from external location i.e outside of the spring boot project. Please refer to below steps.
Note: This Solution created and executed windows.Few commands and folders naming convention may vary if you are deploying application on other operating system like Linux..etc.
1. Create a folder in suitable drive.
eg: D:/boot-ext-config
2. Create a .properties file in above created folder with relevant property key/values and name it as you wish.I created dev.properties for testing purpose.
eg :D:/boot-ext-config/dev.properties
sample values:
dev.hostname=www.example.com
3. Create a java class in your application as below
------------------------------------------------------
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
#PropertySource("classpath:dev.properties")
#ConfigurationProperties("dev")
public class ConfigProperties {
private String hostname;
//setters and getters
}
--------------------------------------------
4. Add #EnableConfigurationProperties(ConfigProperties.class) to SpringBootApplication as below
--------------------------------------------
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties.class)
public class RestClientApplication {
public static void main(String[] args) {
SpringApplication.run(RestClientApplication.class, args);
}
}
---------------------------------------------------------
5. In Controller classes we can inject the instance using #Autowired and fetch properties
#Autowired
private ConfigProperties configProperties;
and access properties using getter method
System.out.println("**********hostName******+configProperties.getHostName());
Build your spring boot maven project and run the below command to start application.
-> set SPRING_CONFIG_LOCATION=<path to your properties file>
->java -jar app-name.jar

Spring Boot - deploy .properties file to a folder different than 'WEB-INF/classes'

I'm trying to convert a traditional Tomcat Spring MVC webapp to Spring Boot. The new application should still use .war deployment.
For various reasons I have the obligatory requirement that the application.properties file resides inside a WEB-INF/conf folder in the deployed app and NOT inside the WEB-INF/classes folder where Spring Boot puts it by default.
In the original webapp I could put the application.properties file inside the src/main/webapp/WEB-INF/conf folder (so they get copied to WEB-INF/conf in the deployed application) and then use it like this:
<context:property-placeholder location="/WEB-INF/conf/application.properties"/>
What is the Spring Boot way to refer to this location?
I tried adding each of the following:
spring.config.location=WEB-INF/conf/application.properties
but my application.properties file still doesn't get loaded.
What finally worked was the following #PropertySource annotation.
#SpringBootApplication
#PropertySource(value = {"WEB-INF/conf/application.properties"})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
It seems that not specifying classpath: or file: at the beginning of a path makes it use a path relative to the webapp.
I'm still not sure as to why specifying
spring.config.location=WEB-INF/conf/application.properties
didn't have the same effect.

Resources