if (txcount++ > 1000) {  localrtx.closeConnections (); break; }
    public void run () {        int txcount = 0;        // first, set a local variable to the current transactor thread so we know        // when it's time to quit because another thread took over.        Transactor localrtx = (Transactor) Thread.currentThread ();        // evaluators are only initialized as needed, so we need to check that here        if (!initialized)	app.typemgr.initRequestEvaluator (this);        try {	do {	    // IServer.getLogger().log ("got request "+reqtype);	    switch (reqtype) {	    case HTTP:	        int tries = 0;	        boolean done = false;	        String error = null;	        while (!done) {	            current = null;	            currentNode = null;	            reqPath.setSize (0);	            try {	                String requestPath = app.getName()+"/"+req.path;	                // set Timer to get some profiling data	                localrtx.timer.reset ();	                localrtx.timer.beginEvent (requestPath+" init");	                localrtx.begin (requestPath);	                Action action = null;	                root = app.getDataRoot ();	                ESUser esu = (ESUser) getNodeWrapper (user.getNode ());	                esu.setUser (user);	                global.putHiddenProperty ("root", getNodeWrapper (root));	                global.putHiddenProperty("user", esu);	                global.putHiddenProperty ("req", new ESWrapper (req, evaluator));	                global.putHiddenProperty ("res", new ESWrapper (res, evaluator));	                global.putHiddenProperty ("path", reqPath);	                global.putHiddenProperty ("app", appnode);	                // set and mount the request data object	                reqData.setData (req.getReqData());	                req.data = reqData;	                try {	                    if (error != null) {	                        // there was an error in the previous loop, call error handler	                        currentNode = root;	                        current = getNodeWrapper (root);	                        reqPath.putProperty (0, current);	                        Prototype p = app.getPrototype (root);	                        String errorAction = app.props.getProperty ("error", "error");	                        action = p.getActionOrTemplate (errorAction);		                    } else if (req.path == null || "".equals (req.path.trim ())) {	                        currentNode = root;	                        current = getNodeWrapper (root);	                        reqPath.putProperty (0, current);	                        Prototype p = app.getPrototype (root);	                        action = p.getActionOrTemplate (null);	                    } else {	                        // march down request path...	                        // is the next path element a subnode or a property of the last one?	                        // currently only used for users node	                        boolean isProperty = false;	                        StringTokenizer st = new StringTokenizer (req.path, "/");	                        int ntokens = st.countTokens ();	                        String next = null;	                        currentNode = root;	                        reqPath.putProperty (0, getNodeWrapper (currentNode));	                        // the first token in the path needs to be treated seprerately,	                        // because "/user" is a shortcut to the current user session, while "/users"	                        // is the mounting point for all users.	                        if (ntokens > 1) {	                            next = st.nextToken ();	                            if ("user".equalsIgnoreCase (next)) {	                                currentNode = user.getNode ();	                                ntokens -=1;	                                if (currentNode != null)	                                    reqPath.putProperty (1, getNodeWrapper (currentNode));	                            } else if ("users".equalsIgnoreCase (next)) {	                                currentNode = app.getUserRoot ();	                                ntokens -=1;	                                isProperty = true;	                            } else {	                                currentNode = currentNode.getSubnode (next);	                                ntokens -=1;	                                if (currentNode != null)	                                    reqPath.putProperty (1, getNodeWrapper (currentNode));	                            }	                        }		                        for (int i=1; i<ntokens; i++) {	                            if (currentNode == null)	                                throw new FrameworkException ("Object not found.");	                            next = st.nextToken ();	                            if (isProperty)  // get next element as property	                                currentNode = currentNode.getNode (next, false);	                            else  // get next element as subnode	                                currentNode = currentNode.getSubnode (next);	                            isProperty = false;	                            if (currentNode != null && currentNode.getState() != INode.VIRTUAL) // add to reqPath array	                                reqPath.putProperty (reqPath.size(), getNodeWrapper (currentNode));	                            // limit path to < 50 tokens	                            if (i > 50) throw new RuntimeException ("Path too deep");	                        }	                        if (currentNode == null)	                            throw new FrameworkException ("Object not found.");	                        Prototype p = app.getPrototype (currentNode);                                     next = st.nextToken ();	                        if (p != null)	                            action = p.getActionOrTemplate (next);	                        if (p == null || action == null) {	                            currentNode = currentNode.getSubnode (next);	                            if (currentNode == null)	                                throw new FrameworkException ("Object or Action '"+next+"' not found.");	                            p = app.getPrototype (currentNode);	                            // add to reqPath array	                            if (currentNode != null && currentNode.getState() != INode.VIRTUAL)	                                reqPath.putProperty (reqPath.size(), getNodeWrapper (currentNode));	                            if (p != null)	                                action = p.getActionOrTemplate ("main");	                        }	                        current = getNodeWrapper (currentNode);	                    }	                    if (action == null)	                        throw new FrameworkException ("Action not found");	                } catch (FrameworkException notfound) {	                    if (error != null)	                        // we already have an error and the error template wasn't found,	                        // display it instead of notfound message	                        throw new RuntimeException ();	                    // The path could not be resolved. Check if there is a "not found" action	                    // specified in the property file.	                    String notFoundAction = app.props.getProperty ("notFound", "notfound");	                    Prototype p = app.getPrototype (root);	                    action = p.getActionOrTemplate (notFoundAction);	                    if (action == null)	                        throw new FrameworkException (notfound.getMessage ());	                    current = getNodeWrapper (root);	                }	                localrtx.timer.endEvent (requestPath+" init");	                try {	                    localrtx.timer.beginEvent (requestPath+" execute");	                    current.doIndirectCall (evaluator, current, action.getFunctionName (), new ESValue[0]);	                    localrtx.timer.endEvent (requestPath+" execute");	                    done = true;	                } catch (RedirectException redirect) {	                    res.redirect = redirect.getMessage ();	                    done = true;	                }	                // check if we're still the one and only or if the waiting thread has given up on us already	                commitTransaction ();	                done = true;	            } catch (ConcurrencyException x) {	                res.reset ();	                if (++tries < 10) {	                    // try again after waiting some period	                    abortTransaction (true);	                    try {	                        // wait a bit longer with each try	                        int base = 200 * tries;	                        Thread.currentThread ().sleep ((long) (base + Math.random ()*base*2));	                    } catch (Exception ignore) {}	                    continue;	                } else {	                    abortTransaction (false);	                    if (error == null) {	                        app.errorCount += 1;	                        error = "Couldn't complete transaction due to heavy object traffic (tried "+tries+" times)";	                    } else {	                        // error in error action. use traditional minimal error message	                        res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>"+error+"</pre>");	                        done = true;	                    }	                }	            } catch (Exception x) {	                abortTransaction (false);	                IServer.getLogger().log ("### Exception in "+app.getName()+"/"+req.path+": "+x);	                // Dump the profiling data to System.err	                if (app.debug) {	                    ((Transactor) Thread.currentThread ()).timer.dump (System.err);	                    x.printStackTrace ();	                }	                // If the transactor thread has been killed by the invoker thread we don't have to	                // bother for the error message, just quit.	                if (localrtx != rtx)	                    break;	                res.reset ();	                if (error == null) {	                    app.errorCount += 1;	                    error = x.getMessage ();	                    if (error == null || error.length() == 0)	                        error = x.toString ();	                } else {	                    // error in error action. use traditional minimal error message	                    res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>"+error+"</pre>");	                    done = true;	                }	            }	        }	        break;	    case XMLRPC:	        try {	            localrtx.begin (app.getName()+":xmlrpc/"+method);	            root = app.getDataRoot ();	            global.putHiddenProperty ("root", getNodeWrapper (root));	            global.deleteProperty("user", "user".hashCode());	            global.deleteProperty ("req", "req".hashCode());	            global.putHiddenProperty ("res", ESLoader.normalizeValue(new ResponseTrans (), evaluator));	            global.deleteProperty ("path", "path".hashCode());	            global.putHiddenProperty ("app", appnode);	            // convert arguments	            int l = args.size ();	            current = getNodeWrapper (root);	            if (method.indexOf (".") > -1) {	                StringTokenizer st = new StringTokenizer (method, ".");	                int cnt = st.countTokens ();	                for (int i=1; i<cnt; i++) {	                    String next = st.nextToken ();	                    try {	                        current = (ESObject) current.getProperty (next, next.hashCode ());	                    } catch (Exception x) {	                        throw new EcmaScriptException ("The property \""+next+"\" is not defined in the remote object.");	                    }	                }	                if (current == null)	                    throw new EcmaScriptException ("Method name \""+method+"\" could not be resolved.");	                method = st.nextToken ();	            }	            // check XML-RPC access permissions	            String proto = ((ESNode) current).getNode().getPrototype ();	            app.checkXmlRpcAccess (proto, method);	            ESValue esa[] = new ESValue[l];	            for (int i=0; i<l; i++) {    	                esa[i] = FesiRpcUtil.convertJ2E (args.elementAt (i), evaluator);	            }		            result = FesiRpcUtil.convertE2J (current.doIndirectCall (evaluator, current, method, esa));	            commitTransaction ();	        } catch (Exception wrong) {	            abortTransaction (false);	            // If the transactor thread has been killed by the invoker thread we don't have to	            // bother for the error message, just quit.	            if (localrtx != rtx) {	                return;	            }	            this.exception = wrong;	        }	        break;	    case INTERNAL:	        esresult = ESNull.theNull;	        // Just a human readable descriptor of this invocation	        String funcdesc = app.getName()+":internal/"+method;	        try {	            localrtx.begin (funcdesc);	            root = app.getDataRoot ();	            global.putHiddenProperty ("root", getNodeWrapper (root));	            global.deleteProperty("user", "user".hashCode());	            global.deleteProperty ("req", "req".hashCode());	            global.putHiddenProperty ("res", ESLoader.normalizeValue(new ResponseTrans (), evaluator));	            global.deleteProperty ("path", "path".hashCode());	            global.putHiddenProperty ("app", appnode);                         if (current == null) {	                if (user == null) {	                    current = global;	                } else {	                    ESUser esu = (ESUser) getNodeWrapper (user.getNode ());	                    esu.setUser (user);	                    current = esu;	                }	            }	            esresult = current.doIndirectCall (evaluator, current, method, new ESValue[0]);	            commitTransaction ();	        } catch (Throwable wrong) {	            abortTransaction (false);	            // If the transactor thread has been killed by the invoker thread we don't have to	            // bother for the error message, just quit.	            if (localrtx != rtx) {	                return;	            }	            String msg = wrong.getMessage ();	            if (msg == null || msg.length () == 0)	                msg = wrong.toString ();	            IServer.getLogger().log ("Error executing "+funcdesc+": "+msg);	            this.exception = new Exception (msg);	        }	        break;	    }	    // create a new Thread every 1000 requests. The current one will fade out	    if (txcount++ > 1000) {	        // stop thread - a new one will be created when needed	        localrtx.closeConnections ();	        break;	    }	    // make sure there is only one thread running per instance of this class	    if (localrtx != rtx) {	        localrtx.closeConnections ();	        return;	    }	    notifyAndWait ();            }  while (localrtx == rtx);        } finally {            localrtx.closeConnections ();        }    }