6799345: JFC demos threw exception in the Java Console when applets are closed
Reviewed-by: alexp, peterz
--- a/jdk/src/share/classes/javax/swing/SwingWorker.java Thu Feb 05 19:16:13 2009 +0300
+++ b/jdk/src/share/classes/javax/swing/SwingWorker.java Thu Feb 12 14:19:06 2009 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -778,35 +778,33 @@
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 disposed = (Boolean)pce.getNewValue();
+ if (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;
}
--- a/jdk/src/share/classes/javax/swing/TimerQueue.java Thu Feb 05 19:16:13 2009 +0300
+++ b/jdk/src/share/classes/javax/swing/TimerQueue.java Thu Feb 12 14:19:06 2009 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -191,7 +191,12 @@
} finally {
timer.getLock().unlock();
}
- } catch (InterruptedException ignore) {
+ } catch (InterruptedException ie) {
+ // Shouldn't ignore InterruptedExceptions here, so AppContext
+ // is disposed gracefully, see 6799345 for details
+ if (AppContext.getAppContext().isDisposed()) {
+ break;
+ }
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/system/6799345/TestShutdown.java Thu Feb 12 14:19:06 2009 +0300
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ @bug 6799345
+ @summary Tests that no exceptions are thrown from TimerQueue and
+SwingWorker on AppContext shutdown
+ @author art
+ @run main TestShutdown
+*/
+
+import java.awt.*;
+import java.awt.event.*;
+
+import java.util.*;
+
+import javax.swing.*;
+
+import sun.awt.*;
+
+public class TestShutdown
+{
+ private static AppContext targetAppContext;
+
+ private static JFrame f;
+ private static JTextField tf;
+
+ private static volatile boolean exceptionsOccurred = false;
+ private static volatile boolean appcontextInitDone = false;
+
+ private static int timerValue = 0;
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ ThreadGroup tg = new TestThreadGroup("TTG");
+ Thread t = new Thread(tg, new TestRunnable(), "InitThread");
+ t.start();
+
+ while (!appcontextInitDone)
+ {
+ Thread.sleep(500);
+ }
+
+ targetAppContext.dispose();
+
+ if (exceptionsOccurred)
+ {
+ throw new RuntimeException("Test FAILED: some exceptions occurred");
+ }
+ }
+
+ static void initGUI()
+ {
+ f = new JFrame("F");
+ f.setBounds(100, 100, 200, 100);
+ tf = new JTextField("Test");
+ f.add(tf);
+ f.setVisible(true);
+ }
+
+ static void startGUI()
+ {
+ // caret blink Timer
+ tf.requestFocusInWindow();
+
+ // misc Timer
+ ActionListener al = new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent ae)
+ {
+ System.out.println("Timer tick: " + timerValue++);
+ }
+ };
+ new javax.swing.Timer(30, al).start();
+ }
+
+ static class TestThreadGroup extends ThreadGroup
+ {
+ public TestThreadGroup(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public synchronized void uncaughtException(Thread thread, Throwable t)
+ {
+ if (t instanceof ThreadDeath)
+ {
+ // this one is expected, rethrow
+ throw (ThreadDeath)t;
+ }
+ System.err.println("Test FAILED: an exception is caught in the " +
+ "target thread group on thread " + thread.getName());
+ t.printStackTrace(System.err);
+ exceptionsOccurred = true;
+ }
+ }
+
+ static class TestRunnable implements Runnable
+ {
+ @Override
+ public void run()
+ {
+ SunToolkit stk = (SunToolkit)Toolkit.getDefaultToolkit();
+ targetAppContext = stk.createNewAppContext();
+
+ // create and show frame and text field
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ initGUI();
+ }
+ });
+ stk.realSync();
+
+ // start some Timers
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ startGUI();
+ }
+ });
+ stk.realSync();
+
+ // start multiple SwingWorkers
+ while (!Thread.interrupted())
+ {
+ try
+ {
+ new TestSwingWorker().execute();
+ Thread.sleep(40);
+ }
+ catch (Exception e)
+ {
+ // exception here is expected, skip
+ break;
+ }
+ }
+ }
+ }
+
+ static class TestSwingWorker extends SwingWorker<String, Integer>
+ {
+ @Override
+ public String doInBackground()
+ {
+ Random r = new Random();
+ for (int i = 0; i < 10; i++)
+ {
+ try
+ {
+ int delay = r.nextInt() % 50;
+ Thread.sleep(delay);
+ publish(delay);
+ }
+ catch (Exception z)
+ {
+ break;
+ }
+ }
+ if (!appcontextInitDone)
+ {
+ appcontextInitDone = true;
+ }
+ return "Done";
+ }
+
+ @Override
+ public void process(java.util.List<Integer> chunks)
+ {
+ for (Integer i : chunks)
+ {
+ System.err.println("Processed: " + i);
+ }
+ }
+ }
+}