I am using Spring to develop a Java application. I have stored certain properties in a properties file, which is packaged as a JAR. The properties are read in code using #Value annotation. Now when i deploy the JAR i want to supply new values for some of these properties. I know that we can give the new property value as "-Dproperty-name=property-value". But is there a way to give a new property file itself as input which has many properties to be overridden together?
Thanks
Chenbaga
You can have a number of different property files and then load then with a specific system parameter.
#Configuration
#PropertySource(value = {"classpath:/app.properties", "file:/${configLocation}/app.properties"}, ignoreResourceNotFound = true)
public class AppConfig {
Add your default app.properties to /src/resources/app.properties
Then when you start your JVM you can override with
-DconfigLocation=/home/config
If the configLocation is present it will first pick up the default properties, then override then with the ones from the file. If the system parameter is not present it will not file the file and use the defaults.
See http://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/context/annotation/PropertySource.html for more examples
Related
Coming from Play Framework, a handy feature that has helped to organize the application configurations was to use includes (Link) to spilt the various configurations into multiple .conf files as below.
application.conf Content
include "play-http.conf"
include "play-modules.conf"
include "play-i18n.conf"
include "authentication.conf"
include "hbase.conf"
include "custom-caches.conf"
include "custom-filters.conf"
#Any other root level application configurations
Is there an equivalent to this in Spring Boot .properties files?
From Spring 2.4, we can create multiple properties file for each profiles as below.
application-main1.properties
application-sub1.properties
application-sub2.properties
And then in default application.properties file we can group all sub profiles and activate the main profile
spring.profiles.group.main1=sub1,sub2
spring.profiles.active=main1
I am not sure if we can group sub profiles under default profile. You can try out
spring.profiles.group.default=sub1,sub2
This way you don't need to have another file for main profile.
I use yaml configuration files myself but I think that the configuration is mostly similar. You should take a look at the PropertySourcesPlaceholderConfigurer.
I have defined a PropertySourcesPlaceholderConfigurer bean to use a configuration override file located outside of the jar. Anything that is in the override file will be used instead of the default configuration. Anything that is not in the override file is still retrieved from the default configuration file. I think you can create a similar bean to achieve what you are looking for.
Here's my code:
#Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
var properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new FileSystemResource("./application.yaml"));
properties.setIgnoreResourceNotFound(true);
return properties;
}
For my use case, I only needed to define one properties location, but it is also possible to specify multiple locations:
...
properties.setLocations(Resource... locations);
...
My requirement was simply achieved using the spring.config.import (Link).
I created multiple property files such as hbase.properties, custom-caches.properties etc. And then in my application.properties imported those additional property files as below.
spring.config.import=hbase.properties,custom-caches.properties
#Any other properties in the application.properties file
Thanks
I am building an app that mostly provide REST services, nothing fancy. since my data consumed by the app can have multiple languages I thought about using the bundle files.
I created 3 files, one with the default file name and another two with specific languages. The files created using intellij IDE I am using.
I followed this guide https://www.baeldung.com/java-resourcebundle however on each run I am getting:
MissingResourceException: Can't find bundle for base name tp_app_strings, locale en_US
I tried numerous articles but none of them seems to resolve the issue.
One fun fact is that if I am using the #Value("classpath:tp_app_strings.properties") on a 'Resource' field I am able to get a reference to that file, so it spring is able to find it.
Additional thing that I tried was to create a WEB-INF directory and place the files there (read it in some article) but still no positive affect
The project structure is quite straight forward:
Spring boot version 2.2 running tomcat.
Any suggeestions would be highly appriciated
You can load the .properties file to the application context using #PropertySource annotation instead using #Value to load the .properties file to a org.springframework.core.io.Resource instance.
The usage;
#Configuration
#PropertySource("classpath:tp_app_strings.properties")
public class DefaultProperties {
#Value("${property1.name}") // Access properties in the above file here using SpringEL.
private String prop1;
#Value("${property2.name}")
private String prop2;
}
You wouldn't need java.util.ResourceBundle access properties this way. Use different or same class to load other .properties files as well.
Update 1:
In order to have the functionality of java.util.ResourceBundle, you can't just use org.springframework.core.io.Resource class. This class or non of it sub-classes don't provide functions to access properties by its name java.util.ResourceBundle whatsoever.
However, if you want a functionality like java.util.ResourceBundle, you could implement something custom like this using org.springframework.core.io.Resource;
#Configuration
public class PropertyConfig {
#Value("classpath:tp_app_strings.properties")
private Resource defaultProperties;
#Bean("default-lang")
public java.util.Properties getDefaultProperties() throws IOException {
Properties props = new Properties();
props.load(defaultProperties.getInputStream());
return props;
}
}
Make sure to follow correct naming convention when define the property file as java.util.Properties#load(InputStream) expect that.
Now you can #Autowire and use this java.util.Properties bean wherever you want just like with java.util.ResourceBundle using java.util.Properties#getProperty(String) or its overloaded counterpart.
I think it's problem of you properties file naming convention. use underline "_" for specifying locale of file like
filename_[languageCode]_[regionCode]
[languageCode] and [regionCode] are two letters standard code that [regionCode] section is optional
about code abbrivation standard take a look on this question
in your case change file name to tp_app_strings_en_US.properties
I am trying to load external properties files based on the current active profile
and the properties files I have defined as below :
default -> resources/config/application.properties (for dev)
qa -> c:\external-configuration\config\application-qa.properties
prod -> c:\external-configuration\config\application-prod.properties
how spring can be configured to read all these application*.properties from different sources?
I tried to define a PropertySourcesPlaceholderConfigurer as below but spring could resolve the properties value based on the active profile, I always get the default values defined in application.properties
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new FileSystemResource("c:\external-configuration\config\application-qa.properties"),new FileSystemResource("c:\external-configuration\config\application-prod.properties"));
properties.setIgnoreResourceNotFound(false);
return properties;
}
First specify which profile you want to load with spring.profiles.active. Secondly, as it isn't one of the default locations, add spring.config.additional-location to add additional locations to scan. So when you start your line should look like
java -jar <your-jar>.jar --spring.profiles.active=prod --spring.config.additional-location=file:C:/external-configuration/config/
This is also documented in the Spring Boot documentation.
And remove your custom PropertySourcesPlaceholderConfigurer as that isn't needed.
You can also use java annotation for property resource and use server environment (active profile) to identify which properties file to load.
Like, this code snippet here, it will look for property 'envTarget' and in case its not found or null, it will use default 'qa':
#PropertySource({
"classpath:application-${envTarget:qa}.properties"
})
I have the following property files:
application.properties - Base spring config
common.properties - Common config
servicea.properties - Service specific config
password.properties - Password Config
Based on the last three files, I have 3 <Name>Property classes in the following format.
#Configuration
#PropertySource("file:<filepath>")
public class ServiceAProperties {
private final Environment env;
#Autowired
public ServiceAProperties (Environment env) {
this.env = env;
}
public String getTest() {
String test = env.getProperty("application.test"); // Accessible - Not Intended
test = env.getProperty("common.test"); // Accessible - Not Intended
test = env.getProperty("servicea.test"); // Accessible - Intended
test = env.getProperty("password.test"); // Accessible - Not Intended
return env.getProperty("servicea.test");
}
}
For some reason even though I only have the respective Property classes marked with their specific property file paths, they are also picking up paths from other files and adding it to the env.
How can I make sure that I my environment to be generated only from the files I specify?
The spring docs for #PropertySource says:
Annotation providing a convenient and declarative mechanism for adding
a PropertySource to Spring's Environment. To be used in conjunction
with #Configuration classes.
This means that there is only one Spring Environment. When you have multiple classes annotated with this annotation they will always contribute to the same environment because there is only one.
So, to answer your question, in your case the environment will always be filled with data from all classes that have #Configuration and #PropertySource annotations.
In order to fill the environment with data that you specify, you can use profile specific properties. You can separate the data in multiple profiles and choose the profiles that will be activated (and which data will be accessible in the environment).
I am sharing my own solution to this since I was not able to find an acceptable answer.
Using a new ResourcePropertySource("classpath:<location>") allows you to load in multiple individual property files using their respective individual objects.
Once loaded, the configuration can be accessed in the same way as before propertiesObj.getProperty("propKey")
Is there a way to access Spring properties within a logback.xml file?
I know one can import a properties file if you know its location, but I'm using Spring profiles to control where the properties file should be loaded or not.
Is there done kind of connector that asked me to feed Spring data into logback? This would only be at startup; I don't need to be able to do this on the fly.
I'm guessing you do have to import a property file (common property file, non-environment specific one) that will contain the name of the property that you are going to use in the logback.xml, and that you want to optionally override the value of the property for some environment (you need at least one property file containing the name of the property, because you will be using that property in the logback.xml, and you need it to be available to be able to use it).
For the optional environment-override, how about including an additional property file? For example, we use both application.properties and application-${spring.profiles.active}.properties files. Then if we don't need to override the property for some environment, we simply don't include it in the environment specific property file (application-dev.properties, etc.)