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:
So without further ado, let’s dive into some code. First, and most importantly, would be to take a look at how the remote proxy can be used from a client’s perspective.
This simple unit test is testing (and highlights) two basic features.
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.
That was easy. So much for the client side.
How can we now create the server that serves these two proxies? The only thing we have to do is to create a 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
Here are some code excerpts, highlighting the algorithm:
If the client asks for a server created proxy, then the server would create it like this: 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: