optimization updates
[debian/openrocket] / src / net / sf / openrocket / gui / main / ExceptionHandler.java
index 8496ad5be57f6400d37d986a2f4764e54a999412..74f3091757ad4dc1cbcf6136228668d489ae7549 100644 (file)
@@ -4,11 +4,16 @@ import javax.swing.JOptionPane;
 import javax.swing.SwingUtilities;
 
 import net.sf.openrocket.gui.dialogs.BugReportDialog;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.logging.TraceException;
+import net.sf.openrocket.startup.Application;
 
 
 public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
-
-       private static final int MEMORY_RESERVE = 512*1024;
+       
+       private static final LogHelper log = Application.getLogger();
+       
+       private static final int MEMORY_RESERVE = 512 * 1024;
        
        /**
         * A memory reserve of 0.5 MB of memory, that can be freed when showing the dialog.
@@ -19,51 +24,61 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        
        private static ExceptionHandler instance = null;
        
-       
+
        private volatile boolean handling = false;
        
        
-       
-       
+
+
        @Override
-       public void uncaughtException(final Thread t, final Throwable e) {
+       public void uncaughtException(final Thread thread, final Throwable throwable) {
                
                // Free memory reserve if out of memory
-               if (isOutOfMemoryError(e)) {
+               if (isOutOfMemoryError(throwable)) {
                        memoryReserve = null;
                        handling = false;
+                       log.error("Out of memory error detected", throwable);
+               }
+               
+               if (isNonFatalJREBug(throwable)) {
+                       log.warn("Ignoring non-fatal JRE bug", throwable);
+                       return;
+               }
+               
+               log.error("Handling uncaught exception on thread=" + thread, throwable);
+               throwable.printStackTrace();
+               
+               if (handling) {
+                       log.warn("Exception is currently being handled, ignoring");
+                       return;
                }
-
-               e.printStackTrace();
                
                try {
-                       
-                       if (handling) {
-                               System.err.println("Exception is currently being handled, ignoring:");
-                               e.printStackTrace();
-                               return;
-                       }
-                       
                        handling = true;
                        
                        // Show on the EDT
                        if (SwingUtilities.isEventDispatchThread()) {
-                               showDialog(t, e);
+                               log.info("Exception handler running on EDT, showing dialog");
+                               showDialog(thread, throwable);
                        } else {
-                   SwingUtilities.invokeAndWait(new Runnable() {
-                       public void run() {
-                           showDialog(t, e);
-                       }
-                   });
+                               log.info("Exception handler not on EDT, invoking dialog on EDT");
+                               SwingUtilities.invokeAndWait(new Runnable() {
+                                       @Override
+                                       public void run() {
+                                               showDialog(thread, throwable);
+                                       }
+                               });
                        }
                        
                } catch (Throwable ex) {
                        
                        // Make sure the handler does not throw any exceptions
                        try {
+                               log.error("Caught exception while handling exception", ex);
                                System.err.println("Exception in exception handler, dumping exception:");
                                ex.printStackTrace();
-                       } catch (Throwable ignore) { }
+                       } catch (Exception ignore) {
+                       }
                        
                } finally {
                        // Mark handling as completed
@@ -71,27 +86,35 @@ 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.
+        * <p>
+        * This method is guaranteed never to throw an exception, and can thus be safely
+        * used in finally blocks.
         * 
         * @param message       the error message.
         */
        public static void handleErrorCondition(String message) {
+               log.error(1, message, new TraceException());
                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.
+        * <p>
+        * This method is guaranteed never to throw an exception, and can thus be safely
+        * used in finally blocks.
         * 
         * @param message       the error message.
         * @param exception     the exception that occurred.
         */
-       public static void handleErrorCondition(String message, Exception exception) {
+       public static void handleErrorCondition(String message, Throwable exception) {
+               log.error(1, message, exception);
                handleErrorCondition(new InternalException(message, exception));
        }
        
@@ -99,33 +122,39 @@ 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.
+        * <p>
+        * This method is guaranteed never to throw an exception, and can thus be safely
+        * used in finally blocks.
         * 
         * @param exception             the exception that occurred.
         */
-       public static void handleErrorCondition(final Exception exception) {
-               final ExceptionHandler handler;
-
+       public static void handleErrorCondition(final Throwable exception) {
                try {
-
-                       if (instance == null) {
-                               handler = new ExceptionHandler();
-                       } else {
-                               handler = instance;
+                       if (!(exception instanceof InternalException)) {
+                               log.error(1, "Error occurred", exception);
                        }
-
                        final Thread thread = Thread.currentThread();
-
+                       final ExceptionHandler handler = instance;
+                       
+                       if (handler == null) {
+                               log.error("Error condition occurred before exception handling has been initialized", exception);
+                               return;
+                       }
+                       
                        if (SwingUtilities.isEventDispatchThread()) {
+                               log.info("Running in EDT, showing dialog");
                                handler.showDialog(thread, exception);
                        } else {
-                               SwingUtilities.invokeAndWait(new Runnable() {
+                               log.info("Not in EDT, invoking dialog later");
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       @Override
                                        public void run() {
                                                handler.showDialog(thread, exception);
                                        }
                                });
                        }
                } catch (Exception e) {
-                       e.printStackTrace();
+                       log.error("Exception occurred in error handler", e);
                }
        }
        
@@ -140,58 +169,62 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
                
                // Out of memory
                if (isOutOfMemoryError(e)) {
-                       JOptionPane.showMessageDialog(null, 
-                                       new Object[] { 
-                                               "OpenRocket can out of available memory!",
-                                               "You should immediately close unnecessary design windows,",
-                                               "save any unsaved designs and restart OpenRocket!"
+                       log.info("Showing out-of-memory dialog");
+                       JOptionPane.showMessageDialog(null,
+                                       new Object[] {
+                                                       "OpenRocket is 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;
                }
                
-               // Unknown Error
-               if (!(e instanceof Exception)) {
-                       JOptionPane.showMessageDialog(null, 
-                                       new Object[] { 
-                                               "An unknown Java error occurred:",
-                                               e.getMessage(),
-                                               "<html>You should immediately close unnecessary design windows,<br>" +
-                                               "save any unsaved designs and restart OpenRocket!"
-                                       }, "Unknown Java error", JOptionPane.ERROR_MESSAGE);
-                       return;
-               }
-               
-               
-               // Normal exception, show question dialog
+               // 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) && !(e instanceof LinkageError)) {
+                       log.info("Showing Error dialog");
+                       JOptionPane.showMessageDialog(null,
+                                       new Object[] {
+                                                       "An unknown Java error occurred:",
+                                                       msg,
+                                                       "<html>You should immediately close unnecessary design windows,<br>" +
+                                                                       "save any unsaved designs and restart OpenRocket!"
+                                       }, "Unknown Java error", JOptionPane.ERROR_MESSAGE);
+                       return;
+               }
                
+
+               // Normal exception, show question dialog               
+               log.info("Showing Exception 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, 
-                               JOptionPane.ERROR_MESSAGE, null, 
+                               }, "Uncaught exception", JOptionPane.DEFAULT_OPTION,
+                               JOptionPane.ERROR_MESSAGE, null,
                                new Object[] { "View bug report", "Close" }, "View bug report");
                
                if (selection != 0) {
                        // User cancelled
+                       log.user("User chose not to fill bug report");
                        return;
                }
                
                // Show bug report dialog
+               log.user("User requested sending bug report");
                BugReportDialog.showExceptionDialog(null, t, e);
-
        }
        
        
-       
+
        /**
         * Registers the uncaught exception handler.  This should be used to ensure that
         * all necessary registrations are performed.
@@ -217,13 +250,13 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
         */
        private static void reserveMemory() {
                memoryReserve = new byte[MEMORY_RESERVE];
-               for (int i=0; i<MEMORY_RESERVE; i++) {
-                       memoryReserve[i] = (byte)i;
+               for (int i = 0; i < MEMORY_RESERVE; i++) {
+                       memoryReserve[i] = (byte) i;
                }
        }
-
        
        
+
        /**
         * Return whether this throwable was caused by an OutOfMemoryError
         * condition.  An exception is deemed to be caused by OutOfMemoryError
@@ -245,49 +278,132 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        }
        
        
-       
+
        /**
         * Handler used in modal dialogs by Sun Java implementation.
         */
        public static class AwtHandler {
                public void handle(Throwable t) {
+                       if (instance != null) {
+                               instance.uncaughtException(Thread.currentThread(), t);
+                       }
+               }
+       }
+       
+       
+       /**
+        * Detect various non-fatal Sun JRE bugs.
+        * 
+        * @param t             the throwable
+        * @return              whether this exception should be ignored
+        */
+       private static boolean isNonFatalJREBug(Throwable t) {
+               
+               // NOTE:  Calling method logs the entire throwable, so log only message here
+               
+               /*
+                * 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()))) {
+                               log.warn("Ignoring Sun JRE bug 6828938:  " +
+                                               "(see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6828938): " + t);
+                               return true;
+                       }
+               }
+               
+               /*
+                * Detect and ignore bug 6561072 in Sun JRE 1.6.0_?
+                */
+               if (t instanceof NullPointerException) {
+                       StackTraceElement[] trace = t.getStackTrace();
                        
-                       /*
-                        * 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 (trace.length > 3 &&
+                                       trace[0].getClassName().equals("javax.swing.JComponent") &&
+                                       trace[0].getMethodName().equals("repaint") &&
+
+                                       trace[1].getClassName().equals("sun.swing.FilePane$2") &&
+                                       trace[1].getMethodName().equals("repaintListSelection") &&
+
+                                       trace[2].getClassName().equals("sun.swing.FilePane$2") &&
+                                       trace[2].getMethodName().equals("repaintSelection")) {
+                               log.warn("Ignoring Sun JRE bug 6561072 " +
+                                               "(see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6561072): " + t);
+                               return true;
                        }
+               }
+               
+
+               /*
+                * Detect and ignore bug 6933331 in Sun JRE 1.6.0_18 and others
+                */
+               if (t instanceof IllegalStateException) {
+                       StackTraceElement[] trace = t.getStackTrace();
                        
+                       if (trace.length > 1 &&
+                                       trace[0].getClassName().equals("sun.awt.windows.WComponentPeer") &&
+                                       trace[0].getMethodName().equals("getBackBuffer")) {
+                               log.warn("Ignoring Sun JRE bug 6933331 " +
+                                               "(see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6933331): " + t);
+                               return true;
+                       }
+               }
+               
+               /*
+                * Detect and ignore bug in Sun JRE 1.6.0_19
+                */
+               if (t instanceof NullPointerException) {
+                       StackTraceElement[] trace = t.getStackTrace();
                        
-                       if (instance != null) {
-                               instance.uncaughtException(Thread.currentThread(), t);
+                       if (trace.length > 3 &&
+                                       trace[0].getClassName().equals("sun.awt.shell.Win32ShellFolder2") &&
+                                       trace[0].getMethodName().equals("pidlsEqual") &&
+
+                                       trace[1].getClassName().equals("sun.awt.shell.Win32ShellFolder2") &&
+                                       trace[1].getMethodName().equals("equals") &&
+
+                                       trace[2].getClassName().equals("sun.awt.shell.Win32ShellFolderManager2") &&
+                                       trace[2].getMethodName().equals("isFileSystemRoot")) {
+                               log.warn("Ignoring Sun JRE bug " +
+                                               "(see http://forums.sun.com/thread.jspa?threadID=5435324): " + t);
+                               return true;
                        }
                }
+               
+               /*
+                * Detect Sun JRE bug in D3D
+                */
+               if (t instanceof ClassCastException) {
+                       if (t.getMessage().equals("sun.awt.Win32GraphicsConfig cannot be cast to sun.java2d.d3d.D3DGraphicsConfig")) {
+                               log.warn("Ignoring Sun JRE bug " +
+                                               "(see http://forums.sun.com/thread.jspa?threadID=5440525): " + t);
+                               return true;
+                       }
+               }
+               
+               return false;
        }
        
        
+       @SuppressWarnings("unused")
        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);
                }