On Wed, Jul 8, 2009 at 11:37 PM, Phil Hagelberg<p...@hagelb.org> wrote:
>
> Robert Campbell <rrc...@gmail.com> writes:
>
>> The main reason this is an issue for me is during development I
>> sometimes find I need another library added to my classpath. Right now
>> the only way I know how to modify the classpath in Emacs is to change
>> the .emacs file with an add-to-list 'swank-clojure-extra-classpaths
>> and reboot. I think my looking for an image solution might be a
>> cop-out itself; I need to learn Emacs better so I can figure out how
>> to modify the classpath without rebooting.
>
> Unfortunately this is impossible due to the way the classloaders in the
> JVM work; you can't modify your classpath at runtime and have it work
> consistently.
>
> The solution I've settled on is to have a static classpath; no matter
> what the project is, my classpath is always the same:
> src/:target/dependency/:target/classes/:test/
>
> When you need to add a new library, instead of putting the jar on the
> classpath, just unpack the jar in the target/dependency directory. Then
> you can access it without restarting the JVM.
>

</snip>

> You don't run into this problem with Java dev in Eclipse because Java
> doesn't have a REPL. I agree that it sucks though. Unpacking your
> dependencies all in one place is a lot nicer anyway; no restarts required.

Uhm, that's not exactly true. The classpath is what you give your VM
as initial set of available classes to what it defines internally. The
definition is static, but there are other ways to inject code into the
class lookup mechanism inside the JVM which is where the classloaders
come in. It's perfectly fine to reload any class at runtime, including
the ones in a jar if they are defined in a classloader that you can
control. There are two problems you have to overcome though.

1) If you want to change classes that are already loaded, one strategy
is to hot swap the code (either via JVMTI, that's what you can do when
you're in debug mode in an IDE, or you use JavaRebel (commercial),
which goes a bit further in what you're able to redefine without
killing the JVM, and losing state). Not sure if that's needed for
Clojure code, since I haven't looked much at the bytecode so far, but
it's really handy for Java and Scala for instance).
2) If you really need to add new things to the classpath (e.g. a new
Jar), or you just want to reload a whole bunch of stuff, then you have
to ditch the classloader that has these classes loaded (does not apply
if you're adding new stuff), and add a new classloader that finds the
new dependency at runtime. This is not that hard to do (check
classloader javadoc), but it has quite a few cornercases, so it's not
recommended to try it adhoc. There are systems that implement the
whole system for you. Eclipse itself (the IDE, not the projects in it)
can install plugins at runtime (although it asks you whether you want
to restart), as can Maven (plugins are installed at runtime). The
mechanism that supports this in Eclipse is OSGi (in Maven it's
homegrown - classworlds), which is a nifty specification for dynamic
service management inside the JVM. It can do a lot of things, but what
is most interesting here is that it tracks versioned dependencies of
runtime components. Think Maven or Ivy at runtime, and it's
dynamically loading/unloading code based on what you specify. All of
the OSGi implementations feature a management console (a REPL) where
you can interact with the system at runtime (load and unload
dependencies, it will warn if there are unmet dependencies etc.).

I think making Clojure work well with OSGi is under 'longterm' on the
roadmap, but I don't know what the current status is. I'm definitely
giving it a vote though.

Just to be clear, the classpath you define on the commandline is just
a shortcut to define a classloader that looks up classes inside the
jars and directories you define on the commandline. If you have your
own classloaders (instantiated) you can load your code from a special
directory (that's what the webcontainers (tomcat etc.) do to support
hot redeployment of apps), the web (that's what applets do), a
database (don't know who's doing that), an uncompiled script file
(that's what scripting languages do unless they have precompiled to
class files), or any other storage you can think of. The only
requirement for a classloader is to return a bytearray that is valid
java bytecode according to the spec, and it can only load the code
once (it's cached after that, that's why you have to ditch the
classloader).

(And although the -cp argument supports wildcards since Java 1.6, they
are expanded early and cannot be reevaluated during runtime :( )

More information available at:
http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html

So depending on your approach and what you want to be able to do
(redefine/reload or add), it's a bit of work. The easiest thing is to
unpack your dependencies, but for my taste it's pretty messy.

Cheers,
Daniel

(today seems to be the day of long posts. Sorry guys)

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to