Dynamically Loading Jar Strategy

566 views
Skip to first unread message

Pierre-Yves Ritschard

unread,
Dec 7, 2011, 10:26:17 AM12/7/11
to Clojure
Hi,

I have a use case where a daemon needs to read full namespaces from an
external jar.
I can successfuly access the namespace in the jar with tools.namespace/
find-namespaces-in-jarfile, then from the jarfile, selecting
appropriate entries, coercing into readers and then loading with load-
reader.

This approach breaks as soon as the supplied jar does requires, since
the jar is not on the classpath. I am a bit surprised that setting a
classloader in the current thread with setContextClassLoader does not
work, as my binding for *use-context-classloader* is the default:
true.

I could obviously supply a fixed directory that is always in the
classpath but that would require having two configuration files, which
I thought I could avoid.

Is there a way around this, or am I stuck ?

vitalyper

unread,
Dec 7, 2011, 2:12:59 PM12/7/11
to Clojure

Pierre-Yves Ritschard

unread,
Dec 7, 2011, 2:53:50 PM12/7/11
to clo...@googlegroups.com
I ended up doing that, all the other approaches fail for me.
Thanks for the confirmation.

On Wed, Dec 7, 2011 at 8:12 PM, vitalyper <vita...@yahoo.com> wrote:
> You can add jar to a classpath at runtime via the hack below.

> http://groups.google.com/group/clojure/browse_thread/thread/95ea6e918c430e/69c0d195defeeed3?lnk=gst&q=classpath#69c0d195deeed3


>
> HTH
>
> On Dec 7, 10:26 am, Pierre-Yves Ritschard <p...@spootnik.org> wrote:
>> Hi,
>>
>> I have a use case where a daemon needs to read full namespaces from an
>> external jar.
>> I can successfuly access the namespace in the jar with tools.namespace/
>> find-namespaces-in-jarfile, then from the jarfile, selecting
>> appropriate entries, coercing into readers and then loading with load-
>> reader.
>>
>> This approach breaks as soon as the supplied jar does requires, since
>> the jar is not on the classpath. I am a bit surprised that setting a
>> classloader in the current thread with setContextClassLoader does not
>> work, as my binding for *use-context-classloader* is the default:
>> true.
>>
>> I could obviously supply a fixed directory that is always in the
>> classpath but that would require having two configuration files, which
>> I thought I could avoid.
>>
>> Is there a way around this, or am I stuck ?
>

> --
> 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

Stuart Sierra

unread,
Dec 7, 2011, 2:57:28 PM12/7/11
to clo...@googlegroups.com

Kevin Downey

unread,
Dec 7, 2011, 3:00:01 PM12/7/11
to clo...@googlegroups.com
try something like

https://github.com/hiredman/clojurebot/blob/master/src/clojurebot/plugin.clj

--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

Pierre-Yves Ritschard

unread,
Dec 7, 2011, 4:43:15 PM12/7/11
to clo...@googlegroups.com
Thanks a lot for the good advice. Pomegranate is very nice and very
useful for testing.
As for your trick Kevin, certainly nicer that the reflection mess.

Brent Millare

unread,
Dec 7, 2011, 8:14:29 PM12/7/11
to clo...@googlegroups.com
To better understand what's going underneath, you're just calling the addURL method of the classloader. But since you might be evaluating this at the repl, there is an important point regarding the classloader. Everytime clojure evaluates a form, it will use a new classloader on that form, and the parent will be the classloader of the caller of the eval. So this means if you evaluate two forms consecutively, the first being the addURL, and the second, the command depending on the jar, the second will fail (unless you wrap both commands in a let). You need to ensure that the parent of the current classloader in the call to addURL is set. This way, all future evals will delegate to the classloader that knows about the jar.

So in summary, the heart of the command should just be:

(.addURL (.getContextClassLoader (Thread/currentThread)) (.toURL (.toURI file)))

For runtime dependency management, pomegranate does this, and so does my library, dj https://github.com/bmillare/dj

Pierre-Yves Ritschard

unread,
Dec 8, 2011, 3:04:15 AM12/8/11
to clo...@googlegroups.com
Thanks, this clarifies why my initial tests setting the current class
loader failed.

Brent Millare

unread,
Dec 8, 2011, 11:20:21 AM12/8/11
to clo...@googlegroups.com
Correction, I forgot to add .getParent, the code should be 

(.addURL (.getParent (.getContextClassLoader (Thread/currentThread))) (.toURL (.toURI file)))
Reply all
Reply to author
Forward
0 new messages