Enable configuration property in test without loading full Spring Boot application context - spring

Assume we have Spring Boot application and want to load only specific slice of application context.
Particularly load YAML file configuration and map spring.datasource to DataSourceProperties marked by #ConfigurationProperties.
Naive not working declaration of test is:
#RunWith(SpringRunner.class)
#ContextConfiguration(
classes = {DataSourceAutoConfiguration.class, DataSourceProperties.class},
loader = AnnotationConfigContextLoader.class,
initializers = ConfigFileApplicationContextInitializer.class)
#TestPropertySource({"classpath:application.yaml", "classpath:application-dev.yaml"})
#EnableConfigurationProperties({DataSourceProperties.class})
#Slf4j
public class HibernateTest {
#Autowired
private DataSourceProperties dataSourceProperties;
#Test
public void dataSourceTest() throws SQLException {
log.info("DS URL: {}", dataSourceProperties.getUrl());
}
}
application-dev.yaml has:
spring.datasource:
url: jdbc:oracle:thin:#localhost:1521/APP
Test prints:
DS URL: null
I am looking for a way to map YAML config to #ConfigurationProperties marked class (DataSourceProperties) and make it used by #Configuration class (DataSourceAutoConfiguration) without loading any other services/components/etc...

Those links answer my questions:
https://jira.spring.io/browse/SPR-13912
Add support for YAML files to #PropertySource (Resolution: Unresolved).
https://jira.spring.io/browse/SPR-16563
Document that #PropertySource and #TestPropertySource do not support YAML.
https://github.com/spring-projects/spring-boot/issues/12388
Property Override not Working For YML Files.
https://github.com/spring-projects/spring-boot/issues/10772
Map properties in yaml from TestPropertySource not working in boot 2.0.0.
In short Spring Framework developers don't want to support YAML format for #TestPropertySource & #PropertySource annotation explaining that YAML support in Spring Boot made in tricky way / broken way (though I lost in explanations).
After switching code to use .properties files my test works fine.

Related

Define properties in a custom file, for Spring data JPA

I have a custom properties file for my spring boot application for database configurations. I use spring data JPA for persistence, By default spring boot choose application.properties to load the configurations.
How to avoid it and use any custom file, to read the database configuration on application startup
Example : database-connection.properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://ip:5432/HP
spring.datasource.username=hpadmin
spring.datasource.password=hp#12345
Thank you
You will avoid future complications if you use just one and the classic application.properties.
Spring requires more than just database configurations so replace the entire application.properties by a simple database-connection.properties is not a good idea.
Anyway, if you need to add more properties alongside application.properties you could use
one of the following approaches:
PropertyPlaceholderConfigurer
You could load extra properties alongside the application.properties or replace it entirely if you delete the application.properties
In your main class:
#Configuration
public class PropertiesConfiguration {
#Bean
public PropertyPlaceholderConfigurer properties() {
final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
final List<Resource> resourceLst = new ArrayList<Resource>();
resourceLst.add(new ClassPathResource("database-connection.properties"));
ppc.setLocations(resourceLst.toArray(new Resource[]{}));
return ppc;
}
Shell
You could replace the entire application.properties file at the moment of run of your jar with spring parameter --spring.config.location
java -jar app.jar --spring.config.location=/foo/bar/database-connection.properties
PropertySource
Similar to PropertiesConfiguration but without java code, just #annotations
#PropertySources({#PropertySource(value = "classpath:database-connection.properties")})
public class Application {

Creating a custom FactoryBean in Sprint Boot 2.3/Spring 5

I've got a spring-boot web application that's mostly working; my DataSource is properly configured by an external application.properties file.
Now I want to add properties to that file to help me instantiate and configure two instances of a class in my app. I have a APNsFactory that I currently instantiate manually and configure using JNDI, but I want to get away from JNDI calls:
#Bean
public
APNsFactory
apnsFactory()
throws
javax.naming.NamingException
{
sLogger.info("Configuring APNsFactory");
InitialContext ctx = new InitialContext();
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath((String) ctx.lookup("java:comp/env/apns/prod/keystorePath"));
f.setProductionKeystorePassword((String) ctx.lookup("java:comp/env/apns/prod/keystorePassword"));
f.setDevelopmentKeystorePath((String) ctx.lookup("java:comp/env/apns/dev/keystorePath"));
f.setDevelopmentKeystorePassword((String) ctx.lookup("java:comp/env/apns/dev/keystorePassword"));
return f;
}
When running before in a standalone webapp container, Spring properly called that method and the JNDI context from the container’s <env-entry> tags was available.
I'm trying to update my APNsFactory to be a proper Spring FactoryBean<>, and I’ve given it a couple of #Autowire String variables that I want to be set by Spring Boot from the application.properties file.
For bonus points, I want this to be usable both in Spring Boot and in a standalone container like Tomcat or Resin.
For the life of me, I can't figure out how to get Spring to do this. There are dozens of examples for DataSources and other Beans already implemented by Spring, but none for a completely custom one, using application.properties, in a Spring Boot web environment.
I've seen some examples that use an XML config file, but I'm not sure how to do that with Spring Boot.
I don't think you need a factory bean here.
You already have spring boot that can read application.properties out-of-the-box:
So try the following:
Create key/values in the application.properties file:
myapp.keystore.path=...
myapp.keystore.passwd=...
// the same for other properties
Create ConfigurationProperties class
#ConfigurationProperties(prefix="myapp.keystore")
public class MyAppKeyStoreConfigProperties {
private String path; // the names must match to those defined in the properties file
private String passwd;
... getters, setters
}
In the class marked with #Configuration (the one where you create #Bean public APNsFactory apnsFactory()) do the following:
#Configuration
// Note the following annotation:
#EnableConfigurationProperties(MyAppKeyStoreConfigProperties.class)
public class MyConfiguration {
// Note the injected configuration parameter
#Bean public APNsFactory apnsFactory(MyAppKeyStoreConfigProperties config) {
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath(config.getKeyPath());
and so on
}
}
I've intentionally didn't show the separation between production/dev stuff.
In spring boot you have profiles so that the same artifact (WAR, JAR whatever) can be configured to run with different profile and depending on that the corresponding properties will be read.
Example:
If you're running with prod profile, then in addition to application.properties that will be loaded anyway, you can put these keystore related definitions to application-prod.properties (the suffix matches the profile name) - spring boot will load those automatically. The same goes for dev profile of course.
Now I haven't totally understand the "bonus points" task :) This mechanism is spring boot proprietary way of dealing with configuration. In "standalone" server it should still have a WAR with spring boot inside so it will use this mechanism anyway. Maybe you can clarify more, so that I / our colleagues could provide a better answer

Split jackson configuration into separate properties

I'm using Spring Boot 2.2.5.RELEASE and would like to split my application.properties into separate files. There are already similar questions on StackOverflow but none of them seem to work for configuring Jackson.
My current non working solution is the following:
root/
- application.properties (without Jackson configuration)
- jackson-configuration.properties (includes Jackson configuration)
Jackson configuration class:
#Configuration
#PropertySource("/jackson-configuration.properties")
public class JacksonConfiguration {
}
Please note, I've tried different ways to specify the path including:
"/jackson-configuration.properties"
"jackson-configuration.properties"
"classpath:/jackson-configuration.properties"
"classpath:jackson-configuration.properties"
Spring Boot does not seem to use the configuration. If I copy it over into the application.properties - it works.
Content of jackson-configuration.properties:
spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.mapper.sort-properties-alphabetically=true
spring.jackson.deserialization.fail-on-unknown-properties=true
spring.jackson.parser.strict-duplicate-detection=true
spring.jackson.time-zone=Europe/Zurich
My application is annotated with #SpringBootApplication , so it should scan for additional properties.
/edit
I just realized the problem is the testing, not the productive code itself. If I start the application it works. What doess not work is testing with #JsonTest. I can fix this problem by adding the following line to my tests #ContextConfiguration(classes = {JacksonConfiguration.class}). But in turn, this causes the annotation #JsonComponent to stop working but only for the #JsonTest annotated classes.
See the documentation here. Here is an excerpt from the documentation
In order to resolve ${...} placeholders in definitions or
#Value annotations using properties from a PropertySource, you must
ensure that an appropriate embedded value resolver is registered in
the BeanFactory used by the ApplicationContext. This happens
automatically when using in XML. When
using #Configuration classes this can be achieved by explicitly
registering a PropertySourcesPlaceholderConfigurer via a static #Bean
method.
You need to create a bean like this
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}

Spring boot #ConfigurationProperties not loading from external properties file or class path property file

Hi I am trying to use a reusable library that I have created using spring boot(2.0.5) from 2 applications I am able to bind properties from application.properties which is in my classpath to my bean as follows and I see the schemas being set through the setters in my debug in my first spring batch application which is also created with spring boot(2.0.5)
This is the property bean class in my library which holds some service api- this library is just a jar package created with spring boot.
package com.test.lib.config
#ConfigurationProperties("job")
#PropertySources({
#PropertySource(value = "${ext.prop.dir}", ignoreResourceNotFound = true),
#PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true)
})
public class ServiceProperties {
/**
* Schema for the service
*/
private String schema;
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
}
And the config bean for this library is as follows in the same package.
#Configuration
#EnableConfigurationProperties(ServiceProperties.class)
#ComponentScan("com.test.lib")
public class LibraryModuleConfig {
}
This code works perfectly fine when called from a sprint boot spring batch application which includes this library as a dependency and the corresponding setters are called and I can see the schema set when I add job.schema=testSchema in application.properties
I try to use this same library in an existing spring mvc web application started from a tomcat server with external files directory as launch arguments( This application was not created with spring boot) and added the appropriate context:component-scan to include the beans (java config beans) from the library in the application-context(appn-context.xml). The job.schema property is passed from both application.properties file and even a external file in C drive as given by the ${ext.prop.dir}" in the #propertySources annotation. The schema property in ServiceProperties Bean never gets set and even the setters never get called in the debug. Why will this libray config bean not work in an existing spring mvc application but work with the spring batch application. Both of them add the library as a dependency. I have been at this at a long time and except for the spring property binding the other functionality seem to work.
#EnableConfigurationProperties is a handy annotation that Spring Boot provides. (Spring MVC doesn't provides in default)
For a legacy Spring MVC app (specifically for Spring 3.x), you can use #Value annotation for the properties.
#Value annotation also works in Spring Boot so I guess you can make a change so that it works with older version (Spring 3.x) and newer version just works without changing anything at all.
Hope this helps! Happy Coding :)

Spring boot tests - Can't find test properties

I have a spring boot project and it works great. I now want to write tests for my application and I am running into some configuration headaches.
Spring boot created a test class for me called ApplicationTests. It's real simple and it looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class DuurzaamApplicationTests {
#Test
public void contextLoads() {
}
}
Now when I start the tests I get this error:
java.lang.IllegalArgumentException: Could not resolve placeholder 'company.upload' in value "${company.upload}"
I have a properties.yml file in the src/test/resources directory and for some reason it isn't loaded. I have tried all different kind of annotations from examples on the Internet and yet none of them work.
How can I tell spring boot tests to use an application.yml file to load the properties from?
We can use #TestPropertySource or #PropertySource to load the properties file.
Example:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource("classpath:properties.yml")
#ActiveProfiles("test")
public class DuurzaamApplicationTests {
#Test
public void contextLoads() {
}
}
Docs: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html
To my surprise, when you load properties files in Spring Boot Test, .yml is not supported. It's noted in the documentation, although implicitly.
From the link above:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html
Supported File Formats
Both traditional and XML-based properties file formats are supported — for example, "classpath:/com/example/test.properties" or "file:/path/to/file.xml".
.yml is not mentioned.
And, after changing my .yml to .properties and rewrite the values in xx.xx.xx=value form, the key-values pairs can be read correctly.
So strange.
EDIT:
Now I find a ticket address this issue; seems a long-known bug in Spring.
https://github.com/spring-projects/spring-framework/issues/18486
#PropertySource and #TestPropertySource do not work with YAML. See this.
I also tested it myself. Try creating 2 files - *.yml and *.properties and see it for yourself.
To make *.yml work most people use #SpringBootTest, but if it's not what you want and you would like to use #ContextConfiguration instead, you are in for a bit of surprise.
For me the above solutions did not work and any environment variables were still overriding the test properties defined in #TestPropertySource even though https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html indicates that this source should have higher precedence than environment variables. The only solution that worked for me was to manually define a PropertyPlaceholderConfigurer bean in a test configuration class and set it with highest precedence.
This was with Spring Boot 1.5.15.RELEASE
#Configuration
#TestPropertySource(properties = "/application-test.properties")
#Slf4j
public class IntegrationTestConfiguration {
#Bean
public static PropertyPlaceholderConfigurer properties() {
PropertyPlaceholderConfigurer ppc
= new PropertyPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[]
{ new ClassPathResource( "/application-test.properties" ) };
ppc.setLocations( resources );
ppc.setIgnoreUnresolvablePlaceholders( true );
ppc.setOrder( Ordered.HIGHEST_PRECEDENCE );
return ppc;
}
/// ....
#RunWith( SpringRunner.class )
#ActiveProfiles( "test" )
#Import( IntegrationTestConfiguration.class )
#SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
public class MyTest {
I had the same error message, my problem was a application.properties in src\test\resources which was missing the new properties
Sometimes your application-test.properties file can't be found because it is in a subfolder off the class path.
for example this may not be found, because the file is actually not directly in the class path.
#TestPropertySource("classpath:application-test.properties")
but this will be found if the file is in the config folder off of a path in the class path
#TestPropertySource("classpath:config/application-test.properties")
We can use annotation of #ActiveProfiles("test") that support application-test.yml or application-test.properties

Resources