001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.tools; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Component; 007 import java.awt.GridBagLayout; 008 import java.io.PrintWriter; 009 import java.io.StringWriter; 010 import java.net.URL; 011 import java.nio.ByteBuffer; 012 013 import javax.swing.JCheckBox; 014 import javax.swing.JLabel; 015 import javax.swing.JOptionPane; 016 import javax.swing.JPanel; 017 import javax.swing.JScrollPane; 018 import javax.swing.JTextArea; 019 import javax.swing.SwingUtilities; 020 021 import org.openstreetmap.josm.Main; 022 import org.openstreetmap.josm.actions.ShowStatusReportAction; 023 import org.openstreetmap.josm.gui.ExtendedDialog; 024 import org.openstreetmap.josm.gui.JMultilineLabel; 025 import org.openstreetmap.josm.plugins.PluginHandler; 026 027 /** 028 * An exception handler that asks the user to send a bug report. 029 * 030 * @author imi 031 */ 032 public final class BugReportExceptionHandler implements Thread.UncaughtExceptionHandler { 033 034 private static boolean handlingInProgress = false; 035 private static int exceptionCounter = 0; 036 private static boolean suppressExceptionDialogs = false; 037 038 public void uncaughtException(Thread t, Throwable e) { 039 handleException(e); 040 } 041 042 //http://stuffthathappens.com/blog/2007/10/15/one-more-note-on-uncaught-exception-handlers/ 043 public void handle(Throwable t) { 044 handleException(t); 045 } 046 047 public static void handleException(final Throwable e) { 048 if (handlingInProgress) 049 return; // we do not handle secondary exceptions, this gets too messy 050 if (suppressExceptionDialogs) 051 return; 052 handlingInProgress = true; 053 exceptionCounter++; 054 try { 055 e.printStackTrace(); 056 if (Main.parent != null) { 057 if (e instanceof OutOfMemoryError) { 058 // do not translate the string, as translation may raise an exception 059 JOptionPane.showMessageDialog(Main.parent, "JOSM is out of memory. " + 060 "Strange things may happen.\nPlease restart JOSM with the -Xmx###M option,\n" + 061 "where ### is the number of MB assigned to JOSM (e.g. 256).\n" + 062 "Currently, " + Runtime.getRuntime().maxMemory()/1024/1024 + " MB are available to JOSM.", 063 "Error", 064 JOptionPane.ERROR_MESSAGE 065 ); 066 return; 067 } 068 069 070 SwingUtilities.invokeLater(new Runnable() { 071 @Override 072 public void run() { 073 // Give the user a chance to deactivate the plugin which threw the exception (if it 074 // was thrown from a plugin) 075 // 076 PluginHandler.disablePluginAfterException(e); 077 078 // Then ask for submitting a bug report, for exceptions thrown from a plugin too 079 // 080 ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Unexpected Exception"), new String[] {tr("Do nothing"), tr("Report Bug")}); 081 ed.setIcon(JOptionPane.ERROR_MESSAGE); 082 JPanel pnl = new JPanel(new GridBagLayout()); 083 pnl.add(new JLabel( 084 "<html>" 085 + tr("An unexpected exception occurred.<br>" + 086 "This is always a coding error. If you are running the latest<br>" + 087 "version of JOSM, please consider being kind and file a bug report." 088 ) 089 + "</html>"), GBC.eol()); 090 JCheckBox cbSuppress = null; 091 if (exceptionCounter > 1) { 092 cbSuppress = new JCheckBox(tr("Suppress further error dialogs for this session.")); 093 pnl.add(cbSuppress, GBC.eol()); 094 } 095 ed.setContent(pnl); 096 ed.showDialog(); 097 if (cbSuppress != null && cbSuppress.isSelected()) { 098 suppressExceptionDialogs = true; 099 } 100 if (ed.getValue() != 2) return; 101 102 try { 103 final int maxlen = 6000; 104 StringWriter stack = new StringWriter(); 105 e.printStackTrace(new PrintWriter(stack)); 106 107 String text = ShowStatusReportAction.getReportHeader() 108 + stack.getBuffer().toString(); 109 String urltext = text.replaceAll("\r",""); /* strip useless return chars */ 110 if(urltext.length() > maxlen) 111 { 112 urltext = urltext.substring(0,maxlen); 113 int idx = urltext.lastIndexOf("\n"); 114 /* cut whole line when not loosing too much */ 115 if(maxlen-idx < 200) { 116 urltext = urltext.substring(0,idx+1); 117 } 118 urltext += "...<snip>...\n"; 119 } 120 121 URL url = new URL("http://josm.openstreetmap.de/josmticket?" + 122 "tdata="+Base64.encode(ByteBuffer.wrap(urltext.getBytes("UTF8")), true)); 123 124 JPanel p = new JPanel(new GridBagLayout()); 125 p.add(new JMultilineLabel( 126 tr("You have encountered an error in JOSM. Before you file a bug report " + 127 "make sure you have updated to the latest version of JOSM here:")), GBC.eol()); 128 p.add(new UrlLabel("http://josm.openstreetmap.de/#Download",2), GBC.eop().insets(8,0,0,0)); 129 p.add(new JMultilineLabel( 130 tr("You should also update your plugins. If neither of those help please " + 131 "file a bug report in our bugtracker using this link:")), GBC.eol()); 132 p.add(new UrlLabel(url.toString(), "http://josm.openstreetmap.de/josmticket?...",2), GBC.eop().insets(8,0,0,0)); 133 p.add(new JMultilineLabel( 134 tr("There the error information provided below should already be " + 135 "filled in for you. Please include information on how to reproduce " + 136 "the error and try to supply as much detail as possible.")), GBC.eop()); 137 p.add(new JMultilineLabel( 138 tr("Alternatively, if that does not work you can manually fill in the information " + 139 "below at this URL:")), GBC.eol()); 140 p.add(new UrlLabel("http://josm.openstreetmap.de/newticket",2), GBC.eop().insets(8,0,0,0)); 141 if (Utils.copyToClipboard(text)) { 142 p.add(new JLabel(tr("(The text has already been copied to your clipboard.)")), GBC.eop()); 143 } 144 145 JTextArea info = new JTextArea(text, 18, 60); 146 info.setCaretPosition(0); 147 info.setEditable(false); 148 p.add(new JScrollPane(info), GBC.eop()); 149 150 for (Component c: p.getComponents()) { 151 if (c instanceof JMultilineLabel) { 152 ((JMultilineLabel)c).setMaxWidth(400); 153 } 154 } 155 156 JOptionPane.showMessageDialog(Main.parent, p, tr("You have encountered a bug in JOSM"), JOptionPane.ERROR_MESSAGE); 157 } catch (Exception e1) { 158 e1.printStackTrace(); 159 } 160 } 161 }); 162 } 163 } finally { 164 handlingInProgress = false; 165 } 166 } 167 public static boolean exceptionHandlingInProgress() { 168 return handlingInProgress; 169 } 170 }