diff --git a/src/helma/framework/RedirectException.java b/src/helma/framework/RedirectException.java index 9785b005..1e43d979 100644 --- a/src/helma/framework/RedirectException.java +++ b/src/helma/framework/RedirectException.java @@ -3,14 +3,13 @@ package helma.framework; -import FESI.Exceptions.EcmaScriptException; -/** +/** * RedirectException is thrown internally when a response is redirected to a * new URL. */ - -public class RedirectException extends EcmaScriptException { + +public class RedirectException extends RuntimeException { String url; @@ -18,11 +17,11 @@ public class RedirectException extends EcmaScriptException { super ("Redirection Request to "+url); this.url = url; } - + public String getMessage () { return url; } - + public void printStackTrace(java.io.PrintStream s) { // do nothing } diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 72314d90..817672dc 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -35,8 +35,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat protected NodeManager nmgr; // the class name of the scripting environment implementation - static final String scriptEnvironmentName = "helma.scripting.fesi.Environment"; - ScriptingEnvironment scriptEnv; + ScriptingEnvironment scriptingEngine; // the root of the website, if a custom root object is defined. // otherwise this is managed by the NodeManager and not cached here. @@ -49,6 +48,11 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat */ public TypeManager typemgr; + /** + * The skin manager for this application + */ + protected SkinManager skinmgr; + /** * Each application has one internal request evaluator for calling * the scheduler and other internal functions. @@ -227,7 +231,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat /** * Get the application ready to run, initializing the evaluators and type manager. */ - public void init () throws DbException { + public void init () throws DbException, ScriptingException { + scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment (); + scriptingEngine.init (this, props); eval = new RequestEvaluator (this); logEvent ("Starting evaluators for "+name); @@ -249,6 +255,8 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat typemgr.createPrototypes (); // logEvent ("Started type manager for "+name); + skinmgr = new SkinManager (this); + rootMapping = getDbMapping ("root"); userMapping = getDbMapping ("user"); SystemProperties p = new SystemProperties (); @@ -366,7 +374,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat try { RequestEvaluator re = (RequestEvaluator) freeThreads.pop (); allThreads.removeElement (re); - typemgr.unregisterRequestEvaluator (re); + // typemgr.unregisterRequestEvaluator (re); re.stopThread (); } catch (EmptyStackException empty) { return false; @@ -520,6 +528,13 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat return nmgr.safe; } + /** + * Return a transient node that is shared by all evaluators of this application ("app node") + */ + public INode getAppNode () { + return appnode; + } + /** * Returns a Node representing a registered user of this application by his or her user name. @@ -552,9 +567,16 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat * Return a collection containing all prototypes defined for this application */ public Collection getPrototypes () { - return typemgr.prototypes.values (); + return typemgr.prototypes.values (); } + /** + * Retrurn a skin for a given object. The skin is found by determining the prototype + * to use for the object, then looking up the skin for the prototype. + */ + public Skin getSkin (Object object, String skinname, Object[] skinpath) { + return skinmgr.getSkin (object, skinname, skinpath); // not yet implemented + } /** * Return the user currently associated with a given Hop session ID. This may be @@ -815,6 +837,8 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat * within the Helma scripting and rendering framework. */ public String getPrototypeName (Object obj) { + if (obj == null) + return "global"; // check if e implements the IPathElement interface if (obj instanceof IPathElement) // e implements the getPrototype() method @@ -902,6 +926,14 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat } + /** + * Get scripting environment for this application + */ + public ScriptingEnvironment getScriptingEnvironment () { + return scriptingEngine; + } + + /** * The run method performs periodic tasks like executing the scheduler method and * kicking out expired user sessions. @@ -917,7 +949,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat try { eval.invokeFunction ((INode) null, "onStart", new Object[0]); - } catch (Exception ignore) {} + } catch (Exception ignore) { + System.err.println ("Error in "+name+"/onStart(): "+ignore); + } while (Thread.currentThread () == worker) { // get session timeout @@ -1022,7 +1056,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat * Check whether a prototype is for scripting a java class, i.e. if there's an entry * for it in the class.properties file. */ - protected boolean isJavaPrototype (String typename) { + public boolean isJavaPrototype (String typename) { for (Enumeration en = classMapping.elements(); en.hasMoreElements(); ) { String value = (String) en.nextElement (); if (typename.equals (value)) @@ -1129,7 +1163,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat * */ public int countMaxActiveEvaluators () { - return typemgr.countRegisteredRequestEvaluators () -1; + // return typemgr.countRegisteredRequestEvaluators () -1; + // not available due to framework refactoring + return -1; } /** @@ -1194,7 +1230,7 @@ class XmlRpcInvoker implements XmlRpcHandler { RequestEvaluator ev = null; try { ev = app.getEvaluator (); - retval = ev.invokeXmlRpc (method, argvec); + retval = ev.invokeXmlRpc (method, argvec.toArray()); } finally { app.releaseEvaluator (ev); } diff --git a/src/helma/framework/core/Prototype.java b/src/helma/framework/core/Prototype.java index e2849a12..6a64b5a3 100644 --- a/src/helma/framework/core/Prototype.java +++ b/src/helma/framework/core/Prototype.java @@ -8,11 +8,8 @@ import java.util.Iterator; import java.io.*; import helma.framework.*; import helma.scripting.*; -import helma.scripting.fesi.*; import helma.objectmodel.*; import helma.util.Updatable; -import FESI.Data.*; -import FESI.Exceptions.EcmaScriptException; /** @@ -66,12 +63,12 @@ public class Prototype { // if parent has changed, update ES-prototypes in request evaluators if (parent != old) { - Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); + /* Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); while (evals.hasNext ()) { try { RequestEvaluator reval = (RequestEvaluator) evals.next (); ObjectPrototype op = reval.getPrototype (getName()); - // use hopobject (node) as prototype even if prototype is null - + // use hopobject (node) as prototype even if prototype is null - // this is the case if no hopobject directory exists ObjectPrototype opp = parent == null ? reval.esNodePrototype : reval.getPrototype (parent.getName ()); @@ -81,7 +78,7 @@ public class Prototype { op.setPrototype (opp); } catch (Exception ignore) { } - } + } */ } } @@ -97,8 +94,8 @@ public class Prototype { return (FunctionFile) functions.get (ffname); } - public Action getAction (String afname) { - return (Action) actions.get (afname); + public ActionFile getActionFile (String afname) { + return (ActionFile) actions.get (afname); } public SkinFile getSkinFile (String sfname) { @@ -128,76 +125,11 @@ public class Prototype { } } return upd; - - } - - public void initRequestEvaluator (RequestEvaluator reval) { - // see if we already registered with this evaluator - if (reval.getPrototype (name) != null) - return; - - ObjectPrototype op = null; - - // get the prototype's prototype if possible and necessary - ObjectPrototype opp = null; - if (parent != null) { - // see if parent prototype is already registered. if not, register it - opp = reval.getPrototype (parent.getName ()); - if (opp == null) { - parent.initRequestEvaluator (reval); - opp = reval.getPrototype (parent.getName ()); - } - } - if (!"global".equalsIgnoreCase (name) && !"hopobject".equalsIgnoreCase (name) && opp == null) { - if (isJavaPrototype) - opp = reval.esObjectPrototype; - else - opp = reval.esNodePrototype; - } - - if ("user".equalsIgnoreCase (name)) { - op = reval.esUserPrototype; - op.setPrototype (opp); - } else if ("global".equalsIgnoreCase (name)) - op = reval.global; - else if ("hopobject".equalsIgnoreCase (name)) - op = reval.esNodePrototype; - else { - op = new ObjectPrototype (opp, reval.evaluator); - try { - op.putProperty ("prototypename", new ESString (name), "prototypename".hashCode ()); - } catch (EcmaScriptException ignore) {} - } - reval.putPrototype (name, op); - - // Register a constructor for all types except global. - // This will first create a node and then call the actual (scripted) constructor on it. - if (!"global".equalsIgnoreCase (name)) { - try { - FunctionPrototype fp = (FunctionPrototype) reval.evaluator.getFunctionPrototype(); - reval.global.putHiddenProperty (name, new NodeConstructor (name, fp, reval)); - } catch (EcmaScriptException ignore) {} - } - for (Iterator it = functions.values().iterator(); it.hasNext(); ) { - FunctionFile ff = (FunctionFile) it.next (); - ff.updateRequestEvaluator (reval); - } - for (Iterator it = templates.values().iterator(); it.hasNext(); ) { - Template tmp = (Template) it.next (); - try { - tmp.updateRequestEvaluator (reval); - } catch (EcmaScriptException ignore) {} - } - for (Iterator it = actions.values().iterator(); it.hasNext(); ) { - Action act = (Action) it.next (); - try { - act.updateRequestEvaluator (reval); - } catch (EcmaScriptException ignore) {} - } } + public String toString () { return "[Prototype "+ app.getName()+"/"+name+"]"; } diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index f93621f1..ebc6c631 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -7,24 +7,15 @@ import helma.objectmodel.*; import helma.objectmodel.db.*; import helma.framework.*; import helma.scripting.*; -import helma.scripting.fesi.*; -import helma.scripting.fesi.extensions.*; -import helma.xmlrpc.fesi.*; import helma.util.*; import java.util.*; -import java.io.*; -import java.lang.reflect.*; -import Acme.LruHashtable; -import FESI.Data.*; -import FESI.Interpreter.*; -import FESI.Exceptions.*; /** - * This class does the work for incoming requests. It holds a transactor thread - * and an EcmaScript evaluator to get the work done. Incoming threads are + * This class does the work for incoming requests. It holds a transactor thread + * and an EcmaScript evaluator to get the work done. Incoming threads are * blocked until the request has been serviced by the evaluator, or the timeout * specified by the application has passed. In the latter case, the evaluator thread - * is killed and an error message is returned. + * is killed and an error message is returned. */ public class RequestEvaluator implements Runnable { @@ -38,43 +29,26 @@ public class RequestEvaluator implements Runnable { volatile Transactor rtx; + // the object on which to invoke a function, if specified + Object thisObject; + + // the method to be executed String method; - ESObject current; + + // the user object associated with the current request User user; - Vector args; - ESValue[] esargs; - ESValue esresult; + + // arguments passed to the function + Object[] args; + + // the object path of the request we're evaluating + List requestPath; + + // the result of the Object result; + + // the exception thrown by the evaluator, if any. Exception exception; - protected ArrayPrototype reqPath; - - private ESMapWrapper reqData; - private ESMapWrapper resData; - - // vars for FESI EcmaScript support - public Evaluator evaluator; - public ObjectPrototype esObjectPrototype; - public ObjectPrototype esNodePrototype; - public ObjectPrototype esUserPrototype; - - public LruHashtable objectcache; - Hashtable prototypes; - // Used to cache skins within one request evaluation - HashMap skincache; - - GlobalObject global; - HopExtension hopx; - MailExtension mailx; - FesiRpcServer xmlrpc; - ESAppNode appnode; - static String[] extensions = new String[] { - "FESI.Extensions.BasicIO", - "FESI.Extensions.FileIO", - "helma.xmlrpc.fesi.FesiRpcExtension", - "helma.scripting.fesi.extensions.ImageExtension", - "helma.scripting.fesi.extensions.FtpExtension", - "FESI.Extensions.JavaAccess", - "FESI.Extensions.OptionalRegExp"}; // the type of request to be serviced int reqtype; @@ -83,7 +57,6 @@ public class RequestEvaluator implements Runnable { static final int XMLRPC = 2; // via XML-RPC static final int INTERNAL = 3; // generic function call, e.g. by scheduler - Object[] skinsets; /** * Build a RenderContext from a RequestTrans. Checks if the path is the user home node ("user") @@ -91,46 +64,8 @@ public class RequestEvaluator implements Runnable { * that contains the data nodes, and anotherone with the corresponding Prototypes or Prototype.Parts. */ public RequestEvaluator (Application app) { - this.app = app; - objectcache = new LruHashtable (100, .80f); - prototypes = new Hashtable (); - skincache = new HashMap (); - initEvaluator (); + this.app = app; initialized = false; - // startThread (); - } - - - // init Script Evaluator - private void initEvaluator () { - try { - evaluator = new Evaluator(); - evaluator.reval = this; - global = evaluator.getGlobalObject(); - for (int i=0; i -1) { String soname = res.skin.substring (0, dot); - sobj = reqPath.getProperty (soname, soname.hashCode ()); - if (sobj == null || sobj == ESUndefined.theUndefined) + int l = requestPath.size(); + for (int i=l-1; i>=0; i--) { + Object pathelem = requestPath.get (i); + if (soname.equalsIgnoreCase (app.getPrototypeName (pathelem))) { + skinObject = pathelem; + break; + } + } + + if (skinObject == null) throw new RuntimeException ("Skin "+res.skin+" not found in path."); - sname = res.skin.substring (dot+1); + skinName = res.skin.substring (dot+1); } - Skin skin = getSkin ((ESObject) sobj, sname); - // get the java object wrapped by the script object, if not global - Object obj = sobj == null ? null : sobj.toJavaObject (); - if (skin != null) - skin.render (this, obj, null); - else - throw new RuntimeException ("Skin "+res.skin+" not found in path."); + Object[] skinNameArg = new Object[1]; + skinNameArg[0] = skinName; + app.scriptingEngine.invoke (skinObject, "renderSkin", skinNameArg, globals, this); } - localrtx.timer.endEvent (requestPath+" execute"); + localrtx.timer.endEvent (txname+" execute"); } catch (RedirectException redirect) { // res.redirect = redirect.getMessage (); // if there is a message set, save it on the user object for the next request @@ -472,45 +359,31 @@ public class RequestEvaluator implements Runnable { root = app.getDataRoot (); - global.putHiddenProperty ("root", getElementWrapper (root)); - global.deleteProperty("user", "user".hashCode()); - global.deleteProperty ("req", "req".hashCode()); - global.putHiddenProperty ("res", ESLoader.normalizeValue(res, evaluator)); - global.deleteProperty ("path", "path".hashCode()); - global.putHiddenProperty ("app", appnode); + HashMap globals = new HashMap (); + globals.put ("root", root); + globals.put ("res", res); + globals.put ("app", app.getAppNode()); - resData.setData (res.getResponseData()); - res.data = resData; + currentElement = root; - // convert arguments - int l = args.size (); - current = getElementWrapper (root); if (method.indexOf (".") > -1) { StringTokenizer st = new StringTokenizer (method, "."); int cnt = st.countTokens (); for (int i=1; i>> "+obj); - } */ - notifyAll (); try { - // wait for request, max 30 min - wait (1800000l); + // wait for request, max 10 min + wait (1000*60*10); // if no request arrived, release ressources and thread if (reqtype == NONE && rtx == localrtx) rtx = null; @@ -693,7 +534,7 @@ public class RequestEvaluator implements Runnable { } - public synchronized Object invokeXmlRpc (String method, Vector args) throws Exception { + public synchronized Object invokeXmlRpc (String method, Object[] args) throws Exception { this.reqtype = XMLRPC; this.user = null; this.method = method; @@ -714,42 +555,18 @@ public class RequestEvaluator implements Runnable { } protected Object invokeDirectFunction (Object obj, String functionName, Object[] args) throws Exception { - ESObject eso = null; - if (obj == null) - eso = global; - else - eso = getElementWrapper (obj); - ESValue[] esv = args == null ? new ESValue[0] : new ESValue[args.length]; - for (int i=0; i 0 && n.getDbMapping () == null) { - n.setDbMapping (app.getDbMapping (protoname)); - } - - op = getPrototype (protoname); - - // no prototype found for this node? - if (op == null) - op = esNodePrototype; - - - DbMapping dbm = n.getDbMapping (); - if (dbm != null && dbm.isInstanceOf ("user")) - esn = new ESUser (n, this, null); - else - esn = new ESNode (op, evaluator, n, this); - - objectcache.put (n, esn); - // app.logEvent ("Wrapper for "+n+" created"); - } - - return esn; - } - - /** - * Get a scripting wrapper object for a user object. Active user objects are represented by - * the special ESUser wrapper class. - */ - public ESNode getNodeWrapper (User u) { - if (u == null) - return null; - - ESUser esn = (ESUser) objectcache.get (u); - - if (esn == null) { - esn = new ESUser (u.getNode(), this, u); - objectcache.put (u, esn); - } else { - // the user node may have changed (login/logout) while the ESUser was - // lingering in the cache. - esn.updateNodeFromUser (); - } - - return esn; - } - - /** - * Get the object prototype for a prototype name - */ - public ObjectPrototype getPrototype (String protoName) { - if (protoName == null) - return null; - return (ObjectPrototype) prototypes.get (protoName); - } - - /** - * Register an object prototype for a certain prototype name. - */ - public void putPrototype (String protoName, ObjectPrototype op) { - if (protoName != null && op != null) - prototypes.put (protoName, op); - } - - /** - * Check if an object has a function property (public method if it - * is a java object) with that name. - */ - public boolean hasFunction (Object obj, String fname) { - ESObject eso = null; - if (obj == null) - eso = global; - else - eso = getElementWrapper (obj); - try { - ESValue func = eso.getProperty (fname, fname.hashCode()); - if (func != null && func instanceof FunctionPrototype) - return true; - } catch (EcmaScriptException esx) { - // System.err.println ("Error in getProperty: "+esx); - return false; - } - return false; - } - - - /** - * Check if an object has a defined property (public field if it - * is a java object) with that name. - */ - public Object getProperty (Object obj, String propname) { - if (obj == null || propname == null) - return null; - - String prototypeName = app.getPrototypeName (obj); - if ("user".equalsIgnoreCase (prototypeName) && - "password".equalsIgnoreCase (propname)) - return "[macro access to password property not allowed]"; - - // if this is a HopObject, check if the property is defined - // in the type.properties db-mapping. - if (obj instanceof INode) { - DbMapping dbm = app.getDbMapping (prototypeName); - if (dbm != null) { - Relation rel = dbm.propertyToRelation (propname); - if (rel == null || !rel.isPrimitive ()) - return "[property \""+propname+"\" is not defined for "+prototypeName+"]"; - } - } - - ESObject eso = getElementWrapper (obj); - try { - ESValue prop = eso.getProperty (propname, propname.hashCode()); - if (prop != null && !(prop instanceof ESNull) && - !(prop instanceof ESUndefined)) - return prop.toJavaObject (); - } catch (EcmaScriptException esx) { - System.err.println ("Error in getProperty: "+esx); - return null; - } - return null; - } - - /** - * Utility class to use for caching skins in a Hashtable. - * The key consists out of two strings: prototype name and skin name. - */ - final class SkinKey { - - final String first, second, third; - - public SkinKey (String first, String second, String third) { - this.first = first; - this.second = second; - this.third = third; - } - - public boolean equals (Object other) { - try { - SkinKey key = (SkinKey) other; - return first.equals (key.first) && second.equals (key.second) && third.equals (key.third); - } catch (Exception x) { - return false; - } - } - - public int hashCode () { - return first.hashCode () + second.hashCode () + third.hashCode (); - } - } } diff --git a/src/helma/framework/core/Skin.java b/src/helma/framework/core/Skin.java index dffbcd59..078aacac 100644 --- a/src/helma/framework/core/Skin.java +++ b/src/helma/framework/core/Skin.java @@ -278,9 +278,9 @@ public class Skin { if (handlerObject == null) { // eiter because thisObject == null or the right object wasn't found in the object's parent path // go check request path for an object with matching prototype - int l = reval.reqPath.size(); + int l = reval.requestPath.size(); for (int i=l-1; i>=0; i--) { - Object pathelem = reval.reqPath.getProperty (i).toJavaObject (); + Object pathelem = reval.requestPath.get (i); if (handler.equalsIgnoreCase (app.getPrototypeName (pathelem))) { handlerObject = pathelem; break; @@ -302,12 +302,12 @@ public class Skin { // if so, the macro evaluates to the function. Otherwise, // a property/field with the name is used, if defined. Object v = null; - if (reval.hasFunction (handlerObject, name+"_macro")) { + if (app.scriptingEngine.hasFunction (handlerObject, name+"_macro", reval)) { // System.err.println ("Getting macro from function"); - v = reval.invokeDirectFunction (handlerObject, name+"_macro", arguments); + v = app.scriptingEngine.invoke (handlerObject, name+"_macro", arguments, null, reval); } else { // System.err.println ("Getting macro from property"); - v = reval.getProperty (handlerObject, name); + v = app.scriptingEngine.get (handlerObject, name, reval); } if (v != null) writeToResponse (v.toString (), reval.res); diff --git a/src/helma/framework/core/SkinManager.java b/src/helma/framework/core/SkinManager.java new file mode 100644 index 00000000..2e8b2a88 --- /dev/null +++ b/src/helma/framework/core/SkinManager.java @@ -0,0 +1,134 @@ +// SkinManager.java +// Copyright (c) Hannes Wallnöfer 2002 + +package helma.framework.core; + +import java.util.HashMap; +import java.util.Iterator; +import helma.objectmodel.INode; +import java.io.*; + + +/** + * Manages skins for a Helma application + */ + + +public class SkinManager { + + Application app; + + + public SkinManager (Application app) { + this.app = app; + } + + public Skin getSkin (Object object, String skinname, Object[] skinpath) { + Prototype proto = app.getPrototype (object); + return getSkin (proto, skinname, "skin", skinpath); + } + + + public Skin getSkin (Prototype proto, String skinname, String extension, Object[] skinpath) { + if (proto == null) + return null; + Skin skin = null; + // First check if the skin has been already used within the execution of this request + /* SkinKey key = new SkinKey (proto.getName(), skinname, extension); + Skin skin = (Skin) skincache.get (key); + if (skin != null) { + return skin; + } */ + // check for skinsets set via res.skinpath property + do { + if (skinpath != null) { + for (int i=0; i 0) { try { - Action af = new Action (tmpfile, tmpname, proto); + ActionFile af = new ActionFile (tmpfile, tmpname, proto); updatables.put (list[i], af); nact.put (tmpname, af); } catch (Throwable x) { @@ -257,11 +257,12 @@ public class TypeManager { proto.updatables = updatables; // init prototype on evaluators that are already initialized. - Iterator evals = getRegisteredRequestEvaluators (); + /* Iterator evals = getRegisteredRequestEvaluators (); while (evals.hasNext ()) { RequestEvaluator reval = (RequestEvaluator) evals.next (); proto.initRequestEvaluator (reval); - } + }*/ + app.scriptingEngine.updatePrototype (proto); } @@ -303,9 +304,9 @@ public class TypeManager { if (!needsUpdate) return; - + proto.lastUpdate = System.currentTimeMillis (); - + // let the thread know we had to do something. idleSeconds = 0; // app.logEvent ("TypeManager: Updating prototypes for "+app.getName()+": "+updatables); @@ -347,7 +348,7 @@ public class TypeManager { } else if (list[i].endsWith (app.actionExtension)) { try { - Action af = new Action (tmpfile, tmpname, proto); + ActionFile af = new ActionFile (tmpfile, tmpname, proto); proto.updatables.put (list[i], af); proto.actions.put (tmpname, af); } catch (Throwable x) { @@ -362,29 +363,29 @@ public class TypeManager { } // next go through existing updatables - if (updatables == null) - return; - for (Iterator i = updatables.iterator(); i.hasNext(); ) { - Updatable upd = (Updatable) i.next(); + if (updatables != null) { + for (Iterator i = updatables.iterator(); i.hasNext(); ) { + Updatable upd = (Updatable) i.next(); - if (upd.needsUpdate ()) { - if (upd instanceof DbMapping) - rewire = true; - try { - upd.update (); - } catch (Exception x) { - if (upd instanceof DbMapping) - app.logEvent ("Error updating db mapping for type "+name+": "+x); - else - app.logEvent ("Error updating "+upd+" of prototye type "+name+": "+x); + if (upd.needsUpdate ()) { + if (upd instanceof DbMapping) + rewire = true; + try { + upd.update (); + } catch (Exception x) { + if (upd instanceof DbMapping) + app.logEvent ("Error updating db mapping for type "+name+": "+x); + else + app.logEvent ("Error updating "+upd+" of prototye type "+name+": "+x); + } } } } + app.scriptingEngine.updatePrototype (proto); } - - public void initRequestEvaluator (RequestEvaluator reval) { + /*public void initRequestEvaluator (RequestEvaluator reval) { if (!registeredEvaluators.contains (reval)) registeredEvaluators.add (reval); for (Iterator it = prototypes.values().iterator(); it.hasNext(); ) { @@ -404,7 +405,7 @@ public class TypeManager { public int countRegisteredRequestEvaluators () { return registeredEvaluators.size (); - } + } */ } diff --git a/src/helma/framework/core/ZippedAppFile.java b/src/helma/framework/core/ZippedAppFile.java index 193eae96..f3032629 100644 --- a/src/helma/framework/core/ZippedAppFile.java +++ b/src/helma/framework/core/ZippedAppFile.java @@ -72,16 +72,14 @@ public class ZippedAppFile implements Updatable { String name = fname.substring (0, fname.lastIndexOf (".")); String content = getZipEntryContent (zip, entry); // System.err.println ("["+content+"]"); - Action act = new Action (null, name, proto); - act.update (content); + ActionFile act = new ActionFile (content, name, proto); proto.actions.put (name, act); } else if (fname.endsWith (".hsp")) { String name = fname.substring (0, fname.lastIndexOf (".")); String content = getZipEntryContent (zip, entry); // System.err.println ("["+content+"]"); - Template tmp = new Template (null, name, proto); - tmp.update (content); + Template tmp = new Template (content, name, proto); proto.templates.put (name, tmp); } else if (fname.endsWith (".skin")) { diff --git a/src/helma/scripting/ActionFile.java b/src/helma/scripting/ActionFile.java new file mode 100644 index 00000000..4bceaf60 --- /dev/null +++ b/src/helma/scripting/ActionFile.java @@ -0,0 +1,128 @@ +// ActionFile.java +// Copyright (c) Helma.org 1998-2002 + +package helma.scripting; + +import java.util.Vector; +import java.util.Iterator; +import java.io.*; +import helma.framework.*; +import helma.framework.core.*; +import helma.util.Updatable; + + +/** + * An ActionFile is a file containing function code that is exposed as a URI + * of objects of this class/type. It is + * usually represented by a file with extension .hac (hop action file) + * that contains the raw body of the function. + */ + + +public class ActionFile implements Updatable { + + String name; + String functionName; + Prototype prototype; + Application app; + File file; + String content; + long lastmod; + + + public ActionFile (File file, String name, Prototype proto) { + this.prototype = proto; + this.app = proto.getApplication (); + this.name = name; + functionName = getName()+"_action"; + this.file = file; + this.content = null; + if (file != null) + update (); + } + + public ActionFile (String content, String name, Prototype proto) { + this.prototype = proto; + this.app = proto.getApplication (); + this.name = name; + functionName = getName()+"_action"; + this.file = null; + this.content = content; + } + + + /** + * Abstract method that must be implemented by subclasses to update evaluators with + * new content of action file. + */ + // protected abstract void update (String content) throws Exception; + + /** + * Abstract method that must be implemented by subclasses to remove + * action from evaluators. + */ + // protected abstract void remove (); + + + /** + * Tell the type manager whether we need an update. this is the case when + * the file has been modified or deleted. + */ + public boolean needsUpdate () { + return lastmod != file.lastModified (); + } + + + public void update () { + + if (!file.exists ()) { + // remove functions declared by this from all object prototypes + remove (); + } else { + try { + FileReader reader = new FileReader (file); + char cbuf[] = new char[(int) file.length ()]; + reader.read (cbuf); + reader.close (); + content = new String (cbuf); + // update (content); + } catch (Exception filex) { + app.logEvent ("*** Error reading action file "+file+": "+filex); + } + lastmod = file.lastModified (); + } + } + + protected void remove () { + prototype.actions.remove (name); + if (file != null) + prototype.updatables.remove (file.getName()); + } + + public String getName () { + return name; + } + + public String getContent () { + return content; + } + + public String getFunctionName () { + return functionName; + } + + public Prototype getPrototype () { + return prototype; + } + + public Application getApplication () { + return app; + } + + public String toString () { + return "ActionFile["+prototype.getName()+"/"+functionName+"]"; + } + +} + + diff --git a/src/helma/scripting/FunctionFile.java b/src/helma/scripting/FunctionFile.java index 85a08768..85076c18 100644 --- a/src/helma/scripting/FunctionFile.java +++ b/src/helma/scripting/FunctionFile.java @@ -11,13 +11,10 @@ import java.io.*; import helma.framework.*; import helma.framework.core.*; import helma.util.Updatable; -import FESI.Data.*; -import FESI.Exceptions.EcmaScriptException; -import FESI.Interpreter.*; /** - * This represents a File containing JavaScript functions for a given Object. + * This represents a File containing script functions for a given class/prototype. */ @@ -44,8 +41,8 @@ public class FunctionFile implements Updatable { } /** - * Create a function file without a file, passing the code directly. This is used for - * files contained in zipped applications. The whole update mechanism is bypassed + * Create a function file without a file, passing the code directly. This is used for + * files contained in zipped applications. The whole update mechanism is bypassed * by immediately parsing the code. */ public FunctionFile (String body, String name, Prototype proto) { @@ -54,20 +51,7 @@ public class FunctionFile implements Updatable { this.name = name; this.file = null; this.content = body; - - Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); - while (evals.hasNext ()) { - try { - - StringEvaluationSource es = new StringEvaluationSource (body, null); - StringReader reader = new StringReader (body); - - RequestEvaluator reval = (RequestEvaluator) evals.next (); - updateRequestEvaluator (reval, reader, es); - - } catch (Exception ignore) {} - } - + update (); } /** @@ -75,108 +59,41 @@ public class FunctionFile implements Updatable { * the file has been modified or deleted. */ public boolean needsUpdate () { - return lastmod != file.lastModified (); + return file != null && lastmod != file.lastModified (); } public void update () { - if (!file.exists ()) { - remove (); - - } else { - - lastmod = file.lastModified (); - // app.typemgr.readFunctionFile (file, prototype.getName ()); - - Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); - while (evals.hasNext ()) { - try { - - RequestEvaluator reval = (RequestEvaluator) evals.next (); - FileReader fr = new FileReader(file); - EvaluationSource es = new FileEvaluationSource(file.getPath(), null); - updateRequestEvaluator (reval, fr, es); - - } catch (Throwable ignore) {} - } - } - - } - - - public synchronized void updateRequestEvaluator (RequestEvaluator reval) { if (file != null) { - try { - FileReader fr = new FileReader (file); - EvaluationSource es = new FileEvaluationSource (file.getPath (), null); - updateRequestEvaluator (reval, fr, es); - } catch (IOException iox) { - app.logEvent ("Error updating function file: "+iox); + if (!file.exists ()) { + remove (); + } else { + lastmod = file.lastModified (); + // app.typemgr.readFunctionFile (file, prototype.getName ()); + // app.getScriptingEnvironment().evaluateFile (prototype, file); } } else { - StringReader reader = new StringReader (content); - StringEvaluationSource es = new StringEvaluationSource (content, null); - updateRequestEvaluator (reval, reader, es); + // app.getScriptingEnvironment().evaluateString (prototype, content); } } - public synchronized void updateRequestEvaluator (RequestEvaluator reval, Reader reader, EvaluationSource source) { + /* public void evaluate (ScriptingEnvironment env) { + if (file != null) + env.evaluateFile (prototype, file); + else + env.evaluateString (prototype, content); + }*/ + public boolean hasFile () { + return file != null; + } - HashMap priorProps = null; - HashSet newProps = null; + public File getFile () { + return file; + } - try { - - ObjectPrototype op = reval.getPrototype (prototype.getName()); - - // extract all properties from prototype _before_ evaluation, so we can compare afterwards - // but only do this is declaredProps is not up to date yet - if (declaredPropsTimestamp != lastmod) { - priorProps = new HashMap (); - // remember properties before evaluation, so we can tell what's new afterwards - try { - for (Enumeration en=op.getAllProperties(); en.hasMoreElements(); ) { - String prop = (String) en.nextElement (); - priorProps.put (prop, op.getProperty (prop, prop.hashCode())); - } - } catch (Exception ignore) {} - } - - // do the update, evaluating the file - reval.evaluator.evaluate(reader, op, source, false); - - // check what's new - if (declaredPropsTimestamp != lastmod) try { - newProps = new HashSet (); - for (Enumeration en=op.getAllProperties(); en.hasMoreElements(); ) { - String prop = (String) en.nextElement (); - if (priorProps.get (prop) == null || op.getProperty (prop, prop.hashCode()) != priorProps.get (prop)) - newProps.add (prop); - } - } catch (Exception ignore) {} - - } catch (Throwable e) { - app.logEvent ("Error parsing function file "+source+": "+e); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException ignore) {} - } - - // now remove the props that were not refreshed, and set declared props to new collection - if (declaredPropsTimestamp != lastmod) { - declaredPropsTimestamp = lastmod; - if (declaredProps != null) { - declaredProps.removeAll (newProps); - removeProperties (declaredProps); - } - declaredProps = newProps; - // System.err.println ("DECLAREDPROPS = "+declaredProps); - } - - } + public String getContent () { + return content; } @@ -185,10 +102,10 @@ public class FunctionFile implements Updatable { prototype.updatables.remove (file.getName()); // if we did not add anything to any evaluator, we're done - if (declaredProps == null || declaredProps.size() == 0) + /* if (declaredProps == null || declaredProps.size() == 0) return; - removeProperties (declaredProps); + removeProperties (declaredProps); */ } /** @@ -199,7 +116,7 @@ public class FunctionFile implements Updatable { void removeProperties (HashSet props) { // first loop through other function files in this prototype to make a set of properties // owned by other files. - HashSet otherFiles = new HashSet (); +/* HashSet otherFiles = new HashSet (); for (Iterator it=prototype.functions.values ().iterator (); it.hasNext (); ) { FunctionFile other = (FunctionFile) it.next (); if (other != this && other.declaredProps != null) @@ -220,7 +137,7 @@ public class FunctionFile implements Updatable { // System.err.println ("REMOVING PROP: "+fname); } } catch (Exception ignore) {} - } + } */ } public String toString () { @@ -234,40 +151,3 @@ public class FunctionFile implements Updatable { } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/scripting/ScriptingEnvironment.java b/src/helma/scripting/ScriptingEnvironment.java index 4c75bc93..4ded3664 100644 --- a/src/helma/scripting/ScriptingEnvironment.java +++ b/src/helma/scripting/ScriptingEnvironment.java @@ -1,11 +1,13 @@ // ScriptingEnvironment.java // Copyright (c) Hannes Wallnöfer 1998-2001 - + package helma.scripting; - +import helma.framework.core.Application; +import helma.framework.core.Prototype; +import helma.framework.core.RequestEvaluator; import java.util.*; - +import java.io.File; /** * This is the interface that must be implemented to make a scripting environment @@ -16,14 +18,30 @@ public interface ScriptingEnvironment { /** * Initialize the environment using the given properties */ - public void init (Properties props) throws ScriptingException; + public void init (Application app, Properties props) throws ScriptingException; + /** + * A prototype has been updated and must be re-evaluated. + */ + public void updatePrototype (Prototype prototype); /** * Invoke a function on some object, using the given arguments and global vars. */ - public Object invoke (Object thisObject, Object[] args, HashMap globals) throws ScriptingException; + public Object invoke (Object thisObject, String functionName, Object[] args, + HashMap globals, RequestEvaluator reval) + throws ScriptingException; + /** + * Get a property on an object + */ + public Object get (Object thisObject, String key, RequestEvaluator reval); + + /** + * Return true if a function by that name is defined for that object. + */ + public boolean hasFunction (Object thisObject, String functionName, RequestEvaluator reval) + throws ScriptingException; } @@ -51,9 +69,6 @@ public interface ScriptingEnvironment { - - - diff --git a/src/helma/scripting/Template.java b/src/helma/scripting/Template.java index 6aa4c76e..d996f9c9 100644 --- a/src/helma/scripting/Template.java +++ b/src/helma/scripting/Template.java @@ -9,14 +9,14 @@ import java.util.Iterator; import java.util.StringTokenizer; import helma.framework.*; import helma.framework.core.*; -import FESI.Data.*; -import FESI.Exceptions.*; +// import FESI.Data.*; +// import FESI.Exceptions.*; /** * This represents a Helma template, i.e. a file with the extension .hsp * (Helma server page) that contains both parts that are to be evaluated - * as EcmaScript and parts that are to be delivered to the client as-is. + * as EcmaScript and parts that are to be delivered to the client as-is. * Internally, templates are regular functions. * Helma templates are callable via URL, but this is just a leftover from the * days when there were no .hac (action) files. The recommended way @@ -24,18 +24,24 @@ import FESI.Exceptions.*; * template files to do the formatting. */ -public class Template extends Action { +public class Template extends ActionFile { // this is the *_as_string function, which is in addition to the normal one - TypeUpdater psfunc; + // TypeUpdater psfunc; public Template (File file, String name, Prototype proto) { super (file, name, proto); + functionName = name; + } + + public Template (String content, String name, Prototype proto) { + super (content, name, proto); + functionName = name; } - public void update (String content) throws Exception { + public String getContent () { // IServer.getLogger().log ("Reading text template " + name); Vector partBuffer = new Vector (); @@ -99,7 +105,7 @@ public class Template extends Action { // append a CRLF newLineCount++; templateBody.append ("\\r\\n"); - } else if (!"\r".equals (nextLine)){ + } else if (!"\r".equals (nextLine)) try { StringReader lineReader = new StringReader (nextLine); int c = lineReader.read (); while (c > -1) { @@ -109,7 +115,7 @@ public class Template extends Action { templateBody.append ((char) c); c = lineReader.read (); } - } + } catch (IOException srx) {} nextLine = st.hasMoreTokens () ? st.nextToken () : null; @@ -124,7 +130,7 @@ public class Template extends Action { // append the number of lines we have "swallowed" into // one write statement, so error messages will *approximately* // give correct line numbers. - for (int i=0; i "+esv.getClass()); + if (esv instanceof ConstructedFunctionObject || esv instanceof FesiActionAdapter.ThrowException) + op.deleteProperty (prop, prop.hashCode()); + } catch (Exception x) {} + } + } + + + + /** + * Invoke a function on some object, using the given arguments and global vars. + */ + 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 0 && n.getDbMapping () == null) { + n.setDbMapping (app.getDbMapping (protoname)); + } + + op = getPrototype (protoname); + + // no prototype found for this node? + if (op == null) + op = getPrototype("hopobject"); + + + DbMapping dbm = n.getDbMapping (); + if (dbm != null && dbm.isInstanceOf ("user")) + esn = new ESUser (n, this, null); + else + esn = new ESNode (op, evaluator, n, this); + + wrappercache.put (n, esn); + // app.logEvent ("Wrapper for "+n+" created"); + } + + return esn; + } + + + /** + * Register a new Node wrapper with the wrapper cache. This is used by the + * Node constructor. + */ + public void putNodeWrapper (INode n, ESNode esn) { + wrappercache.put (n, esn); + } + + /** + * Get a scripting wrapper object for a user object. Active user objects are represented by + * the special ESUser wrapper class. + */ + public ESNode getNodeWrapper (User u) { + if (u == null) + return null; + + ESUser esn = (ESUser) wrappercache.get (u); + + if (esn == null) { + esn = new ESUser (u.getNode(), this, u); + wrappercache.put (u, esn); + } else { + // the user node may have changed (login/logout) while the ESUser was + // lingering in the cache. + esn.updateNodeFromUser (); + } + + return esn; + } + + /** + * Return the RequestEvaluator owning and driving this FESI evaluator. + */ + public RequestEvaluator getRequestEvaluator () { + return reval; + } + + /** + * Return the Response object of the current evaluation context. Proxy method to RequestEvaluator. + */ + public ResponseTrans getResponse () { + return reval.res; + } + + /** + * Return the Request object of the current evaluation context. Proxy method to RequestEvaluator. + */ + public RequestTrans getRequest () { + return reval.req; + } + + public synchronized void evaluateFile (Prototype prototype, File file) { + try { + FileReader fr = new FileReader (file); + EvaluationSource es = new FileEvaluationSource (file.getPath (), null); + updateEvaluator (prototype, fr, es); + } catch (IOException iox) { + app.logEvent ("Error updating function file: "+iox); + } + } + + public synchronized void evaluateString (Prototype prototype, String code) { + StringReader reader = new StringReader (code); + StringEvaluationSource es = new StringEvaluationSource (code, null); + updateEvaluator (prototype, reader, es); + } + + public synchronized void updateEvaluator (Prototype prototype, Reader reader, EvaluationSource source) { + + // HashMap priorProps = null; + // HashSet newProps = null; + + try { + + ObjectPrototype op = getPrototype (prototype.getName()); + + // extract all properties from prototype _before_ evaluation, so we can compare afterwards + // but only do this is declaredProps is not up to date yet + /*if (declaredPropsTimestamp != lastmod) { + priorProps = new HashMap (); + // remember properties before evaluation, so we can tell what's new afterwards + try { + for (Enumeration en=op.getAllProperties(); en.hasMoreElements(); ) { + String prop = (String) en.nextElement (); + priorProps.put (prop, op.getProperty (prop, prop.hashCode())); + } + } catch (Exception ignore) {} + } */ + + // do the update, evaluating the file + evaluator.evaluate(reader, op, source, false); + + // check what's new + /* if (declaredPropsTimestamp != lastmod) try { + newProps = new HashSet (); + for (Enumeration en=op.getAllProperties(); en.hasMoreElements(); ) { + String prop = (String) en.nextElement (); + if (priorProps.get (prop) == null || op.getProperty (prop, prop.hashCode()) != priorProps.get (prop)) + newProps.add (prop); + } + } catch (Exception ignore) {} */ + + } catch (Throwable e) { + app.logEvent ("Error parsing function file "+source+": "+e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignore) {} + } + + // now remove the props that were not refreshed, and set declared props to new collection + /* if (declaredPropsTimestamp != lastmod) { + declaredPropsTimestamp = lastmod; + if (declaredProps != null) { + declaredProps.removeAll (newProps); + removeProperties (declaredProps); + } + declaredProps = newProps; + // System.err.println ("DECLAREDPROPS = "+declaredProps); + } */ + + } + } + + +} diff --git a/src/helma/scripting/fesi/FesiScriptingEnvironment.java b/src/helma/scripting/fesi/FesiScriptingEnvironment.java new file mode 100644 index 00000000..d58da662 --- /dev/null +++ b/src/helma/scripting/fesi/FesiScriptingEnvironment.java @@ -0,0 +1,85 @@ +// FesiScriptingEnvironment.java +// Copyright (c) Hannes Wallnöfer 2002 + +package helma.scripting.fesi; + +import helma.scripting.*; +import helma.framework.core.*; +import java.util.*; +import java.io.File; +import FESI.Data.*; +import FESI.Interpreter.*; +import FESI.Exceptions.*; + +/** + * This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter. + */ +public class FesiScriptingEnvironment implements ScriptingEnvironment { + + Application app; + Properties props; + HashMap evaluators; + + /** + * Initialize the environment using the given properties + */ + public void init (Application app, Properties props) throws ScriptingException { + this.app = app; + this.props = props; + evaluators = new HashMap (); + } + + + /** + * A prototype has been updated and must be re-evaluated. + */ + public void updatePrototype (Prototype prototype) { + for (Iterator i = evaluators.values().iterator(); i.hasNext(); ) { + FesiEvaluator fesi = (FesiEvaluator) i.next(); + fesi.evaluatePrototype (prototype); + } + } + + /** + * Invoke a function on some object, using the given arguments and global vars. + */ + public Object invoke (Object thisObject, String functionName, Object[] args, + HashMap globals, RequestEvaluator reval) + throws ScriptingException { + // check if there is already a FesiEvaluator for this RequestEvaluator. + // if not, create one. + FesiEvaluator fesi = getEvaluator (reval); + return fesi.invoke (thisObject, functionName, args, globals); + } + + /** + * Get a property on an object + */ + public Object get (Object thisObject, String key, RequestEvaluator reval) { + FesiEvaluator fesi = getEvaluator (reval); + return fesi.getProperty (thisObject, key); + } + + /** + * Return true if a function by that name is defined for that object. + */ + public boolean hasFunction (Object thisObject, String functionName, RequestEvaluator reval) + throws ScriptingException { + FesiEvaluator fesi = getEvaluator (reval); + return fesi.hasFunction (thisObject, functionName); + } + + + Collection getEvaluators () { + return evaluators.values(); + } + + FesiEvaluator getEvaluator (RequestEvaluator reval) { + FesiEvaluator fesi = (FesiEvaluator) evaluators.get (reval); + if (fesi == null) { + fesi = new FesiEvaluator (app, reval); + evaluators.put (reval, fesi); + } + return fesi; + } +} diff --git a/src/helma/scripting/fesi/HopExtension.java b/src/helma/scripting/fesi/HopExtension.java index 2a1bfd53..69777ff7 100644 --- a/src/helma/scripting/fesi/HopExtension.java +++ b/src/helma/scripting/fesi/HopExtension.java @@ -17,29 +17,28 @@ import java.util.*; import java.text.*; import org.xml.sax.InputSource; -/** - * This is the basic Extension for FESI interpreters used in HOP. It sets up - * varios constructors, global functions and properties etc. +/** + * This is the basic Extension for FESI interpreters used in Helma. It sets up + * varios constructors, global functions and properties on the HopObject prototype + * (Node objects), the user prototype, the global prototype etc. */ - + public class HopExtension { protected Application app; - protected RequestEvaluator reval; + protected FesiEvaluator fesi; - public HopExtension () { - super(); + public HopExtension (Application app) { + this.app = app; } /** * Called by the evaluator after the extension is loaded. */ - public void initializeExtension (RequestEvaluator reval) throws EcmaScriptException { - - this.reval = reval; - this.app = reval.app; - Evaluator evaluator = reval.evaluator; + public void initializeExtension (FesiEvaluator fesi) throws EcmaScriptException { + this.fesi = fesi; + Evaluator evaluator = fesi.getEvaluator (); GlobalObject go = evaluator.getGlobalObject(); FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); @@ -56,42 +55,42 @@ public class HopExtension { ESObject dp = evaluator.getDatePrototype (); dp.putHiddenProperty ("format", new DatePrototypeFormat ("format", evaluator, fp)); - sp.putHiddenProperty ("trim", new StringTrim ("trim", evaluator, fp)); + sp.putHiddenProperty ("trim", new StringTrim ("trim", evaluator, fp)); // generic (Java wrapper) object prototype - reval.esObjectPrototype = new ObjectPrototype (op, evaluator); + ObjectPrototype esObjectPrototype = new ObjectPrototype (op, evaluator); // the Node prototype - reval.esNodePrototype = new ObjectPrototype(op, evaluator); + ObjectPrototype esNodePrototype = new ObjectPrototype(op, evaluator); // the User prototype - reval.esUserPrototype = new ObjectPrototype (reval.esNodePrototype, evaluator); + ObjectPrototype esUserPrototype = new ObjectPrototype (esNodePrototype, evaluator); // the Node constructor - ESObject node = new NodeConstructor ("Node", fp, reval); + ESObject node = new NodeConstructor ("Node", fp, fesi); // register the default methods of Node objects in the Node prototype - reval.esNodePrototype.putHiddenProperty ("add", new NodeAdd ("add", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("addAt", new NodeAddAt ("addAt", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("remove", new NodeRemove ("remove", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("link", new NodeLink ("link", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("list", new NodeList ("list", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("set", new NodeSet ("set", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("get", new NodeGet ("get", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("count", new NodeCount ("count", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("contains", new NodeContains ("contains", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("size", new NodeCount ("size", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("editor", new NodeEditor ("editor", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("path", new NodeHref ("path", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("setParent", new NodeSetParent ("setParent", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("invalidate", new NodeInvalidate ("invalidate", evaluator, fp)); - reval.esNodePrototype.putHiddenProperty ("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, false, false)); - reval.esNodePrototype.putHiddenProperty ("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, false, true)); - reval.esNodePrototype.putHiddenProperty ("clearCache", new NodeClearCache ("clearCache", evaluator, fp)); + esNodePrototype.putHiddenProperty ("add", new NodeAdd ("add", evaluator, fp)); + esNodePrototype.putHiddenProperty ("addAt", new NodeAddAt ("addAt", evaluator, fp)); + esNodePrototype.putHiddenProperty ("remove", new NodeRemove ("remove", evaluator, fp)); + esNodePrototype.putHiddenProperty ("link", new NodeLink ("link", evaluator, fp)); + esNodePrototype.putHiddenProperty ("list", new NodeList ("list", evaluator, fp)); + esNodePrototype.putHiddenProperty ("set", new NodeSet ("set", evaluator, fp)); + esNodePrototype.putHiddenProperty ("get", new NodeGet ("get", evaluator, fp)); + esNodePrototype.putHiddenProperty ("count", new NodeCount ("count", evaluator, fp)); + esNodePrototype.putHiddenProperty ("contains", new NodeContains ("contains", evaluator, fp)); + esNodePrototype.putHiddenProperty ("size", new NodeCount ("size", evaluator, fp)); + esNodePrototype.putHiddenProperty ("editor", new NodeEditor ("editor", evaluator, fp)); + esNodePrototype.putHiddenProperty ("path", new NodeHref ("path", evaluator, fp)); + esNodePrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); + esNodePrototype.putHiddenProperty ("setParent", new NodeSetParent ("setParent", evaluator, fp)); + esNodePrototype.putHiddenProperty ("invalidate", new NodeInvalidate ("invalidate", evaluator, fp)); + esNodePrototype.putHiddenProperty ("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, false, false)); + esNodePrototype.putHiddenProperty ("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, false, true)); + esNodePrototype.putHiddenProperty ("clearCache", new NodeClearCache ("clearCache", evaluator, fp)); // default methods for generic Java wrapper object prototype. // This is a small subset of the methods in esNodePrototype. - reval.esObjectPrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); - reval.esObjectPrototype.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, false, false)); - reval.esObjectPrototype.putHiddenProperty("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, false, true)); + esObjectPrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); + esObjectPrototype.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, false, false)); + esObjectPrototype.putHiddenProperty("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, false, true)); // methods that give access to properties and global user lists go.putHiddenProperty("Node", node); // register the constructor for a plain Node object. @@ -122,14 +121,19 @@ public class HopExtension { go.deleteProperty("exit", "exit".hashCode()); // and some methods for session management from JS... - reval.esUserPrototype.putHiddenProperty("logon", new UserLogin ("logon", evaluator, fp)); - reval.esUserPrototype.putHiddenProperty("login", new UserLogin ("login", evaluator, fp)); - reval.esUserPrototype.putHiddenProperty("register", new UserRegister ("register", evaluator, fp)); - reval.esUserPrototype.putHiddenProperty("logout", new UserLogout ("logout", evaluator, fp)); - reval.esUserPrototype.putHiddenProperty("onSince", new UserOnSince ("onSince", evaluator, fp)); - reval.esUserPrototype.putHiddenProperty("lastActive", new UserLastActive ("lastActive", evaluator, fp)); - reval.esUserPrototype.putHiddenProperty("touch", new UserTouch ("touch", evaluator, fp)); + esUserPrototype.putHiddenProperty("logon", new UserLogin ("logon", evaluator, fp)); + esUserPrototype.putHiddenProperty("login", new UserLogin ("login", evaluator, fp)); + esUserPrototype.putHiddenProperty("register", new UserRegister ("register", evaluator, fp)); + esUserPrototype.putHiddenProperty("logout", new UserLogout ("logout", evaluator, fp)); + esUserPrototype.putHiddenProperty("onSince", new UserOnSince ("onSince", evaluator, fp)); + esUserPrototype.putHiddenProperty("lastActive", new UserLastActive ("lastActive", evaluator, fp)); + esUserPrototype.putHiddenProperty("touch", new UserTouch ("touch", evaluator, fp)); + // register object prototypes with FesiEvaluator + fesi.putPrototype ("global", go); + fesi.putPrototype ("hopobject", esNodePrototype); + fesi.putPrototype ("__javaobject__", esObjectPrototype); + fesi.putPrototype ("user", esUserPrototype); } class NodeAdd extends BuiltinFunctionObject { @@ -437,7 +441,7 @@ public class HopExtension { if (unode == null) return ESNull.theNull; else - return reval.getNodeWrapper (unode); + return fesi.getNodeWrapper (unode); } } @@ -552,12 +556,13 @@ public class HopExtension { } public ESValue callFunction (ESObject thisObj, ESValue[] arguments) throws EcmaScriptException { if (arguments.length < 1 || arguments.length > 2 || arguments[0] ==null || arguments[0] == ESNull.theNull) - throw new EcmaScriptException ("renderSkin must be called with one Skin argument and an optional parameter argument"); + throw new EcmaScriptException ("renderSkin requires one argument containing the skin name and an optional parameter object argument"); try { Skin skin = null; ESObject thisObject = global ? null : thisObj; HashMap params = null; if (arguments.length > 1 && arguments[1] instanceof ESObject) { + // create an parameter object to pass to the skin ESObject paramObject = (ESObject) arguments[1]; params = new HashMap (); for (Enumeration en=paramObject.getProperties(); en.hasMoreElements(); ) { @@ -573,17 +578,29 @@ public class HopExtension { skin = (Skin) obj; } + // if res.skinpath is set, transform it into an array of java objects + // (strings for directory names and INodes for internal, db-stored skinsets) + ResponseTrans res = fesi.getResponse(); + Object[] skinpath = new Object[0]; + if (res.skinpath != null && res.skinpath instanceof ArrayPrototype) { + ArrayPrototype array = (ArrayPrototype) res.skinpath; + skinpath = new Object[array.size()]; + for (int i=0; i 0) { - String uname = arguments[0].toString ().trim (); - user = app.getUserNode (uname); - } - if (user == null) - return ESNull.theNull; - return reval.getNodeWrapper (user); + INode user = null; + if (arguments.length > 0) { + String uname = arguments[0].toString ().trim (); + user = app.getUserNode (uname); + } + if (user == null) + return ESNull.theNull; + return fesi.getNodeWrapper (user); } } @@ -618,15 +635,15 @@ public class HopExtension { super (fp, evaluator, name, 1); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - User user = null; - if (arguments.length > 0) { - String sid = arguments[0].toString ().trim (); - user = app.getUser (sid); - } - if (user == null || user.getUID() == null) - return ESNull.theNull; - user.touch (); - return reval.getNodeWrapper (user.getNode ()); + User user = null; + if (arguments.length > 0) { + String sid = arguments[0].toString ().trim (); + user = app.getUser (sid); + } + if (user == null || user.getUID() == null) + return ESNull.theNull; + user.touch (); + return fesi.getNodeWrapper (user.getNode ()); } } @@ -663,7 +680,7 @@ public class HopExtension { User u = (User) e.nextElement (); // Note: we previously sorted out duplicate users - now we simply enumerate all active sessions. // if (u.uid == null || !visited.containsKey (u.uid)) { - theArray.setElementAt (reval.getNodeWrapper (u), i++); + theArray.setElementAt (fesi.getNodeWrapper (u), i++); // if (u.uid != null) visited.put (u.uid, u); // } } @@ -901,18 +918,6 @@ public class HopExtension { } } - - /* class NodePath extends BuiltinFunctionObject { - NodePath (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - INode n = ((ESNode) thisObject).getNode (); - String tmpname = arguments[0].toString (); - return new ESString (app.getNodePath (n, tmpname)); - } - } */ - class NodeSetParent extends BuiltinFunctionObject { NodeSetParent (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 2); @@ -964,11 +969,11 @@ public class HopExtension { return new ESString (basicHref); } private ESString renderSkin (Skin skin, String path, Object skinElem) throws EcmaScriptException { - reval.res.pushStringBuffer (); + fesi.getResponse().pushStringBuffer (); HashMap param = new HashMap (); param.put ("path", path); - skin.render (reval, skinElem, param); - return new ESString (reval.res.popStringBuffer ().trim ()); + skin.render (fesi.getRequestEvaluator(), skinElem, param); + return new ESString (fesi.getResponse().popStringBuffer ().trim ()); } } @@ -985,7 +990,7 @@ public class HopExtension { double d = from <= to ? 1.0 : -1.0; for (double i=from; i*d<=to*d; i+=step) { if (Math.abs (i%1) < l) { - int j = (int) i; + int j = (int) i; b.append ("