JDK-4853785 : RMI reuses connections even if set sun.rmi.transport.connectionTimeout prop.to 0
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.rmi
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2003-04-24
  • Updated: 2005-06-21
  • Resolved: 2005-06-21
Description
J2SE Version (please include all output from java -version flag):

java version "1.4.2-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-beta-b19)
Java HotSpot(TM) Client VM (build 1.4.2-beta-b19, mixed mode)


Does this problem occur on J2SE 1.3, 1.4 or 1.4.1?  Yes / No (pick one)

Yes (tested with 1.4.1)


Operating System Configuration Information (be specific):

Windows 2000, but it should also happen on other platforms.


Hardware Configuration Information (be specific):

regular P4 PC


Bug Description:

RMI reuses RMI connections even if I set sun.rmi.transport.connectionTimeout property to "0".



Steps to Reproduce (be specific):

Code to reproduce this bug is not trivial, but bugfix should be. If I make two consecutive RMI calls, same RMI connection is used (not always, but quite often), even if I set sun.rmi.transport.connectionTimeout property to zero.
I need to set different timeouts for calls of different methods of the same remote object. As RMI lacks this functionality, I've made my own RMISocketFactory, that makes it possible - but connections should not be reused, to make it work.


Please see the attachment "rmitest.jar" for test case.

Steps to reproduce the problem:

1. Put rmitest.jar on the classpath.
2. Run rmitest.RmiServer
3. Run rmiTest.RmiClient
The result is:
Start
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
Got exception: java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: 
    java.net.SocketTimeoutException: Read timed out
java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: 
    java.net.SocketTimeoutException: Read timed out
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:203)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:133)
    at rmitest.RmiServer_Stub.rGetData(RmiServer_Stub.java:35)
    at rmitest.RmiClient.main(RmiClient.java:16)
Caused by: java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:183)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:201)
    at java.io.DataInputStream.readByte(DataInputStream.java:331)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:189)
    ... 3 more

, as the response from RmiServer takes 1000ms, but RmiClient reused the socket and therefore crashed.
If I uncomment the first sleep (the code in RmiClient looks like):

      Thread.sleep(1);
      TimeoutableRMISocketFactory.setTimeout(0, true);
      rmiServer.rGetData("To je tudi en test.");
      //Thread.sleep(1);
      //TimeoutableRMISocketFactory.setTimeout(100, true);
      rmiServer.rGetData("To je tudi en test.");

, this is what I get:

Start
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 0
Done.

Actually - this is also wrong, as the second RMI call should be performed with the default timeout (500ms), but socket from the first RMI call was resused.
If I ucomment also the second sleep, I get what this code really should do:
Start
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 0
--> Created socket with soTimeout: 500
Got exception: java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: 
    java.net.SocketTimeoutException: Read timed out
java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: 
    java.net.SocketTimeoutException: Read timed out
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:203)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:133)
    at rmitest.RmiServer_Stub.rGetData(RmiServer_Stub.java:35)
    at rmitest.RmiClient.main(RmiClient.java:19)
Caused by: java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:183)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:201)
    at java.io.DataInputStream.readByte(DataInputStream.java:331)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:189)
    ... 3 more

, and if I use the code like this:
      Thread.sleep(1);
      TimeoutableRMISocketFactory.setTimeout(0, true);
      rmiServer.rGetData("To je tudi en test.");
      Thread.sleep(1);
      TimeoutableRMISocketFactory.setTimeout(100, true);
      rmiServer.rGetData("To je tudi en test.");

the result is also what I expected:

Start
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 500
--> Created socket with soTimeout: 0
--> Created socket with soTimeout: 100
Got exception: java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: 
    java.net.SocketTimeoutException: Read timed out
java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: 
    java.net.SocketTimeoutException: Read timed out
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:203)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:133)
    at rmitest.RmiServer_Stub.rGetData(RmiServer_Stub.java:35)
    at rmitest.RmiClient.main(RmiClient.java:19)
Caused by: java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:183)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:201)
    at java.io.DataInputStream.readByte(DataInputStream.java:331)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:189)
    ... 3 more

To make short things short: this is what I should get without calling Thread.sleep(1) in my code. This "workaround" is not really a workaround, as if the RmiClient was a multithreaded application, I couldn't rely on sleeping to disable connection reuse. Is there any other way to prevent RMI connection reuse, then setting sun.rmi.transport.connectionTimeout to zero?

Comments
EVALUATION This is not a bug, as there is no guarantee that the Sun-implementation-specific system property "sun.rmi.transport.ConnectionTimeout" can be used to disable connection reuse (and it was definitely not intended to serve that purpose). I'm changing this report to an RFE for some future consideration, but we're still disinclined to advocate using this global system property setting this way. A fully-portable way of acheiving the desired effect is to separate the methods that require different timeouts into two different remote objects, and export those remote objects with separate client socket factories (RMIClientSocketFactory instances) that each carry the appropriate socket read timeout value in their instance states. A not-quite-so-portable way that should work with Sun's current RMI implementations is to have the InputStream.read methods of the sockets produced by the client socket factory set the socket read timeout based on the setting of a thread local variable, and use an API on the socket factory to set this thread local variable to the desired timeout value before making a remote call. This approach depends on the assumption that the socket read operations for reading the result of a remote call will be done by the RMI implementation in the same thread as the remote call is made in, which happens to be true in Sun's current implementations. ###@###.### 2003-04-25
25-04-2003