* Make error handling more flexible, adding res.exception, res.scriptStack, res.javaStack
to get further information on unhandled exceptions during page execution. * Make default error page include script and java stack traces when debug is set to true.
This commit is contained in:
parent
b5f7f14b2f
commit
b29bf58253
6 changed files with 134 additions and 39 deletions
|
@ -17,9 +17,12 @@
|
|||
package helma.framework;
|
||||
|
||||
import helma.objectmodel.db.Transactor;
|
||||
import helma.scripting.ScriptingException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -373,9 +376,43 @@ public class ResponseBean implements Serializable {
|
|||
* @return the error message
|
||||
*/
|
||||
public String getError() {
|
||||
return res.getErrorMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the uncaught exception for the response, if any
|
||||
* @return the uncaught exception
|
||||
*/
|
||||
public Throwable getException() {
|
||||
return res.getError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Javascript stack trace of an uncought exception.
|
||||
* @return the script stack trace of any uncaught exception or null.
|
||||
*/
|
||||
public String getScriptStack() {
|
||||
Throwable t = res.getError();
|
||||
if (t instanceof ScriptingException)
|
||||
return ((ScriptingException) t).getScriptStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Java stack trace of an uncaught exception.
|
||||
* @return the java stack trace of an uncaught exception or null.
|
||||
*/
|
||||
public String getJavaStack() {
|
||||
Throwable t = res.getError();
|
||||
if (t == null)
|
||||
return null;
|
||||
else if (t instanceof ScriptingException)
|
||||
return ((ScriptingException) t).getJavaStackTrace();
|
||||
StringWriter w = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(w));
|
||||
return w.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current message for the response, if set
|
||||
*
|
||||
|
|
|
@ -19,6 +19,7 @@ package helma.framework;
|
|||
import helma.framework.core.Skin;
|
||||
import helma.framework.core.Application;
|
||||
import helma.util.*;
|
||||
import helma.scripting.ScriptingException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
|
@ -95,8 +96,8 @@ public final class ResponseTrans extends Writer implements Serializable {
|
|||
// field for generic message to be displayed
|
||||
private transient String message;
|
||||
|
||||
// field for error message
|
||||
private transient String error;
|
||||
// field for error
|
||||
private transient Throwable error;
|
||||
|
||||
// the res.data map of form and cookie data
|
||||
private transient Map values = new SystemMap();
|
||||
|
@ -190,7 +191,8 @@ public final class ResponseTrans extends Writer implements Serializable {
|
|||
buffers = null;
|
||||
response = null;
|
||||
cacheable = true;
|
||||
redir = forward = message = error = null;
|
||||
redir = forward = message = null;
|
||||
error = null;
|
||||
etag = realm = charset = null;
|
||||
contentType = "text/html";
|
||||
values.clear();
|
||||
|
@ -546,21 +548,59 @@ public final class ResponseTrans extends Writer implements Serializable {
|
|||
* Write a vanilla error report. Callers should make sure the ResponeTrans is
|
||||
* new or has been reset.
|
||||
*
|
||||
* @param appName the application name
|
||||
* @param message the error message
|
||||
* @param throwable the error
|
||||
*/
|
||||
public void reportError(String appName, String message) {
|
||||
public void reportError(Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
// just to be safe
|
||||
reportError("Unspecified error");
|
||||
return;
|
||||
}
|
||||
if (reqtrans.isXmlRpc()) {
|
||||
writeXmlRpcError(new RuntimeException(message));
|
||||
writeXmlRpcError(new RuntimeException(throwable));
|
||||
} else {
|
||||
status = 500;
|
||||
if (!"true".equalsIgnoreCase(app.getProperty("suppressErrorPage"))) {
|
||||
write("<html><body><h3>");
|
||||
write("<html><body>");
|
||||
write("<h2>Error in application " + app.getName() + "</h2><p>");
|
||||
encode(getErrorMessage(throwable));
|
||||
writeln("</p>");
|
||||
if (app.debug()) {
|
||||
if (throwable instanceof ScriptingException) {
|
||||
ScriptingException scriptx = (ScriptingException) throwable;
|
||||
writeln("<h4>Script Stack</h4>");
|
||||
writeln("<pre>" + scriptx.getScriptStackTrace() + "</pre>");
|
||||
writeln("<h4>Java Stack</h4>");
|
||||
writeln("<pre>" + scriptx.getJavaStackTrace() + "</pre>");
|
||||
} else {
|
||||
writeln("<h4>Java Stack</h4>");
|
||||
writeln("<pre>");
|
||||
throwable.printStackTrace(new PrintWriter(this));
|
||||
writeln("</pre>");
|
||||
}
|
||||
}
|
||||
writeln("</body></html>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a vanilla error report. Callers should make sure the ResponeTrans is
|
||||
* new or has been reset.
|
||||
* @param errorMessage the error message
|
||||
*/
|
||||
public void reportError(String errorMessage) {
|
||||
if (reqtrans.isXmlRpc()) {
|
||||
writeXmlRpcError(new RuntimeException(errorMessage));
|
||||
} else {
|
||||
status = 500;
|
||||
if (!"true".equalsIgnoreCase(app.getProperty("suppressErrorPage"))) {
|
||||
write("<html><body><h2>");
|
||||
write("Error in application ");
|
||||
write(appName);
|
||||
write("</h3>");
|
||||
write(message);
|
||||
write("</body></html>");
|
||||
write(app.getName());
|
||||
write("</h2><p>");
|
||||
encode(errorMessage);
|
||||
writeln("</p></body></html>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1010,7 +1050,7 @@ public final class ResponseTrans extends Writer implements Serializable {
|
|||
* Get the error message to display to the user, if any.
|
||||
* @return the error message
|
||||
*/
|
||||
public String getError() {
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -1018,10 +1058,25 @@ public final class ResponseTrans extends Writer implements Serializable {
|
|||
* Set a message to display to the user.
|
||||
* @param error the error message
|
||||
*/
|
||||
public void setError(String error) {
|
||||
public void setError(Throwable error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
if (error == null)
|
||||
return null;
|
||||
return getErrorMessage(error);
|
||||
}
|
||||
|
||||
private static String getErrorMessage(Throwable t) {
|
||||
String msg = t.getMessage();
|
||||
if (msg == null || msg.length() == 0)
|
||||
msg = t.toString();
|
||||
if (msg == null || msg.length() == 0)
|
||||
return "Unspecified Error: " + t.getClass().getName();
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debug messages to append to the response, if any.
|
||||
* @return the response's debug buffer
|
||||
|
|
|
@ -701,7 +701,7 @@ public final class Application implements Runnable {
|
|||
} catch (Exception x) {
|
||||
errorCount += 1;
|
||||
res = new ResponseTrans(this, req);
|
||||
res.reportError(name, x.getMessage());
|
||||
res.reportError(x);
|
||||
} finally {
|
||||
if (primaryRequest) {
|
||||
activeRequests.remove(req);
|
||||
|
|
|
@ -149,7 +149,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
|
||||
int tries = 0;
|
||||
boolean done = false;
|
||||
String error = null;
|
||||
Throwable error = null;
|
||||
String functionName = function instanceof String ?
|
||||
(String) function : null;
|
||||
|
||||
|
@ -543,7 +543,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
Thread.sleep((long) (base + (Math.random() * base * 2)));
|
||||
} catch (InterruptedException interrupt) {
|
||||
// we got interrrupted, create minimal error message
|
||||
res.reportError(app.getName(), error);
|
||||
res.reportError(interrupt);
|
||||
done = true;
|
||||
// and release resources and thread
|
||||
rtx = null;
|
||||
|
@ -555,11 +555,8 @@ public final class RequestEvaluator implements Runnable {
|
|||
}
|
||||
abortTransaction();
|
||||
|
||||
if (error == null)
|
||||
error = "Application too busy, please try again later";
|
||||
|
||||
// error in error action. use traditional minimal error message
|
||||
res.reportError(app.getName(), error);
|
||||
res.reportError("Application too busy, please try again later");
|
||||
done = true;
|
||||
}
|
||||
} catch (Throwable x) {
|
||||
|
@ -587,15 +584,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
|
||||
// set done to false so that the error will be processed
|
||||
done = false;
|
||||
error = x.getMessage();
|
||||
|
||||
if ((error == null) || (error.length() == 0)) {
|
||||
error = x.toString();
|
||||
}
|
||||
|
||||
if (error == null) {
|
||||
error = "Unspecified error";
|
||||
}
|
||||
error = x;
|
||||
|
||||
app.logError(txname + ": " + error, x);
|
||||
|
||||
|
@ -610,7 +599,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
}
|
||||
} else {
|
||||
// error in error action. use traditional minimal error message
|
||||
res.reportError(app.getName(), error);
|
||||
res.reportError(error);
|
||||
done = true;
|
||||
}
|
||||
} finally {
|
||||
|
@ -755,7 +744,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
|
||||
if (reqtype != NONE && stopTransactor()) {
|
||||
res.reset();
|
||||
res.reportError(app.getName(), "Request timed out");
|
||||
res.reportError("Request timed out");
|
||||
}
|
||||
|
||||
session.commit(this);
|
||||
|
|
|
@ -688,7 +688,7 @@ public final class Skin {
|
|||
if ("message".equals(propName))
|
||||
value = cx.reval.getResponse().getMessage();
|
||||
else if ("error".equals(propName))
|
||||
value = cx.reval.getResponse().getError();
|
||||
value = cx.reval.getResponse().getErrorMessage();
|
||||
if (value != null)
|
||||
return filter(value, cx);
|
||||
}
|
||||
|
|
|
@ -18,10 +18,7 @@ package helma.scripting;
|
|||
|
||||
import org.mozilla.javascript.RhinoException;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.File;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* The base class for wrapped exceptions thrown by invocation of the scripting engine.
|
||||
|
@ -60,9 +57,26 @@ public class ScriptingException extends Exception {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script stack, or null if none is available
|
||||
* @return the script stack trace
|
||||
*/
|
||||
public String getScriptStackTrace() {
|
||||
return scriptStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the java stack trace.
|
||||
* @return the java stack trace
|
||||
*/
|
||||
public String getJavaStackTrace() {
|
||||
StringWriter w = new StringWriter();
|
||||
getCause().printStackTrace(new PrintWriter(w));
|
||||
return w.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Adaption from Throwable.printStackTrace() to only print Script file stack elements.
|
||||
* Adaption from Throwable.printStackTrace() to also print Script file stack elements.
|
||||
*/
|
||||
public void printStackTrace(PrintStream s) {
|
||||
synchronized (s) {
|
||||
|
@ -77,7 +91,7 @@ public class ScriptingException extends Exception {
|
|||
|
||||
|
||||
/*
|
||||
* Adaption from Throwable.printStackTrace() to only print Script file stack elements.
|
||||
* Adaption from Throwable.printStackTrace() to also print Script file stack elements.
|
||||
*/
|
||||
public void printStackTrace(PrintWriter s) {
|
||||
synchronized (s) {
|
||||
|
|
Loading…
Add table
Reference in a new issue