JDK-6799345 : JFC demos threw exception in the Java Console when applets are closed
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 7
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2009-01-30
  • Updated: 2011-10-18
  • Resolved: 2009-12-28
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 JDK 7
6u18Fixed 7 b55Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
There is some exception threw in Java Console during JDK7-b45 SWAT testing.

Bundles location: http://jre.sfbay/java/re/jdk/7/nightly/bundles/<PLATFORM-ARCH>/latest 
OS tested: Windows Vista, Solaris Sparc 10, RHEL5.2

How to reproduce:
1. Install jre from the location above.
2. Use the browser to load SwingSet2 Applet or Java 2D from http://java.sun.com/products/plugin/1.5.0/demos/applets.html
3. The exception is printed to the Java Console when the applet is closed.
This is the stack trace of the exception:
Exception in thread "TimerQueue" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
	at java.util.concurrent.locks.ReentrantLock.unlock(Unknown Source)
	at java.util.concurrent.DelayQueue.take(Unknown Source)
	at javax.swing.TimerQueue.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "SwingWorker-pool-1-thread-1" java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThreadGroup)
	at java.security.AccessControlContext.checkPermission(Unknown Source)
	at java.security.AccessController.checkPermission(Unknown Source)
	at java.lang.SecurityManager.checkPermission(Unknown Source)
	at sun.applet.AppletSecurity.checkAccess(Unknown Source)
	at java.lang.ThreadGroup.checkAccess(Unknown Source)
	at java.lang.Thread.init(Unknown Source)
	at java.lang.Thread.<init>(Unknown Source)
	at java.util.concurrent.Executors$DefaultThreadFactory.newThread(Unknown Source)
	at javax.swing.SwingWorker$6.newThread(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.<init>(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.addWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.processWorkerExit(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

It's a regression starting from b15.

Comments
EVALUATION There are 2 exceptions reported in the description, and it seems both are caused by different problems in Swing code. First, javax.swing.TimerQueue.run() method silently ignores all the interruptions. I don't know why it is implemented this way, but it seems wrong to me, and every InterruptedException should result in a break statement. Second exception occurs in javax.swing.SwingWorker code. This class uses ThreadPoolExecutor to spawn new worker threads, and even registers a ShutdownHook to shut it down correctly. Unfortunately, it is not enough as there may be several AppContexts in the application, and disposing one of them not necessarily leads to JVM shutdown. That's why the code from ShutdownHook should be refactored, for example, to listen to AppContext disposal. See suggested fix for details.
09-02-2009

SUGGESTED FIX --- old/src/share/classes/javax/swing/SwingWorker.java 2009-02-09 18:59:53.000000000 +0300 +++ new/src/share/classes/javax/swing/SwingWorker.java 2009-02-09 18:59:52.000000000 +0300 @@ -778,35 +778,34 @@ threadFactory); appContext.put(SwingWorker.class, executorService); - //register shutdown hook for this executor service + // Don't use ShutdownHook here as it's not enough. We should track + // AppContext disposal instead of JVM shutdown, see 6799345 for details final ExecutorService es = executorService; - final Runnable shutdownHook = - new Runnable() { - final WeakReference<ExecutorService> executorServiceRef = - new WeakReference<ExecutorService>(es); - public void run() { - final ExecutorService executorService = - executorServiceRef.get(); - if (executorService != null) { - AccessController.doPrivileged( - new PrivilegedAction<Void>() { - public Void run() { - executorService.shutdown(); - return null; + appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME, + new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + boolean wasDisposed = (Boolean)pce.getOldValue(); + boolean disposed = (Boolean)pce.getNewValue(); + if (!wasDisposed && disposed) { + final WeakReference<ExecutorService> executorServiceRef = + new WeakReference<ExecutorService>(es); + final ExecutorService executorService = + executorServiceRef.get(); + if (executorService != null) { + AccessController.doPrivileged( + new PrivilegedAction<Void>() { + public Void run() { + executorService.shutdown(); + return null; + } } - }); + ); + } } } - }; - - AccessController.doPrivileged( - new PrivilegedAction<Void>() { - public Void run() { - Runtime.getRuntime().addShutdownHook( - new Thread(shutdownHook)); - return null; - } - }); + } + ); } return executorService; } --- old/src/share/classes/javax/swing/TimerQueue.java 2009-02-09 18:59:54.000000000 +0300 +++ new/src/share/classes/javax/swing/TimerQueue.java 2009-02-09 18:59:54.000000000 +0300 @@ -191,7 +191,10 @@ } finally { timer.getLock().unlock(); } - } catch (InterruptedException ignore) { + } catch (InterruptedException ie) { + // Shouldn't ignore InterruptedExceptions here, so AppContext + // is disposed gracefully, see 6799345 for details + break; } } }
09-02-2009

EVALUATION see comments
05-02-2009