swaldman / c3p0

a mature, highly concurrent JDBC Connection pooling library, with support for caching and reuse of PreparedStatements.

Home Page:http://www.mchange.com/projects/c3p0

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Make `ConnectionCustomizer` a bit easier to work with regarding class loaders

dpsutton opened this issue · comments

I want to be able to create a ConnectionCustomizer to instrument our connection pools to monitor for activity for our health endpoint without necessarily hitting the database in the endpoint itself if we can avoid it.

But I'm having issues getting the class I'm creating visible to c3p0's classloader because we are using Clojure. Clojure uses a dynamic classloader to load classfiles compiled by each form.

In Clojure, I can do

(defrecord CheckinTracker []
  ConnectionCustomizer
  (onAcquire [_ _connection _identity-token])
  (onCheckIn [_ _connection _identity-token])
  (onCheckOut [_ _connection _identity-token])
  (onDestroy [_ _connection _identity-token]))

with suitable bodies of these methods for our purposes. And then pass "connectionCustomizerClassName" (.getName CheckinTracker) as connection properties.

the problem for us though is that c3p0 looks for these ConnectionCustomizer with Class.forName (source). This happens from threads created by a threadpool somewhere that has the application class loader rather than Clojure's dynamic classloader. I'm able to work around this issue with a bit of reflection and setting the instantiated customizer in the cache with

(let [field (doto (.getDeclaredField com.mchange.v2.c3p0.C3P0Registry "classNamesToConnectionCustomizers")
              (.setAccessible true))]

  (.put (.get field com.mchange.v2.c3p0.C3P0Registry)
        (.getName CheckinTracker) (->CheckinTracker)))

and this works, but I was wondering if this could be made a bit easier. Some possible solutions:

  • a static method to register connection customizers
  • passing an instantiated connection customizer to one of Datasources/pooledDataSource methods
  • an easy way to configure the thread factories used by c3p0 so i can set my context class loader

Thank you for the lovely library otherwise and wondering if you have any thoughts about a way to make this easier.

Hi!

I am sorry for the long delay. I am not from this timeline.

c3p0 does have the config parameter contextClassLoaderSource, and by default (ordinarily by default), that's the parent Thread's (in c3p0-speak the "caller") context ClassLoader, in practice the context ClassLoader of the first Thread to call getConnection() on a PooledDataSource. So, ensuring the ClassLoader you want at that initializing call would be one way to go, although it seems a bit fragile.

As of c3p0-0.10.0, c3p0 allows you to take full control over threading by defining a TaskRunnerFactory. So you can set whatever context ClassLoader you want.

However, though its fun to use Executors and loom Threads and stuff, most users should have little reason to switch from c3p0's old-school, battle-hardened hand-rolled Thread pool. (c3p0 predates java.util.concurrent.*) That's DefaultTaskRunnerFactory. c3p0 plug-ins are all no-arg public constructor reflectons, because being old-school JavaBeans means we are happiest with simple sorts of properties. But if there were an abstract version of DefaultTaskRunnerFactory with an abstract method like ClassLoader customContextClassLoader() that returns either null or an overriding context ClassLoader, might that help? Could you set-up the ClassLoader you need somewhere it could be looked up prior to c3p0 init?

Let me know! If you are working at my pace, I'll expect to hear from you, er, sometime in the next year or two. 😊