On Mon, Feb 28, 2011 at 9:25 AM, Jules <jules.gosn...@gmail.com> wrote:
> This is actualy my preferred route - however I've just revisited some
> test code I wrote a while back, that I thought I had working... - I
> only had it half working :-( - and in having another go at it, I've
> realised that there is a big problem with this approach. I can't see a
> way to get from a class object to class bytecode for dynamically
> created types. So my client's URLClassLoader can request user.Foo from
> my server, which can look up the class in the current
> ContextClassLoader, but is then stumped :-(

OK, this is what you're going to have to do.

First, hack your version of Clojure to alter
clojure.lang.DynamicClassLoader. You need to wrap the defineClass
method body in one that saves a copy of the method's parameters
somewhere where Clojure can get at it. Say in a "public static Atom
bytecode" in a PersistentHashMap, with an added last line in
defineClass using compareAndSet to update the map with an association
of the Class object just created and a PersistentVector of the
parameters. You'll have to work with the Atom, the PersistentHashMap,
and the PersistentVector from inside Java, but it can be done.

Now, in Clojure:

(defn class-data [f]
  (@(clojure.lang.DynamicClassLoader/bytecode) (.getClass f)))

(def class-loader (.getClassLoader (.getClass #(+ %1 %2 %3))))

(defn fn-from-class-data [[nm bytecode src]]
  (.newInstance (.defineClass class-loader nm bytecode obj))))

And now you just use class-data to extract a magic cookie from a fn
which, when passed to fn-from-class-data, can recreate the fn. The
cookie is a String (the class name), a byte array (the bytecode), and
a quoted Clojure form (the source code). This cookie should be
Serializable and in fact if you convert back to a byte array from a
seq on reception it should survive going through prn and read, too.

I'd probably just pass the source code around and use eval, though. :)
In particular there's no need to use a modified Clojure runtime in
that case -- just use your own macro wrapping fn to make the functions
whose source you'l need (the macro can just capture its arguments to
somewhere) and a function that takes an argument set like that and
evals up the corresponding function, something like

(def fn-sources (atom {}))

(defmacro mobile-fn [& args]
  `(let [f# (fn ~@args)]
     (swap! fn-sources assoc f# (quote ~args))
     f#))

(defn source-of [f]
  (@fn-sources f))

(defn mobile-fn-from-source [src]
  (eval `(mobile-fn ~@src)))

user=> (def q (mobile-fn [x] (+ 3 x)))
#'user/q
user=> (q 4)
7
user=> (source-of q)
([x] (+ 3 x))
user=> (def r (mobile-fn-from-source (source-of q)))
#'user/r
user=> (r 4)
7
user=>

As you can see, source-of's output can be moved around and even
serialized with prn and deserialized with read; mobile-fn functions'
source can be extracted with source-of; and mobile-fn-from-source
regenerates the function at the receiving end (and in a form that can
be passed on).

WARNING: obviously you can actually write a computer virus that will
infect runtimes that have the above four things defined! Security is
going to be a big concern if internet-visible ports are going to be
receiving serialized function sources. The same applies to bytecode
schlepped if you use the modified DynamicClassLoader scheme instead.

> If there is a way to lookup an engineered class' bytecode from within
> Clojure, I'd be very grateful to hear about it.

There doesn't seem to be without modifying DynamicClassLoader as described.

> Yes - creation of a custom constructor based on my extra metadata is
> one of the extra things that my wrapper macro does.
>
> I guess what I would like to be able to do is to look up my metadata
> on the Foo symbol after def-record has run - but then, if I had loaded
> Foo from a URLClassLoader I would not find the metadata there, so it
> is a good thing that I have not come to rely on this :-)

If Foo is being moved around remotely you'll need the metadata to go
elsewhere -- in an associated hashtable that can be serialized and
shipped around, say, keyed by classname String (I wouldn't key on
Class objects -- the key may not serialize, or may not match up with
the local copy of Foo defined with the local ClassLoader, etc.)

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