* Fix exception handling:

- Always print source file name and line number
   - Only print stack trace once
   - Slways print stack trace
   - Always print stack trace for original exception
   - Also log full error stack trace for exceptions caught in macros
* Implement HopObject.__proto__ and JavaObject.__proto__ containing prototype object
* Fix constructor property in HopObject protos to be set to the actual constructor
* Implement JavaObject.__javaObject__ to contain the original java object in an unscripted wrapper
* Make sure JS functions in script-extended java objects actually override java methods
* Use unscripted wrapper rather than HopObject prototype if the prototype for java class is not defined
This commit is contained in:
hns 2005-09-21 10:11:10 +00:00
parent f752601c16
commit 0d0b99f4c0
7 changed files with 96 additions and 131 deletions

View file

@ -505,6 +505,7 @@ public final class RequestEvaluator implements Runnable {
done = true;
}
} catch (Throwable x) {
String txname = localrtx.getTransactionName();
abortTransaction();
// If the transactor thread has been killed by the invoker thread we don't have to
@ -518,7 +519,6 @@ public final class RequestEvaluator implements Runnable {
// check if we tried to process the error already
if (error == null) {
app.errorCount += 1;
app.logError("Error in " + Thread.currentThread(), x);
// set done to false so that the error will be processed
done = false;
@ -531,6 +531,13 @@ public final class RequestEvaluator implements Runnable {
if (error == null) {
error = "Unspecified error";
}
if (x instanceof ScriptingException) {
x = ((ScriptingException) x).getWrappedException();
}
app.logError(txname + ": " + error, x);
} else {
// error in error action. use traditional minimal error message
res.writeErrorReport(app.getName(), error);

View file

@ -20,6 +20,7 @@ import helma.framework.*;
import helma.framework.repository.Resource;
import helma.objectmodel.ConcurrencyException;
import helma.util.*;
import helma.scripting.ScriptingException;
import java.util.*;
import java.io.UnsupportedEncodingException;
@ -573,16 +574,18 @@ public final class Skin {
} catch (TimeoutException timeout) {
throw timeout;
} catch (Exception x) {
// x.printStackTrace();
String msg = x.getMessage();
if ((msg == null) || (msg.length() < 10)) {
msg = x.toString();
}
msg = "[Macro error in " + fullName + ": " + msg + "]";
reval.getResponse().write(" " + msg + " ");
app.logEvent(msg);
msg = new StringBuffer("Macro error in ").append(fullName)
.append(": ").append(x.getMessage()).toString();
reval.getResponse().write(" [" + msg + "] ");
// for ScriptingExceptions get the original exception
if (x instanceof ScriptingException) {
x = ((ScriptingException) x).getWrappedException();
}
app.logError(msg, x);
}
}

View file

@ -23,83 +23,24 @@ import java.io.PrintWriter;
* The base class for exceptions thrown by Helma scripting package
*/
public class ScriptingException extends Exception {
// original exception that caused this ScriptingException to be thrown.
Exception wrapped;
/**
* Construct a ScriptingException given an error message
* Construct a ScriptingException given an error message and wrapped exception.
*/
public ScriptingException(String msg) {
public ScriptingException(String msg, Exception wrapped) {
super(msg);
wrapped = null;
this.wrapped = wrapped;
}
/**
* Construct a ScriptingException given an error message
* Get the original exception that caused this exception to be thrown.
*
* @return the wrapped exception
*/
public ScriptingException(Exception w) {
wrapped = w;
}
/**
*
*
* @return ...
*/
public String toString() {
if (wrapped == null) {
return super.toString();
} else {
return wrapped.toString();
}
}
/**
*
*
* @return ...
*/
public String getMessage() {
if (wrapped == null) {
return super.getMessage();
} else {
return wrapped.getMessage();
}
}
/**
*
*/
public void printStackTrace() {
if (wrapped == null) {
super.printStackTrace();
} else {
wrapped.printStackTrace();
}
}
/**
*
*
* @param stream ...
*/
public void printStackTrace(PrintStream stream) {
if (wrapped == null) {
super.printStackTrace(stream);
} else {
wrapped.printStackTrace(stream);
}
}
/**
*
*
* @param writer ...
*/
public void printStackTrace(PrintWriter writer) {
if (wrapped == null) {
super.printStackTrace(writer);
} else {
wrapped.printStackTrace(writer);
}
public Exception getWrappedException() {
return wrapped;
}
}

View file

@ -149,7 +149,7 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
hobj.init(core, node);
if (proto != null) {
engine.invoke(hobj, "constructor", args, ScriptingEngine.ARGS_WRAP_NONE);
engine.invoke(hobj, "__constructor__", args, ScriptingEngine.ARGS_WRAP_NONE);
}
return hobj;
@ -710,22 +710,25 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
}
/**
* Set a property in this HopObject
*
*
* @param name ...
* @param start ...
* @param name property name
* @param start
* @param value ...
*/
public void put(String name, Scriptable start, Object value) {
// register property for PropertyRecorder interface
if (isRecording) {
changedProperties.add(name);
}
if (node == null) {
// redirect the scripted constructor to __constructor__,
// constructor is set to the native constructor method.
if ("constructor".equals(name) && value instanceof NativeFunction) {
name = "__constructor__";
}
// register property for PropertyRecorder interface
if (isRecording) {
changedProperties.add(name);
}
super.put(name, start, value);
} else {
checkNode();
if ("subnodeRelation".equals(name)) {
@ -769,12 +772,11 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
}
/**
* Check if a property is set in this HopObject
*
*
* @param name ...
* @param start ...
*
* @return ...
* @param name the property name
* @param start the object in which the lookup began
* @return true if the property was found
*/
public boolean has(String name, Scriptable start) {
if (node != null) {
@ -895,8 +897,12 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
return node.getID();
}
if ("__proto__".equals(name)) {
return getPrototype(); // prototype object
}
if ("__prototype__".equals(name) || "_prototype".equals(name)) {
return node.getPrototype();
return node.getPrototype(); // prototype name
}
if ("__parent__".equals(name) || "_parent".equals(name)) {

View file

@ -32,6 +32,7 @@ public class JavaObject extends NativeJavaObject {
RhinoCore core;
String protoName;
NativeJavaObject unscriptedJavaObj;
static HashMap overload;
@ -57,6 +58,7 @@ public class JavaObject extends NativeJavaObject {
this.protoName = protoName;
this.core = core;
staticType = obj.getClass();
unscriptedJavaObj = new NativeJavaObject(scope, obj, staticType);
setPrototype(prototype);
initMembers();
}
@ -181,10 +183,30 @@ public class JavaObject extends NativeJavaObject {
return new FunctionObject(name, (Method) obj, this);
}
if ("_prototype".equals(name)) {
// we really are not supposed to walk down the prototype chain in get(),
// but we break the rule in order to be able to override java methods,
// which are looked up by super.get() below
Scriptable proto = getPrototype();
while (proto != null) {
obj = proto.get(name, start);
if (obj != NOT_FOUND) {
return obj;
}
proto = proto.getPrototype();
}
if ("_prototype".equals(name) || "__prototype__".equals(name)) {
return protoName;
}
if ("__proto__".equals(name)) {
return getPrototype();
}
if ("__javaObject__".equals(name)) {
return unscriptedJavaObj;
}
return super.get(name, start);
}

View file

@ -218,7 +218,8 @@ public final class RhinoCore implements ScopeProvider {
// the actual (scripted) constructor on it.
if (!"global".equals(lowerCaseName)) {
try {
installConstructor(name, op);
FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global);
fo.addAsConstructor(global, op);
} catch (Exception ignore) {
System.err.println("Error adding ctor for " + name + ": " + ignore);
ignore.printStackTrace();
@ -285,29 +286,6 @@ public final class RhinoCore implements ScopeProvider {
}
}
/**
* This is a version of org.mozilla.javascript.FunctionObject.addAsConstructor()
* that does not set the constructor property in the prototype. This is because
* we want our own scripted constructor function to be visible, if it is defined.
*
* @param name the name of the constructor
* @param op the object prototype
*/
private void installConstructor(String name, Scriptable op) {
FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global);
ScriptRuntime.setFunctionProtoAndParent(global, fo);
fo.setImmunePrototypeProperty(op);
// add static getById() function
fo.defineProperty("getById", new GetById(name), GetById.ATTRIBUTES);
op.setParentScope(fo);
ScriptableObject.defineProperty(global, name, fo, ScriptableObject.DONTENUM);
fo.setParentScope(global);
}
/**
* This method is called before an execution context is entered to let the
* engine know it should update its prototype information. The update policy
@ -619,12 +597,12 @@ public final class RhinoCore implements ScopeProvider {
Scriptable op = getPrototype(prototypeName);
if (op == null) {
prototypeName = "HopObject";
op = getPrototype("HopObject");
// no prototype found, return an unscripted wrapper
w = new NativeJavaObject(global, e, e.getClass());
} else {
w = new JavaObject(global, e, prototypeName, op, this);
}
w = new JavaObject(global, e, prototypeName, op, this);
wrappercache.put(e, w);
}

View file

@ -314,9 +314,12 @@ public class RhinoEngine implements ScriptingEngine {
// create and throw a ScriptingException with the right message
String msg;
if (x instanceof JavaScriptException) {
msg = ((JavaScriptException) x).getValue().toString();
// created by javascript throw statement
msg = ((JavaScriptException) x).getMessage();
} else if (x instanceof WrappedException) {
Throwable wrapped = ((WrappedException) x).getWrappedException();
// wrapped java excepiton
WrappedException wx = (WrappedException) x;
Throwable wrapped = wx.getWrappedException();
// if this is a wrapped concurrencyException, rethrow it.
if (wrapped instanceof ConcurrencyException) {
throw (ConcurrencyException) wrapped;
@ -325,17 +328,22 @@ public class RhinoEngine implements ScriptingEngine {
if (wrapped instanceof RedirectException) {
throw (RedirectException) wrapped;
}
msg = wrapped.toString();
} else {
// we need to build our own message here, default implementation is broken
StringBuffer b = new StringBuffer(wrapped.toString());
b.append(" (").append(wx.getSourceName()).append("#")
.append(wx.getLineNumber()).append(")");
msg = b.toString();
// replace wrapper with original exception
if (wrapped instanceof Exception) {
x = (Exception) wrapped;
}
} else if (x instanceof EcmaError) {
msg = x.toString();
} else {
msg = x.getMessage();
}
if (app.debug()) {
System.err.println("Error in Script: " + msg);
x.printStackTrace();
}
throw new ScriptingException(msg);
throw new ScriptingException(msg, x);
}
}