I am trying to implement the CommonsPoolTargetSource but I can't figure out what I need to do to get it to create my minimum objects at start? By reading the docs what I have in my app configuration should be the what I need to get started but tests show that the pool is at zero instead of the minimum I set which is 4.
Here is my App Configuration:
@Configuration
@PropertySource({"classpath:config.properties"})
@ComponentScan(basePackages = {"com.mf.bb"})
public class AppConfig {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:i18n/messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(0);
return messageSource;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
@Scope("prototype")
public BBIDSApp bbIDSApp() {
return new BBIDSApp();
}
@Bean
public CommonsPoolTargetSource commonsPoolTargetSource() {
CommonsPoolTargetSource commonsPoolTargetSource = new CommonsPoolTargetSource();
commonsPoolTargetSource.setMinIdle(4);
commonsPoolTargetSource.setMaxSize(50);
commonsPoolTargetSource.setTargetBeanName("bbIDSApp");
commonsPoolTargetSource.setTargetClass(BBIDSApp.class);
System.err.println("I'm alive!!!");
return commonsPoolTargetSource;
}
@Bean
public ProxyFactoryBean proxyFactoryBean() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTargetSource(commonsPoolTargetSource());
return proxyFactoryBean;
}
@Bean
public MethodInvokingFactoryBean poolConfigAdvisor() {
MethodInvokingFactoryBean poolConfigAdvisor = new MethodInvokingFactoryBean();
poolConfigAdvisor.setTargetObject(commonsPoolTargetSource());
poolConfigAdvisor.setTargetMethod("getMaxIdle");
return poolConfigAdvisor;
}
}
This is working as expected, or better said it is working but not as you would expect it to. Not Spring's CommonsPoolTargetSource
nor the normal Commons Pool's GenericObjectPool
don't initialize the number of objects in pool by simply creating the pool object.
In Spring, CommonsPoolTargetSource
creates the pool by calling new GenericObjectPool(this)
. The same can be done with plain simple GenericObjectPool
: GenericObjectPool pool = new GenericObjectPool(new MyBeanFactory());
and in the constructor there is no initialization logic, so objects will not be created by default when the pool instance is created.
With plain old vanilla Commons Pool GenericObjectPool
you can initialize the minIdle
objects like this:
manually:
GenericObjectPool<MyBean> pool = new GenericObjectPool<MyBean>(new MyBeanFactory());
pool.setMinIdle(4);
for (int i = 0; i < 4; i++) {
pool.addObject();
}
somewhat automatic, but ugly:
GenericObjectPool<MyBean> pool = new GenericObjectPool<MyBean>(new MyBeanFactory());
pool.setMinIdle(4);
pool.setTimeBetweenEvictionRunsMillis(1000);
Thread.currentThread().sleep(2000);
With Spring's CommonsPoolTargetSource
you don't have access to the pool instance or the addObject()
method. So, you can either use the sleep() method (which is ugly) together with timeBetweenEvictionRunsMillis
or let the normal implementation to size the pool automatically when needed (objects are created when needed and added to the pool).
There would be another, third approach, of a custom CommonsPoolTargetSource that should include a init method:
public class CustomCommonsPoolTargetSource extends CommonsPoolTargetSource {
public void initializeMinIdleObjects() throws Exception {
List<BBIDSApp> apps = new ArrayList<BBIDSApp>();
for(int i = 0; i < getMinIdle(); i++) {
apps.add((BBIDSApp) this.getTarget());
}
for(BBIDSApp app : apps) {
this.releaseTarget(app);
}
apps.clear();
}
}
and then in your AppConfig:
@Bean(initMethod="initializeMinIdleObjects")
public CommonsPoolTargetSource commonsPoolTargetSource() {
CommonsPoolTargetSource commonsPoolTargetSource = new CustomCommonsPoolTargetSource();
commonsPoolTargetSource.setMinIdle(4);
commonsPoolTargetSource.setMaxSize(50);
commonsPoolTargetSource.setTargetBeanName("bbIDSApp");
commonsPoolTargetSource.setTargetClass(BBIDSApp.class);
System.err.println("I'm alive!!!");
return commonsPoolTargetSource;
}