19

Why is use-context-classloader set to true by default?

Why doesn't Clojure use the current class loader?

mudgen
  • 7,213
  • 11
  • 46
  • 46
  • 4
    Reading through this article right now: http://www.javaworld.com/javaqa/2003-06/01-qa-0606-load.html This is an interesting topic to me, the conclusion the author comes to is that the context class loader is less likely to be overridden by a framework making the behavior more predictable. I'll leave the answering of this question to someone who actually knows. – Jason Sperske Nov 16 '12 at 19:36

1 Answers1

6

you can override its behavior by setting clojure.lang.Compiler.LOADER to the class loader ie.

final ClassLoader ccl= ClojurePlugin.class.getClassLoader();
clojure.lang.Var.pushThreadBindings(clojure.lang.RT.map( clojure.lang.Compiler.LOADER, ccl) );
try {
  ...
  clojure.lang.RT.loadResourceScript( cljFile );
  ...
}finally{
  clojure.lang.RT.popThreadBindings();
}

where ClojurePlugin is your class.

but when you're using RT class the first time ever(ie. when RT class is loaded) it will use context classloader to load ie. clojure/core, so you might want to wrap the above code into:

ClassLoader previous = Thread.currentThread().getContextClassLoader();
final ClassLoader parentClassLoader = ClojurePlugin.class.getClassLoader();
Thread.currentThread().setContextClassLoader(parentClassLoader);
try {
  ...
  //above code here
  ...
} finally {
  Thread.currentThread().setContextClassLoader(previous);
}

else you risk getting some error messages like:

Caused by: java.io.FileNotFoundException: Could not locate clojure/core__init.class or clojure/core.clj on classpath:
        at clojure.lang.RT.load(RT.java:432)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.lang.RT.doInit(RT.java:436)
        at clojure.lang.RT.<clinit>(RT.java:318)
        ... 16 more

otherwise if you do this but not the former, you get something like:

21:10:59 [SEVERE] java.io.FileNotFoundException: Could not locate Clojure resource on classpath: cljminecraft/core.clj
21:10:59 [SEVERE]       at clojure.lang.RT.loadResourceScript(RT.java:366)
21:10:59 [SEVERE]       at clojure.lang.RT.loadResourceScript(RT.java:346)
21:10:59 [SEVERE]       at clojure.lang.RT.loadResourceScript(RT.java:338)
21:10:59 [SEVERE]       at cljminecraft.ClojurePlugin.loadClojureFile(ClojurePlugin.java:25)
21:10:59 [SEVERE]       at cljminecraft.ClojurePlugin.loadClojureNameSpace(ClojurePlugin.java:38)
21:10:59 [SEVERE]       at cljminecraft.ClojurePlugin.start(ClojurePlugin.java:53)
21:10:59 [SEVERE]       at cljminecraft.BasePlugin.onEnable(BasePlugin.java:235)
21:10:59 [SEVERE]       at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:217)
21:10:59 [SEVERE]       at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:374)
21:10:59 [SEVERE]       at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:381)
21:10:59 [SEVERE]       at org.bukkit.craftbukkit.CraftServer.loadPlugin(CraftServer.java:270)
21:10:59 [SEVERE]       at org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:252)
21:10:59 [SEVERE]       at net.minecraft.server.MinecraftServer.j(MinecraftServer.java:320)
21:10:59 [SEVERE]       at net.minecraft.server.MinecraftServer.e(MinecraftServer.java:299)
21:10:59 [SEVERE]       at net.minecraft.server.MinecraftServer.a(MinecraftServer.java:258)
21:10:59 [SEVERE]       at net.minecraft.server.DedicatedServer.init(DedicatedServer.java:147)
21:10:59 [SEVERE]       at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:398)
21:10:59 [SEVERE]       at net.minecraft.server.ThreadServerApplication.run(SourceFile:856)

if you do neither (but you still call something like clojure.lang.RT.loadResourceScript( cljFile ); then you get the first error (obviously)

  • I actually used a different ccl (not the one from the class): final clojure.lang.DynamicClassLoader ccl = (clojure.lang.DynamicClassLoader)AccessController.doPrivileged( new PrivilegedAction() { @Override public Object run() { return new clojure.lang.DynamicClassLoader( ClojurePlugin.class.getClassLoader()); } }); to which I later add jar files to its classpath via ccl.addURL(jarURL); – basicsensei Dec 04 '12 at 20:19
  • 1
    Interesting solutions. I ended up modifying the source code of Clojure itself, changing the classloader to the current one instead of the context classloader: https://groups.google.com/forum/?fromgroups=#!topic/clojure/gRhD0HOwFGc – mudgen Dec 05 '12 at 20:53