Consider the following app:
package com.example.demo;
import javax.sql.DataSource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
@SpringBootApplication
public class CustomDatasourceApplication {
public static void main(String[] args) {
SpringApplication.run(CustomDatasourceApplication.class, "--app.datasource.schema=classpath:does-not-exist.sql");
}
@Bean
@Primary
@ConfigurationProperties("app.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
return dataSourceProperties().initializeDataSourceBuilder().build();
}
}
Running it results in the following exception being thrown:
Caused by: org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException: Property spring.datasource.schema with value 'class path resource [does-not-exist.sql]' is invalid: The specified resource does not exist.
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.getResources(DataSourceInitializer.java:169) ~[spring-boot-autoconfigure-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.getScripts(DataSourceInitializer.java:151) ~[spring-boot-autoconfigure-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.createSchema(DataSourceInitializer.java:95) ~[spring-boot-autoconfigure-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker.afterPropertiesSet(DataSourceInitializerInvoker.java:64) ~[spring-boot-autoconfigure-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1767) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1704) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 27 common frames omitted
The property name is incorrect. It is spring.datasource.schema
but should be app.datasource.schema
. We need to know the prefix that's been used to bind the DataSourceProperties
that we're working with.
Comment From: snicoll
That probably largely reduces the interest of InvalidConfigurationPropertyValueException
unless we find a way to detect to which prefix it was bound. I don't see an easy way to do that from that context.
Comment From: wilkinsona
The only thing that I could think of was for something (the binding post-processor?) to keep a map of the beans that have bean bound and the prefix that was used. We could then ask for the prefix for the DataSourceProperties
that are being used. I'm not sure it's worth it though…
Comment From: mbhave
It also means we don't get the failure analysis.
Comment From: snicoll
It also means we don't get the failure analysis.
I am not sure I got that. Or do you mean that the failure analysis shows the wrong prefix?
Comment From: mbhave
From what I could tell there would be no failure analysis when the property name is wrong.
Comment From: mbhave
We discussed this on the call and decided that we could move the validation up to a @PostConstruct
on DataSourceProperties
and throw the InvalidConfigurationPropertyValueException
there for an invalid property. The bind handlers would then kick in and handle the exception.
Comment From: mbhave
This solution isn't great since it jumps through a few different exceptions. We should probably put this on hold till #14880 is done.
Comment From: mbhave
Although that might mean that this doesn't end up in 2.0.x
Comment From: wilkinsona
This doesn't happen any more in 2.5 due to the script-based initialization being decoupled from DataSource
auto-configuration and moving into the spring-boot
module. This means that the initializer is completely unaware of where it's settings came from and the possibility that they may have some from some @ConfigurationProperties
. As a result, a more general IllegalStateException
is now thrown when no scripts are found at a configured location.
If we want to keep the old behaviour and make it more accurate, we could allow a callback to be configured or a method to be sub-classed on AbstractScriptDatabaseInitializer
that handles missing scripts. The default could throw an IllegalStateException
while R2dbcInitializationConfiguration
and both DataSourceInitializationConfiguration
s could provide something that throws an InvalidConfigurationPropertyValueException
pointing to the appropriate spring.sql.init
or spring.datasource
property.
We don't have this sort of behaviour in many other places so I'm skeptical that it warrants adding complexity to the API of AbstractScriptDatabaseInitializer
. Flagging for team attention to see what everyone else thinks.
Comment From: snicoll
Yeah, I thought that adding this exception would be generally useful but it turned out that we don't have many practical case of it, especially considering we're trying to rationalize and let folks use the lower-level API for advanced use case. So +1 to that.
Comment From: wilkinsona
Thanks, @snicoll. I'll close this one then.