I’ve been trying to figure out how to dynamically add an entry to the classpath, in a way that works under Java 8 and Java 9, that works when running under Leiningen or through
clj. I’ve gone down the rabbit hole trying to understand classloaders and the Java 9 changes, and I’m still utterly confused and without a working solution.
I’ve compared Pomegranate 1.0.0, Pomegranate 0.4.0, Dynapath, and the experimentel
add-lib from tools.deps.alpha, and it seems the only thing that actually works in using Pomegranate 0.4.0 on Java 8. Here’s a repo with my experiments.
Before Java 9 Pomegranate/Dynapath would use Java reflection to change Java’s URLClassLoader to make it modifiable/dynamic. This is no longer possible in Java 9 (the dynapath README says it only results in a warning, but it still seems to break the existing behaviour).
The Dynapath README hints that you should implement your own dynamic class loaded. Clojure already implements one though,
clojure.lang.DynamicClassLoader. You can try installing one of these
(let [thread (Thread/currentThread) contextloader (.getContextClassLoader thread) classloader (clojure.lang.DynamicClassLoader. contextloader)] (.setContextClassLoader thread classloader))
This seems to work at first, after this use any of the above libraries to add a directory to the classpath and e.g.
(io/resource "...") will work, but a
(require '...) will not. Clojure does not “see” this new entry.
I’d like to understand why this is so hard? How do you install a dynamic classloader that actually works? Why isn’t there a library that handles this? Has Java 9 doomed us to a static reality?