#Refreshscope with Datasource configuration - spring-boot

I am having a datasource configuration class in a Spring boot app. Snippet below
My configuration is fetched from Spring cloud config server. When I change my DB hostname and refresh using /refresh endpoint, the app is NOT using new DB host. ANy idea why ?
#Configuration
#RefreshScope
public classe DBConfig
{
#Resource
private Environment env;
private DataSource ehubDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("datasource.driverClassName"));
dataSource
.setUrl(env.getProperty("datasource.url"));
dataSource.setUsername(env.getProperty("datasource.username"));
dataSource.setPassword(env.getProperty("datasource.password"));
return dataSource;
}
}

As per docs,#RefreshScope will technically work on #Configuration, provided anything that depends on those beans cannot rely on them being updated when a refresh is initiated, unless it is itself in #RefreshScope
So could you please check your "Environment.java", You may forget to specify #RefreshScope in Environment.java. Please share your Environment.java if it is not working.

Normally, the #Configuration class contains beans, which means the datasource method should be marked as #Bean. You need #RefreshScope on each bean.
For a datasource, you probably want #ConfigurationProperties, rather than writing code for each property. #ConfigurationProperties automatically includes #RefreshScope, so you actually don't need RefreshScope here.
With #ConfigurationProperties almost no code is needed.
#Configuration
public class DBConfig
{
#Bean
#ConfigurationProperties("datasource")
public DataSource ehubDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
return dataSource;
}
}
If your Environment does something other than read the properties files, then this may not work for you.
If you want the bean name to be different from the method name, you can provide a parameter to #Bean. The code below creates the same bean as above.
#Bean(name = "ehubDataSource")
#ConfigurationProperties("datasource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
return dataSource;
}

Related

How can I create a db2 connection in Spring boot without use application.properties?

I need to build a connection to db2 database, but I can not use application.properties at this moment, so how can I create this connection?
You can implement a datasource bean in the app itself. For example:
#Configuration
public class MyClass {
#Bean
public DataSource getDataSource() {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName(JDBC_DRIVER_CLASS_NAME);
dataSourceBuilder.url(myprops.getJDBCUrl());
dataSourceBuilder.username(myprops.getJDBCUsername());
dataSourceBuilder.password(myprops.getJDBCPassword());
return dataSourceBuilder.build();
}
}
Then, make sure any autowired references to the data source are "lazy", so the app has time to load the properties (maybe from another file) and to instantiate the data source, as in:
#Lazy
#Autowired
private DataSource dataSource;

Will spring Boot datasource properties work if we configure datasource programmatically in Spring Boot

Will Spring Boot datasource properties work if we configure datasource programmatically?
The following properties worked only when I fetch DB configuration from application.properties. If I configure datasource programmatically the following properties are not working.
spring.datasource.tomcat.initial-size=10
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=5
I used the following code to configure datasource programmatically
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder.create().username(userName).password(password).url(url).driverClassName(driverName)
.build();
}
To make it work programmatically I used the following code snippet.But I'm not convinced. I feel it is not a cleaner solution. I have to read at least 20 properties from application.properties and add it to PoolProperties.
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
#Primary
public DataSource dataSource() {
PoolProperties poolProperties = new PoolProperties();
poolProperties.setUrl(url);
poolProperties.setDriverClassName(driverName);
poolProperties.setUsername(userName);
poolProperties.setPassword(password);
poolProperties.setTestWhileIdle(false);
poolProperties.setTestOnBorrow(true);
poolProperties.setValidationQuery("SELECT 1 FROM DUAL");
poolProperties.setTestOnReturn(false);
poolProperties.setValidationInterval(30000);
poolProperties.setTimeBetweenEvictionRunsMillis(30000);
poolProperties.setInitialSize(10);
poolProperties.setMaxActive(10);
poolProperties.setMaxIdle(5);
poolProperties.setMinIdle(5);
poolProperties.setMaxWait(10000);
poolProperties.setRemoveAbandonedTimeout(60);
poolProperties.setMinEvictableIdleTimeMillis(30000);
poolProperties.setLogAbandoned(true);
poolProperties.setRemoveAbandoned(true);
DataSource datasource = new DataSource(); // import org.apache.tomcat.jdbc.pool.DataSource;
datasource.setPoolProperties(poolProperties);
return datasource;
}
It there way we can make the following default Spring Boot properties work?
spring.datasource.tomcat.initial-size=10
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=5
remember to give tomcat from properties for db in property files like this.
# Oracle DB - "foo"
spring.datasource.tomcat.url=jdbc:oracle:thin:#//db-server-foo:1521/FOO
spring.datasource.tomcat.username=fooadmin
spring.datasource.tomcat.password=foo123
spring.datasource.tomcat.initial-size=10
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=5
then configure datasource like this.
/**
* Auto-configured DataSource
*/
#ConfigurationProperties(prefix = "spring.datasource.tomcat")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}

How to change expected datasource property name in Spring

I would like to change the expected property name in Spring for data source from:
spring.datasource.url: jdbc:oracle:thin:#127.0.0.1:151:xe
to:
com.foo.bar.spring.datasource.url: jdbc:oracle:thin:#127.0.0.1:151:xe
i.e. have a certain prefix for all spring properties in my application.
Is that possible? If so, how.
Thanks in advance!
Write a configuration class where you instantate your DataSource per #Bean annotation like this:
#Configuration
public MyConfig {
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "com.foo.bar.spring.datasource")
public DataSource dataSource() {
DataSource dataSource = DataSourceBuilder.create().build();
return dataSource;
}
}
this should do the trick..
see spring doc here:

How do you autowire/inject your datasource in Spring-boot?

I have been working with Spring boot for a bit now, and the datasource is always configured in your application.properties in every example I have seen, kind of like this:
# DataSource configuration
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/abcdef
spring.datasource.username=******
spring.datasource.password=******
However, lately I have been trying to integrate Spring Social, and the examples I have seen configure it in java in a config file like this:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
This allows for the datasource object to later be injected or autowired into the social config as seen here for example.
My question is, do I need to configure a datasource bean like this to be able to later inject the datasource, or will Spring-boot handle that for me?
Not a Spring (or Boot) expert by any means, but Spring Boot will auto-provide a Bean of type DataSource if the properties are there and there's a requirement for it. To use it you just #Autowire it.
Try this . If there are multiple #Configuration in springboot , You can import the the other config(DataSourceConfig) into your main AppConfig.
And then Using #PropertySource pull in the db url,username,password etc
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
#Configuration
#Import(DataSourceConfig.class)
#PropertySource("classpath:application.properties")
public class SpringbatchConfig {
#Autowired
DataSourceConfig dataSourceConfig;
#Bean
public void myService myService() {
return new myServiceImpl(dataSourceConfig.dataSource());
}
}

Spring Java based config "chicken and Egg situation"

Just recently started looking into Spring and specifically its latest features, like Java config etc.
I have this somewhat strange issue:
Java config Snippet:
#Configuration
#ImportResource({"classpath*:application-context.xml","classpath:ApplicationContext_Output.xml"})
#Import(SpringJavaConfig.class)
#ComponentScan(excludeFilters={#ComponentScan.Filter(org.springframework.stereotype.Controller.class)},basePackages = " com.xx.xx.x2.beans")
public class ApplicationContextConfig extends WebMvcConfigurationSupport {
private static final Log log = LogFactory.getLog(ApplicationContextConfig.class);
#Autowired
private Environment env;
#Autowired
private IExtendedDataSourceConfig dsconfig;
#PostConstruct
public void initApp() {
...
}
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle() {
return new DataSourceTransactionManager(dsconfig.oracleDataSource());
}
IExtendedDataSourceConfig has two implementations which are based on spring active profile one or the other in instantiated. For this example let say this is the implementation :
#Configuration
#PropertySources(value = {
#PropertySource("classpath:MYUI.properties")})
#Profile("dev")
public class MYDataSourceConfig implements IExtendedDataSourceConfig {
private static final Log log = LogFactory.getLog(MYDataSourceConfig.class);
#Resource
#Autowired
private Environment env;
public MYDataSourceConfig() {
log.info("creating dev datasource");
}
#Bean
public DataSource oracleDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl(env.getProperty("oracle.url"));
dataSource.setUsername(env.getProperty("oracle.user"));
dataSource.setPassword(env.getProperty("oracle.pass"));
return dataSource;
}
The problem is that when transactionManagerOracle bean is called, (even if I try to mark it as lazy) dsconfig variable value appears to be null.
I guess #beans are processed first and then all Autowires, is there a fix for this? How do I either tell spring to inject dsconfig variable before creating beans, or somehow create #beans after dsconfig is injected?
You can just specify DataSource as method parameter for the transaction manager bean. Spring will then automatically inject the datasource, which is configured in the active profile:
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
If you still want to do this through the configuration class, specify that as parameter:
public DataSourceTransactionManager transactionManagerOracle(IExtendedDataSourceConfig dsconfig) {}
In both ways you declare a direct dependency to another bean, and Spring will make sure, that the dependent bean exists and will be injected.

Resources