0
votes

I'm seeing a very strange behavior in my application.

My application setup: Spring + Hibernate + C3p0

Application keeps running fine, when all of a sudden I start seeing these errors in logs and system totally stop processing any database specific requests.

WARN c3p0.C3P0Registry - Could not create for find ConnectionCustomizer with class name ''.
java.lang.ClassNotFoundException: 
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at com.mchange.v2.c3p0.C3P0Registry.getConnectionCustomizer(C3P0Registry.java:181)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager.getConnectionCustomizer(C3P0PooledConnectionPoolManager.java:636)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager.createPooledConnectionPool(C3P0PooledConnectionPoolManager.java:738)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager.getPool(C3P0PooledConnectionPoolManager.java:257)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager.getPool(C3P0PooledConnectionPoolManager.java:271)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:80)
at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:423)
at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:144)
at org.hibernate.jdbc.AbstractBatcher.prepareSelectStatement(AbstractBatcher.java:123)
at org.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:73)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:99)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
at org.springframework.orm.hibernate3.HibernateTemplate$18.doInHibernate(HibernateTemplate.java:690)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:365)
at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:687)
  • Why would C3p0 require to create a new connection pool at this particular time, before these exceptions application is 100% working fine and responding perfectly.
  • Also I've not provided any connectionCustomizerClassName property in my c3p0 configurations, why would it load one? in this stack trace I see it's not-null empty string ''.

Any clues?

============================================================================== Following hibernate jars I see in application's classpath:

  • hibernate-3.2.6.ga.jar
  • spring-hibernate-1.2.6.jar

Following c3p0 jars I see in application's classpath:

  • c3p0-0.9.1.jar
  • c3p0-0.9.2-pre5.jar
  • c3p0-oracle-thin-extras-0.9.2-pre5.jar

Code that manually read these properties and set on datasource (I do not read/set any connectionCustomizerClassName property here at all)

ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setMinPoolSize(Integer.parseInt(props.getProperty("jdbc.hibernate.c3p0.minPoolSize")));
.....

Here are C3p0 properties being used:

jdbc.hibernate.c3p0.minPoolSize=100
jdbc.hibernate.c3p0.initialPoolSize=100
jdbc.hibernate.c3p0.maxPoolSize=1000
jdbc.hibernate.c3p0.maxIdleTime=21600
jdbc.hibernate.c3p0.maxStatementsPerConnection=0
jdbc.hibernate.c3p0.maxStatements=0
jdbc.hibernate.c3p0.numHelperThreads=30
jdbc.hibernate.c3p0.checkoutTimeout=30000
jdbc.hibernate.c3p0.idleConnectionTestPeriod=900
jdbc.hibernate.c3p0.preferredTestQuery=SELECT 1 FROM dual
jdbc.hibernate.c3p0.maxConnectionAge=0
jdbc.hibernate.c3p0.maxIdleTimeExcessConnections=3600
jdbc.hibernate.c3p0.acquireIncrement=10
jdbc.hibernate.c3p0.acquireRetryDelay=5000
jdbc.hibernate.c3p0.acquireRetryAttempts=6
jdbc.hibernate.c3p0.propertyCycle=180
1
Post some configuration and/or relevant classes.M. Deinum
By any chance are you using multi-tenancy features of Hibernate? And what are your versions of Hibernate and C3P0?Pavel Horal
somewhere -- in c3p0.properties, system properties, c3p0-config.xml, hibernate or spring config files, JMX, etc, you must have a connectionCustomizerClassName getting set to an empty string. perhaps c3p0 should guard against that case, and treat an empty string as null. but that's what's happening. c3p0 pools get recreated whenever their DataSource's config properties are altered. also a new pool is created for every new set of authentication credentials. either of those might be triggering pool creation.Steve Waldman
c3p0 DataSources are JavaBeans. construction of a new pool (and the problem you see above) would be triggered by someone calling dataSource.setConnectionCustomizerClassName(""). i doubt any code explicitly does this. but perhaps some JMX tool you are using? (certainly within c3p0, there is no "event" that triggers config changes. within your larger app, i don't know.) the getPool(...) method is overloaded, including versions with the default auth and others accepting authentication info (i'd need to know your version to check line numbers).Steve Waldman
(it should be both visible and editable. so if ppl are accidentally updating the property to a blank string, that would explain what you are seeing.)Steve Waldman

1 Answers

1
votes

Following up a conversation in the comments on the posted question, it looks like the issue here is that VisualVM updates the null valued property connectionCustomizerClassName to an empty String value, which c3p0 currently treats an non-null and interprets as a class name.

Going forward (c3p0-0.9.5-pre7 and above), c3p0 will guard against this, interpret an all-whitespace connectionCustomizerClassName as equivalent to null. But in the meantime or for older versions, take care.

One easy workaround would be to define a NullConnectionCustomizer:

package mypkg;

import com.mchange.v2.c3p0.*;

public class NullConnectionCustomizer extends AbstractConnectionCustomizer
{}

And then use mypkg.NullConnectionCustomizer for connectionCustomizerClassName, so that the corresponding field in VisualVM is not empty and ambiguously interpretable as empty String or null.