Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ConnectionCustomizer a bit easier to work with regarding class loaders #166

Open
dpsutton opened this issue Dec 12, 2022 · 1 comment

Comments

@dpsutton
Copy link

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.

@swaldman
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants