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

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;

Related

Disable spring-context-indexer by profile

I want to enable Spring Context Indexer on a project but I am having issues with Swagger3 (check here and here).
I understand the limitations and would like to enable it, at least, at the DEV profile where we do not need Swagger running.
My goal is to disable indexing on PROD environment. From docs:
you can fallback to a regular classpath arrangement (as though no index was present at all) by setting spring.index.ignore to true, either as a system property or in a spring.properties file at the root of the classpath.
My first approach (without success) was setting an env var (Windows 10) with the following:
SPRING_APPLICATION_JSON={"spring":{"index":{"ignore":true}}}
If I create a spring.properties file and set the value accordingly it works. But I can't figure how to use different properties for each profile, I imagined it was something like the application.properties file but I was wrong.
How can I achieve that?
EDIT:
Just to be clear, this config (spring.index.ignore) will just work if inside a spring.properties file, not an application.properties one. (Just double-checked before this edit)
Actually I have one application-prod.properties and one application-dev.properties.
I don't know the difference between spring.properties and application.properties but the first one doesn't seem to work with multiple profiles as the later.
Edit 2:
Just went through org.springframework.context.index.CandidateComponentsIndexLoader and it uses SpringProperties.getFlag(IGNORE_INDEX) to read the value.
SpringProperties class is clear about the file it uses:
Reads a spring.properties file from the root of the Spring library classpath, and also allows for programmatically setting properties through setProperty. When checking a property, local entries are being checked first, then falling back to JVM-level system properties through a System.getProperty check.
I think I will need to pass a property to Java runner during initialization. Will research a little bit more about it.
you can use different application.properties file as follow:
add the following files to the resource folder:
application.properties
spring.profiles.active=dev #place profile name you want to use
application-dev.properties
#dev properties
...
You can create many application-env.properties you may wish
application-env.properties
#env properties
...
Spring Boot supports profile-specific properties files. You have to name these files with the following format: application-{profile}.properties. You can activate a profile via JVM system parameter: -Dspring.profiles.active=dev. You can read more about this subject here: https://www.baeldung.com/spring-profiles

Overriden application.yml in multi-module Spring Boot app

There is a Spring Boot 2 app with such a structure:
parent-module
module-1
src
main
java
resources
- application.yml
module-2
src
main
java
resources
- application.yml
Also, module-1 depends on module-2, specified in pom.xml dependencies section.
The problem is that when I specify some properties in module-2's application.yml - they are not visible in main module-1's components (via #Value annotation).
As was answered here seems like module-1's application.yml overrides module-2's application.yml. There is a workaround - if I use name application.yaml in module-2 everything works fine, but I'm going to add more modules and, finally, it's dirty hack.
What I'm doing wrong? Should such an hierarchy of property files specified somehow?
I will be happy to provide more details if it's needed.
Thank you!
Spring Boot is a runtime framework. I understand that your modules are not spring-boot applications by themselves (you can't make a dependency on a spring boot application packaged with spring boot maven plugin, because it produces an artifact that is not really a JAR from the Java's standpoint although it does have *.jar extension).
If so, they're probably regular jars. So you should have a "special" module that assembles the application. This special module lists both 'module1' and 'module2' in <dependency> section and should contain a definition of spring-boot-maven-plugin in its build section (assuming you're using maven). But if so you shouldn't really have more than one application.yml - it will be misleading. Instead, put the application.yml to the src/main/resources of that "special" module.
If you really have to for whatever reason work with multiple application.yaml files, make sure you've read this thread
I know, this is already a well-aged post.
I just came accross the same issue and the best solution I found was to import the module-specific configurations with the spring.config.import directive as described here.
In this case you still have your module specific configuration in property or yaml files within that specific module and do not have too much unwanted dependencies in your project setup.
application.yml is, as the name indicates, an application-level file, not a module-level file.
It is the build script that assembles the final application, e.g. the .war file, that needs to include a application.yml file, if any.
If modules need properties, and cannot rely on the defaults, e.g. using the : syntax in #Value("${prop.name:default}"), they need to provide a module-level property file using #PropertySource("classpath:/path/to/module-2.properties").
Note: By default, #PropertySource doesn't load YAML files (see official documentation), but Spring Boot can be enhanced to support it. See #PropertySource with YAML Files in Spring Boot | Bealdung.
Alternative: Have the application-level build script (the one building the .war file) merge multiple module-level build scripts into a unified application.yml file.

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

Spring Initialized Properties using propertysourcesplaceholderconfigurer referred from another project

I have a mvn project packaged as applicationConfig.jar that contains commonly shared properties across different other projects (WAR's(web application eg: application.war) and JAR's(batch eg: applicationBatch.jar)).
I have used propertysourcesplaceholderconfigurer using annotation to initialize these properties in my applicationConfig.jar
This applicationConfig.jar is now added as dependency in pom.xml's for application.war & applicationBatch.jar
1) The java code in application.war is able to access properties initialized by code executed in applicationConfig.jar on server startup property. No issues here.
2) The applicationBatch.jar which is run from command line on linux machine, is unable to access properties. It appears like the properties initialization code is never executed, when the applicationBatch.jar is run.
Can anyone please help how can i ensure, code sitting in Jar file (applicationConfig.jar responsible for initializing properties using propertysourcesplaceholderconfigurer), is executed, when a batch jar (applicationBatch.jar) is run from command line.
Code Snippet Below:
applicationConfig.jar:
[Attachment ][1]
[Property referenced using annotation and property from properties file][2]
Spring will manage InternalConfig if you import it using #Import or have configured component scan with a base package parent of the InternalConfig one. applicationBatch seems to miss one of this method.
Could you check that ?

How to use multiple configuration files for Log4j2?

I am writing Java code that tests a Java library. The library includes its own Log4j2 configuration as part of the distribution.
I would like to use Log4j2 in my test code without modifying the library's configuration.
Is there a way to have a separate Log4j2 configuration for my test code?
This is all running as command-line Java, no servers or web involvement at all.
EDIT
What I want is to be able to configure loggers, appenders, etc for the test code to use, and at the same time have the library code use its own separate configuration file for its logging.
The idea is to use Log4j2 in my test code, but without having to change the library's configuration file. Since the library configuration file is part of the library's distribution, I don't want to change it for testing.
This may be helpful:
Log4j2 will first look for log4j2-test.xml in the classpath
if that file is not found, it will look for log4j2.xml in the classpath
So one option is to copy the library's configuration (log4j2.xml) to log4j2-test.xml and add your own configuration to log4j2-test.xml.
Furthermore, Log4j2 supports XInclude in XML configuration, so you could use that feature to avoid duplicating the library's configuration in your log4j2-test.xml.
Log4j2 supports "Composite Configuration" which exactly matches your requirement. All you need to do is provide path to multiple files in log4j.configurationFile property. This can be passed from command line or added to log4j2.component.properties file in your application.
References
https://logging.apache.org/log4j/2.x/manual/configuration.html#CompositeConfiguration
https://logging.apache.org/log4j/2.x/manual/configuration.html#SystemProperties
There are two step you can try to solve for your issue
Create your own configuration file with your custom name(eg: xyz.properties/.xml)
You must add the following line to your java runtime command
cmd> java -Dlog4j.configuration=location/xyz.properties
If you use diffent name for configuration rather log4j.properties/.xml file you need to configure that file at runtime by above command for more info have a look here..
Correct format for using an alternate XML file to log4j2.xml:
java -Dlog4j.configurationFile=./location/log4j2-custom.xml
Assuming ./location/log4j2-custom.xml exists and is the new XML to replace log4j2.xml in this run
See:
https://github.com/kamalcph/Log4j2Examples/blob/master/src/main/java/in/co/nmsworks/log4j2/examples/CompositeConfigurationExample.java
Referencing https://logging.apache.org/log4j/2.x/manual/configuration.html states that you can add multiple comma separated files under log4j2.configurationFile property.
To use multiple configuration files, depending on the environment you must set the.
for example:
if (env.equals("DEV")) {
setConfigFile("log4j2-dev.xml");
}
public static void setConfigFile(String logConfigFile) {
File file = new File(logConfigFile);
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
context.setConfigLocation(file.toURI());
}
first configure application.yaml file
spring:
profiles:
active: dev
---
spring:
message: running in the dev profile //just to output the message in the log
profiles: dev
logging.config: classpath:log4j2-dev.xml
---
spring:
profiles: prod
logging.config: classpath:log4j2-prod.xml
and create these similar files in your classpath
* log4j2-dev.xml
* log4j2-prod.xml

Categories