Detouring Through Dynamic Proxies
JDK 1.3 introduced dynamic proxies. A dynamic proxy is just an automatically generated class that implements a set of interfaces. The important point is that the interfaces are specified at runtime, and the implementations of all of the methods are handled by a single invocation handler that you define.
The following code snippet illustrates the general idea:
MimickingInvocationHandler handler = new MimickingInvocationHandler (....);
Class[] delegationInterfaces = new Class[] { RichardNixon.class,
GeraldFord.class, RichardSimmons.class};
Class richLittleProxyClass = Proxy.getProxyClass(
getClass().getClassLoader(), delegationInterfaces);
Constructor proxyClassConstructor =
RichLittleProxyClassProxyClass.getConstructor(Class[] {
MimickingInvocationHandler});
return proxyClassConstructor.newInstance(Object[] {handler});
This rather artificial code results in the creation of a new class object and a new instance. In this example, a class will be created that implements the RichardNixon, GeraldFord, and RichardSimmons interfaces (and we will store it in the field richLittleProxyClass). It does so using an instance of MimickingInvocationHandler, which simply must implement the invoke method:
public Object invoke(Object proxy, Method method, Object[] args) {
// All methods that are called on the RichLittleProxyClass
// actually wind up calling this method.
If you're a distributed systems guy, this looks wonderful -- the obvious thing to do is wrap your servers in logging, authentication, and processor control layers that are all defined using dynamic proxies. (Distributed systems guys tend to have this reaction a lot. At the first AspectJ talk I ever attended, a distributed systems guy leaned over and said "Cool! We can use this to simplify authentication.")
Note: In fact, if you're an EJB guy, you're probably thinking that EJB already has those wrappers and asking yourself if they're implemented using dynamic proxies. Some EJB containers are.
Authentication is a good example. You might have a whole lot of methods that require an authentication token and check permissions. For example, you might have the following authentication rules implemented inside of the isValid(...) method of an AuthenticationChecker class.
- Check that the authentication token is a valid authentication token.
- Check that the IP address of the caller matches the IP address associated to the token.
- Get the permissions for the token and check whether this method is allowed.
And then somehow tie this in to your server using a dynamic proxy. Ideally, you'd have something like this:
- A class that is the "server" for the purposes of RMI (e.g., incoming remote method invocations are sent to it) and that uses an invocation handler that first checks authentication and then forwards the call.
- A class that would have been the server, but is now the server's delegate instead.
Unfortunately, this doesn't work without a lot more effort on your part. For one thing, the proxy classes that are generated are subclasses of java.lang.reflect.Proxy, which means they can't also be subclasses of UnicastRemoteObject (or any other RMI server base class). While, strictly speaking, it's not necessary for servers to subclass UnicastRemoteObject, it's very convenient. Moreover, the client still needs stubs, and this means that you're going to have to write them by hand (or build something very similar to rmic to generate them).
So when JDK 1.3 shipped (and this was around the time that AspectJ first started to get some traction, as well), there was a lot of speculation about proxies and servers. The potential of dynamic proxies, and more generally, of aspect-oriented programming for server construction is obvious. Consequently, many object distribution and application frameworks have begin to make this style of proxy use core to their architecture. But it wasn't (and still isn't) possible in RMI.
Client-Side Fun and Games
On the server side, dynamic proxies are tantalizing. They hint at what we want to do, but they don't quite allow us to get it done. On the client side, however, the addition of dynamic proxies in JDK 1.3 allows us to do a very cool thing. In order to explain it, we need to discuss a slight change in RMI's serialization algorithm. This change actually happened in JDK 1.2.2, but at the time, everyone viewed it as an entirely innocuous bug fix. Here's the relevant snippet from the release notes:
Serializing remote objects (since 1.2.2)
Prior to 1.2.2, an attempt to pass an unexported remote object in a RMI call would result in a java.rmi.StubNotFoundException. This exception was a result of the RMI runtime's failure to locate a stub object during an attempt to replace a remote object implementation with its corresponding stub. In 1.2.2 and later releases, an unexported remote object passed in an RMI call will no longer result in an exception, but rather the remote object will be serialized instead of its stub. If the remote object implementation is not serializable, an attempt to pass an unexported object in an RMI call will result in a java.rmi.RemoteException with the nested exception java.io.NotSerializableException.
This might not seem like a very big deal -- it simply says that if an object implements both Remote and Serializable, then if it's not listening on a socket, it gets serialized (otherwise, a stub to it gets serialized).
The interesting part is that you can write dynamic proxies that include both Remote and Serializable in their list of interfaces (and, since neither Remote nor Serializable declare any methods, it's easy to do so). For example, suppose you implement an RMI server as a subclass of UnicastRemoteObject, as is traditional. Then you wrap it in a proxy, and both the proxy and the invocation handler implement Serializable. You can then bind the proxy into your naming service. It gets serialized out, and stored there. (And since it will contain a reference to the server, the serialized copy of the invocation handler will contain a reference to a stub for the server.) Figure 4 illustrates the situation:

Figure 4. Wrapping a server with a dynamic proxy. The key point is that the skeleton, which listens for remote method invocations, is bound to the actual RMI server (not to the dynamic proxy)
Going one step further, the invocation handler is actually downloaded to, and executes on, the client. The way it works is illustrated in Figure 5. There's an instance of the dynamic proxy on both the client and the server, but it winds up getting bypassed on the server because the remote method invocation uses the stub to the subclass of UnicastRemoteObject.

Figure 5. The remote call flow. The client talks to the dynamic proxy, which talks to the stub, etcetera. The key point here is that the dynamic proxy on the server side is skipped over
I haven't found a killer use for this yet, but it can be useful (for implementing some simple caching and retry strategies). And, as I said, it's quite intriguing.
JDK 1.5 Eliminating the Stubs (the Implementation)
At this point, we've talked about how to get rid of skeletons using reflection and about how to use dynamic proxies in various contexts, including wrapping stubs on the client side to get a very simple form of aspect-oriented programming.
The next step is to use dynamic proxies to get rid of stubs entirely, instead of simply wrapping them. After all, stubs don't have any special magic; they're instances of an automatically generated class designed to forward method calls over the wire. Abstractly, there's no reason they couldn't simply be replaced by a proxy object.
Unfortunately, before JDK 1.5, the structure of RMI made it impossible for you to get rid of stubs without a significant amount of skill and effort (and an even more significant amount of brittle code).
In JDK 1.5? Well, this article started with a very short quote. I think you're ready for the longer version now.
Dynamic Generation of Stub Classes
This release adds support for the dynamic generation of stub classes at runtime, obviating the need to use the Java(tm) Remote Method Invocation (Java RMI) stub compiler, rmic, to pregenerate stub classes for remote objects. Note that rmic must still be used to pregenerate stub classes for remote objects that need to support clients running on earlier versions. When an application exports a remote object (using the constructors or static exportObject methods of the classes java.rmi.server.UnicastRemoteObject or java.rmi.activation.Activatable) and a pregenerated stub class for the remote object's class cannot be loaded, the remote object's stub will be a java.lang.reflect.Proxy instance (whose class is dynamically generated) with a java.rmi.server.RemoteObjectInvocationHandler as its invocation handler. An existing application can be deployed to use dynamically generated stub classes unconditionally (that is, whether or not pregenerated stub classes exist) by setting the system property java.rmi.server.ignoreStubClasses to true. If this property is set to true, pregenerated stub classes are never used.
In short: dynamic proxies replace stubs.
There are two very interesting codas to this, though. The first is that the earlier trick, of wrapping the server in a proxy, still works with dynamically generated stubs. The second is that the dynamic proxy class is apparently generated independently on the client and server -- if you launch an application without enabling dynamic code loading, and without creating stubs or skeletons, and with clients and servers running from separate classpaths, you'll still be able to connect clients with servers.
Closing Thoughts
RMI has been remarkably stable for a long time. Changes have been slow and incremental. But each change has been towards simplifying development and deployment, without any accompanying sacrifices. It's still a strongly typed system, it still preserves as much of the local-process call syntax and semantics as is reasonable, and it still is entirely Java. But, over time, it's also gotten more agile. Slowly and subtly, RMI has evolved into a low-process, low-deployment-overhead, and lightweight framework for strongly typed remote method invocation. It's become as good for very dynamic environments as any of the more loosely coupled frameworks without sacrificing any of its original strengths.