Spring Boot Property file precedence - java

In my Spring Boot program I'm getting a failure due to a bad property value on load. In particular, it uses the DB2 hibernate dialect but it's not defined in the property file I thought I was using.
Assuming no annotations, where does Spring look for the properties file? Yes I know it normally resides in src/main/resources/application.properties
What if I have a property in my test cases; does it ignore the one in main and use the one in test? Or does it start with the main version and let the test one override the main where it applies?
Does the application profile affect the property file used? Some people use the same application.properties file name in both main and test.
If I do have a TestSource annotation with a class path location, does it still augment it with something somewhere else?
Finally, how can I get Spring to tell me everywhere it looked for properties and not just one of them?

#Woodsman
It's possible to use many settings in each profile/environment in spring application on src/main/resources/ folder
application-dev.properties
application-local.properties
application-onlytests.properties
application-prd.properties
application.properties
the name after hyphen will be the name profile got by spring
Ex: application-{myenviroment}.properties
When you start spring application will be used application.properties by default.
You can tell spring to use an specific properties passing the environment in one of ways below:
Putting spring.profiles.active=prd inside application.properties file
Passing by parameters when start spring app --spring.profiles.active=local
Running your jar on command line java -jar myjar.jar --spring.profiles.active=dev
Setting an environment var in your machine/docker/container SET SPRING_ACTIVES_PROFILE=local
There are other ways using annotations on beans, passing jvm arguments and others
If you need run your tests in a specific configuration ( vars, database, settings ), It's possible to pass which .properties will be used
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#SpringBootTest
#TestPropertySource(locations = "classpath:application-onlytests.properties")
public class RunTest_from_onlytests_properties {
#Autowired
private MockMvc mockMvc;
#Test // org.junit.jupiter.api.Test
public void test() throws Exception{
// ...
}
}

Related

Spring Boot refuses to pick up application test properties [duplicate]

I have a spring boot application that I can package in a war that I want to deploy to different environments. To automate this deployment it'd be easier to have the configuration file externalized.
Currently everything works fine with a application.properties file in src/main/resources. Then I use ´mvn install´ to build a war deployable to tomcat.
But I would like to use a .yml file that does not need to be present on mvn install but that would be read from during deployment of the war and is in the same or a directory relative to my war.
24. externalized configuration shows where spring boot will look for files and 72.3 Change the location of external properties of an application gives more detail on how to configure this but I just do not understand how to translate this to my code.
My application class looks like this:
package be.ugent.lca;
Updated below
Do I need to add a #PropertySource to this file? How would I refer to a certain relative path?
I feel like it's probably documented in there as most spring boot documentation but I just don't understand how they mean me to do this.
EDIT
Not sure if this should be a separate issue but I think it's still related.
Upon setting the os variable the error of yaml file not found went away. Yet I still get the same error again as when I had no application .properties or .yml file.
Application now looks like this:
#Configuration
**#PropertySource("file:${application_home}/application.yml")**
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
The application_home OS variable
$ echo $application_home
C:\Masterproef\clones\la15-lca-web\rest-service\target
My application.yml file(part it complains about):
sherpa:
package:
base: be.ugent.lca
Error upon java -jar *.war
All variations upon:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'sherpa.package.base' in string value "${sherpa.package.base}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:808)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1027)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 142 more
Using external properties files
The answer lies in the Spring Boot Docs, I'll try to break it down for you.
First of all, no you should not use #PropertySource when working with Yaml configuration, as mentioned here under the Yaml shortcomings :
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
So, how to load propery files? That is explained here Application Property Files
One is loaded for you: application.yml , place it in one of the directories as mentioned in the link above. This is great for your general configuration.
Now for your environment specific configuration (and stuff like passwords) you want to use external property files, how to do that is also explained in that section :
If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).
So you use the spring.config.location environment property.
Imagine you have an external config file: application-external.yml in the conf/ dir under your home directory, just add it like this:
-Dspring.config.location=file:${home}/conf/application-external.yml as a startup parameter of your JVM.
If you have multiple files, just seperate them with a comma. Note that you can easily use external properties like this to overwrite properties, not just add them.
I would advice to test this by getting your application to work with just your internal application.yml file , and then overwrite a (test) property in your external properties file and log the value of it somewhere.
Bind Yaml properties to objects
When working with Yaml properties I usually load them with #ConfigurationProperties, which is great when working with for example lists or a more complex property structure. (Which is why you should use Yaml properties, for straightforward properties you are maybe better of using regular property files). Read this for more information: Type-Safe Configuration properties
Extra: loading these properties in IntelliJ, Maven and JUnit tests
Sometimes you want to load these properties in your maven builds or when performing tests. Or just for local development with your IDE
If you use IntelliJ for development you can easily add this by adding it to your Tomcat Run Configuration : "Run" -> "Edit Configurations" , select your run configuration under "Tomcat Server" , check the Server tab and add it under "VM Options".
To use external configuration files in your Maven build : configure the maven surefire plugin like this in your pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.config.location=file:${home}/conf/application-external.yml</argLine>
</configuration>
</plugin>
When running JUnit tests in IntelliJ:
Run → Edit Configurations
Defaults → JUnit
add VM Options -> -ea -Dspring.config.location=file:${home}/conf/application-external.yml
Yes, you need to use #PropertySource as shown below.
The important point here is that you need to provide the application_home property (or choose any other name) as OS environment variable or System property or you can pass as a command line argument while launching Spring boot. This property tells where the configuration file (.properties or .yaml) is exactly located (example: /usr/local/my_project/ etc..)
#Configuration
#PropertySource("file:${application_home}config.properties")//or specify yaml file
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
There is a very simple way to achieve this.
Inside your original application.properties file you can just specify the following line:
spring.config.import=file:Directory_To_The_File/Property_Name.properties
It will automatically sync all the properties from the external property file.
Now lets say that you have a situation where you need to get properties from multiple property files. In that case, you can mention the same line in the external property file which in turn will take the remaining properties from the second property file and so on.
Consider the following example.
application.properties:
spring.config.import=file:Resources/Custom1.properties
Custom1.properties:
server.port=8090
.
.
.
spring.config.import=file:Resources/Custom2.properties
One of the easiest way to use externalized property file using system environment variable is, in application.properties file you can use following syntax:
spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PORT}
Now, declare above used environment variables,
export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"
This way you can use different value for same variable in different environments.
Use below code in your boot class:
#PropertySource({"classpath:omnicell-health.properties"})
use below code in your controller:
#Autowired
private Environment env;

Spring Cloud Config- Customizing the Bootstrap context to read config URI from another property Source

So I have the following use case-
I need to customize the bootstrap context to read the uri where the config Server lies from a particular file and then inject that uri into the property spring.cloud.config.uri to get properties from the configServer.
I have been looking at documentation on Spring cloud commons and have experimented around customizing Bootstrap property sources. This, unfortunately does not work, because this acts as another source of properties for the application context, not a source of properties for the bootstrap context( bootstrap.yml or properties).
The only solution I have so far, which is probably not the most elegant one is to set the uri property as a system property before we run the spring Application.
So, something like the following.
public static void main(String args[]) {
* Read properties from file and set as system properties*
System.setProperty("uri","http://localhost:8097");
SpringApplication.run(Application.class,args);
}
And then reference the above property in bootstrap.properties as-
spring.cloud.config.uri=${uri}
This works and is okay,but i would like to do this still in a more Spring Friendly way.
Thank you in advance.
Unfortunately this cant be done.
You can change the location of bootstrap.yml/properties using config
spring.cloud.bootstrap.location
But , you cannot add a bean to read properties which will be part of the Bootstrap context, as the Bootstrap Context will be the parent of the most senior ancestor created by the user himself.
I think you can use run arg arguments and give a defult value like this:
spring.cloud.config.uri=${config.uri:http://localhost:8097}
java jar xxx.jar --config.uri=http://localhost:8099

Setting environment for property location

I am configuring my spring application using property file. But I have to make a switch between development and production property file. Current I have this code snippet
#Configuration
#PropertySource(value = "classpath:config/simulator.properties", ignoreResourceNotFound = false)
public class AppConfiguration
But I would like something with value = "classpath:${env:local}/simulator.properties"
which means if I not set the environment variable env than it must point to local/simulator.properties else if environment env variable points to production the location must be production/simulator.properties.
So, the local is the fallback environment.
Is there any way to achieve this. I do not want to use profiles, it must be controlled by an environment variable
I do not want to set a -D option for profiles
Thanks
Johan
You can use multiple #PropertySource annotations, if the first file and the second file are found, and the keys in both the file matches then the later one will be taken. Please have a look at here
#PropertySource(value="classpath:local/simulator.properties",ignoreResourceNotFound=true)
#PropertySource(value="classpath:${env.production}/simulator.properties",ignoreResourceNotFound=true)
Spring automatically look at from system root path if we have windows then c:/ will be automatically understood by spring and if we have Linux machine then / will be root.
So here we don't need to set classpath or -D tags.
Actually in my Tests (Unit Test 4) I used annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:com/myTest/MyTestApplicationContext.xml"})
This allows you to have a separate Application context for each test and separate one for development environment. Then of course in each of your Application contexts you can configure properties to be read from different place. Works great for me

Excluding PropertySource from scanning in SpringBoot WebMvcTest or other application context for WebMvcTest

I develop web app with Spring Boot. I have problem with unit test for web layer.
For these tests I'm using annotation #WebMvcTest. Problem is that my main configuration class contains #PropertySource with java arg, which contains path to external properties file, and when I start my unit web test, error is occured that this java arg can't be parsed(of course I can add this java arg to run configuration of my test, but my web unit tests don't need this file).
My main config class:
#SpringBootApplication
#PropertySource(value = {"${pathto.configfile}"})
#EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My first solution was to create separate configuration class with "!test" profile, and relocate #PropertySource("..") to it. And to my unit test add #ActiveProfiles("test")
My second Configuration class:
#Configuration
#Profile({"!test"})
#PropertySource(value = {"${pathto.configfile}"})
public class PropertyConfiguration {
}
For unit web test this solution works very good. But new problem appears in starting of my web app. In my external properties file I have
property spring.profiles.active. To this property I assign or db or file value. Depending on its value, apropriate implementation of Repository is created and injected to my service layer. When value is db, app starts good,
but when value is file error is being thrown: NoSuchBeanDefinitionException.
When I come back to my previous version(without second configuration file), app starts good in both cases(but not web unit tests)
So, explain please, why when value of spring.profiles.active=db, app starts good, but when spring.profiles.active=file- failed.And how I can solve my task?
I attempted to find how I can add other application context to my web unit tests, but I didn't find.
Any idea?:)
For my database repositories I'm using Spring Data JPA, so I don't create implementation of these repositories, but I create implementations of my file
repositories, and my implementations had #Profile("file"). After deleting this annotation from implementations, it leaved only on interfaces. I don't know why one config class worked, but two classes didn't. But problem is solved)

Loading environment specific properties file in spring

I am working on a spring-boot application, I need your assistance on below scenario.
I have properties files for each environment something like application-dev.properties, application-prod.properties etc. Is there way that my application can load environment specific properties file by using spring #Profile annotation.
Please help.
You don't need to use #Profiles annotation at all. Just use
#ConfigurationProperties(locations = "classpath:myapp-${environment.type}.properties")
and define environment type via system property. E.g. via command line -Denvironment.type=dev.
#Profile is not for loading environment specific properties file. It is for specifying profile of a bean. For example,
#Profile("dev")
#Component
class Foo {
}
It means the bean of Foo is only available when the active profiles include dev. Or the opposite #Profile("!dev"), which means the bean is available when dev is not an active profile.
So for loading environment specific properties file, since it is spring-boot, you can just specify the active profiles. There are several ways to specify the active profiles.
Environment variable: SPRING_PROFILES_ACTIVE=dev,prod
command line argument: java -jar app.jar --spring.profiles.active=dev,prod
Programmatically : SpringApplicationBuilder(...).properties("spring.profiles.active=dev,prod).run(...)
Default application.properties or yaml: spring.profiles.active:dev, prod

Categories