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
}