Search code examples
javasocketsrmi

It seems that I cannot get the socket factory from an RMI stub. Why is it so?


I am writing an application that uses RMI connection factories so that I can set a time-out on the client side before calling a remote method. I want to do this so the client can wait on a call to a remote method for a predetermined amount of time before giving up and abandoning the call.

I've created a socket factory that facilitates this mechanism. I create the remote stub with UnicastRemoteObject.exportObject(Remote, int, RMIServerSocketFactory, RMIClientSocetFactory) so that the client can use the stub with the customised socket factory - the class definition of which is known to both devices.

The client's socket factory needs to set the time-out before it calls the server. The client decides the length of this time-out. I can craft a socket factory that works in this way. However, it seems that I cannot ensure on the client that the remote stub has this customised socket factory, and hence I cannot ensure that the client socket factory will create a client socket with a time-out.

I am wondering if there is a method which would work in a way like I would envisage Remote.getClientFactory() should work? This seems to me to be an obvious feature that isn't covered by the RMI spec. In the absence of this method, is there perhaps any well-used 'hack' to retrieve the client's socket factory on the client so a time-out can be specified?


Solution

    1. It wouldn't do you much good even if you could, as what you need is not the factory but the actual socket that is about to be used to make the call, and that's not predicable due to client-side connection pooling.

    2. You could try adjusting sun.rmi.transport.tcp.responseTimeout prior to each call, but I have a nasty feeling it is only read once in the lifetime of the JVM.

    3. Otherwise you could use a different socket factory per remote object, and a different remote object per remote interface, and a different remote interface per remote method, so that each socket factory is uniquely associated with a unique remote method ... and then assign a socket read timeout to each factory as required, but it is all still server-side; and it plays havoc with connection-pooling.

    4. Or if you want the unofficial under-the-hood may-not-work-next-release non-compliant don't use sun.* classes naughty version:

      RemoteRef   remoteRef;
      if (stub instanceof RemoteStub)
      {
          remoteRef = ((RemoteStub)stub).getRef();
      }
      else
      {
          // dynamic proxy
          RemoteObjectInvocationHandler   roih = (RemoteObjectInvocationHandler)java.lang.reflect.Proxy.getInvocationHandler(stub);
          remoteRef = roih.getRef();
      }
      if (remoteRef instanceof sun.rmi.server.UnicastRef2)
      {
          // JRMP stub with client socket factory.
          // NB UnicastRef.getLiveRef() was added somewhere between 1.3 and 1.6.
          // Previously it was only obtainable via reflection.
          RMIClientSocketFactory csf = ((sun.rmi.server.UnicastRef2)remoteRef).getLiveRef().getClientSocketFactory();
          // YOUR CODE GOES HERE
          // Note that 'csf' can still be null here, if you exported the remote object with an *explicitly null* client socket factory parameter.
      }
      

      However note the caveat I started with at (1). This may not do you much good at all.