Description
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.