JDK-5026942 : rmi srv in applet throws IllegalStateException when returns remote obj
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.rmi
  • Affected Version: 1.4.2,6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2004-04-05
  • Updated: 2005-09-20
  • Resolved: 2005-09-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 betaFixed
Related Reports
Relates :  
Description
Name: gm110360			Date: 04/05/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

ADDITIONAL OS VERSION INFORMATION :

Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
When an applet starts a RMI server and then reloaded, IllegalStateException is thrown on server side when the RMI server returns new remote object.

Thereafter,  client frequently gets UnmarshallException / IOException  when using remote object returned by rmi server. Sometimes, the client also rceives NoSuchObjectException.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. run build.bat to build the attached source files
2. put everything in a web server
3. visit the page TestApplet.html from a browser
4. run TestHello



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -

say hello!
get hello
...
ACTUAL -
say hello!
get hello

[.. refresh browser here ..]

say hello!
get hello
say Failed: no such object in table
java.rmi.NoSuchObjectException: no such object in table
        at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Stream
RemoteCall.java:247)
        at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:
223)
        at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:133)
        at HelloImpl_Stub.sayHello(Unknown Source)
        at TestHello.main(TestHello.java:11)



REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
File list:
------------
build.bat
GetHello.java
Hello.java
TestApplet.html
TEstApplet.java
TestHello.java

build.bat
-----------
javac *.java
rmic GetHelloImpl
rmic HelloImpl


GetHello.java
-----------
public interface GetHello extends java.rmi.Remote {

	public Hello getHello () throws java.rmi.RemoteException;

}

Hello.java
-----------
public interface Hello extends java.rmi.Remote {

	public String sayHello () throws java.rmi.RemoteException;

}

TestApplet.html
-----------
<html>
<body>
<applet code="TestApplet.class" codebase="." width="2" height="2">
</applet>
</body>
</html>


TestHello.java
----------------
import java.rmi.*;
import java.security.*;


public class TestHello {

	public static void main (String [] args) {
	    try {
		GetHello gh = GetHelloFactory.getGetHello();
		Hello h = null;
		while (true) {
			System.out.println ("get hello");
			h=gh.getHello();
			System.out.print ("say ");
			System.out.println (h.sayHello());
		}
	    } catch (Exception e)
	    {
			System.out.println ("Failed: "+ e.getMessage());
			e.printStackTrace();
	    }
	    
	}
}

class GetHelloFactory {

	public static GetHello getGetHello ()
		throws Exception	{
				
			return  (GetHello)Naming.lookup(GetHello.class.getName());
	}

}





TEstApplet.java
-----------
import java.applet.*;

import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.RemoteException;
import java.rmi.server.*;


public class TestApplet extends Applet implements Remote {

	public void init() {

		Registry reg = null;

		try {
			reg = LocateRegistry.createRegistry(1099);
		} catch (RemoteException e) {}

		try {
			if (reg == null) reg = LocateRegistry.getRegistry(1099);						Naming.rebind(GetHello.class.getName(), new GetHelloImpl());
		} catch (Exception e)
		{
			System.out.println ("Failed: "+ e.getMessage());
			e.printStackTrace();
		}
	}

		

	public static void main (String [] args)
	{
		Applet applet=new TestApplet();
		applet.init();
	}
}

class GetHelloImpl extends java.rmi.server.UnicastRemoteObject implements GetHello {

	public GetHelloImpl ()  throws java.rmi.RemoteException { super(); }
	
	public Hello getHello () throws java.rmi.RemoteException {
		return (Hello) new HelloImpl();
	}

}


class HelloImpl extends java.rmi.server.UnicastRemoteObject implements Hello {

	public HelloImpl ()  throws java.rmi.RemoteException { super(); }

	public String sayHello () throws java.rmi.RemoteException {
		return "hello!";
	}
}


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Launch a JVM using Runtime.exec() and run the rmi server from it
(Incident Review ID: 245860) 
======================================================================

Comments
EVALUATION Fixed in Mustang along with 5059346, verified with this bug's test case.
16-09-2005

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mustang
13-08-2004

EVALUATION The provided source codes do not seem to work. I revised the rmi client-server and was able to reproduce the reported problem. See attached src package applet.tar.gz for codes to reproduce problem. Platform/OS/Browser involved: I586/WinXP/IE 6. Problem is better described as: RMI server is launched (binding) from an applet (ServerApplet). RMI client is a java application (HelloClient) which continuously invokes remote object (GetHello) to retrieve the "BONJOUR!" string. Operation is successful until ServerApplet is restarted (using browser's reload page) on the same browser session. Even after "rebind" of GetHello, restarting HelloClient runs only for a short while then exits with error: "Failed: no such object in table java.rmi.NoSuchObjectException: no such object in table at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:247) at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:223) at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:126) at HelloImpl_Stub.sayHello(Unknown Source) at HelloClient.main(HelloClient.java:13)" Steps to reproduce: - Unbundle src package at a web server's location (e.g, Apache Web Server: http://localhost/rmi/) and compile using build.bat. Both server and client refer to "localhost" for hostname; please change it appropriately if needed for your testing environment. - On a console, start rmiregistry at port 1100: $ rmiregistry 1100 - Open IE browser, open its Java Console and set trace level to 5. - browse to Test.html location, e.g, http://localhost/rmi/Test.html. Once applet is up, click on "Bind Remote Object". - On a console, start the client: $ java -classpath . HelloClient. Should see continuous stream of: "say: BONJOUR!" - Refresh the page on the browser, HelloClient stops. - Click on "Bind Remote Object" on the browser then restart HelloClient on the console. - Notice that the client runs for a short while then the above mentioned error occurs. On the server side, the Java Console trace shows the following: "Exception in thread "RMI TCP Connection(817)-127.0.0.1" java.lang.IllegalStateException: Timer already cancelled. at java.util.Timer.sched(Unknown Source) at java.util.Timer.schedule(Unknown Source) at sun.rmi.transport.DGCAckHandler.startTimer(Unknown Source) at sun.rmi.transport.ConnectionOutputStream.done(Unknown Source) at sun.rmi.transport.StreamRemoteCall.releaseOutputStream(Unknown Source) at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) at sun.rmi.transport.Transport$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.Transport.serviceCall(Unknown Source) at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) at java.lang.Thread.run(Unknown Source)" - Regardless of how many times you try to "rebind". You can also type "x" on the Java Console panel to clear the AppletClassLoader before restarting ServerApplet. But as long as the same browser instance is used, the operation still fails. The only way to make operation resume again is to kill that browser instance and restart ServerApplet on a new browser session. For this problem: You can start ServerApplet in "-verbose" for more details. With the AppletClassLoader cleared before restarting ServerApplet, the only things remained "old" with the browser session are: the JVM and RMIClassLoader. It's possible that the above issue is because of RMIClassLoader reusing of some "static" thread object(s) which were in strange state due to Applet.destroy(). On JPI side, the best that we can do when restarting applet on the same browser session with a new AppletClassLoader. But that doesn't seem to take care of this problem as RMI's classes are handled with its own classloader. Will seek advice with RMI team. ###@###.### 2004-04-08 One of the RMI implementation's threads is (for no intended reason) getting created in the applet's thread group (for the first incarnation of the applet), and when the browser page is refreshed, the first incarnation of the applet gets destroyed, which involes terminating all of the threads in its thread group, including this thread of the RMI implementation. Terminating this thread of the RMI implementation causes a couple of problems: (1) After marshalling the result of a remote invocation in which the result included remote references, it causes the above IllegalStateException to occur. Depending on timing, this could cause an immediately subsequent invocation over the same connection to fail (see 4529396, although this effect hasn't been observed), and the exception trace output is itself undesirable. (2) It also disables one aspect of RMI's distributed garbage collection (DGC) algorithm, which opens up a race condition: there becomes a window of time during which an otherwise unreferenced remote object being returned from a remote method invocation (like in this applet's GetHelloImpl.getHello method) could get locally garbage collected (even when no network failures have occurred). An invocation on the stub for a garbage collected remote object throws a NoSuchObjectException, as observed here. Repeated invocations after this thread has been terminated will eventually expose this race condition. The J2SE RMI implementation generally tries to be very careful about creating its internal threads in such a way that they do not get permanently associated with any arbitrary application-specific context (thread group, access control context, context class loader, etc.). Therefore, almost all of the threads created by the RMI implementation do not get created in an application-specific thread group, such as in an arbitrary applet's thread group. In 1.4, however, the fix for 4017232 introduced the use of a java.util.Timer by the RMI implementation, and the java.util.Timer API does not support a way to specify a factory for the thread it creates (or a way to control the thread group of the thread it creates)-- the Timer implementation just creates its thread in the default thread group (as determined by the security manager and the group of the creating thread). Therefore, this one internal RMI implementation thread can get created in the arbitrary thread group that is the default when it first needs to get created-- in this case, the thread group of the first incarnation of the applet. When the applet is destroyed, this thread in the applet's thread group is terminated, and the associated Timer will be effectively disabled. This Timer, however, is intended to work past the lifetime of the applet (or any other particular application-specific context). The fix is to change the RMI implementation so that it again no longer creates long-lived threads in an arbitrary thread group-- which implies replacing the use of java.util.Timer with something else. In 1.5, the java.util.concurrent package provides utilities (ScheduledExecutorService/ScheduledThreadPool) with the same ability to schedule delayed execution of tasks but also the ability to specify a thread factory (ThreadFactory) for controlling the creation of the threads used to execute the tasks, which seems like exactly what we need here. (And there are other delayed asynchronous activities performed by the RMI implementation which are currently performed by starting independent threads that sleep until the delay has expired; it would seem better to use a shared ScheduledExecutorService for all such activities.) ###@###.### 2004-06-04
04-06-2004