A pure RMI solution: the client-side listener object needs to implement java.rmi.server.UnicastRemoteObject
. If it does, and each of its methods throw RemoteException
then when it is passed to the server through the manager proxy everything is wired up automatically, and method invocations on the server-side proxy to this listener are remote invocations of methods on the real client-side object.
This will do, but it's even better to be able to wrap the object for export without requiring a particular superclass. We can use a CGLIB Enhancer to "proxy" the listener as a subclass of UnicastRemoteObject
that also implements the service interfaces. This still requires that the target object implement java.rmi.Remote
and declare throws RemoteException
.
Next step is a solution that can export arbitrary objects for remote invocation of their methods, without requiring that they implement Remote
or declare throws RemoteException
. We must integrate this proxying with the existing Spring infrastructure, which we can do with a new implementation of RmiBasedExporter
modelled on the non-registry bits of RmiServiceExporter#prepare()
to export the RMI stub of our proxy and on the invocation part of RmiClientInterceptor.doInvoke(MethodInvocation, RmiInvocationHandler)
. We need to be able to get hold of an exported proxy instance of our service interfaces. We can model this on the means used by Spring to apparently "export" non-RMI interfaces. Spring proxies the interface to generate a RmiInvocationWrapper
for invocation of a non-RMI method, serialises the method details and arguments, then invokes this on the far side of the RMI connection.
- Use a
ProxyFactory
and an RmiInvocationHandler
implementation to proxy the target object.
- Use a new implementation of
RmiBasedExporter
to getObjectToExport()
, and export it using UnicastRemoteObject#export(obj, 0)
.
- For the invocation handler,
rmiInvocationHandler.invoke(invocationFactory.createRemoteInvocation(invocation))
, with a DefaultRemoteInvocationFactory
.
- Handle exceptions and wrap appropriately to avoid seeing
UndeclaredThrowableException
s.
So, we can use RMI to export arbitrary objects. This means we can use one of these objects on the client-side as a parameter to an RMI method call on an RMI server-side object, and when the deserialised stub on the server-side has methods invoked, those methods will execute on the client-side. Magic.