Catching uncaught exception in GUI
In this section, we will discuss how to catch uncaught exceptions in GUI.
Lets see the given below code to identify the uncaught exception :
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; public class Gui extends JFrame { private static final int[] DAYS_PER_MONTH = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; public Gui() { super("GUI Example"); final JTextArea text = new JTextArea(14, 30); getContentPane().add(new JScrollPane(text)); getContentPane().add(new JButton(new AbstractAction("Calculate") { public void actionPerformed(ActionEvent e) { text.setText(""); for (int i=0; i<=DAYS_PER_MONTH.length; i++) { text.append("Month " + (i+1) + ": " + DAYS_PER_MONTH[i] + "\n"); } } }), BorderLayout.NORTH); } public static void main(String[] args) { Gui gui = new Gui(); gui.pack(); gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); gui.show(); } }
First we compile and run this code using javaw.exe as
javaw Gui
When you press the button, everything works ok except the button stay pressed.
When you run the same code using java.exe you will get the following exception displayed on the screen:
java.lang.ArrayIndexOutOfBoundsException: 12 at Gui$1.actionPerformed(Gui.java:16) at javax.swing.AbstractButton.fireActionPerformed at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed at javax.swing.DefaultButtonModel.fireActionPerformed at javax.swing.DefaultButtonModel.setPressed at javax.swing.plaf.basic.BasicButtonListener$ReleasedAction.actionPerformed at javax.swing.SwingUtilities.notifyAction at javax.swing.JComponent.processKeyBinding at javax.swing.JComponent.processKeyBindings at javax.swing.JComponent.processKeyEvent at java.awt.Component.processEvent at java.awt.Container.processEvent at java.awt.Component.dispatchEventImpl at java.awt.Container.dispatchEventImpl at java.awt.Component.dispatchEvent at java.awt.KeyboardFocusManager.redispatchEvent at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions at java.awt.DefaultKeyboardFocusManager.dispatchEvent at java.awt.Component.dispatchEventImpl at java.awt.Container.dispatchEventImpl at java.awt.Window.dispatchEventImpl at java.awt.Component.dispatchEvent at java.awt.EventQueue.dispatchEvent at java.awt.EventDispatchThread.pumpOneEventForHierarchy at java.awt.EventDispatchThread.pumpEventsForHierarchy at java.awt.EventDispatchThread.pumpEvents at java.awt.EventDispatchThread.pumpEvents at java.awt.EventDispatchThread.run |
After printing the above exceptions at console the java event dispatch thread dies after this.
Solution
The Solution of the above problem is ThreadGroup. A thread may corresponds to a Thread Group. The ThreadGroup has a function called as uncaughtException(Thread t, Throwable e) This method can be override and we call it whenever an uncaught exception occur. The event dispatch thread still dies, but at least it will let us know about it.
import javax.swing.*; import java.awt.*; public class ExceptionGroup extends ThreadGroup { public ExceptionGroup() { super("ExceptionGroup"); } public void uncaughtException(Thread t, Throwable e) { JOptionPane.showMessageDialog(findActiveFrame(), e.toString(), "Exception Occurred", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } /** * I hate ownerless dialogs. With this method, we can find the * currently visible frame and attach the dialog to that, instead * of always attaching it to null. */ private Frame findActiveFrame() { Frame[] frames = JFrame.getFrames(); for (int i = 0; i < frames.length; i++) { Frame frame = frames[i]; if (frame.isVisible()) { return frame; } } return null; } }
All we now need to do is start the Gui from within a Thread that belongs to this ExceptionGroup, and we will catch all uncaught exceptions that are caused by the event dispatch thread:
import javax.swing.*; public class BetterGui { public static void main(String[] args) { ThreadGroup exceptionThreadGroup = new ExceptionGroup(); new Thread(exceptionThreadGroup, "Init thread") { public void run() { Gui gui = new Gui(); gui.pack(); gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); gui.show(); } }.start(); } }