/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven.error;
import haven.Config;
import java.io.*;
import java.net.*;
import java.util.*;
public class ErrorHandler extends ThreadGroup {
private final URL errordest;
private static final String[] sysprops = {
"java.version",
"java.vendor",
"os.name",
"os.arch",
"os.version",
};
private final ThreadGroup initial;
private Map<String, Object> props = new HashMap<String, Object>();
private Reporter reporter;
public static ErrorHandler find() {
for(ThreadGroup tg = Thread.currentThread().getThreadGroup(); tg != null; tg = tg.getParent()) {
if(tg instanceof ErrorHandler)
return((ErrorHandler)tg);
}
return(null);
}
public static void setprop(String key, Object val) {
ErrorHandler tg = find();
if(tg != null)
tg.lsetprop(key, val);
}
public void lsetprop(String key, Object val) {
props.put(key, val);
}
private class Reporter extends Thread {
private Queue<Report> errors = new LinkedList<Report>();
private ErrorStatus status;
public Reporter(ErrorStatus status) {
super(initial, "Error reporter");
setDaemon(true);
this.status = status;
}
public void run() {
while(true) {
synchronized(errors) {
try {
errors.wait();
} catch(InterruptedException e) {
return;
}
Report r;
while((r = errors.poll()) != null) {
try {
doreport(r);
} catch(Exception e) {
r.t.printStackTrace();
status.senderror(e);
}
}
}
}
}
private void doreport(Report r) throws IOException {
if(!status.goterror(r.t))
return;
if(errordest != null){
dourlreport(r);
} else {
dolocalreport(r);
}
}
private void dolocalreport(Report r) throws IOException {
FileOutputStream fs = new FileOutputStream(Config.userhome+"/error.html");
HtmlReporter.makereport(fs, r);
fs.close();
status.done(null, null);
}
private void dourlreport(Report r) throws IOException {
URLConnection c = errordest.openConnection();
status.connecting();
c.setDoOutput(true);
c.addRequestProperty("Content-Type", "application/x-java-error");
c.connect();
ObjectOutputStream o = new ObjectOutputStream(c.getOutputStream());
status.sending();
o.writeObject(r);
o.close();
String ctype = c.getContentType();
StringWriter buf = new StringWriter();
Reader i = new InputStreamReader(c.getInputStream(), "utf-8");
char[] dbuf = new char[1024];
while(true) {
int len = i.read(dbuf);
if(len < 0)
break;
buf.write(dbuf, 0, len);
}
i.close();
if(ctype.equals("text/x-report-info")) {
status.done("text/x-report-info", buf.toString());
} else if(ctype.equals("text/x-report-error")) {
throw(new ReportException(buf.toString()));
} else {
status.done(null, null);
}
}
public void report(Thread th, Throwable t) {
Report r = new Report(t);
r.props.putAll(props);
r.props.put("thnm", th.getName());
r.props.put("thcl", th.getClass().getName());
synchronized(errors) {
errors.add(r);
errors.notifyAll();
}
try {
r.join();
} catch(InterruptedException e) { /* XXX? */ }
}
}
private void defprops() {
for(String p : sysprops)
props.put(p, System.getProperty(p));
Runtime rt = Runtime.getRuntime();
props.put("cpus", rt.availableProcessors());
InputStream in = ErrorHandler.class.getResourceAsStream("/buildinfo");
try {
try {
if(in != null) {
Properties info = new Properties();
info.load(in);
for(Map.Entry<Object, Object> e : info.entrySet())
props.put("jar." + (String)e.getKey(), e.getValue());
}
} finally {
in.close();
}
} catch(IOException e) {
throw(new Error(e));
}
}
public ErrorHandler(ErrorStatus ui, URL errordest) {
super("Haven client");
this.errordest = errordest;
initial = Thread.currentThread().getThreadGroup();
reporter = new Reporter(ui);
reporter.start();
defprops();
}
public ErrorHandler(URL errordest) {
this(new ErrorStatus.Simple(), errordest);
}
public ErrorHandler(ErrorStatus ui) {
this(ui, null);
}
public ErrorHandler() {
this(new ErrorStatus.Simple());
}
public void sethandler(ErrorStatus handler) {
reporter.status = handler;
}
public void uncaughtException(Thread t, Throwable e) {
reporter.report(t, e);
}
}