I was going through some of the older parts of my darcs repository the other day when I stumbled upon a pretty neat and powerful little remoting library that I wrote back in 2001. It is based on dynamic proxies and plain sockets, almost legacy programming techniques nowadays in a world of BCI (bytecode instrumentation), AOP, EJBs, Terracotta etc. Anyway, I thought that the implementation is fairly interesting (and useful) and would make good blog post. It basically implements a remote proxy, that can either be instantiated by the client or instantiated by the server and sent to the client. In either case, when methods are invoked upon the proxy then they are executed on the server. The communication is socket-based and the server is holding a resizable thread pool that can grow and shrink based on usage. The beauty of using dynamic proxies is that once the proxy has been instantiated, the RPC is transparent, e.g. pretty much the same as with RMI but without all the stub and skeleton mess. Is a very simple library, not meant as a full RMI replacement, but does its job pretty well. I have used it among other things for:
- getting a remote Hibernate Context in 5 lines of code
- the remoting layer in a JNDI implementation
- remote access to AspectWerkz aspects and mixins
- Create and use a client side remote proxy (that should create and use a matching instance on the server).
- Let the server create a proxy and send it to the client which uses it.
RemoteProxyServer by passing in the class loader that we want to use to instantiate our proxied objects as well as an implementation of the
Invoker interface (which has one single method called
invoke), an interface that gives you the possibility to invoke methods on your proxied objects any way you want. This example simply shows the most basic way of doing it:
Let's now dive into the implementation of the server a little bit. When we invoke
remoteProxyServer.start() then the server starts up X worker threads (managed by a thread pool). The work done by of one of these thread is roughly - in pseudo code:
- Get the object output and input streams
- Loop:: read from input stream
- if command == CREATE: create an instance on the server and send a handle to the output stream
- else if command == INVOKE: grab the parameters, invoke the method and send the result to the output stream
- else if command == CLOSE: exit the thread
RemoteProxy proxy = RemoteProxy.createServerProxy(myInstance, "localhost", 6663);, and write it to the object output stream - or even more simple have one of the already proxied objects create it (on the server) and return it (to the client).
This is pretty much the whole server. But if you're still with me, I'm sure you're eager to know what the
RemoteProxy looks like. Well, here are some of the more interesting parts of its internals:
That's all there is to it.
Ok, perhaps not as powerful and flexible as Spring Remoting, EJBs, RMI or Terracotta's Network-Attached Memory.
But plain sockets and DPs are not so bad either. Or is it just me that's being a bit sentimental? At least it was pretty cool stuff back in 2001 :-)
If you want to take a look at the code or are thinking of using it then you can check it out using this command (if you don't have darcs installed you can get it here):
darcs get http://jonasboner.com/darcs/remoteproxy