logging and unit test updates
[debian/openrocket] / src / net / sf / openrocket / gui / main / ExceptionHandler.java
index 93474b1c6b17262e96b0ac43fefcf9203570ed06..4099f019120d4c4942c5db5c1b795854ea96d29d 100644 (file)
@@ -12,8 +12,10 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        
        /**
         * A memory reserve of 0.5 MB of memory, that can be freed when showing the dialog.
+        * <p>
+        * This field is package-private so that the JRE cannot optimize its use away.
         */
-       private static volatile byte[] memoryReserve = null;
+       static volatile byte[] memoryReserve = null;
        
        private static ExceptionHandler instance = null;
        
@@ -27,16 +29,17 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(final Thread t, final Throwable e) {
                
                // Free memory reserve if out of memory
-               if (e instanceof OutOfMemoryError) {
+               if (isOutOfMemoryError(e)) {
                        memoryReserve = null;
                        handling = false;
                }
 
+               e.printStackTrace();
+               
                try {
                        
                        if (handling) {
-                               System.err.println("Exception is currently being handled, " +
-                                               "dumping exception instead:");
+                               System.err.println("Exception is currently being handled, ignoring:");
                                e.printStackTrace();
                                return;
                        }
@@ -70,6 +73,63 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        }
 
        
+       /**
+        * Handle an error condition programmatically without throwing an exception.
+        * This can be used in cases where recovery of the error is desirable.
+        * 
+        * @param message       the error message.
+        */
+       public static void handleErrorCondition(String message) {
+               handleErrorCondition(new InternalException(message));
+       }
+       
+
+       /**
+        * Handle an error condition programmatically without throwing an exception.
+        * This can be used in cases where recovery of the error is desirable.
+        * 
+        * @param message       the error message.
+        * @param exception     the exception that occurred.
+        */
+       public static void handleErrorCondition(String message, Exception exception) {
+               handleErrorCondition(new InternalException(message, exception));
+       }
+       
+       
+       /**
+        * Handle an error condition programmatically without throwing an exception.
+        * This can be used in cases where recovery of the error is desirable.
+        * 
+        * @param exception             the exception that occurred.
+        */
+       public static void handleErrorCondition(final Exception exception) {
+               final ExceptionHandler handler = instance;
+
+               try {
+
+                       if (handler == null) {
+                               // Not initialized, simply print the exception
+                               exception.printStackTrace();
+                               return;
+                       }
+
+                       final Thread thread = Thread.currentThread();
+
+                       if (SwingUtilities.isEventDispatchThread()) {
+                               handler.showDialog(thread, exception);
+                       } else {
+                               SwingUtilities.invokeAndWait(new Runnable() {
+                                       public void run() {
+                                               handler.showDialog(thread, exception);
+                                       }
+                               });
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+       
+       
        /**
         * The actual handling routine.
         * 
@@ -79,22 +139,28 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        private void showDialog(Thread t, Throwable e) {
                
                // Out of memory
-               if (e instanceof OutOfMemoryError) {
+               if (isOutOfMemoryError(e)) {
                        JOptionPane.showMessageDialog(null, 
                                        new Object[] { 
-                                               "Out of memory!",
-                                               "<html>You should immediately close unnecessary design windows,<br>" +
+                                               "OpenRocket can out of available memory!",
+                                               "You should immediately close unnecessary design windows,",
                                                "save any unsaved designs and restart OpenRocket!"
                                        }, "Out of memory", JOptionPane.ERROR_MESSAGE);
                        return;
                }
                
+               // Create the message
+               String msg = e.getClass().getSimpleName() + ": " + e.getMessage();
+               if (msg.length() > 90) {
+                       msg = msg.substring(0, 80) + "...";
+               }
+               
                // Unknown Error
                if (!(e instanceof Exception)) {
                        JOptionPane.showMessageDialog(null, 
                                        new Object[] { 
                                                "An unknown Java error occurred:",
-                                               e.getMessage(),
+                                               msg,
                                                "<html>You should immediately close unnecessary design windows,<br>" +
                                                "save any unsaved designs and restart OpenRocket!"
                                        }, "Unknown Java error", JOptionPane.ERROR_MESSAGE);
@@ -102,11 +168,12 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
                }
                
                
-               // Normal exception, show question dialog
-               
+               // Normal exception, show question dialog               
                int selection = JOptionPane.showOptionDialog(null, new Object[] {
                                "OpenRocket encountered an uncaught exception.  This typically signifies " +
-                               "a bug in the software.", " ",
+                               "a bug in the software.", 
+                               "<html><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + msg + "</em>",
+                               " ",
                                "Please take a moment to report this bug to the developers.",
                                "This can be done automatically if you have an Internet connection."
                                }, "Uncaught exception", JOptionPane.DEFAULT_OPTION, 
@@ -145,6 +212,9 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        }
        
        
+       /**
+        * Reserve the buffer memory that is freed in case an OutOfMemoryError occurs.
+        */
        private static void reserveMemory() {
                memoryReserve = new byte[MEMORY_RESERVE];
                for (int i=0; i<MEMORY_RESERVE; i++) {
@@ -153,14 +223,73 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        }
 
        
+       
+       /**
+        * Return whether this throwable was caused by an OutOfMemoryError
+        * condition.  An exception is deemed to be caused by OutOfMemoryError
+        * if the throwable or any of its causes is of the type OutOfMemoryError.
+        * <p>
+        * This method is required because Apple's JRE implementation sometimes
+        * masks OutOfMemoryErrors within RuntimeExceptions.  Idiots.
+        * 
+        * @param t             the throwable to examine.
+        * @return              whether this is an out-of-memory condition.
+        */
+       private boolean isOutOfMemoryError(Throwable t) {
+               while (t != null) {
+                       if (t instanceof OutOfMemoryError)
+                               return true;
+                       t = t.getCause();
+               }
+               return false;
+       }
+       
+       
+       
        /**
         * Handler used in modal dialogs by Sun Java implementation.
         */
        public static class AwtHandler {
                public void handle(Throwable t) {
+                       
+                       /*
+                        * Detect and ignore bug 6828938 in Sun JRE 1.6.0_14 - 1.6.0_16.
+                        */
+                       if (t instanceof ArrayIndexOutOfBoundsException) {
+                               final String buggyClass = "sun.font.FontDesignMetrics";
+                               StackTraceElement[] elements = t.getStackTrace();
+                               if (elements.length >= 3 &&
+                                               (buggyClass.equals(elements[0].getClassName()) ||
+                                                buggyClass.equals(elements[1].getClassName()) ||
+                                                buggyClass.equals(elements[2].getClassName()))) {
+                                       System.err.println("Ignoring Sun JRE bug 6828938:  " + t);
+                                       return;
+                               }
+                       }
+                       
+                       
                        if (instance != null) {
                                instance.uncaughtException(Thread.currentThread(), t);
                        }
                }
        }
+       
+       
+       private static class InternalException extends Exception {
+               public InternalException() {
+                       super();
+               }
+
+               public InternalException(String message, Throwable cause) {
+                       super(message, cause);
+               }
+
+               public InternalException(String message) {
+                       super(message);
+               }
+
+               public InternalException(Throwable cause) {
+                       super(cause);
+               }
+       }
 }