/**
* 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;
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;
}
}
+ /**
+ * 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.
*
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);
}
- // 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> " + 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,
}
+ /**
+ * 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++) {
}
+
+ /**
+ * 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);
+ }
+ }
}