Thanks, Guys, for all your help.
Here is an update on where I have got to :
I've given up no trying to aspect the DynamicClassLoader for a while
(maybe forever) as spring/aspecjt/load-time-weaving does not seem to
work at the repl - I've posted another thread regarding this - no
answers as yet.
I've pulled a copy of the latest DynamicClassLoader (DCL) into my own
source tree and hacked it thus :
...
protected static Map<String, byte[]> nameToByteCode = new
HashMap<String, byte[]>();
public static byte[] byteCodeForName(String name) {
synchronized (nameToByteCode) {
return nameToByteCode.get(name);
}
}
public Class defineClass(String name, byte[] bytes, Object srcForm){
Util.clearCache(rq, classCache);
Class c = defineClass(name, bytes, 0, bytes.length);
classCache.put(name, new SoftReference(c,rq));
synchronized (nameToByteCode) {nameToByteCode.put(name,
bytes);} // this is the point of the hack
return c;
}
...
I'm not suggesting this as a patch - it doesn't consider GC, lockless
concurrency etc - but simply as a way to test my idea.
I've set up a webserver in my server to serve URLClassLoader requests
for classes created server-side of which instances have been
deserialised client side :
;; only works when URLClassLoader has been given a URL ending in '/'
(defn handle-request [^String target ^Request base-request ^Request
request ^HttpServletResponse response]
(let [^String path-info (.getPathInfo base-request)
class-name (.replace (.substring path-info 1 (- (.length path-info)
6)) \/ \.)]
(if-let [^"[B" bytes (DynamicClassLoader/byteCodeForName class-
name)]
(let [size (count bytes)]
(info (str "Serving: " class-name " (" size " bytes)"))
(doto response
(.setContentType "application/binary")
(.setContentLength (count bytes))
(.setStatus 200))
(with-open [^OutputStream stream (.getOutputStream response)]
(doseq [byte bytes] (.write stream (int byte)))))
(do
(info (str "Not Serving: " class-name))
(doto response
(.setContentLength 0)
(.setStatus 404)))))
(.setHandled request true))
(defn ^Server start-jetty [^Integer port]
(doto (Server. port)
(.setHandler (proxy [AbstractHandler] [] (handle [& args] (apply
handle-request args))))
(.start)))
(defn stop-jetty [^Server jetty]
(.stop jetty))
(start-jetty 8888)
I've installed my own ClassLoader into the hierarchy in the Client
JVM :
(let [current-thread (Thread/currentThread)]
(.setContextClassLoader
current-thread
(URLClassLoader.
(into-array [(URL. "http://localhost:8888/")])
(.getContextClassLoader current-thread))))
Now for the magic :
server-side:
user=> (defrecord Foo [a b c])
user.Foo
user=> (Foo. 1 2 3)
#:user.Foo{:a 1, :b 2, :c 3}
user=>
and now client-side
user=> (Class/forName "user.Foo")
user.Foo
user=> (Foo. 1 2 3)
CompilerException java.lang.IllegalArgumentException: Unable to
resolve classname: Foo, compiling:(NO_SOURCE_PATH:5)
user=> (user.Foo. 1 2 3)
{:a 1, :b 2, :c 3}
user=>
as you can see - not quite there yet (I seem to have namespace issues)
- but possibilities.
I guess when I called this thread "Serialising functions" I should
have called it "Serialising types" instead, as that is my ultimate
goal.
server-side in response to the requests for unknown classes from the
client I get :
user=> [INFO] web - Serving: user.Foo (9901 bytes)
[INFO] web - Serving: user.Foo$reify__601 (1546 bytes)
[INFO] web - Serving: user.Foo$reify__603 (1546 bytes)
[INFO] web - Not Serving: Foo
looks like trying (Foo. 1 2 3) fell through and looked for user.Foo
again server side...
I then plugged all this in underneath my app to see how it behaved and
I think that I have come up against class name collisions in my client
jvm :
Caused by: java.io.InvalidClassException: org.dada.demo.whales
$fn__330$fn__331; local class incompatible: stream classdesc
serialVersionUID = -2470942646180928550, local class serialVersionUID
= -6698199004290814850
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:
1583)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:
1496)
at
java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1732)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1667)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1323)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:
1947)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:
1871)
at
java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:
1947)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:
1871)
at
java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:
1947)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:
1871)
at
java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:
1947)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:
1871)
at
java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at
org.apache.activemq.command.ActiveMQObjectMessage.getObject(ActiveMQObjectMessage.java:
177)
... 9 more
This is caused by an object of which the class has already been pulled
by the URLClassLoader, arriving client-side in an ActiveMQMessage but
mistaking a homonymous local class for a remote one of different shape
and trying to use it to deserialise itself - I think.. !
I don't have time to look at this any further today, but I think it is
looking promising if I can find a way to avoid class name collisions -
more hacking of clojure.lang I'm afraid :-(
Apologies for posting all the source code here - but I thought that it
would enable others to follow my track if interested.
That's all for now. I'll post if I get any further.
cheers
Jules
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en