Skip to content

Commit

Permalink
Improvements to work with Java 9
Browse files Browse the repository at this point in the history
Changes include:

* update to dynapath 0.2.5 (0.2.4 was broken when AOT'd under < Java 9,
  but used under Java 9)
* conditionally seal the AppClassLoader if it is available (it isn't
  under Java 9)
* seal the new ParentClassLoader from boot-bin
* use a URLClassLoader subclass that exposes .addURL so it can be
  modified (we can't call .setAccessible on URLClassLoader.addURL by
  default under Java 9) as the highest loader in the shim
  • Loading branch information
tobias committed Dec 9, 2016
1 parent 20efe5b commit a046a49
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 12 deletions.
14 changes: 14 additions & 0 deletions boot/base/src/main/java/boot/AddableClassLoader.java
Original file line number Diff line number Diff line change
@@ -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); }}
4 changes: 2 additions & 2 deletions boot/base/src/main/java/boot/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -306,7 +305,7 @@ public class App {

for (int i=0; i<jarFiles.length; i++) urls[i] = jarFiles[i].toURI().toURL();

ClassLoader cl = new URLClassLoader(urls, App.class.getClassLoader());
ClassLoader cl = new AddableClassLoader(urls, App.class.getClassLoader());
ClojureRuntimeShim rt = ClojureRuntimeShim.newRuntime(cl);

rt.setName(name != null ? name : "anonymous");
Expand All @@ -319,6 +318,7 @@ public class App {

rt.require("boot.pod");
rt.invoke("boot.pod/seal-app-classloader");
rt.invoke("boot.pod/extend-addable-classloader");
rt.invoke("boot.pod/set-data!", data);
rt.invoke("boot.pod/set-pods!", pods);
rt.invoke("boot.pod/set-this-pod!", new WeakReference<ClojureRuntimeShim>(rt));
Expand Down
2 changes: 1 addition & 1 deletion boot/pod/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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"]])
49 changes: 40 additions & 9 deletions boot/pod/src/boot/pod.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down

0 comments on commit a046a49

Please sign in to comment.