System.err.println ("INVOKE-ERROR: "+msg); x.printStackTrace ();
if (app.debug ()) { System.err.println ("Error in Script: "+msg); x.printStackTrace (); }
    public Object invoke (Object thisObject, String functionName, Object[] args, HashMap globals) throws ScriptingException {	ESObject eso = null;	if (thisObject == null)	    eso = global;	else	    eso = getElementWrapper (thisObject);	GlobalObject global = evaluator.getGlobalObject ();	// if we are provided with global variables to set for this invocation,	// remember the global variables before invocation to be able to reset them afterwards.	Set globalVariables = null;	try {	    ESValue[] esv = args == null ? new ESValue[0] : new ESValue[args.length];	    for (int i=0; i<esv.length; i++) {	        // for java.util.Map objects, we use the special "tight" wrapper	        // that makes the Map look like a native object	        if (args[i] instanceof Map)	            esv[i] = new ESMapWrapper (this, (Map) args[i]);	        else	            esv[i] = ESLoader.normalizeValue (args[i], evaluator);	    }	    if (globals != null) {	        // remember all global variables before invocation	        Set tmpGlobal = new HashSet ();	        for (Enumeration en = global.getAllProperties(); en.hasMoreElements(); ) {	            tmpGlobal.add (en.nextElement ().toString ());	        }	        globalVariables = tmpGlobal;	        // loop through global vars and set them	        for (Iterator i=globals.keySet().iterator(); i.hasNext(); ) {	            String k = (String) i.next();	            Object v = globals.get (k);	            ESValue sv = null;	            // we do a lot of extra work to make access to global variables	            // comfortable to EcmaScript coders, i.e. we use a lot of custom wrappers	            // that expose properties and functions in a special way instead of just going	            // with the standard java object wrappers.	            if (v instanceof RequestTrans)	                ((RequestTrans) v).data = new ESMapWrapper (this, ((RequestTrans) v).getRequestData ());	            else if (v instanceof ResponseTrans)	                ((ResponseTrans) v).data = new ESMapWrapper (this, ((ResponseTrans) v).getResponseData ());	            if (v instanceof Map)	                sv = new ESMapWrapper (this, (Map) v);	            else if ("path".equals (k)) {	                ArrayPrototype parr = new ArrayPrototype (evaluator.getArrayPrototype(), evaluator);	                List path = (List) v;	                // register path elements with their prototype	                for (int j=0; j<path.size(); j++) {	                    Object pathElem = path.get (j);	                    ESValue wrappedElement = getElementWrapper (pathElem);	                    parr.putProperty (j, wrappedElement);	                    String protoname = app.getPrototypeName (pathElem);	                    if (protoname != null)	                        parr.putHiddenProperty (protoname, wrappedElement);	                }	                sv = parr;	            } else if ("user".equals (k)) {	                sv = getNodeWrapper ((User) v);	            } else if ("app".equals (k)) {	                sv = new ESAppNode ((INode) v, this);	            }	            else	                sv = ESLoader.normalizeValue (v, evaluator);	            global.putHiddenProperty (k, sv);	        }	    }	    evaluator.thread = Thread.currentThread ();	    ESValue retval =  eso.doIndirectCall (evaluator, eso, functionName, esv);	    return retval == null ? null : retval.toJavaObject ();	} catch (Exception x) {	    // check if this is a redirect exception, which has been converted by fesi	    // into an EcmaScript exception, which is why we can't explicitly catch it	    if (reval.res.getRedirect() != null)	        throw new RedirectException (reval.res.getRedirect ());	    // create and throw a ScriptingException with the right message	    String msg = x.getMessage ();	    if (msg == null || msg.length() < 10)	        msg = x.toString ();	    System.err.println ("INVOKE-ERROR: "+msg);	    x.printStackTrace ();	    throw new ScriptingException (msg);	} finally {	    // remove global variables that have been added during invocation.	    // this are typically undeclared variables, and we don't want them to	    // endure from one request to the next since this leads to buggy code that	    // relies on requests being served by the same evaluator, which is typically the	    // case under development conditions but not in deployment.	    if (globalVariables != null) {	        for (Enumeration en = global.getAllProperties(); en.hasMoreElements(); ) {	            String g = en.nextElement ().toString ();	            if (!globalVariables.contains (g)) try {	                global.deleteProperty (g, g.hashCode());	            } catch (Exception x) {	                System.err.println ("Error resetting global property: "+g);	            }	        }	    }	}    }