19

I have a simple Spring Boot application using org.apache.commons.dbcp2.BasicDataSource as dataSource bean.

The data source is exposed as MBean automatically by Spring boot.

The bean declaration:

@Bean
public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setUrl(dbUrl);
    dataSource.setDriverClassName(jdbcDriver);
    dataSource.setUsername(dbUserName);
    dataSource.setPassword(dbPassword);
    return dataSource;
}

Everything works fine . However, I see error while shutting down the application. This error only occurs when running the executable jar. When using Gradle Spring plugin (gradle bootRun), this is not shown.

javax.management.InstanceNotFoundException: org.apache.commons.dbcp2:name=dataSource,type=BasicDataSource
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(DefaultMBeanServerInterceptor.java:1095)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.exclusiveUnregisterMBean(DefaultMBeanServerInterceptor.java:427)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.unregisterMBean(DefaultMBeanServerInterceptor.java:415)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.unregisterMBean(JmxMBeanServer.java:546)
    at org.apache.commons.dbcp2.BasicDataSource.close(BasicDataSource.java:1822)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:350)
    at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:273)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:540)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:516)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:827)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:485)
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:921)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:895)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.doClose(EmbeddedWebApplicationContext.java:152)
    at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:809)

I am wondering, 1. How does this bean get exposed as JMX MBean? 2. How to properly unregister this MBean?

jlai
  • 909
  • 1
  • 10
  • 17
  • This does not seem to have anything to do with Boot. The datasource itself tries to unregister from a JMX domain and fails. How about looking that data source type documentation? Any reason why you can't use the infrastructure that boot provides by the way? – Stephane Nicoll Jul 25 '14 at 06:02
  • 1
    Stumbled across this as I have encountered the same issue. It is actually not an error - the log reports the condition as a warning. As to why not use the Boot data sources, I would wager a guess that the OP wants to fine-tune the connection pooling, and if you do not run in a container (which would pool for you), you do not actually get a pooled connection from Boot (SimpleDriverDataSource does not actually give you pooled connections). I'd love to know how to fix this with DBCP2, but if it is only a warning that occurs on shutdown, I wouldn't worry too much about it. – Will Apr 16 '15 at 16:39
  • This comes from Spring. As @Will said this is only a warning. The BasicDataSource tries to unregister but is already unregistered by Spring (destroy() in MBeanExporter). Thus it should be no problem. – mklnwt Jun 24 '15 at 14:13

4 Answers4

26

Spring is trying to close BasicDataSource twice:

  1. BasicDataSource close itself automatically when application close
  2. Spring use default destroy method to close DataSource but it's already closed

To avoid this, use:

@Bean(destroyMethod = "")
public DataSource dataSource() 

In your Java Configuration

Marcon
  • 385
  • 4
  • 11
1

I ran into the same problem. Adding an MBean server and registering the datasource can not fix it either.

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jmx.html

My conclusion is that DBCP2's BasicDataSource has a bug at unregistering itself from the MBean server.

I fixed mine by switching to mchange's c3p0: http://www.mchange.com/projects/c3p0/

user832462
  • 21
  • 1
1

BasicDataSource extends BasicDataSourceMXBean, so it is auto registered with JMX server as MBean [org.apache.commons.dbcp2:name=dataSource,type=BasicDataSource]. When springboot shutdowns, MBeanExporter unregisters the MBean, then springboot tries to destroy BasicDataSource, and calls BasicDataSource’s method close(), unregisters the MBean again (BasicDataSource catches the JMException, and print this warning). It’s just a warning. If you don’t want to print it, you can disable JMX in springboot.

application.yml
spring:
jmx:
enabled: false
Rarepuppers
  • 723
  • 1
  • 10
  • 21
helinxiang
  • 11
  • 1
0

I had the same issue. c3p0 works very well.

if using spring framework - pom.xml

<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
   <groupId>com.mchange</groupId>
   <artifactId>c3p0</artifactId>
   <version>0.9.5.2</version>
</dependency>

initially used

DataSource ds_unpooled = DataSources.unpooledDataSource(persistenceUrl,
            persistenceUsername,
            persistencePassword);
return DataSources.pooledDataSource(ds_unpooled);

but it couldn't handle the load I need to perform and switched to the following

ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass( persistenceDriver ); //loads the jdbc driver
    cpds.setJdbcUrl( persistenceUrl );
    cpds.setUser(persistenceUsername);
    cpds.setPassword(persistencePassword);
    cpds.setMinPoolSize(5);
    cpds.setMaxPoolSize(50);
    cpds.setUnreturnedConnectionTimeout(1800);
    cpds.setMaxStatements(50);
    cpds.setMaxIdleTime(21600);
    cpds.setIdleConnectionTestPeriod(10800);
return cpds;

those values are from other posts that I have gathered online.

in my experience for my specific task, running c3p0 performs faster than dbcp2 v:2.1.1 under the same environment.

hope this help a bit. cheers!

simple
  • 49
  • 2