updates for 0.9.4
[debian/openrocket] / src / net / sf / openrocket / gui / main / ExceptionHandler.java
1 package net.sf.openrocket.gui.main;
2
3 import javax.swing.JOptionPane;
4 import javax.swing.SwingUtilities;
5
6 import net.sf.openrocket.gui.dialogs.BugReportDialog;
7
8
9 public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
10
11         private static final int MEMORY_RESERVE = 512*1024;
12         
13         /**
14          * A memory reserve of 0.5 MB of memory, that can be freed when showing the dialog.
15          */
16         private static volatile byte[] memoryReserve = null;
17         
18         private static ExceptionHandler instance = null;
19         
20         
21         private volatile boolean handling = false;
22         
23         
24         
25         
26         @Override
27         public void uncaughtException(final Thread t, final Throwable e) {
28                 
29                 // Free memory reserve if out of memory
30                 if (e instanceof OutOfMemoryError) {
31                         memoryReserve = null;
32                         handling = false;
33                 }
34
35                 e.printStackTrace();
36                 
37                 try {
38                         
39                         if (handling) {
40                                 System.err.println("Exception is currently being handled, ignoring:");
41                                 e.printStackTrace();
42                                 return;
43                         }
44                         
45                         handling = true;
46                         
47                         // Show on the EDT
48                         if (SwingUtilities.isEventDispatchThread()) {
49                                 showDialog(t, e);
50                         } else {
51                     SwingUtilities.invokeAndWait(new Runnable() {
52                         public void run() {
53                             showDialog(t, e);
54                         }
55                     });
56                         }
57                         
58                 } catch (Throwable ex) {
59                         
60                         // Make sure the handler does not throw any exceptions
61                         try {
62                                 System.err.println("Exception in exception handler, dumping exception:");
63                                 ex.printStackTrace();
64                         } catch (Throwable ignore) { }
65                         
66                 } finally {
67                         // Mark handling as completed
68                         handling = false;
69                 }
70                 
71         }
72
73         
74         /**
75          * Handle an error condition programmatically without throwing an exception.
76          * This can be used in cases where recovery of the error is desirable.
77          * 
78          * @param message       the error message.
79          */
80         public static void handleErrorCondition(String message) {
81                 handleErrorCondition(new InternalException(message));
82         }
83         
84
85         /**
86          * Handle an error condition programmatically without throwing an exception.
87          * This can be used in cases where recovery of the error is desirable.
88          * 
89          * @param message       the error message.
90          * @param exception     the exception that occurred.
91          */
92         public static void handleErrorCondition(String message, Exception exception) {
93                 handleErrorCondition(new InternalException(message, exception));
94         }
95         
96         
97         /**
98          * Handle an error condition programmatically without throwing an exception.
99          * This can be used in cases where recovery of the error is desirable.
100          * 
101          * @param exception             the exception that occurred.
102          */
103         public static void handleErrorCondition(final Exception exception) {
104                 final ExceptionHandler handler;
105
106                 try {
107
108                         if (instance == null) {
109                                 handler = new ExceptionHandler();
110                         } else {
111                                 handler = instance;
112                         }
113
114                         final Thread thread = Thread.currentThread();
115
116                         if (SwingUtilities.isEventDispatchThread()) {
117                                 handler.showDialog(thread, exception);
118                         } else {
119                                 SwingUtilities.invokeAndWait(new Runnable() {
120                                         public void run() {
121                                                 handler.showDialog(thread, exception);
122                                         }
123                                 });
124                         }
125                 } catch (Exception e) {
126                         e.printStackTrace();
127                 }
128         }
129         
130         
131         /**
132          * The actual handling routine.
133          * 
134          * @param t             the thread that caused the exception, or <code>null</code>.
135          * @param e             the exception.
136          */
137         private void showDialog(Thread t, Throwable e) {
138                 
139                 // Out of memory
140                 if (e instanceof OutOfMemoryError) {
141                         JOptionPane.showMessageDialog(null, 
142                                         new Object[] { 
143                                                 "Out of memory!",
144                                                 "<html>You should immediately close unnecessary design windows,<br>" +
145                                                 "save any unsaved designs and restart OpenRocket!"
146                                         }, "Out of memory", JOptionPane.ERROR_MESSAGE);
147                         return;
148                 }
149                 
150                 // Unknown Error
151                 if (!(e instanceof Exception)) {
152                         JOptionPane.showMessageDialog(null, 
153                                         new Object[] { 
154                                                 "An unknown Java error occurred:",
155                                                 e.getMessage(),
156                                                 "<html>You should immediately close unnecessary design windows,<br>" +
157                                                 "save any unsaved designs and restart OpenRocket!"
158                                         }, "Unknown Java error", JOptionPane.ERROR_MESSAGE);
159                         return;
160                 }
161                 
162                 
163                 // Normal exception, show question dialog
164                 String msg = e.getClass().getSimpleName() + ": " + e.getMessage();
165                 if (msg.length() > 90) {
166                         msg = msg.substring(0, 90) + "...";
167                 }
168                 
169                 
170                 int selection = JOptionPane.showOptionDialog(null, new Object[] {
171                                 "OpenRocket encountered an uncaught exception.  This typically signifies " +
172                                 "a bug in the software.", 
173                                 "<html><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + msg + "</em>",
174                                 " ",
175                                 "Please take a moment to report this bug to the developers.",
176                                 "This can be done automatically if you have an Internet connection."
177                                 }, "Uncaught exception", JOptionPane.DEFAULT_OPTION, 
178                                 JOptionPane.ERROR_MESSAGE, null, 
179                                 new Object[] { "View bug report", "Close" }, "View bug report");
180                 
181                 if (selection != 0) {
182                         // User cancelled
183                         return;
184                 }
185                 
186                 // Show bug report dialog
187                 BugReportDialog.showExceptionDialog(null, t, e);
188
189         }
190         
191         
192         
193         /**
194          * Registers the uncaught exception handler.  This should be used to ensure that
195          * all necessary registrations are performed.
196          */
197         public static void registerExceptionHandler() {
198                 
199                 if (instance == null) {
200                         instance = new ExceptionHandler();
201                         Thread.setDefaultUncaughtExceptionHandler(instance);
202                         
203                         // Handler for modal dialogs of Sun's Java implementation
204                         // See bug ID 4499199.
205                         System.setProperty("sun.awt.exception.handler", AwtHandler.class.getName());
206                         
207                         reserveMemory();
208                 }
209                 
210         }
211         
212         
213         private static void reserveMemory() {
214                 memoryReserve = new byte[MEMORY_RESERVE];
215                 for (int i=0; i<MEMORY_RESERVE; i++) {
216                         memoryReserve[i] = (byte)i;
217                 }
218         }
219
220         
221         /**
222          * Handler used in modal dialogs by Sun Java implementation.
223          */
224         public static class AwtHandler {
225                 public void handle(Throwable t) {
226                         
227                         /*
228                          * Detect and ignore bug 6828938 in Sun JRE 1.6.0_14 - 1.6.0_16.
229                          */
230                         if (t instanceof ArrayIndexOutOfBoundsException) {
231                                 final String buggyClass = "sun.font.FontDesignMetrics";
232                                 StackTraceElement[] elements = t.getStackTrace();
233                                 if (elements.length >= 3 &&
234                                                 (buggyClass.equals(elements[0].getClassName()) ||
235                                                  buggyClass.equals(elements[1].getClassName()) ||
236                                                  buggyClass.equals(elements[2].getClassName()))) {
237                                         System.err.println("Ignoring Sun JRE bug 6828938:  " + t);
238                                         return;
239                                 }
240                         }
241                         
242                         
243                         if (instance != null) {
244                                 instance.uncaughtException(Thread.currentThread(), t);
245                         }
246                 }
247         }
248         
249         
250         private static class InternalException extends Exception {
251                 public InternalException() {
252                         super();
253                 }
254
255                 public InternalException(String message, Throwable cause) {
256                         super(message, cause);
257                 }
258
259                 public InternalException(String message) {
260                         super(message);
261                 }
262
263                 public InternalException(Throwable cause) {
264                         super(cause);
265                 }
266         }
267 }