How to add an URL into the classpath?

667 views
Skip to first unread message

Yoshinori Kohyama

unread,
Jul 27, 2012, 4:38:57 AM7/27/12
to clo...@googlegroups.com
Hello clojurians,

Adding an URL to the classpath dynamically in REPL succeeded.

  $ java -cp clojure-1.3.0.jar clojure.main
  Clojure 1.3.0
  user=> (def ccl (.getContextClassLoader (Thread/currentThread)))
  #'user/ccl
  user=> (class ccl)
  clojure.lang.DynamicClassLoader
  user=> (.addURL ccl (java.net.URL. "file:///some/path/"))
  nil

But the program below fails.

  $ cat addcp.clj 
  (def ccl (.getContextClassLoader (Thread/currentThread)))
  (println (class ccl))
  (.addURL ccl (java.net.URL. "file:///some/path/"))
  $ java -cp clojure-1.3.0.jar clojure.main addcp.clj 
  sun.misc.Launcher$AppClassLoader
  Exception in thread "main" java.lang.IllegalArgumentException: No matching method found: addURL for class sun.misc.Launcher$AppClassLoader
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:52)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:30)
at user$eval3.invoke(addcp.clj:3)
at clojure.lang.Compiler.eval(Compiler.java:6465)
at clojure.lang.Compiler.load(Compiler.java:6902)
at clojure.lang.Compiler.loadFile(Compiler.java:6863)
at clojure.main$load_script.invoke(main.clj:282)
at clojure.main$script_opt.invoke(main.clj:342)
at clojure.main$main.doInvoke(main.clj:426)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.lang.Var.invoke(Var.java:401)
at clojure.lang.AFn.applyToHelper(AFn.java:161)
at clojure.lang.Var.applyTo(Var.java:518)
at clojure.main.main(main.java:37)

While in REPL the loader gotten with .getContextClassLoader is an instance of clojure.lang.DynamicClassLoader,
the latter is an sun.misc.Launcher$AppClassLoader.

How can I get an clojure.lang.DynamicClassLoader?
Or am I wrong with something?

Please teach me any information about this.

Regards,
Yoshinori Kohyama

Stuart Sierra

unread,
Jul 27, 2012, 9:04:04 AM7/27/12
to clo...@googlegroups.com
Hello Yoshinori,

In general, you cannot modify the JVM classpath at runtime. Clojure uses its own classloader to do dynamic code generation, but you cannot rely on being able to control the classloader which is running the whole JVM.

-S

Yoshinori Kohyama

unread,
Jul 29, 2012, 2:05:00 PM7/29/12
to clo...@googlegroups.com
Hello programmers,

Hi, Sierra.  Thank you replying.

O.K. I can not always contol the classloader of the whole JVM.

Then, how can I get the dynamic classloader for my current code/thread?
Or can't I?

Regards,
Yoshinori Kohyama

Stuart Sierra

unread,
Jul 29, 2012, 10:08:15 PM7/29/12
to clo...@googlegroups.com
You can't. The dynamic classloader is an internal implementation detail of Clojure; you can't rely on it being available anywhere.

If you're interested in runtime control over the the Java classpath, look at https://github.com/cemerick/pomegranate

-S

Yoshinori Kohyama

unread,
Jul 29, 2012, 11:31:39 PM7/29/12
to clo...@googlegroups.com
Hi Sierra,

Thank you for your kind and quick answers to my questions.
I see.
I'll read the page you referred.

Regards,
Yoshinori Kohyama

Yoshinori Kohyama

unread,
Jul 30, 2012, 2:14:30 AM7/30/12
to clo...@googlegroups.com
Hello clojurians,

It seems not to be able to control the whole classpath of my runtime.
But my aim, compiling with a path given at runtime and execute it, has been achieved,
with
  (.setContextClassLoader (Thread/currentThread)
    (DynamicClassLoader. (.getContextClassLoader (Thread/currentThread)))

Details reported below.

Regards,
Yoshinori Kohyama



$ cat project.clj 
(defproject dce "0.0.1"
  :description "Dynamic Compling And Execution"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :main dce.core)

$ cat src/dce/core.clj 
(ns dce.core
  (:import java.net.URL clojure.lang.DynamicClassLoader)
  (:gen-class))

(defn -main [abs-path target-ns & args]
  (let [ccl (.getContextClassLoader (Thread/currentThread))
        dcl (if (instance? DynamicClassLoader ccl) ccl
                (let [l (DynamicClassLoader. ccl)]
                  (.setContextClassLoader (Thread/currentThread) l)
                  l))]
    (.addURL dcl (URL. (str "file://" abs-path "/src/")))
    (.addURL dcl (URL. (str "file://" abs-path "/classes/")))
    (binding [*compile-path* (str abs-path "/classes")]
      (compile (symbol target-ns)))
    (def f (future (apply (resolve (symbol (str target-ns "/-main"))) args)))
    (Thread/sleep 5000)
    (future-cancel f)))

$ tree samples
samples
├── classes
└── src
    └── foo
        └── core.clj

3 directories, 1 file

$ lein repl
REPL started; server listening on localhost port 5997
dce.core=> (-main (.getCanonicalPath (java.io.File. "samples")) "foo.core" "arg1" "arg2")
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
true
dce.core=> ^D

$ tree samplessamples
├── classes
│   └── foo
│       ├── core$_main.class
│       ├── core$loading__4505__auto__.class
│       └── core__init.class
└── src
    └── foo
        └── core.clj

4 directories, 4 files

$ lein uberjar
...
Created ... dce/dce-0.0.1-standalone.jar

$ rm -Rf samples/classes/*
$ tree samples
samples
├── classes
└── src
    └── foo
        └── core.clj

3 directories, 1 file

$ java -jar dce-0.0.1-standalone.jar `pwd`/samples foo.core arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
Foo:  arg1 arg2
^C

$ tree samples
samples
├── classes
│   └── foo
│       ├── core$_main.class
│       ├── core$loading__4505__auto__.class
│       └── core__init.class
└── src
    └── foo
        └── core.clj

4 directories, 4 files

Yoshinori Kohyama

unread,
Jul 30, 2012, 2:17:15 AM7/30/12
to clo...@googlegroups.com
I forgot to show foo/core.clj

$ cat samples/src/foo/core.clj 
(ns foo.core)

(defn -main [& args]
  (loop []
    (println "Foo: " (apply str (interpose " " args)))
    (Thread/sleep 1000)
    (recur)))

Vladimir Tsichevski

unread,
Nov 21, 2012, 11:14:05 AM11/21/12
to clo...@googlegroups.com
Hi Yoshinori,

after (unsuccessfully) struggling with Clojure dynamic ClassLoaders in clojure-1.4, I ended up with the 'brute force' solution--just add URL entries to the Java system classloader. Here is a simple example on how to do it from clojure:

(defn add-system-classpath
  "Add an url path to the system class loader"
  [url-string]
  (let [field (aget (.getDeclaredFields java.net.URLClassLoader) 0)]
    (.setAccessible field true)
    (let [ucp (.get field (ClassLoader/getSystemClassLoader))]
      (.addURL ucp (java.net.URL. url-string)))))

(add-system-classpath "file:/some/clojure/root/")
(load "some/clojure/file") ;; load the some/clojure/file.clj from the /some/clojure/root/ catalog
(add-system-classpath "file:/some/java/classes/")
some.java.MyClass ;; use the some.java.MyClass in the /some/java/classes/ catalog

Regards,
Vladimir

Alex Ott

unread,
Nov 21, 2012, 1:04:41 PM11/21/12
to clo...@googlegroups.com
You can also use https://github.com/cemerick/pomegranate - it works fine...
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en



--
With best wishes, Alex Ott
http://alexott.net/
Twitter: alexott_en (English), alexott (Russian)
Skype: alex.ott

Yoshinori Kohyama

unread,
Dec 10, 2012, 11:37:54 PM12/10/12
to clo...@googlegroups.com
Hi Vladimir and Alex,

Thank you for very useful informations.

With regards,

Yoshinori Kohyama
Reply all
Reply to author
Forward
0 new messages