How to use YAML files using Spring Framework? - java

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.

Related

Is it possible to have a default application.yml in a custom Spring boot starter?

I am facing an issue with my custom spring boot starter and a spring boot app consumer that uses as a dependency. I have in both an application.yml but it seems that the configuration I am looking for it is only pressent if it is defined in the consumer.
My config in the starter is like this:
#Getter
#Setter
#Configuration
#ConfigurationProperties(prefix = "security")
public class StarterSecurityConfig {
private boolean jwtEnabled;
private String[] unsecuredPaths;
private String[] securedPaths;
}
And I have this bean defined in the AutoConfiguration class:
#Bean
public StarterSecurityConfig starterSecurityConfig() {
return new StarterSecurityConfig();
}
It is perfectly retrieved by the consumer which has this application.yml and another variables:
security:
jwt-enabled: true
secured-paths:
- /user/**
unsecured-paths:
- /**
But if I remove that from the consumer and I put it in the application.yml of the starter, the starter beans does not have these properties when creating them.
Maybe am I missing something?
If I understood properly your issue, I have faced such problem just last week ...
I was inspecting this issue and I have some findings (they are not supported by official documentation): if you add dependency and want to use its resources, you have a situation when both application.yml files have the same location - classpath:application.yml, and or they cannot be loaded together, or one of them is overridden by other. In any case, in my application, it did not work.
The straight and simple solution if you just need to load configuration from dependent config file - rename it and load in a possible way (manual loading from YAML, property source's initializer, etc.)
But if this config file should be used anywhere, we can load properties manually in the context. In a dependency (consumer in your case) create another configuration file, e.g. consumer-application.yml and next bean in #configuration class:
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
var propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
var yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(new ClassPathResource("consumer-application.yaml"));
propertySourcesPlaceholderConfigurer.setProperties(yamlPropertiesFactoryBean.getObject());
return propertySourcesPlaceholderConfigurer;
}
And you can use properties from YAML-file in both applications with #Value.
But the simplest way - to use properties configs. In that case, you can just set #PropertySource("classpath:consumer-application.properties") in consumer and #PropertySource(value = {"classpath:application.properties", "classpath:consumer-application.properties"})
In my case both variants work correctly.
You can try initializing the member variables on the starter itself. If consumer wants to override the values they can do it with they're application configuration.
#Getter
#Setter
#Configuration
#ConfigurationProperties(prefix = "security")
public class StarterSecurityConfig {
private boolean jwtEnabled = true;
private String[] unsecuredPaths = { "/user/**" };
private String[] securedPaths = { "/**" };
}
Fews more ideas:
I would make jwtEnabled as false and would remove the #Configuration and #ConfigurationProperties from the above Class and create an SecurityAutoConfiguration Class with other beans.
#Configuration
public class SecurityAutoConfiguration{
#Bean
#ConfigurationProperties(prefix = "security")
public StarterSecurityConfig starterSecurityConfig(){
return new StarterSecurityConfig();
}
#Bean
#ConditionalOnProperty(value="security.jwtEnabled", havingValue = "true")
public JwtService jwtService(StarterSecurityConfig starterSecurityConfig) {
return new JwtService(starterSecurityConfig);
}
}
the consumers will be able to enable or disable the security-starter with their application configuration using security.jwtEnabled flag.

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.

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

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;
}

Loading additional spring profiles from java config

Is there any possibility to load aditional spring profiles from java config?
I know that I can use -Dspring.profile.active argument and also add profiles to spring.profiles.include in application.properties.
What I need is to be able to activate profiles from java config. I've created PropertyPlaceholderConfigurer, where I'm adding some custom property files, which also contains property spring.profiles.include, all properties are load and it works ok, but spring doesn't activate any profiles which are inclded using this property.
#Bean
public static PropertyPlaceholderConfigurer ppc() throws IOException {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(new ClassPathResource("properties/" + property + ".properties"));
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
The active spring profiles are defined in properties via the following configuration: spring.profiles.active:.
You should list in all the files that you import the profiles that they activate via the above configuration key.
EDIT
First, as per the official documentation the configuration spring.profiles.include is more suitable for unconditionally adding active profiles.
Second, I can assume that PropertyPlaceholderConfigurer is not suitable for what you want to achieve. The official documentation lists the ways you can Externalize Configuration. You can try to use #PropertySource:
#PropertySources({
#PropertySource(value = "classpath:application.properties"),
#PropertySource(value = "classpath:other.properties", ignoreResourceNotFound = true)
})
public class Application {
...
}
}
Additionally, you can try to list the other properties files in property spring.config.location inside application.properties as described here.

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.

Categories