I'm REPLed in to a running service, and have a var pointing to a classloader with which a plugin was loaded (with my.package
installed).
The DynamicClassLoader used by the REPL does not include the plugin with which I wish to interact; I wish to be able to work with classes loaded from the plugin despite this limitation.
The following works:
=> (.loadClass plugin-classloader "my.package.MyClass")
my.package.MyClass
...whereas the following doesn't (explicitly overriding the thread context classloader):
=> (do
(.setContextClassLoader (Thread/currentThread) plugin-classloader)
(Class/forName "my.package.MyClass"))
ClassNotFoundException my.package.MyClass java.net.URLClassLoader$1.run (URLClassLoader.java:202)
...and neither does this (explicitly overriding the thread context classloader and the clojure.lang.Compiler/LOADER reference):
=> (let [dcl (clojure.lang.DynamicClassLoader. plugin-classloader)]
(.setContextClassLoader (Thread/currentThread) dcl)
(with-bindings* {clojure.lang.Compiler/LOADER dcl}
(eval '(pr-str (Class/forName "my.package.MyClass")))))
ClassNotFoundException my.package.MyClass java.net.URLClassLoader$1.run (URLClassLoader.java:202)
...and neither does this:
=> my.package.MyClass
CompilerException java.lang.ClassNotFoundException: my.package.MyClass, compiling:(NO_SOURCE_PATH:0)
Shouldn't Class.forName()
use the thread context classloader when set? I'm trying to make some calls into 3rd-party code doing introspection magic; the tools in question are failing with ClassNotFoundExceptions even when the thread context classloader should be set.
In the case where I'm explicitly setting the context classloader, the stack trace demonstrates that Clojure's DynamicClassLoader (rather than the BundleClassLoader in the plugin-classloader var) is in use:
=> (e)
java.lang.ClassNotFoundException: my.package.MyClass
at java.net.URLClassLoader$1.run (URLClassLoader.java:202)
java.security.AccessController.doPrivileged (AccessController.java:-2)
java.net.URLClassLoader.findClass (URLClassLoader.java:190)
clojure.lang.DynamicClassLoader.findClass (DynamicClassLoader.java:61)
java.lang.ClassLoader.loadClass (ClassLoader.java:306)
java.lang.ClassLoader.loadClass (ClassLoader.java:247)
java.lang.Class.forName0 (Class.java:-2)
java.lang.Class.forName (Class.java:169)
clojure.lang.Compiler/eval
, as called by the REPL, uses clojure.lang.Compiler/LOADER
, not the thread-local classloader. An appropriate classloader needs to bound to this var before eval
is called -- so adding a layer of wrapping works around this issue:
=> (let [dcl (clojure.lang.DynamicClassLoader. plugin-classloader)]
(with-bindings {clojure.lang.Compiler/LOADER dcl}
(eval '(Class/forName "my.package.MyClass"))))
my.package.MyClass
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With