1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.xnap.commons.util;
22
23 import java.io.BufferedReader;
24 import java.io.ByteArrayOutputStream;
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.io.PrintWriter;
33 import java.io.UnsupportedEncodingException;
34 import java.net.HttpURLConnection;
35 import java.net.URL;
36 import java.net.URLEncoder;
37 import java.security.MessageDigest;
38 import java.security.NoSuchAlgorithmException;
39 import java.util.HashSet;
40 import java.util.Locale;
41 import java.util.Properties;
42 import java.util.Vector;
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45
46 /***
47 *
48 * @author Tammo van Lessen
49 * @author Steffen Pingel
50 */
51 public class UncaughtExceptionManager implements UncaughtExceptionListener {
52
53 public static String[] DEFAULT_MANGLE_PREFIXES = {
54 "java.lang.IndexOutOfBoundsException",
55 "java.lang.ArrayIndexOutOfBoundsException",
56 "java.lang.RuntimeException",
57 };
58
59 private static Log logger = LogFactory.getLog(UncaughtExceptionManager.class);
60
61 private static UncaughtExceptionListener defaultHandler = new NullExceptionHandler();
62
63 private static String asHex(byte hash[]) {
64 StringBuilder buf = new StringBuilder(hash.length * 2);
65 int i;
66
67 for (i = 0; i < hash.length; i++) {
68 if (((int) hash[i] & 0xff) < 0x10)
69 buf.append("0");
70
71 buf.append(Long.toString((int) hash[i] & 0xff, 16));
72 }
73
74 return buf.toString();
75 }
76
77 public static String removeExceptionDescription(String trace, String prefix)
78 {
79 if (trace.startsWith(prefix + ": ")) {
80 int i = trace.indexOf("\n");
81 if (i != -1) {
82 return prefix + trace.substring(i);
83 }
84 }
85 return trace;
86 }
87
88 public static UncaughtExceptionListener getDefaultHandler()
89 {
90 return defaultHandler;
91 }
92
93 public static void setDefaultHandler(UncaughtExceptionListener handler)
94 {
95 if (handler == null) {
96 defaultHandler = new NullExceptionHandler();
97 }
98 else {
99 defaultHandler = handler;
100 }
101 }
102
103 private HashSet<String> blacklist = new HashSet<String>();
104 private File blacklistFile;
105
106 private Vector<UncaughtExceptionListener> listeners = new Vector<UncaughtExceptionListener>();
107 private String[] manglePrefixes;
108
109 public UncaughtExceptionManager(File blacklistFile, String[] manglePrefixes)
110 {
111 this.blacklistFile = blacklistFile;
112 this.manglePrefixes = manglePrefixes;
113
114 readBlackList();
115
116 setDefaultHandler(this);
117 }
118
119 public UncaughtExceptionManager(File blacklistFile)
120 {
121 this(blacklistFile, DEFAULT_MANGLE_PREFIXES);
122 }
123
124 public UncaughtExceptionManager()
125 {
126 this(null, DEFAULT_MANGLE_PREFIXES);
127 }
128
129 public void addExceptionListener(UncaughtExceptionListener l)
130 {
131 listeners.add(l);
132 }
133
134 public synchronized void addToBlacklist(Throwable e)
135 {
136 blacklist.add(buildMD5Hash(e));
137 writeBlacklist();
138 }
139
140 private String buildMD5Hash(String s)
141 {
142 MessageDigest md;
143 try {
144 md = MessageDigest.getInstance("MD5");
145 return asHex(md.digest(s.getBytes()));
146 } catch (NoSuchAlgorithmException ex) {
147 ex.printStackTrace(System.err);
148 }
149 return "";
150 }
151
152 private String buildMD5Hash(Throwable e)
153 {
154 return buildMD5Hash(toString(e));
155 }
156
157 private String encode(Object o)
158 {
159 try {
160 if (o != null) {
161 return URLEncoder.encode(o.toString(), "UTF-8");
162 }
163 }
164 catch (UnsupportedEncodingException e) {
165
166 logger.error("UTF-8 encoding not supported", e);
167 }
168 return "";
169 }
170
171 private boolean isBlacklisted(Throwable e)
172 {
173 return blacklist.contains(buildMD5Hash(e));
174 }
175
176 /***
177 *
178 */
179 @SuppressWarnings("unchecked")
180 private void readBlackList()
181 {
182 if (blacklistFile == null) {
183 return;
184 }
185 if (!blacklistFile.exists()) {
186 return;
187 }
188
189 FileInputStream in = null;
190 try {
191 in = new FileInputStream(blacklistFile);
192 ObjectInputStream p = new ObjectInputStream(in);
193 blacklist = (HashSet<String>)p.readObject();
194 }
195 catch (Throwable e) {
196 logger.debug("Could not read exception blacklist file: " + blacklistFile.getAbsolutePath(), e);
197 }
198 finally {
199 try {
200 if (in != null) {
201 in.close();
202 }
203 } catch (IOException e) {
204 }
205 }
206
207 public void removeExceptionListener(UncaughtExceptionListener l)
208 {
209 listeners.remove(l);
210 }
211
212
213 /***
214 * @param destination
215 * @param thread
216 * @param throwable
217 * @param version
218 * @param plugin
219 * @throws IOException
220 */
221 public void sendProblemReport(URL destination, Thread thread, Throwable throwable,
222 String version, String plugin)
223 throws IOException
224 {
225 Properties p = System.getProperties();
226 StringBuffer report = new StringBuffer();
227 report.append("version=" + encode(version));
228 report.append("&plugin=" + encode(plugin));
229 report.append("&locale=" + encode(Locale.getDefault()));
230 report.append("&os_name=" + encode(p.get("os.name")));
231 report.append("&os_version=" + encode(p.get("os.version")));
232 report.append("&os_arch=" + encode(p.get("os.arch")));
233 report.append("&java_vendor=" + encode(p.get("java.vendor")));
234 report.append("&java_version=" + encode(p.get("java.version")));
235 report.append("&stacktrace=" + encode(toString(throwable)));
236 String hash = buildMD5Hash(report.toString());
237 String problemHash = buildMD5Hash(throwable);
238 report.append("&hash=" + encode(hash));
239 report.append("&problem_hash=" + encode(problemHash));
240
241 HttpURLConnection conn = (HttpURLConnection)destination.openConnection();
242 try {
243
244 conn.setDoOutput(true);
245 conn.setDoInput(true);
246 conn.setRequestMethod("POST");
247 conn.setUseCaches(false);
248 conn.setAllowUserInteraction(false);
249
250 PrintWriter out = new PrintWriter(conn.getOutputStream());
251 out.println(report);
252 out.flush();
253 out.close();
254
255 BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
256 try {
257 String s;
258 while ((s = in.readLine()) != null) {
259 logger.debug("Received: " + s + "\n");
260 }
261 }
262 finally {
263 try {
264 in.close();
265 }
266 catch (IOException e) {}
267 }
268 }
269 finally {
270 conn.disconnect();
271 }
272
273
274
275
276
277
278 }
279
280 /***
281 * Returns the stacktrace of <code>e</code> as a String.
282 *
283 * @param e the exception
284 */
285 public String toString(Throwable e)
286 {
287 ByteArrayOutputStream baos = new ByteArrayOutputStream();
288 PrintWriter printWriter = new PrintWriter(baos);
289 e.printStackTrace(printWriter);
290 printWriter.close();
291
292 String trace = baos.toString();
293 for (int i = 0; i < manglePrefixes.length; i++) {
294 trace = removeExceptionDescription(trace, manglePrefixes[i]);
295 }
296 return trace;
297 }
298
299 /***
300 * Handles e thrown by t. Notifies all listeners in case e is not
301 * blacklisted.
302 */
303 public synchronized void uncaughtException(Thread t, Throwable e)
304 {
305
306 if (isBlacklisted(e)) {
307 logger.debug("Blacklisted uncaught exception occured!", e);
308 return;
309 }
310
311 UncaughtExceptionListener[] l = listeners.toArray(new UncaughtExceptionListener[0]);
312 if (l != null && l.length > 0) {
313 for (int i = l.length - 1; i >= 0; i--) {
314 l[i].uncaughtException(t, e);
315 }
316 }
317 else {
318 e.printStackTrace(System.err);
319 }
320 }
321
322 /***
323 *
324 */
325 private void writeBlacklist()
326 {
327 if (blacklistFile == null) {
328 return;
329 }
330
331 FileOutputStream out = null;
332 try {
333 out = new FileOutputStream(blacklistFile);
334 ObjectOutputStream p = new ObjectOutputStream(out);
335 p.writeObject(blacklist);
336 }
337 catch (IOException e) {
338 logger.debug("Could not write exception blacklist file: " + blacklistFile.getAbsolutePath(), e);
339 }
340 finally {
341 try {
342 if (out != null) {
343 out.close();
344 }
345 } catch (IOException e) {
346 }
347 }
348
349 private static class NullExceptionHandler implements UncaughtExceptionListener {
350
351 public void uncaughtException(Thread t, Throwable e)
352 {
353 }
354
355 }
356
357 }