Reading properties values from Resource and from System Variables in Spring 4 - java

I'm trying to configure my Spring 4 application to be able to read properties values using the #Value annotation.
I need to be able to read values from .properties files as well as from system properties.
For reading from .properties file I am using #Value("${my.propery.name}") syntax along with #PropertySource("classpath:my.properties").
For reading from system properties I am using the #Value("#{systemProperties['myVariableName']}") syntax.
In my ApplicationConfig.java class, which is the main application configuration file I have both:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
and:
#Bean
public static PropertyPlaceholderConfigurer propertyConfigurer() {
return new PropertyPlaceholderConfigurer();
}
When both Configurers are specified, I experience an issue in which sometimes the values are resolved and sometimes not (and I get a Could not resolve placeholder exception).
When only PropertyPlaceholderConfigurer is configured, I am able to read only from .properties file.
When only PropertySourcesPlaceholderConfigurer is configured, I am able to read only from system properties.
What is the best configuration for reading from both resources?

UPDATE:
I managed to resolve the issue by registering only the PropertyPlaceholderConfigurer bean and specifying a the .properties file path as a Resource in it:
#Bean
public static PropertyPlaceholderConfigurer propertyConfigurer() {
PropertyPlaceholderConfigurer ppc= new PropertyPlaceholderConfigurer();
ppc.setSearchSystemEnvironment(true);
final Resource resource = new ClassPathResource("my.properties");
ppc.setLocation(resource);
return ppc;
}

Related

Replace environment variables in Spring properties file other than application.properties

Spring Boot will automatically resolve any ${ENV} placeholders in application.properties files, with the respective environment variable.
However such resolution will not happen when I provide a quartz.properties through a PropertiesFactoryBean file for Quartz configuration.
#Bean
public Properties getQuartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
Is there any Spring way of replacing these environment variables in the property file without utilising an external library?
You can declare a new class to provide the properties (annotated with #Configuration) and also mention the #PropertySource
#Configuration
#PropertySource("classpath:quartz.properties")
public class QuartzConfig {
//...
}
In this way your spring boot application can read as many properties file as you want.

#Value not working in Spring #Configuration

Need help, where is the issue?
I have a configuration class which is loading properties as
WebConfig.java
#Configuration
#PropertySource(value={"classpath:application.properties"})
class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
I have another configuration class where I am trying to use the properties as
MyServerConfig.java
#Configuration
class MyServerConfig {
#Value("${server.url}")
private String url;
...
}
application.properties
server.url=http://localhost:8080/test/abc
But getting:
java.lang.IllegalArgumentException: Could not resolve placeholder 'server.url'.
Don't know what is missing here? Any thoughts?
Use the #PropertyScan annotation in the class where a certain property will be used:
#Configuration
#PropertyScan("classpath:application.properties")
class MyServerConfig {
#Value( "${server.url}" )
private String url;
}
For getting the values for your #Value variables, the application.properties is not needed to be configured in any special way because this file is always scanned. So remove the #PropertySource annotation and PropertySourcesPlaceholderConfigurer bean.
These are used if you want to add other .properties files (e.g. constants.properties, db-config.properties).
So just remove those and try to run your application again
Very important:
Make sure you scan the class that uses the #Value annotation (If your BootApplication is in some package instead of the 'main' package, add the proper #SpringBootApplication(scanBasePackages = { "com.my.project" }) annotation).
Make sure your application.properties is on your classpath.
Bonus If you are using spring profiles (e.g: prod, dev), you can also have application-prod.properties and application-dev.properties files that will be scanned and included depending on which profile you are running.

How to use YAML files using Spring Framework?

My application is spring based, and I would like to load yml file from a centralized server, but the unable to do so.
My yml file is like this:
spring:
application:
name: signed-in-web
cloud:
config:
uri: ${server.url}
health:
config:
enabled: false
note: server.url is defined in vm options.
I had checked from rest client that properties are valid and available on the server.
Then I tried to copy it on a property file like this:
application.properties
LOCATION_SEARCH=${properties.app.api-key.location-search}
I had java configuration class like this:
#Profile("!test")
#ComponentScan(basePackages = { "com.company.frontend.*" })
#Configuration
#PropertySource("classpath:properties/qa_env.properties")
public class propertiesConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("bootstrap.yml"));
propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
return propertySourcesPlaceholderConfigurer;
}
}
I supposed that this will load the configuration like this:
#Value("${LOCATION_SEARCH}")
public static String LOCATION_SEARCH;
but I am getting null. Somehow I am not been able to find where the problem is. Will
If you're using Spring Boot you just need to call it application-qa.yaml and enable the qa profile. See also spring.config and spring.profiles in Common Application Properties.
Otherwise you just need to declare the yaml file in #PropertySource or #PropertySources.
Only if you don't want the properties in the global environment do you start needing to use PropertiesConfigurationFactory and YamlPropertiesFactoryBean to manually bind them.

Spring Boot Externalizing properties not working

I have looked at the below threads and followed things given there. Still my property override is not happening
Spring Boot - Externalized properties
Profile Specific Property Enablement
Spring Boot External Config
I am on Tomcat 8.0.33 and Spring boot starter web and got this in my setenv.sh
export JAVA_OPTS="$JAVA_OPTS -Dlog.level=INFO -Dspring.config.location=file:/opt/jboss/apache-tomcat-8.0.33/overrides/ -Dspring.profiles.active=dev"
And in the overrides folder I got 2 files
1) application.properties
2) application-dev.properties
The application.properties has a single entry in it
spring.profiles.active=dev
I see that the proper log.level is fed to my code which means this command is working. Its just that I am clueless as to why my override is not happening as expected
I don't have any `PropertyPlaceholderConfigurer code in my workspace. I am not even sure if I need 1
I don't use this method to externalise properties. First, I'll try a suggestion for your method and then I'll show you what I'm using.
The suggestion for your method is to use file:/// instead of file:/ as with Spring I found that when not passing the three slashes after the colon it didn't recognise the property.
I've created a sample project for you, available here with instructions.
Now for the method I use.
I define a Configuration file for each profile and I keep the application.properties file under src/main/resources.
Then I use the #Profile and #PropertySource annotations on each configuration file.
For example:
#Configuration
#Profile("dev")
#PropertySource("file:///${user.home}/.devopsbuddy/application-dev.properties")
public class DevelopmentConfig {
#Bean
public EmailService emailService() {
return new MockEmailService();
}
#Bean
public ServletRegistrationBean h2ConsoleServletRegistration() {
ServletRegistrationBean bean = new ServletRegistrationBean(new WebServlet());
bean.addUrlMappings("/console/*");
return bean;
}
}
And
#Configuration
#Profile("prod")
#PropertySource("file:///${user.home}/.devopsbuddy/application-prod.properties")
public class ProductionConfig {
#Bean
public EmailService emailService() {
return new SmtpEmailService();
}
}
I have also got a Configuration file that is valid for all profiles, which I call ApplicationConfig, as follows:
#Configuration
#EnableJpaRepositories(basePackages = "com.devopsbuddy.backend.persistence.repositories")
#EntityScan(basePackages = "com.devopsbuddy.backend.persistence.domain.backend")
#EnableTransactionManagement
#PropertySource("file:///${user.home}/.devopsbuddy/application-common.properties")
public class ApplicationConfig {
}
My src/main/resources/application.properties file looks like the following:
spring.profiles.active=dev
default.to.address=me#example.com
token.expiration.length.minutes=120
Of course I could externalise the spring.profile.active property by passing it as a system property but for my case and for now it's fine.
When running the application, if I pass the "dev" profile, Spring will load all properties and Beans defined in the DevelopmentConfig class plus all those in ApplicationConfig. If I pass "prod", the ProductionConfig and ApplicationConfig properties will be loaded instead.
I'm completing a course on how to create a Spring Boot website with Security, Email, Data JPA, Amazon Web Services, Stripe and much more. If you want, you can register your interest here and you will get notified when the course is open for enrolment.

Java Main class overriding properties file at runtime

I am using Apache Maven and Spring. In the src/main/resources folder I have a properties file. These property values can have different values.
I am using PropertyPlaceholderConfigurer.
#Configuration
public class ResourceConfig {
#Bean
public PropertyPlaceholderConfigurer properties( ) {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
ppc.setLocations(new ClassPathResource[] {new ClassPathResource("propertiesFile")});
return ppc;
}
}
I replace these values at runtime:
#Configuration
public class DataSourceConfig {
#Value("${jdbc.url}")
private String jdbcUrlDefault;
}
This is just a sample. I have a main method:
public static void main(String[] args) {
// accept a properties file and replace those values defined in DataSourceConfig class
}
When Apache Maven builds the application the properties file will be on the classpath. The properties file are used during the unit testing. I want to some how replace the properties with a new properties file before the main program is launched for production.
I have seen some example of Properties.load(), but I don't want to do this. I want to accept a properties file through the main program that gets replaced, so the Spring side starts the PropertyPlaceholderConfigurer.
How can this be achieved?
you can place your test properties files in src/test/resources/. In test classes, it will use properties file from this location.
properties file placed here above location will not included in your classpath in final build.
use src/main/resources to place resource files that you want in main program

Categories