diff --git a/boot/base/src/main/java/boot/AddableClassLoader.java b/boot/base/src/main/java/boot/AddableClassLoader.java new file mode 100644 index 00000000..e2c0f207 --- /dev/null +++ b/boot/base/src/main/java/boot/AddableClassLoader.java @@ -0,0 +1,14 @@ +package boot; + +import java.net.URL; +import java.net.URLClassLoader; + +// Allows us to have a modifiable ClassLoader without having to call +// .setAccessible on URLClassLoader.addURL(), since that's not allowed +// by default under Java 9 +public class AddableClassLoader extends URLClassLoader { + public AddableClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); } + + public void addURL(URL url) { + super.addURL(url); }} diff --git a/boot/base/src/main/java/boot/App.java b/boot/base/src/main/java/boot/App.java index 1008b60d..8a5f43ee 100644 --- a/boot/base/src/main/java/boot/App.java +++ b/boot/base/src/main/java/boot/App.java @@ -7,7 +7,6 @@ import java.nio.channels.FileChannel; import java.lang.ref.WeakReference; import java.net.URL; -import java.net.URLClassLoader; import java.util.Map; import java.util.Date; import java.util.UUID; @@ -306,7 +305,7 @@ public class App { for (int i=0; i(rt)); diff --git a/boot/pod/project.clj b/boot/pod/project.clj index d912c468..0c280fe6 100644 --- a/boot/pod/project.clj +++ b/boot/pod/project.clj @@ -17,5 +17,5 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[boot/base ~version :scope "provided"] [org.clojure/clojure "1.6.0" :scope "provided"] - [org.tcrawley/dynapath "0.2.4" :scope "compile"] + [org.tcrawley/dynapath "0.2.5" :scope "compile"] [org.projectodd.shimdandy/shimdandy-impl "1.2.0" :scope "compile"]]) diff --git a/boot/pod/src/boot/pod.clj b/boot/pod/src/boot/pod.clj index e39a0bc0..d3b15e03 100644 --- a/boot/pod/src/boot/pod.clj +++ b/boot/pod/src/boot/pod.clj @@ -26,11 +26,32 @@ (let [[group artifact] ((juxt namespace name) sym)] [(or group artifact) artifact])) +(defn extend-addable-classloader + "Opens up the class loader used to create the shim for + modification. Once seal-app-classloader is called, this will be the + highest class loader user code can modify. + + This function is called during Boot's bootstrapping phase, and shouldn't + be needed in client code under normal circumstances." + [] + (extend boot.AddableClassLoader + cp/DynamicClasspath + (assoc dynapath.dynamic-classpath/base-readable-addable-classpath + :classpath-urls #(seq (.getURLs %)) + :add-classpath-url #(.addURL %1 %2)))) + +(def sealed-classloader-fns + (assoc cp/base-readable-addable-classpath + :classpath-urls #(seq (.getURLs %)) + :can-add? (constantly false))) + (defn seal-app-classloader - "Implements the DynamicClasspath protocol to the AppClassLoader class such - that instances of this class will refuse attempts at runtime modification - by libraries that do so via dynapath[1]. The system class loader is of the - type AppClassLoader. + "Implements the DynamicClasspath protocol to the AppClassLoader and + boot's ParentClassLoader classes such that instances of those + classes will refuse attempts at runtime modification by libraries + that do so via dynapath[1]. The system class loader is of the type + AppClassLoader under Java < 9, and the top-level class loader used + by boot is a ParentClassLoader. The purpose of this is to ensure that Clojure libraries do not pollute the higher-level class loaders with classes and interfaces created dynamically @@ -42,11 +63,21 @@ [1]: https://github.com/tobias/dynapath [2]: https://github.com/clojure-emacs/cider-nrepl/blob/36333cae25fd510747321f86e2f0369fcb7b4afd/README.md#with-jboss-asjboss-eapwildfly" [] - (extend sun.misc.Launcher$AppClassLoader - cp/DynamicClasspath - (assoc cp/base-readable-addable-classpath - :classpath-urls #(seq (.getURLs %)) - :can-add? (constantly false)))) + (try + ;; this import will fail if the user doesn't have a new enough boot.sh + (import boot.bin.ParentClassLoader) + (eval '(extend boot.bin.ParentClassLoader + dynapath.dynamic-classpath/DynamicClasspath + boot.pod/sealed-classloader-fns)) + (catch Exception _)) + + (try + ;; this import will fail if the user is using Java 9 + (import sun.misc.Launcher$AppClassLoader) + (eval '(extend sun.misc.Launcher$AppClassLoader + dynapath.dynamic-classpath/DynamicClasspath + boot.pod/sealed-classloader-fns)) + (catch Exception _))) (defn ^{:boot/from :cemerick/pomegranate} classloader-hierarchy "Returns a seq of classloaders, with the tip of the hierarchy first.