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

View file

@ -20,6 +20,7 @@ import helma.framework.*;
import helma.framework.repository.Resource; import helma.framework.repository.Resource;
import helma.objectmodel.ConcurrencyException; import helma.objectmodel.ConcurrencyException;
import helma.util.*; import helma.util.*;
import helma.scripting.ScriptingException;
import java.util.*; import java.util.*;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -573,16 +574,18 @@ public final class Skin {
} catch (TimeoutException timeout) { } catch (TimeoutException timeout) {
throw timeout; throw timeout;
} catch (Exception x) { } catch (Exception x) {
// x.printStackTrace();
String msg = x.getMessage(); String msg = x.getMessage();
if ((msg == null) || (msg.length() < 10)) { if ((msg == null) || (msg.length() < 10)) {
msg = x.toString(); msg = x.toString();
} }
msg = new StringBuffer("Macro error in ").append(fullName)
msg = "[Macro error in " + fullName + ": " + msg + "]"; .append(": ").append(x.getMessage()).toString();
reval.getResponse().write(" " + msg + " "); reval.getResponse().write(" [" + msg + "] ");
app.logEvent(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 * The base class for exceptions thrown by Helma scripting package
*/ */
public class ScriptingException extends Exception { public class ScriptingException extends Exception {
// original exception that caused this ScriptingException to be thrown.
Exception wrapped; 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); 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) { public Exception getWrappedException() {
wrapped = w; return wrapped;
}
/**
*
*
* @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);
}
} }
} }

View file

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

View file

@ -32,6 +32,7 @@ public class JavaObject extends NativeJavaObject {
RhinoCore core; RhinoCore core;
String protoName; String protoName;
NativeJavaObject unscriptedJavaObj;
static HashMap overload; static HashMap overload;
@ -57,6 +58,7 @@ public class JavaObject extends NativeJavaObject {
this.protoName = protoName; this.protoName = protoName;
this.core = core; this.core = core;
staticType = obj.getClass(); staticType = obj.getClass();
unscriptedJavaObj = new NativeJavaObject(scope, obj, staticType);
setPrototype(prototype); setPrototype(prototype);
initMembers(); initMembers();
} }
@ -181,10 +183,30 @@ public class JavaObject extends NativeJavaObject {
return new FunctionObject(name, (Method) obj, this); 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; return protoName;
} }
if ("__proto__".equals(name)) {
return getPrototype();
}
if ("__javaObject__".equals(name)) {
return unscriptedJavaObj;
}
return super.get(name, start); return super.get(name, start);
} }

View file

@ -218,7 +218,8 @@ public final class RhinoCore implements ScopeProvider {
// the actual (scripted) constructor on it. // the actual (scripted) constructor on it.
if (!"global".equals(lowerCaseName)) { if (!"global".equals(lowerCaseName)) {
try { try {
installConstructor(name, op); FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global);
fo.addAsConstructor(global, op);
} catch (Exception ignore) { } catch (Exception ignore) {
System.err.println("Error adding ctor for " + name + ": " + ignore); System.err.println("Error adding ctor for " + name + ": " + ignore);
ignore.printStackTrace(); 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 * This method is called before an execution context is entered to let the
* engine know it should update its prototype information. The update policy * 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); Scriptable op = getPrototype(prototypeName);
if (op == null) { if (op == null) {
prototypeName = "HopObject"; // no prototype found, return an unscripted wrapper
op = getPrototype("HopObject"); 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); wrappercache.put(e, w);
} }

View file

@ -314,9 +314,12 @@ public class RhinoEngine implements ScriptingEngine {
// create and throw a ScriptingException with the right message // create and throw a ScriptingException with the right message
String msg; String msg;
if (x instanceof JavaScriptException) { if (x instanceof JavaScriptException) {
msg = ((JavaScriptException) x).getValue().toString(); // created by javascript throw statement
msg = ((JavaScriptException) x).getMessage();
} else if (x instanceof WrappedException) { } 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 this is a wrapped concurrencyException, rethrow it.
if (wrapped instanceof ConcurrencyException) { if (wrapped instanceof ConcurrencyException) {
throw (ConcurrencyException) wrapped; throw (ConcurrencyException) wrapped;
@ -325,17 +328,22 @@ public class RhinoEngine implements ScriptingEngine {
if (wrapped instanceof RedirectException) { if (wrapped instanceof RedirectException) {
throw (RedirectException) wrapped; throw (RedirectException) wrapped;
} }
msg = wrapped.toString(); // we need to build our own message here, default implementation is broken
} else { 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(); msg = x.toString();
} else {
msg = x.getMessage();
} }
if (app.debug()) { throw new ScriptingException(msg, x);
System.err.println("Error in Script: " + msg);
x.printStackTrace();
}
throw new ScriptingException(msg);
} }
} }