package tk.amberide.ide.gui.misc; import javax.swing.*; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.List; /** * @author Tudor */ public class ErrorHandler extends javax.swing.JDialog { @Retention(RetentionPolicy.RUNTIME) public @interface NoRestart { } public interface ErrorFormatter { /** * Formats the given error * * @param error the Throwable * @param thread the Thread the error occured in * @param out the StringBuilder output: may not be empty */ void format(Throwable error, Thread thread, StringBuilder out); } public static class NativeFormatter implements ErrorFormatter { private List<String> getLoadedLibraries() { try { Field loadedLibraryNames; (loadedLibraryNames = ClassLoader.class.getDeclaredField("loadedLibraryNames")).setAccessible(true); return (List<String>) loadedLibraryNames.get(null); } catch (Exception e) { e.printStackTrace(); return new ArrayList<String>(); } } /** * @inheritDoc */ public void format(Throwable error, Thread thread, StringBuilder out) { out.append("\nLoaded natives:"); for (String lib : getLoadedLibraries()) { out.append("\n ") .append(lib); } } } public static class SystemFormatter implements ErrorFormatter { /** * @inheritDoc */ public void format(Throwable error, Thread thread, StringBuilder out) { out.append("\nDetails:"); out.append("\n Time: ") .append((new SimpleDateFormat()).format(new Date())); Runtime run = Runtime.getRuntime(); out.append("\n Cores: ") .append(run.availableProcessors()); out.append("\n Memory: ") .append(run.maxMemory() / 1024 / 1024) .append("M max, ") .append(run.totalMemory() / 1024 / 1024) .append("M total, ") .append(run.freeMemory() / 1024 / 1024) .append("M free"); out.append("\n Operating System: ") .append(System.getProperty("os.name")) .append(" (") .append(System.getProperty("os.arch")) .append(")"); out.append("\n Java Version: ") .append(System.getProperty("java.version")) .append(", ") .append(System.getProperty("java.vendor")); out.append("\n Java VM Version: ") .append(System.getProperty("java.vm.name")) .append(" (") .append(System.getProperty("java.vm.info")) .append("), ") .append(System.getProperty("java.vm.vendor")); } } private static Thread main = Thread.currentThread(); private static boolean lock; private Thread err; private Throwable thorn; private List<ErrorFormatter> formatters = new LinkedList<ErrorFormatter>() { { add(new SystemFormatter()); add(new NativeFormatter()); } }; /** * Creates new form ErrCatcher */ public ErrorHandler(JFrame parent, Throwable t, Thread th) { super(parent, true); initComponents(); detailsButtonActionPerformed(null); // Hide stacktrace area err = th; thorn = t; localizedMessage.setText(t.getLocalizedMessage()); if (th.getClass().getAnnotation(NoRestart.class) != null) { continueButton.setEnabled(false); } ByteArrayOutputStream os = new ByteArrayOutputStream(); t.printStackTrace(new PrintStream(os)); StringBuilder out = new StringBuilder(); out.append(new String(os.toByteArray())); for (ErrorFormatter form : formatters) { form.format(t, th, out); } stackArea.setText(out.toString()); getRootPane().setDefaultButton(continueButton); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { textAreaScrollpane = new javax.swing.JScrollPane(); stackArea = new javax.swing.JTextArea(); quitButton = new javax.swing.JButton(); detailsButton = new javax.swing.JButton(); continueButton = new javax.swing.JButton(); localizedMessage = new javax.swing.JLabel(); footer = new javax.swing.JPanel(); errIcon = new javax.swing.JLabel(); errorScrollpane = new javax.swing.JScrollPane(); errorArea = new javax.swing.JTextArea(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Houston, we have an error."); setResizable(false); stackArea.setEditable(false); stackArea.setColumns(20); stackArea.setRows(5); stackArea.setOpaque(false); textAreaScrollpane.setViewportView(stackArea); quitButton.setText("Quit"); quitButton.setDefaultCapable(false); quitButton.setFocusable(false); quitButton.setRequestFocusEnabled(false); quitButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { quitButtonActionPerformed(evt); } }); detailsButton.setText("Details"); detailsButton.setDefaultCapable(false); detailsButton.setFocusable(false); detailsButton.setRequestFocusEnabled(false); detailsButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { detailsButtonActionPerformed(evt); } }); continueButton.setText("Continue"); continueButton.setFocusable(false); continueButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { continueButtonActionPerformed(evt); } }); localizedMessage.setText("Crash_Reason"); javax.swing.GroupLayout footerLayout = new javax.swing.GroupLayout(footer); footer.setLayout(footerLayout); footerLayout.setHorizontalGroup( footerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) ); footerLayout.setVerticalGroup( footerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) ); errIcon.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); errIcon.setIcon(UIManager.getIcon("OptionPane.errorIcon")); errorScrollpane.setBorder(null); errorScrollpane.setWheelScrollingEnabled(false); errorArea.setEditable(false); errorArea.setColumns(20); errorArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N errorArea.setLineWrap(true); errorArea.setText("We've encountered an error. Sorry. If you click Continue, the application will ignore this error and attempt to continue. If you click Quit, the application will close immediately."); errorArea.setWrapStyleWord(true); errorArea.setAlignmentX(0.0F); errorArea.setAlignmentY(0.0F); errorArea.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); errorArea.setFocusable(false); errorArea.setOpaque(false); errorScrollpane.setViewportView(errorArea); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(detailsButton, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 115, Short.MAX_VALUE) .addComponent(continueButton, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(quitButton, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(textAreaScrollpane, javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createSequentialGroup() .addComponent(errIcon, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(localizedMessage) .addGap(0, 0, Short.MAX_VALUE)) .addComponent(errorScrollpane)))) .addContainerGap()) .addComponent(footer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(errIcon, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addComponent(errorScrollpane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(localizedMessage))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(detailsButton) .addComponent(quitButton) .addComponent(continueButton)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(textAreaScrollpane, javax.swing.GroupLayout.PREFERRED_SIZE, 172, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(footer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); pack(); setLocationRelativeTo(null); }// </editor-fold>//GEN-END:initComponents private void quitButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_quitButtonActionPerformed System.exit(0); }//GEN-LAST:event_quitButtonActionPerformed private void continueButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_continueButtonActionPerformed Throwable cause = thorn.getCause(); if (cause != null) { while (cause.getCause() != null) { cause = cause.getCause(); } thorn = cause; } Thread thread; if (err == main) { // Main thread StackTraceElement[] trace = thorn.getStackTrace(); int i = 0; // We iterate until the last static method while (i < trace.length) { final Method child; try { Class parent = Class.forName(trace[i].getClassName()); child = parent.getDeclaredMethod(trace[i].getMethodName()); child.setAccessible(true); } catch (Exception ex) { // Oops. ex.printStackTrace(); return; } if (Modifier.isStatic(child.getModifiers())) { thread = new Thread() { @Override public void run() { try { child.invoke(null); } catch (InvocationTargetException ex) { Thread.getDefaultUncaughtExceptionHandler().uncaughtException(this, ex.getCause()); } catch (Exception ex) { Thread.getDefaultUncaughtExceptionHandler().uncaughtException(this, ex); } } }; main = thread; thread.start(); break; } i++; } } else { try { if (!Class.forName("java.awt.EventDispatchThread").isInstance(err)) { // We can't reboot the EDT, nor do we need to try { thread = (Thread) err.getClass().newInstance(); thread.setPriority(err.getPriority()); thread.setDaemon(err.isDaemon()); thread.setName(err.getName()); thread.start(); } catch (Exception ignored) { } } } catch (Exception ex) { // Oops. ex.printStackTrace(); } } dispose(); }//GEN-LAST:event_continueButtonActionPerformed private void detailsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_detailsButtonActionPerformed stackArea.setVisible(!stackArea.isVisible()); textAreaScrollpane.setVisible(!textAreaScrollpane.isVisible()); pack(); }//GEN-LAST:event_detailsButtonActionPerformed /** * Method for handling EDT exceptions pre Java 7: should not be called by a * well-behaved program. Use {@link #alert(Throwable)} instead. * * @param e */ public void handle(Throwable e) { alert(e); } /** * Alerts of an exception * * @param e the Throwable object to display information for */ public static void alert(Throwable e) { // Delegate to ErrorHander Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } /** * Initializes the ErrorHandler to function with the current Thread as the * 'main' Thread. */ public static void init() { System.setProperty("sun.awt.exception.handler", ErrorHandler.class.getName()); // Java < 7 compatibility for EDT Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { e.printStackTrace(); if (!lock) { lock = !lock; new ErrorHandler(new JFrame(), e, t).setVisible(true); lock = !lock; } } }); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton continueButton; private javax.swing.JButton detailsButton; private javax.swing.JLabel errIcon; private javax.swing.JTextArea errorArea; private javax.swing.JScrollPane errorScrollpane; private javax.swing.JPanel footer; private javax.swing.JLabel localizedMessage; private javax.swing.JButton quitButton; private javax.swing.JTextArea stackArea; private javax.swing.JScrollPane textAreaScrollpane; // End of variables declaration//GEN-END:variables }