On Feb 28, 4:03 pm, Ken Wesson <kwess...@gmail.com> wrote:
> 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.

I'm still fiddling with Spring/AspectJ in an effort to avoid this,
but, if successful the outcome will be much the same.

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

sure - no problem - I'll have to plumb something similar into my
aspect. i was planning something like a class-definition-hook which
would allow you to register a function with the ClassDefinitionAspect.
Your function would be called with the class definition inputs (name,
bytecode) and outputs (class) and be able to do whatever it liked with
them, One such implementation could maintain a class-name:bytecode
table as you describe, but from above in Clojure rather than below in
Java.

> 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 was just going to use the class-name:bytecode map to implement the
server side of a URLClassLoader lookup - i.e. receive a request
containing a class-name and return the bytecode over http. I will have
to do a little hacking on the client side as well, to ensure that
there is a URLClassLoader in place and that it knows where to send its
requests for resolution.

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

Understood - but this in intrusive in that all fns that might need to
travel must be defined by mobile-fn. I was hoping that my solution
might be transparent so that it does not further complicate my already
complcated code :-). I figure that going in under Dynamic and URL
ClassLoader in this way should give me the opportunity to achieve this
- but I won't be sure until I have a working solution - there are
bound to be more gotchas around the corner.

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

Security is a concern - if I use URLClassLoader, which inherits its
class definition code from SecureClassLoader, and an https connection,
I would hope that I am managing to delegate most of these issues to
the java runtime, rather than having to reinvent the wheel.

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

Since my solution, if it works, is only responsible for moving Java
classes around I am sidestepping the issue of mobile metadata. The
Clojure level constructs that I am using would be passed around in JMS
messages. I would hope that they would include any associated metadata
in their serialisable envelope. If not, then I will have to handle
this issue separately.

thanks again,


Jules

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