Christopher Oliver wrote:

I started looking into how I could get a meaningful stack trace (with location information) spanning (possibly nested) invocations of the sitemap, flowscript, and JXTemplateGenerator. Currently using the JVM stack trace produces a poor result. Although location information is available it isn't provided according to a consistent model. Rhino stores a JS stack trace in a JavaScriptException. JXTemplateGenerator stores its execution stack trace in a sequence of nested SAXParseExceptions. The Sitemap processor currently doesn't seem to produce an execution stack trace, but ProcessingNode's have location information so it should be possible. In addition, javax.xml.transform.TransformerException has location information. o.a.c.ProcessingException gives special treatment to SAXParseException and TransformerException and adds location information to its own stack trace (but only for one level - it doesn't included nested SAXParseException's, for example). There seem to be various other hacks to attempt to include location information in the exception message or stack trace.

The idea is to allow a Cocoon processing element that operates on source code (sitemap, flowscript, jxtemplate, etc...) to provide location information about the execution stack of that processing element when the exception occurred (see also http://marc.theaimsgroup.com/?t=105597249800001&r=1&w=2).

To accomplish this I chose not to use the JVM exception stack trace but instead to store a "Cocoon" stack trace in the current object model. I added the following to the class o.a.c.Cocoon:

public class Cocoon {
   ....
   public interface StackTraceElement {
       public String getComponent();
       public String getMessage();
       public String getMethod();
       public String getURI();
       public int getLineNumber();
       public int getColumnNumber();
   }

   public static void addCocoonStackTrace(
                                          Map objectModel,
                                          String message,
                                          String component,
                                          String method,
                                          String uri,
                                          int lineNumber,
                                          int columnNumber);

public static ArrayList /* of StackTraceElement */ getStackTrace(Map objectModel)

}

I added calls to addCocoonStackTrace() in JXTemplateGenerator, FOM_JavaScriptInterpreter, and in MountNode.java and CallNode.java.

With a method like the following:

private static void dumpStackTrace(Map objectModel) {


<snip/>

I was able to produce the following output:

jxtemplate: No pointer for xpath: $blah (at file:/C:/cocoon4/build/webapp/samples/flow/jxcalc/screens/getNumberA.xml, Line 26.13)
sitemap: (mount at file:/C:/cocoon4/build/webapp/samples/flow/sitemap.xmap:38:68)
sitemap: (mount at file:/C:/cocoon4/build/webapp/samples/sitemap.xmap:154:65)
sitemap: (mount at file:/C:/cocoon4/build/webapp/sitemap.xmap:738:66)
flowscript: (at resource://org/apache/cocoon/components/flow/javascript/fom/fom_system.js, Line 4)
flowscript: (getNumber at file:/C:/cocoon4/build/webapp/samples/flow/jxcalc/../calc/calc.js, Line 31)
flowscript: (calculator at file:/C:/cocoon4/build/webapp/samples/flow/jxcalc/../calc/calc.js, Line 11)
sitemap: (call at file:/C:/cocoon4/build/webapp/samples/flow/jxcalc/sitemap.xmap:48:42)
sitemap: (mount at file:/C:/cocoon4/build/webapp/samples/flow/sitemap.xmap:38:68)
sitemap: (mount at file:/C:/cocoon4/build/webapp/samples/sitemap.xmap:154:65)
sitemap: (mount at file:/C:/cocoon4/build/webapp/sitemap.xmap:738:66)


It seems straightforward to hook this into Cocoon's error reporter.

WDYT?


/me thinks it's great!

Some low-level remarks, however:

- we need a "popStackFrame()" method, since once a component has been executed successfully and execution continues in its following siblings, it should no longer appear in the stack trace

- is the "message" parameter really needed? It's only meaningful for the statement where the error occured, and will be present in the Java exception that is raised.

- consequently, to minimize performance overhead, I think it would be better to pass a single StackTraceElement parameter (or "CocoonStackFrame"?), since these objects can be pre-allocated at script/template/sitemap load time and are immutable afterwards.

- I don't like much putting this on the "Cocoon" class. What about creating a "org.apache.cocoon.util.debug" package holding the various classes that will certainly follow this first enhancement. I'm thinking to a global debugger that may be built using these stack frames.

In the process of doing this, however, I found that pipeline processing doesn't actually occur during sendPage*(). Instead, it simply sets a flag to redirect to the passed uri and returns. Pipeline processing actually occurs after the flowscript has completed. This is also a problem for the solution proposed here to release components after pipeline processing: http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=107360524206262&w=2.

To solve both problems I modified the implementation of sendPage*() to call the sitemap processor recursively instead. It seems to work. Does anyone know of a problem with this approach?


Or we can also have internal redirects be handled at the location where they occur instead of raising this flag. The purpose of going through this flag was to both ensure a single handling location (this can be achieved by other means) and cut down the length of java stack traces.

Sylvain

--
Sylvain Wallez                                  Anyware Technologies
http://www.apache.org/~sylvain           http://www.anyware-tech.com
{ XML, Java, Cocoon, OpenSource }*{ Training, Consulting, Projects }
Orixo, the opensource XML business alliance  -  http://www.orixo.com




Reply via email to