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