| package org.jdesktop.swinghelper.debug; |
| |
| import java.awt.EventQueue; |
| import java.awt.FlowLayout; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.WindowAdapter; |
| import java.awt.event.WindowEvent; |
| import javax.swing.JButton; |
| import javax.swing.JDialog; |
| import javax.swing.JFrame; |
| import javax.swing.JLabel; |
| import javax.swing.SwingUtilities; |
| |
| final class Tests { |
| |
| public static void main(String[] args) { |
| if (args.length == 0) { |
| return; |
| } |
| String mode = args[0]; |
| |
| new EventDispatchThreadHangMonitor().initMonitoring(); |
| |
| if ("deadlock".equals(mode)) { |
| runDeadlockTest(); |
| return; |
| } |
| |
| EventQueue.invokeLater( |
| () -> { |
| final JFrame frame = new JFrame(); |
| frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
| frame.setLocationRelativeTo(null); |
| switch (mode) { |
| case "exception": |
| runExceptionTest(frame); |
| break; |
| case "focus": |
| runFocusTest(frame); |
| break; |
| case "modal-hang": |
| runModalTest(frame, true); |
| break; |
| case "modal-no-hang": |
| runModalTest(frame, false); |
| break; |
| default: |
| System.err.println("unknown regression test '" + mode + "'"); |
| System.exit(1); |
| } |
| frame.pack(); |
| frame.setVisible(true); |
| }); |
| } |
| |
| public static void runDeadlockTest() { |
| class Locker { |
| |
| private Locker locker; |
| |
| public void setLocker(Locker locker) { |
| this.locker = locker; |
| } |
| |
| public synchronized void tryToDeadlock() { |
| String unused = locker.toString(); |
| } |
| |
| @Override |
| @SuppressWarnings("CatchAndPrintStackTrace") |
| public synchronized String toString() { |
| try { |
| Thread.sleep(50); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| return super.toString(); |
| } |
| } |
| |
| final Locker one = new Locker(); |
| final Locker two = new Locker(); |
| one.setLocker(two); |
| two.setLocker(one); |
| |
| // Deadlock expected here: |
| for (int i = 0; i < 100; i++) { |
| SwingUtilities.invokeLater(one::tryToDeadlock); |
| two.tryToDeadlock(); |
| } |
| } |
| |
| // If we don't do our post-dispatch activity in a finally block, we'll |
| // report bogus hangs. |
| private static void runExceptionTest(final JFrame frame) { |
| JButton button = new JButton("Throw Exception"); |
| button.addActionListener( |
| new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| // This shouldn't cause us to report a hang. |
| throw new IllegalStateException("Nobody expects the Spanish Inquisition!"); |
| } |
| }); |
| frame.add(button); |
| } |
| |
| // A demonstration of nested calls to dispatchEvent caused by SequencedEvent. |
| private static void runFocusTest(final JFrame frame) { |
| final JDialog dialog = new JDialog(frame, "Non-Modal Dialog"); |
| dialog.add(new JLabel("Close me!")); |
| dialog.pack(); |
| dialog.setLocationRelativeTo(frame); |
| dialog.addWindowFocusListener( |
| new WindowAdapter() { |
| @Override |
| public void windowGainedFocus(WindowEvent e) { |
| System.out.println("FocusTest.windowGainedFocus"); |
| // If you don't cope with nested calls to dispatchEvent, you won't detect this. |
| // See java.awt.SequencedEvent for an example. |
| sleep(2500); |
| } |
| }); |
| JButton button = new JButton("Show Non-Modal Dialog"); |
| button.addActionListener( |
| new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| dialog.setVisible(true); |
| } |
| }); |
| frame.add(button); |
| } |
| |
| // A demonstration of the problems of dealing with modal dialogs. |
| private static void runModalTest(final JFrame frame, final boolean shouldSleep) { |
| System.out.println(shouldSleep ? "Expect hangs!" : "There should be no hangs..."); |
| JButton button = new JButton("Show Modal Dialog"); |
| button.addActionListener( |
| new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (shouldSleep) { |
| sleep(2500); // This is easy. |
| } |
| JDialog dialog = new JDialog(frame, "Modal dialog", /* modal= */ true); |
| dialog.setLayout(new FlowLayout()); |
| dialog.add(new JLabel("Close this dialog!")); |
| final JLabel label = new JLabel(" "); |
| dialog.add(label); |
| dialog.pack(); |
| dialog.setLocation(frame.getX() - 100, frame.getY()); |
| |
| // Make sure the new event pump has some work to do, each unit of which is insufficient |
| // to cause a hang. |
| new Thread( |
| () -> { |
| for (int i = 0; i <= 100000; ++i) { |
| final int value = i; |
| EventQueue.invokeLater(() -> label.setText(Integer.toString(value))); |
| } |
| }) |
| .start(); |
| |
| dialog.setVisible(true); |
| |
| if (shouldSleep) { |
| sleep( |
| 2500); // If you don't distinguish different stack traces, you won't report this. |
| } |
| } |
| }); |
| frame.add(button); |
| } |
| |
| @SuppressWarnings("CatchAndPrintStackTrace") |
| private static void sleep(long ms) { |
| try { |
| System.out.println("Sleeping for " + ms + " ms on " + Thread.currentThread() + "..."); |
| Thread.sleep(ms); |
| System.out.println("Finished sleeping..."); |
| } catch (InterruptedException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| private Tests() { |
| } |
| } |