#PropertySource does not bind a String propety to enum automatically? - spring-boot

In our integration test using springboot 1.4, we used
#ConfigurationProperties(locations = "classpath:test.yml")
with the locations attribute. This was mapping a string property to enum automatically. But starting from springboot 1.5, the locations attribute is removed.
As a workaround, I'm using #PropertySource but this doesn't support yaml file. So, I'm using a factory class to convert the yaml to java.util.properites. But I'm facing issues, with string property not binding to enum automatically.
Is there any good solution for this?

You can map yaml file to config class
The relative path of application.yml file is /myApplication/src/main/resources/application.yml.
The Spring application takes the first profile as the default profile unless declared otherwise in the Spring application.
YAML FILE
spring:
profiles: test
name: test-YAML
environment: test
servers:
- www.abc.test.com
- www.xyz.test.com
---
spring:
profiles: prod
name: prod-YAML
environment: production
servers:
- www.abc.com
- www.xyz.com
Binding YAML to a Config Class
To load a set of related properties from a properties file, we will create a bean class:
Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class YAMLConfig {
private String name;
private String environment;
private List<String> servers = new ArrayList<>();
// standard getters and setters
}
The annotation used here are:
#Configuration marks the class as a source of bean definitions
#ConfigurationProperties binds and validates the external configurations to a configuration class
#EnableConfigurationProperties this annotation is used to enable #ConfigurationProperties annotated beans in the Spring application
USAGE:
#SpringBootApplication
public class MyApplication implements CommandLineRunner {
 
    #Autowired
    private YAMLConfig myConfig;
 
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApplication.class);
        app.run();
    }
 
    public void run(String... args) throws Exception {
        System.out.println("using environment: " + myConfig.getEnvironment());
        System.out.println("name: " + myConfig.getName());
        System.out.println("servers: " + myConfig.getServers());
    }
}

Related

Custom AutoConfiguration is not detected by spring application

Hi I created one custom auto configuration library as following:
#Configuration
#EnableConfigurationProperties(RefreshProperties.class)
public class PropertiesRefresherAutoConfiguration {
public static final String AUTO_REFRESH_ENABLED = RefreshProperties.PROPERTIES_AUTO_REFRESH + ".enabled";
#ConditionalOnProperty(
name = PropertiesRefresherAutoConfiguration.AUTO_REFRESH_ENABLED,
havingValue = "true"
)
#Bean
public Refresher getRefresher(){
return new Refresher();
}
}
I have added spring.factories in META-INF with content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bla.PropertiesRefresherAutoConfiguration
What I want to achieve is when I use dependency of this custom library and I set application.yaml properties:
properties:
refresh:
enabled: true
Refresher instance should automatically be created. In Refresher class there is one method that should be called on application start
#PostConstruct
public void startTask() {
//do something
}
In my main application I've added dependency to it and defined properties in application.yaml but Refresher Bean is not created and startTask() method is not called
I have tried to remove #Conditional annotation in order to create instance of Refresh every time, but that didn't help.

Spring boot: how to read specific property from application.yml and ignore others

My goal is to read some property from application.yml file and ignore other properties. This is need for testing purposes - I want to read part of config and validate it. I want to ignore other properties because Spring Boot tring to handle it but this is no need for my test.
Example.
Config:
first:
property:
{cipher}value #for example Spring trying to handle cipher and throws exception
second:
property:
value
Class (using String for simplification):
#Configuration
public class Configuration {
#Bean
#ConfigurationProperties(prefix = "second.property")
public String secondProperty() {
return new String();
}
}
Test class:
#SpringBootTest
public class ConfigPropertiesCheckerIntegrationTest {
#Autowired
String secondProperty;
#Test
void checkAutoConfigEndpointProperties() {
assertThat(secondProperty, is(notNullValue()));
}
}
So question: how to ignore first.property and say Spring just skip it?
You can disable {cipher}ed properties decryption with spring.cloud.decrypt-environment-post-processor.enabled=false. E.g:
#SpringBootTest(properties = "spring.cloud.decrypt-environment-post-processor.enabled=false")

Values not passed into Component from application-dev.properties

I am writing tests in Junit and am using Spring Boot Framework (which I am new to) and need to use different urls to test different environments
Hence, I created 2 resource files in addition to application.properties
1> application-dev.properties
2> application-stage.properties
I created a component which I set the property values to be read into.
Lastly in my test file I am annotating the Test Class with:
#ContextConfiguration(classes = {ComponentName.class})
Also in my application.properties I have this line:
spring.profiles.active=dev
Expected:
When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
Why is the property not getting injected with the value from application-dev.properties?
I've tried several articles from Baeldung (A bit confusing though, the articles demonstrate multiple methods to do the same thing, but does not show 1 full technique to do everything end-to-end
https://www.baeldung.com/spring-profiles
I also tried with setting the active profile multiple ways:
a. Env variable
b. Using #ActiveProfiles annotation
Note: This is a Test Project (I am trying to automate-test a website, so all my resource files are inside src.test.resources
application.properties
#spring
https://ops.dev.website.us
spring.profiles.active=dev
url.sms=https://ops.default.website.us
application-dev.properties
url.sms=https://ops.dev.website.us
application-stage.properties
url.sms=https://ops.stage.website.us
Component File
#Component
#ConfigurationProperties(prefix = "url")
public class DevEnvironment {
private String sms;
public String getSms() {
return sms;
}
public void setSms(String sms) {
this.sms = sms;
}
}
Test File
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
#Autowired
private ConfigurableEnvironment env;
private DevEnvironment devEnvironment;
String url;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
System.out.println("Active profiles:" + env.getActiveProfiles()[0]);
devEnvironment = context.getBean(DevEnvironment.class);
//System.out.println("from set app context:" + devEnvironment.getSms());
url = devEnvironment.getSms();
}
#Test
public void testSms(){
System.out.println("inside test url:" +url);
}
}
Expected: When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
You forgot #SpringBootTest annotation to MyTest class.
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#SpringBootTest //add
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
//
}
I worked fine with this.
reference:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications

spring boot application context was not properly loaded if the yaml file wasn't application.yml

With following configuration, my test can read the properties from the yaml file correctly.
#SpringBootApplication
#PropertySource("classpath:application.yml")
#ComponentScan({ "com.my.service" })
public class MyApplication {
}
Then I renamed the yaml file to my-application.yml, and changed the PropertySource to
#PropertySource("classpath:my-application.yml")
Tests are failed due to the null property value. The configuration class is as following:
#Configuration
#ConfigurationProperties(prefix="my")
#Data
public class MyConfig {
private String attr1;
}
The test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
public class MyConfigTest {
#Autowired
private MyConfig myConfig;
#Test
public void getMyConfigTest() {
Assert.assertNotNull(myConfig.getAttr1());
}
Why spring boot can find the renamed yaml file, but it couldn't load the value correctly?
YAML files can’t be loaded via the #PropertySource annotation
It appears to work with #PropertySource("classpath:application.yml") because that's the default location and spring boot looks there regardless.
You may be able to use #ConfigurationProperties(location="claspath:my-application.yml") instead but it doesn't really achieve the same purpose (and I've never tried it myself).

How to externalize application.properties to an external filesystem location in Spring Boot?

#ComponentScan
#EnableAutoConfiguration
#PropertySource(value = { "file:/Users/Documents/workspace/application.properties" })
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
In this case it gives while deploying:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency:
Not able to find the correct way to externalize the application properties file
I tried autowiring environment variable which is correctly loaded but then I need to manually define all the beans
#Bean
public JdbcTemplate dataSource() {
String driverClassName = env
.getProperty("spring.datasource.driverClassName");
String dsUrl = env.getProperty("spring.datasource.url");
String username = env.getProperty("spring.datasource.username");
String password = env.getProperty("spring.datasource.password");
//DataSource dataSource = new SimpleDriverDataSource(new driverClassName, dsUrl, username, password);
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
return jdbc;
}
This deploys without throwing error but not responding.
If you're deploying a WAR to Tomcat, then you need to define a context XML as described here: https://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Defining_a_context
For example, you would typically define $CATALINA_BASE/conf/Catalina/localhost/my-app.xml if you have http://localhost:8080/my-app/.
The file would then look like this:
<Context docBase='path/to/my-app/my-app.war'>
<Environment name='app_config_dir' value='path/to/my-app/' type='java.lang.String'/>
</Context>
In your code, implement ApplicationContextAware and override setApplicationContext. Here is an example:
public class Setup implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("Set application context. App Config Property: {}",applicationContext.getEnvironment().getProperty("app_config_dir"));
}
}
You can now load the properties file from app_config_dir.
There is no need to define it by using #PropertySource annotation. You should just use spring.config.location property to set your application.properties location. That property can be set up for example in command line:
java -jar myapp.jar --spring.config.location=/Users/Documents/workspace/
You can define an environment variable called SPRING_CONFIG_LOCATION and give it a value that can be a folder (which should end in /) or a file. In any case the location should pe prefixed with file::
SPRING_CONFIG_LOCATION = file:C:/workspace/application.properties
or
SPRING_CONFIG_LOCATION = file:C:/workspace/
More about this here.
You can use #PropertySource too because it is useful when you are deploying application as war file to tomcat.
#PropertySource is not working because you are missing #Configuration annotation. As per the Spring-Boot documentation, #PropertySource annotations should be present on your #Configuration classes.
Following works
#Configuration
#ComponentScan
#EnableAutoConfiguration
#PropertySource(value = { "file:/Users/Documents/workspace/application.properties" })
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
You can also load multiple properties file. For ex:
#PropertySource(value = {"classpath:application.properties", "file:/Users/overriding.propertis"}, ignoreResourceNotFound = true)
Note that the order of the declared files is important. If the same key is defined in two or more files, the value associated with the key in the last declared file will override any previous value(s).

Resources