merged in changes from "lazy_typing" branch, prototypes

are only initialized on demand.
This commit is contained in:
hns 2002-06-07 19:15:51 +00:00
parent f1a304f122
commit aa4c0389d4
10 changed files with 460 additions and 461 deletions

View file

@ -25,16 +25,13 @@ import java.rmi.server.*;
* requests from the Web server or XML-RPC port and dispatches them to
* the evaluators.
*/
public class Application extends UnicastRemoteObject implements IRemoteApp, IPathElement, IReplicatedApp, Runnable {
public final class Application extends UnicastRemoteObject implements IRemoteApp, IPathElement, IReplicatedApp, Runnable {
private String name;
SystemProperties props, dbProps;
File home, appDir, dbDir;
protected NodeManager nmgr;
// the class name of the scripting environment implementation
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.
Object rootObject = null;
@ -111,9 +108,6 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
// Map of extensions allowed for public skins
Properties skinExtensions;
// a cache for parsed skin objects
public CacheMap skincache = new CacheMap (200, 0.80f);
// DocApplication used for introspection
public DocApplication docApp;
@ -228,29 +222,29 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
* Get the application ready to run, initializing the evaluators and type manager.
*/
public void init () throws DatabaseException, ScriptingException {
scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment ();
scriptingEngine.init (this, props);
eval = new RequestEvaluator (this);
logEvent ("Starting evaluators for "+name);
int maxThreads = 12;
try {
maxThreads = Integer.parseInt (props.getProperty ("maxThreads"));
} catch (Exception ignore) {}
freeThreads = new Stack ();
allThreads = new Vector ();
allThreads.addElement (eval);
for (int i=0; i<maxThreads; i++) {
RequestEvaluator ev = new RequestEvaluator (this);
freeThreads.push (ev);
allThreads.addElement (ev);
}
activeRequests = new Hashtable ();
// scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment ();
// scriptingEngine.init (this, props);
typemgr = new TypeManager (this);
typemgr.createPrototypes ();
// logEvent ("Started type manager for "+name);
// eval = new RequestEvaluator (this);
logEvent ("Starting evaluators for "+name);
freeThreads = new Stack ();
allThreads = new Vector ();
// allThreads.addElement (eval);
/* int maxThreads = 12;
try {
maxThreads = Integer.parseInt (props.getProperty ("maxThreads"));
} catch (Exception ignore) {}
for (int i=0; i<maxThreads; i++) {
RequestEvaluator ev = new RequestEvaluator (this);
freeThreads.push (ev);
allThreads.addElement (ev);
} */
activeRequests = new Hashtable ();
skinmgr = new SkinManager (this);
rootMapping = getDbMapping ("root");
@ -267,16 +261,15 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
rewireDbMappings ();
nmgr = new NodeManager (this, dbDir.getAbsolutePath (), props);
xmlrpcHandlerName = props.getProperty ("xmlrpcHandlerName", this.name);
if (xmlrpc != null)
xmlrpc.addHandler (xmlrpcHandlerName, new XmlRpcInvoker (this));
// typemgr.start ();
}
/**
* Create request evaluators and start scheduler and cleanup thread
* Create and start scheduler and cleanup thread
*/
public void start () {
starttime = System.currentTimeMillis();
@ -308,18 +301,21 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
ev.stopThread ();
}
}
// remove evaluators
allThreads.removeAllElements ();
freeThreads = null;
// shut down node manager and embedded db
try {
nmgr.shutdown ();
} catch (DatabaseException dbx) {
System.err.println ("Error shutting down embedded db: "+dbx);
}
// null out type manager
typemgr = null;
// stop logs if they exist
if (eventLog != null) {
eventLog.close ();
@ -339,6 +335,20 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
try {
return (RequestEvaluator) freeThreads.pop ();
} catch (EmptyStackException nothreads) {
synchronized (this) {
int maxThreads = 12;
try {
maxThreads = Integer.parseInt (props.getProperty ("maxThreads"));
} catch (Exception ignore) {
// property not set, use default value
}
if (allThreads.size() < maxThreads) {
logEvent ("Starting evaluator "+(allThreads.size()+1) +" for application "+name);
RequestEvaluator ev = new RequestEvaluator (this);
allThreads.addElement (ev);
return (ev);
}
}
throw new RuntimeException ("Maximum Thread count reached.");
}
}
@ -347,8 +357,10 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
* Returns an evaluator back to the pool when the work is done.
*/
protected void releaseEvaluator (RequestEvaluator ev) {
if (ev != null)
freeThreads.push (ev);
if (ev != null) {
ev.recycle ();
freeThreads.push (ev);
}
}
/**
@ -435,6 +447,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
// response needs to be closed/encoded before sending it back
try {
res.close (charset);
// reset data fields for garbage collection (may hold references to evaluator)
res.data = null;
req.data = null;
} catch (UnsupportedEncodingException uee) {
logEvent ("Unsupported response encoding: "+uee.getMessage());
}
@ -442,7 +457,6 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
res.waitForClose ();
}
}
return res;
}
@ -845,6 +859,10 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
return debug;
}
/**
* Utiliti function invoker for the methods below. This *must* be called
* by an active RequestEvaluator thread.
*/
private Object invokeFunction (Object obj, String func, Object[] args) {
Thread thread = Thread.currentThread ();
RequestEvaluator reval = null;
@ -858,13 +876,13 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
return reval.invokeDirectFunction (obj, func, args);
} catch (Exception x) {
if (debug)
System.err.println ("ERROR invoking function "+func+": "+x);
System.err.println ("Error in Application.invokeFunction ("+func+"): "+x);
}
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
/// The following methods mimic the IPathElement interface. This allows as
/// The following methods mimic the IPathElement interface. This allows us
/// to script any Java object: If the object implements IPathElement (as does
/// the Node class in Helma's internal objectmodel) then the corresponding
/// method is called in the object itself. Otherwise, a corresponding script function
@ -999,14 +1017,6 @@ 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.
@ -1014,12 +1024,19 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
public void run () {
long cleanupSleep = 60000; // thread sleep interval (fixed)
long scheduleSleep = 60000; // interval for scheduler invocation
long lastScheduler = 0;
long lastScheduler = 0; // run scheduler immediately
long lastCleanup = System.currentTimeMillis ();
// logEvent ("Starting scheduler for "+name);
// as first thing, invoke function onStart in the root object
eval = new RequestEvaluator (this);
allThreads.addElement (eval);
// read in standard prototypes to make first request go faster
typemgr.updatePrototype ("root");
typemgr.updatePrototype ("global");
try {
eval.invokeFunction ((INode) null, "onStart", new Object[0]);
} catch (Exception ignore) {
@ -1032,8 +1049,8 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
try {
sessionTimeout = Math.max (0, Integer.parseInt (props.getProperty ("sessionTimeout", "30")));
} catch (Exception ignore) {
System.out.println(ignore.toString());
}
System.out.println(ignore.toString());
}
long now = System.currentTimeMillis ();
@ -1044,9 +1061,12 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
for (Enumeration e = cloned.elements (); e.hasMoreElements (); ) {
Session session = (Session) e.nextElement ();
if (now - session.lastTouched () > sessionTimeout * 60000) {
// if (session.uid != null) {
// FIXME onlogout()! try {eval.invokeFunction (u, "onLogout", new Object[0]);} catch (Exception ignore) {}
// }
INode usernode = session.getUserNode ();
if (usernode != null) {
try {
eval.invokeFunction (usernode, "onLogout", new Object[0]);
} catch (Exception ignore) {}
}
destroySession(session);
}
}
@ -1080,10 +1100,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
worker = null;
break;
}
}
logEvent ("Scheduler for "+name+" exiting");
}

View file

@ -13,22 +13,22 @@ import helma.util.Updatable;
/**
* The Prototype class represents JavaScript prototypes defined in HOP
* applications. This manages a prototypes templates, functions and actions
* The Prototype class represents Script prototypes/type defined in a Helma
* application. This class manages a prototypes templates, functions and actions
* as well as optional information about the mapping of this type to a
* relational database table.
*/
public class Prototype {
public final class Prototype {
String id;
String name;
Application app;
public HashMap templates, functions, actions, skins, updatables;
long lastUpdate;
long lastUpdate, lastCheck;
Prototype parent;
private Prototype parent;
// Tells us whether this prototype is used to script a generic Java object,
// as opposed to a Helma objectmodel node object.
@ -39,7 +39,7 @@ public class Prototype {
this.app = app;
this.name = name;
isJavaPrototype = app.isJavaPrototype (name);
lastUpdate = 0; // System.currentTimeMillis ();
lastUpdate = lastCheck = 0;
}
/**
@ -57,29 +57,7 @@ public class Prototype {
// this is not allowed for the hopobject and global prototypes
if ("hopobject".equalsIgnoreCase (name) || "global".equalsIgnoreCase (name))
return;
Prototype old = this.parent;
this.parent = parent;
// if parent has changed, update ES-prototypes in request evaluators
if (parent != old) {
/* 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 -
// this is the case if no hopobject directory exists
ObjectPrototype opp = parent == null ?
reval.esNodePrototype : reval.getPrototype (parent.getName ());
// don't think this is possible, but check anyway
if (opp == null)
opp = reval.esNodePrototype;
op.setPrototype (opp);
} catch (Exception ignore) {
}
} */
}
}
public Prototype getParentPrototype () {
@ -125,10 +103,23 @@ public class Prototype {
}
}
return upd;
}
public long getLastUpdate () {
return lastUpdate;
}
public void markUpdated () {
lastUpdate = System.currentTimeMillis ();
}
public long getLastCheck () {
return lastCheck;
}
public void markChecked () {
lastCheck = System.currentTimeMillis ();
}
public String toString () {
return "[Prototype "+ app.getName()+"/"+name+"]";

View file

@ -18,11 +18,12 @@ import java.util.*;
* is killed and an error message is returned.
*/
public class RequestEvaluator implements Runnable {
public final class RequestEvaluator implements Runnable {
public Application app;
protected boolean initialized;
public final Application app;
protected final ScriptingEngine scriptingEngine;
public RequestTrans req;
public ResponseTrans res;
@ -44,7 +45,7 @@ public class RequestEvaluator implements Runnable {
// the object path of the request we're evaluating
List requestPath;
// the result of the
// the result of the operation
Object result;
// the exception thrown by the evaluator, if any.
@ -63,7 +64,7 @@ public class RequestEvaluator implements Runnable {
*/
public RequestEvaluator (Application app) {
this.app = app;
initialized = false;
scriptingEngine = helma.scripting.fesi.FesiEngineFactory.getEngine (app, this);
}
@ -79,9 +80,6 @@ public class RequestEvaluator implements Runnable {
// long startCheck = System.currentTimeMillis ();
app.typemgr.checkPrototypes ();
// evaluators are only initialized as needed, so we need to check that here
// if (!initialized)
// app.typemgr.initRequestEvaluator (this);
// System.err.println ("Type check overhead: "+(System.currentTimeMillis ()-startCheck)+" millis");
// object refs to ressolve request path
@ -225,13 +223,16 @@ public class RequestEvaluator implements Runnable {
try {
localrtx.timer.beginEvent (txname+" execute");
// enter execution context
scriptingEngine.enterContext (globals);
// set the req.action property, cutting off the _action suffix
req.action = action.substring (0, action.length()-7);
// try calling onRequest() function on object before
// calling the actual action
try {
app.scriptingEngine.invoke (currentElement, "onRequest", new Object[0], globals, this);
scriptingEngine.invoke (currentElement, "onRequest", new Object[0]);
} catch (RedirectException redir) {
throw redir;
} catch (Exception ignore) {
@ -239,7 +240,7 @@ public class RequestEvaluator implements Runnable {
}
// do the actual action invocation
app.scriptingEngine.invoke (currentElement, action, new Object[0], globals, this);
scriptingEngine.invoke (currentElement, action, new Object[0]);
localrtx.timer.endEvent (txname+" execute");
} catch (RedirectException redirect) {
@ -324,6 +325,8 @@ public class RequestEvaluator implements Runnable {
globals.put ("res", res);
globals.put ("app", app);
scriptingEngine.enterContext (globals);
currentElement = root;
if (method.indexOf (".") > -1) {
@ -343,7 +346,7 @@ public class RequestEvaluator implements Runnable {
String proto = app.getPrototypeName (currentElement);
app.checkXmlRpcAccess (proto, method);
result = app.scriptingEngine.invoke (currentElement, method, args, globals, this);
result = scriptingEngine.invoke (currentElement, method, args);
commitTransaction ();
} catch (Exception wrong) {
@ -366,14 +369,12 @@ public class RequestEvaluator implements Runnable {
// avoid going into transaction if called function doesn't exist
boolean functionexists = true;
if (thisObject == null) try {
functionexists = app.scriptingEngine.hasFunction (null, method, this);
} catch (ScriptingException ignore) {}
functionexists = scriptingEngine.hasFunction (thisObject, method);
if (!functionexists)
// global function doesn't exist, nothing to do here.
if (!functionexists) {
// function doesn't exist, nothing to do here.
reqtype = NONE;
else try {
} else try {
localrtx.begin (funcdesc);
root = app.getDataRoot ();
@ -383,7 +384,9 @@ public class RequestEvaluator implements Runnable {
globals.put ("res", res);
globals.put ("app", app);
app.scriptingEngine.invoke (thisObject, method, args, globals, this);
scriptingEngine.enterContext (globals);
result = scriptingEngine.invoke (thisObject, method, args);
commitTransaction ();
} catch (Exception wrong) {
@ -403,6 +406,9 @@ public class RequestEvaluator implements Runnable {
}
// exit execution context
scriptingEngine.exitContext ();
// make sure there is only one thread running per instance of this class
// if localrtx != rtx, the current thread has been aborted and there's no need to notify
if (localrtx != rtx) {
@ -454,8 +460,13 @@ public class RequestEvaluator implements Runnable {
// wait for request, max 10 min
wait (1000*60*10);
// if no request arrived, release ressources and thread
if (reqtype == NONE && rtx == localrtx)
if (reqtype == NONE && rtx == localrtx) {
// comment this in to release not just the thread, but also the scripting engine.
// currently we don't do this because of the risk of memory leaks (objects from
// framework referencing into the scripting engine)
// scriptingEngine = null;
rtx = null;
}
} catch (InterruptedException ir) {}
}
@ -469,13 +480,12 @@ public class RequestEvaluator implements Runnable {
checkThread ();
wait (app.requestTimeout);
if (reqtype != NONE) {
if (reqtype != NONE) {
app.logEvent ("Stopping Thread for Request "+app.getName()+"/"+req.path);
stopThread ();
res.reset ();
res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>Request timed out.</pre>");
}
return res;
}
@ -505,18 +515,20 @@ public class RequestEvaluator implements Runnable {
checkThread ();
wait (app.requestTimeout);
if (reqtype != NONE) {
if (reqtype != NONE) {
stopThread ();
}
// reset res for garbage collection (res.data may hold reference to evaluator)
res = null;
if (exception != null)
throw (exception);
return result;
}
protected Object invokeDirectFunction (Object obj, String functionName, Object[] args) throws Exception {
return app.scriptingEngine.invoke (obj, functionName, args, null, this);
}
return scriptingEngine.invoke (obj, functionName, args);
}
public synchronized Object invokeFunction (Object object, String functionName, Object[] args)
throws Exception {
@ -532,10 +544,12 @@ public class RequestEvaluator implements Runnable {
checkThread ();
wait (60000l*15); // give internal call more time (15 minutes) to complete
if (reqtype != NONE) {
if (reqtype != NONE) {
stopThread ();
}
// reset res for garbage collection (res.data may hold reference to evaluator)
res = null;
if (exception != null)
throw (exception);
return result;
@ -555,10 +569,12 @@ public class RequestEvaluator implements Runnable {
checkThread ();
wait (app.requestTimeout);
if (reqtype != NONE) {
if (reqtype != NONE) {
stopThread ();
}
// reset res for garbage collection (res.data may hold reference to evaluator)
res = null;
if (exception != null)
throw (exception);
return result;
@ -603,7 +619,18 @@ public class RequestEvaluator implements Runnable {
}
}
/**
* Null out some fields, mostly for the sake of garbage collection.
*/
public void recycle () {
res = null;
req = null;
session = null;
args = null;
requestPath = null;
result = null;
exception = null;
}
/**
* Check if an action with a given name is defined for a scripted object. If it is,
@ -613,12 +640,8 @@ public class RequestEvaluator implements Runnable {
if (obj == null)
return null;
String act = action == null ? "main_action" : action+"_action";
try {
if (app.scriptingEngine.hasFunction (obj, act, this))
return act;
} catch (ScriptingException x) {
return null;
}
if (scriptingEngine.hasFunction (obj, act))
return act;
return null;
}

View file

@ -18,19 +18,24 @@ import java.util.*;
* from the RequestEvaluator object to resolve Macro handlers by type name.
*/
public class Skin {
public final class Skin {
private Macro[] parts;
private Application app;
private char[] source;
private int sourceLength;
private HashSet sandbox;
Macro[] parts;
Application app;
char[] source;
int sourceLength;
HashSet sandbox;
/**
* Create a skin without any restrictions on which macros are allowed to be called from it
*/
public Skin (String content, Application app) {
this (content, app, null);
this.app = app;
sandbox = null;
source = content.toCharArray ();
sourceLength = source.length;
parse ();
}
/**
@ -50,8 +55,8 @@ public class Skin {
public Skin (char[] content, int length, Application app) {
this.app = app;
this.sandbox = null;
this.source = content;
this.sourceLength = length;
source = content;
sourceLength = length;
parse ();
}
@ -315,12 +320,12 @@ public class Skin {
Object v = null;
// remember length of response buffer before calling macro
int oldLength = reval.res.getBufferLength ();
if (app.scriptingEngine.hasFunction (handlerObject, name+"_macro", reval)) {
if (reval.scriptingEngine.hasFunction (handlerObject, name+"_macro")) {
// System.err.println ("Getting macro from function");
v = app.scriptingEngine.invoke (handlerObject, name+"_macro", arguments, null, reval);
v = reval.scriptingEngine.invoke (handlerObject, name+"_macro", arguments);
} else {
// System.err.println ("Getting macro from property");
v = app.scriptingEngine.get (handlerObject, name, reval);
v = reval.scriptingEngine.get (handlerObject, name);
}
// check if macro wrote out to response buffer
int newLength = reval.res.getBufferLength ();

View file

@ -16,7 +16,7 @@ import java.io.*;
* applications and updates the evaluators if anything has changed.
*/
public class TypeManager {
public final class TypeManager {
Application app;
File appDir;
@ -26,36 +26,23 @@ public class TypeManager {
long idleSeconds = 120; // if idle for longer than 5 minutes, slow down
boolean rewire;
// this contains only those evaluatores which have already been initialized
// and thus need to get updates
List registeredEvaluators;
static HashSet standardTypes;
static {
standardTypes = new HashSet ();
standardTypes.add ("user");
standardTypes.add ("global");
standardTypes.add ("root");
standardTypes.add ("hopobject");
}
static String[] standardTypes = {"user", "global", "root", "hopobject"};
public TypeManager (Application app) {
this.app = app;
appDir = app.appDir;
// make sure the directories for the standard prototypes exist, and lament otherwise
if (appDir.list().length == 0) {
for (Iterator it=standardTypes.iterator (); it.hasNext (); ) {
File f = new File (appDir, (String) it.next ());
if (!f.exists() && !f.mkdir ())
app.logEvent ("Warning: directory "+f.getAbsolutePath ()+" could not be created.");
else if (!f.isDirectory ())
app.logEvent ("Warning: "+f.getAbsolutePath ()+" is not a directory.");
}
}
prototypes = new HashMap ();
zipfiles = new HashMap ();
registeredEvaluators = Collections.synchronizedList (new ArrayList ());
this.app = app;
appDir = app.appDir;
// make sure the directories for the standard prototypes exist, and lament otherwise
if (appDir.list().length == 0) {
for (int i=0; i<standardTypes.length; i++) {
File f = new File (appDir, standardTypes[i]);
if (!f.exists() && !f.mkdir ())
app.logEvent ("Warning: directory "+f.getAbsolutePath ()+" could not be created.");
else if (!f.isDirectory ())
app.logEvent ("Warning: "+f.getAbsolutePath ()+" is not a directory.");
}
}
prototypes = new HashMap ();
zipfiles = new HashMap ();
}
@ -64,7 +51,17 @@ public class TypeManager {
* compile or evaluate any scripts.
*/
public void createPrototypes () {
check (false);
checkFiles ();
// check if standard prototypes have been created
// if not, create them.
for (int i=0; i<standardTypes.length; i++) {
String pname = standardTypes[i];
if (prototypes.get (pname) == null) {
Prototype proto = new Prototype (pname, app);
registerPrototype (pname, new File (appDir, pname), proto);
prototypes.put (pname, proto);
}
}
}
@ -73,10 +70,10 @@ public class TypeManager {
* If so, update prototypes and scripts.
*/
public synchronized void checkPrototypes () {
if (System.currentTimeMillis () - lastCheck < 500l)
if (System.currentTimeMillis () - lastCheck < 1000l)
return;
try {
check (true);
checkFiles ();
} catch (Exception ignore) {}
lastCheck = System.currentTimeMillis ();
}
@ -84,7 +81,7 @@ public class TypeManager {
/**
* Run through application's prototype directories and check if anything has been updated.
*/
public void check (boolean update) {
public void checkFiles () {
// long now = System.currentTimeMillis ();
// System.out.print ("checking "+Thread.currentThread ());
File[] list = appDir.listFiles ();
@ -96,12 +93,12 @@ public class TypeManager {
if (proto != null) {
// check if existing prototype needs update
// app.logEvent (protoDir.lastModified ());
updatePrototype (filename, list[i], proto);
// updatePrototype (filename, list[i], proto);
} else if (list[i].isDirectory () && isValidTypeName (filename)) {
// leave out ".." and other directories that contain "."
// create new prototype
proto = new Prototype (filename, app);
registerPrototype (filename, list[i], proto, update);
registerPrototype (filename, list[i], proto);
prototypes.put (filename, proto);
// give logger thread a chance to tell what's going on
// Thread.yield();
@ -121,20 +118,7 @@ public class TypeManager {
zipped.update ();
}
}
// check if standard prototypes have been created
// as a performance hack, we only do this when update is false, i.e. the first time we're called.
if (!update) {
for (Iterator it=standardTypes.iterator (); it.hasNext (); ) {
String pname = (String) it.next();
if (prototypes.get (pname) == null) {
Prototype proto = new Prototype (pname, app);
registerPrototype (pname, new File (appDir, pname), proto, false);
prototypes.put (pname, proto);
}
}
}
if (rewire) {
// there have been changes in the DbMappings
app.rewireDbMappings ();
@ -182,7 +166,8 @@ public class TypeManager {
/**
* Create a prototype from a directory containing scripts and other stuff
*/
public void registerPrototype (String name, File dir, Prototype proto, boolean update) {
public void registerPrototype (String name, File dir, Prototype proto) {
// System.err.println ("REGISTER PROTO: "+app.getName()+"/"+name);
// app.logEvent ("registering prototype "+name);
// show the type checker thread that there has been type activity
@ -194,84 +179,38 @@ public class TypeManager {
HashMap nskins = new HashMap ();
HashMap updatables = new HashMap ();
if (update) {
String list[] = dir.list();
for (int i=0; i<list.length; i++) {
File tmpfile = new File (dir, list[i]);
int dot = list[i].indexOf (".");
if (dot < 0)
continue;
String tmpname = list[i].substring(0, dot);
if (list[i].endsWith (app.templateExtension)) {
try {
Template t = new Template (tmpfile, tmpname, proto);
updatables.put (list[i], t);
ntemp.put (tmpname, t);
} catch (Throwable x) {
app.logEvent ("Error creating prototype: "+x);
}
} else if (list[i].endsWith (app.scriptExtension) && tmpfile.length () > 0) {
try {
FunctionFile ff = new FunctionFile (tmpfile, tmpname, proto);
updatables.put (list[i], ff);
nfunc.put (tmpname, ff);
} catch (Throwable x) {
app.logEvent ("Error creating prototype: "+x);
}
} else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) {
try {
ActionFile af = new ActionFile (tmpfile, tmpname, proto);
updatables.put (list[i], af);
nact.put (tmpname, af);
} catch (Throwable x) {
app.logEvent ("Error creating prototype: "+x);
}
} else if (list[i].endsWith (app.skinExtension)) {
try {
SkinFile sf = new SkinFile (tmpfile, tmpname, proto);
updatables.put (list[i], sf);
nskins.put (tmpname, sf);
} catch (Throwable x) {
app.logEvent ("Error creating prototype: "+x);
}
}
}
}
// Create and register type properties file
File propfile = new File (dir, "type.properties");
SystemProperties props = new SystemProperties (propfile.getAbsolutePath ());
DbMapping dbmap = new DbMapping (app, name, props);
updatables.put ("type.properties", dbmap);
proto.templates = ntemp;
proto.functions = nfunc;
proto.actions = nact;
proto.skins = nskins;
proto.updatables = updatables;
// init prototype on evaluators that are already initialized.
/* Iterator evals = getRegisteredRequestEvaluators ();
while (evals.hasNext ()) {
RequestEvaluator reval = (RequestEvaluator) evals.next ();
proto.initRequestEvaluator (reval);
}*/
app.scriptingEngine.updatePrototype (proto);
// app.scriptingEngine.updatePrototype (proto);
}
/**
* Update a prototype based on the directory which defines it.
*/
public void updatePrototype (String name, File dir, Prototype proto) {
public void updatePrototype (String name) {
// System.err.println ("UPDATE PROTO: "+app.getName()+"/"+name);
Prototype proto = getPrototype (name);
if (proto == null)
return;
if (System.currentTimeMillis() - proto.getLastCheck() < 1000)
return;
synchronized (proto) {
if (System.currentTimeMillis() - proto.getLastCheck() < 1000)
return;
File dir = new File (appDir, name);
boolean needsUpdate = false;
HashSet updatables = null;
@ -290,22 +229,22 @@ public class TypeManager {
if (proto.lastUpdate < dir.lastModified ()) {
String[] list = dir.list();
for (int i=0; i<list.length; i++) {
String fn = list[i];
if (!proto.updatables.containsKey (fn)) {
if (fn.endsWith (app.templateExtension) || fn.endsWith (app.scriptExtension) ||
String fn = list[i];
if (!proto.updatables.containsKey (fn)) {
if (fn.endsWith (app.templateExtension) || fn.endsWith (app.scriptExtension) ||
fn.endsWith (app.actionExtension) || fn.endsWith (app.skinExtension) ||
"type.properties".equalsIgnoreCase (fn)) {
needsUpdate = true;
// updatables.add ("[new:"+proto.getName()+"/"+fn+"]");
}
}
}
needsUpdate = true;
// updatables.add ("[new:"+proto.getName()+"/"+fn+"]");
}
}
}
}
if (!needsUpdate)
return;
proto.lastUpdate = System.currentTimeMillis ();
if (!needsUpdate) {
proto.markChecked ();
return;
}
// let the thread know we had to do something.
idleSeconds = 0;
@ -381,31 +320,12 @@ public class TypeManager {
}
}
}
app.scriptingEngine.updatePrototype (proto);
proto.markUpdated();
} // end of synchronized (proto)
// app.scriptingEngine.updatePrototype (proto);
}
/*public void initRequestEvaluator (RequestEvaluator reval) {
if (!registeredEvaluators.contains (reval))
registeredEvaluators.add (reval);
for (Iterator it = prototypes.values().iterator(); it.hasNext(); ) {
Prototype p = (Prototype) it.next ();
p.initRequestEvaluator (reval);
}
reval.initialized = true;
}
public void unregisterRequestEvaluator (RequestEvaluator reval) {
registeredEvaluators.remove (reval);
}
public Iterator getRegisteredRequestEvaluators () {
return registeredEvaluators.iterator ();
}
public int countRegisteredRequestEvaluators () {
return registeredEvaluators.size ();
} */
}

View file

@ -74,6 +74,8 @@ public class ZippedAppFile implements Updatable {
// System.err.println ("["+content+"]");
ActionFile act = new ActionFile (content, name, proto);
proto.actions.put (name, act);
// mark prototype as updated
proto.markUpdated ();
}
else if (fname.endsWith (".hsp")) {
String name = fname.substring (0, fname.lastIndexOf ("."));
@ -81,6 +83,8 @@ public class ZippedAppFile implements Updatable {
// System.err.println ("["+content+"]");
Template tmp = new Template (content, name, proto);
proto.templates.put (name, tmp);
// mark prototype as updated
proto.markUpdated ();
}
else if (fname.endsWith (".skin")) {
String name = fname.substring (0, fname.lastIndexOf ("."));
@ -95,12 +99,16 @@ public class ZippedAppFile implements Updatable {
// System.err.println ("["+content+"]");
FunctionFile ff = new FunctionFile (content, name, proto);
proto.functions.put (name, ff);
// mark prototype as updated
proto.markUpdated ();
}
else if ("type.properties".equalsIgnoreCase (fname)) {
String name = fname.substring (0, fname.lastIndexOf ("."));
SystemProperties props = new SystemProperties (zip.getInputStream (entry));
// DbMapping does its own registering, just construct it.
new DbMapping (app, proto.getName (), props);
// mark prototype as updated
proto.markUpdated ();
}
}
}

View file

@ -1,4 +1,4 @@
// ScriptingEnvironment.java
// ScriptingEngine.java
// Copyright (c) Hannes Wallnöfer 1998-2001
package helma.scripting;
@ -13,35 +13,37 @@ import java.io.File;
* This is the interface that must be implemented to make a scripting environment
* usable by the Helma application server.
*/
public interface ScriptingEnvironment {
public interface ScriptingEngine {
/**
* Initialize the environment using the given properties
* This method is called when an execution context for a request
* evaluation is entered. The globals parameter contains the global values
* to be applied during this executino context.
*/
public void init (Application app, Properties props) throws ScriptingException;
public void enterContext (HashMap globals) throws ScriptingException;
/**
* A prototype has been updated and must be re-evaluated.
* This method is called to let the scripting engine know that the current
* execution context has terminated.
*/
public void updatePrototype (Prototype prototype);
public void exitContext ();
/**
* 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)
public Object invoke (Object thisObject, String functionName, Object[] args)
throws ScriptingException;
/**
* Get a property on an object
*/
public Object get (Object thisObject, String key, RequestEvaluator reval);
public Object get (Object thisObject, String 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;
public boolean hasFunction (Object thisObject, String functionName);
}

View file

@ -0,0 +1,18 @@
// FesiEngineFactory.java
// Copyright (c) Hannes Wallnöfer 2002
package helma.scripting.fesi;
import helma.scripting.*;
import helma.framework.core.*;
/**
* Factory class for FESI evalator engines.
*/
public final class FesiEngineFactory {
public static ScriptingEngine getEngine (Application app, RequestEvaluator reval) {
return new FesiEvaluator (app, reval);
}
}

View file

@ -22,16 +22,16 @@ import Acme.LruHashtable;
* This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter.
*/
public final class FesiEvaluator {
public final class FesiEvaluator implements ScriptingEngine {
// the application we're running in
public Application app;
public final Application app;
// The FESI evaluator
Evaluator evaluator;
final Evaluator evaluator;
// the global object
GlobalObject global;
final GlobalObject global;
// caching table for JavaScript object wrappers
LruHashtable wrappercache;
@ -40,8 +40,8 @@ public final class FesiEvaluator {
Hashtable prototypes;
// the request evaluator instance owning this fesi evaluator
RequestEvaluator reval;
final RequestEvaluator reval;
// extensions loaded by this evaluator
static String[] extensions = new String[] {
"FESI.Extensions.BasicIO",
@ -57,6 +57,7 @@ public final class FesiEvaluator {
// do lazy cleanup
Map lastGlobals = null;
public FesiEvaluator (Application app, RequestEvaluator reval) {
this.app = app;
this.reval = reval;
@ -67,7 +68,7 @@ public final class FesiEvaluator {
evaluator.reval = this;
global = evaluator.getGlobalObject();
for (int i=0; i<extensions.length; i++)
evaluator.addExtension (extensions[i]);
evaluator.addExtension (extensions[i]);
HopExtension hopx = new HopExtension (app);
hopx.initializeExtension (this);
MailExtension mailx = (MailExtension) evaluator.addExtension ("helma.scripting.fesi.extensions.MailExtension");
@ -93,12 +94,66 @@ public final class FesiEvaluator {
Collection prototypes = app.getPrototypes();
for (Iterator i=prototypes.iterator(); i.hasNext(); ) {
Prototype proto = (Prototype) i.next ();
evaluatePrototype (proto);
initPrototype (proto);
}
// always fully initialize global prototype, because
// we always need it and there's no chance to trigger
// creation on demand.
getPrototype ("global");
}
void initPrototype (Prototype prototype) {
// System.err.println ("FESI INIT PROTO "+prototype);
ObjectPrototype op = null;
// get the prototype's prototype if possible and necessary
ObjectPrototype opp = null;
Prototype parent = prototype.getParentPrototype ();
if (parent != null) {
// see if parent prototype is already registered. if not, register it
opp = getRawPrototype (parent.getName ());
if (opp == null) {
initPrototype (parent);
opp = getRawPrototype (parent.getName ());
}
}
String name = prototype.getName ();
if (!"global".equalsIgnoreCase (name) && !"hopobject".equalsIgnoreCase (name) && opp == null) {
if (app.isJavaPrototype (name))
opp = getRawPrototype ("__javaobject__");
else
opp = getRawPrototype ("hopobject");
}
// if prototype doesn't exist (i.e. is a standard prototype built by HopExtension), create it.
op = getRawPrototype (name);
if (op == null) {
op = new ObjectPrototype (opp, evaluator);
try {
op.putProperty ("prototypename", new ESString (name), "prototypename".hashCode ());
} catch (EcmaScriptException ignore) {}
putPrototype (name, op);
} else {
// set parent prototype just in case it has been changed
op.setPrototype (opp);
}
// Register a constructor for all types except global.
// This will first create a new prototyped hopobject and then calls
// the actual (scripted) constructor on it.
if (!"global".equalsIgnoreCase (name)) {
if (!"root".equalsIgnoreCase (name) && !"user".equalsIgnoreCase (name))
try {
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
global.putHiddenProperty (name, new NodeConstructor (name, fp, this));
} catch (EcmaScriptException ignore) {}
}
// app.typemgr.updatePrototype (prototype.getName());
// evaluatePrototype (prototype);
}
void evaluatePrototype (Prototype prototype) {
// System.err.println ("FESI EVALUATE PROTO "+prototype+" FOR "+this);
ObjectPrototype op = null;
// get the prototype's prototype if possible and necessary
@ -119,7 +174,6 @@ public final class FesiEvaluator {
else
opp = getPrototype ("hopobject");
}
// if prototype doesn't exist (i.e. is a standard prototype built by HopExtension), create it.
op = getPrototype (name);
if (op == null) {
@ -129,18 +183,19 @@ public final class FesiEvaluator {
} catch (EcmaScriptException ignore) {}
putPrototype (name, op);
} else {
// reset prototype to original state
resetPrototype (op);
// set parent prototype just in case it has been changed
op.setPrototype (opp);
}
resetPrototype (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)) {
// This will first create a new prototyped hopobject and then calls
// the actual (scripted) constructor on it.
if (!"global".equalsIgnoreCase (name) && !"root".equalsIgnoreCase (name) && !"user".equalsIgnoreCase (name)) {
try {
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
evaluator.getGlobalObject().putHiddenProperty (name, new NodeConstructor (name, fp, this));
global.putHiddenProperty (name, new NodeConstructor (name, fp, this));
} catch (EcmaScriptException ignore) {}
}
for (Iterator it = prototype.functions.values().iterator(); it.hasNext(); ) {
@ -175,7 +230,6 @@ public final class FesiEvaluator {
String prop = en.nextElement ().toString ();
try {
ESValue esv = op.getProperty (prop, prop.hashCode ());
// System.err.println (protoname+"."+obj+" -> "+esv.getClass());
if (esv instanceof ConstructedFunctionObject || esv instanceof FesiActionAdapter.ThrowException)
op.deleteProperty (prop, prop.hashCode());
} catch (Exception x) {}
@ -183,51 +237,40 @@ public final class FesiEvaluator {
}
/**
* Invoke a function on some object, using the given arguments and global vars.
* This method is called when an execution context for a request
* evaluation is entered. The globals parameter contains the global values
* to be applied during this executino context.
*/
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 && globals != lastGlobals) {
// remember all global variables before invocation
Set tmpGlobal = new HashSet ();
for (Enumeration en = global.getAllProperties(); en.hasMoreElements(); ) {
tmpGlobal.add (en.nextElement ().toString ());
public void enterContext (HashMap globals) throws ScriptingException {
// first loop through existing prototypes and update them if necessary
for (Enumeration e=prototypes.elements(); e.hasMoreElements(); ) {
TypeInfo info = (TypeInfo) e.nextElement ();
// only update prototype if it has already been initialized.
// otherwise, this will be done on demand
if (info.lastUpdate > 0) {
Prototype p = app.typemgr.getPrototype (info.protoName);
if (p != null) {
app.typemgr.updatePrototype(info.protoName);
if (p.getLastUpdate () > info.lastUpdate) {
evaluatePrototype(p);
info.lastUpdate = p.getLastUpdate ();
}
}
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;
}
}
// set globals on the global object
if (globals != null && globals != lastGlobals) {
// 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;
try {
// 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 Map) {
sv = new ESMapWrapper (this, (Map) v);
} else if ("path".equals (k)) {
@ -255,9 +298,53 @@ public final class FesiEvaluator {
sv = ESLoader.normalizeValue (v, evaluator);
}
global.putHiddenProperty (k, sv);
} catch (Exception x) {
app.logEvent ("Error setting global variable "+k+": "+x);
}
// remember the globals set on this evaluator
// lastGlobals = globals;
}
}
// remember the globals set on this evaluator
lastGlobals = globals;
}
/**
* This method is called to let the scripting engine know that the current
* execution context has terminated.
*/
public void exitContext () {
// loop through previous globals and unset them, if necessary.
if (lastGlobals != null) {
for (Iterator i=lastGlobals.keySet().iterator(); i.hasNext(); ) {
String g = (String) i.next ();
try {
global.deleteProperty (g, g.hashCode());
} catch (Exception x) {
System.err.println ("Error resetting global property: "+g);
}
}
lastGlobals = null;
}
}
/**
* Invoke a function on some object, using the given arguments and global vars.
*/
public Object invoke (Object thisObject, String functionName, Object[] args) throws ScriptingException {
ESObject eso = null;
if (thisObject == null)
eso = global;
else
eso = getElementWrapper (thisObject);
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);
}
evaluator.thread = Thread.currentThread ();
ESValue retval = eso.doIndirectCall (evaluator, eso, functionName, esv);
@ -276,24 +363,6 @@ public final class FesiEvaluator {
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 ();
try {
if (!globalVariables.contains (g) &&
!(global.getProperty (g, g.hashCode()) instanceof BuiltinFunctionObject))
global.deleteProperty (g, g.hashCode());
} catch (Exception x) {
System.err.println ("Error resetting global property: "+g);
}
}
}
}
}
@ -303,13 +372,11 @@ public final class FesiEvaluator {
* is a java object) with that name.
*/
public boolean hasFunction (Object obj, String fname) {
ESObject eso = null;
if (obj == null)
eso = evaluator.getGlobalObject ();
else
eso = getElementWrapper (obj);
// System.err.println ("HAS_FUNC: "+fname);
String protoname = app.getPrototypeName (obj);
try {
ESValue func = eso.getProperty (fname, fname.hashCode());
ObjectPrototype op = getPrototype (protoname);
ESValue func = op.getProperty (fname, fname.hashCode());
if (func != null && func instanceof FunctionPrototype)
return true;
} catch (EcmaScriptException esx) {
@ -324,7 +391,7 @@ public final class FesiEvaluator {
* 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) {
public Object get (Object obj, String propname) {
if (obj == null || propname == null)
return null;
@ -333,7 +400,7 @@ public final class FesiEvaluator {
"password".equalsIgnoreCase (propname))
return "[macro access to password property not allowed]";
// if this is a HopObject, check if the property is defined
// 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);
@ -372,13 +439,37 @@ public final class FesiEvaluator {
return app;
}
/**
* Get the object prototype for a prototype name
* Get a raw prototype, i.e. in potentially unfinished state
* without checking if it needs to be updated.
*/
private ObjectPrototype getRawPrototype (String protoName) {
if (protoName == null)
return null;
TypeInfo info = (TypeInfo) prototypes.get (protoName);
return info == null? null : info.objectPrototype;
}
/**
* Get the object prototype for a prototype name and initialize/update it
* if necessary.
*/
public ObjectPrototype getPrototype (String protoName) {
if (protoName == null)
return null;
return (ObjectPrototype) prototypes.get (protoName);
TypeInfo info = (TypeInfo) prototypes.get (protoName);
if (info != null && info.lastUpdate == 0) {
Prototype p = app.typemgr.getPrototype (protoName);
if (p != null) {
app.typemgr.updatePrototype(protoName);
if (p.getLastUpdate () > info.lastUpdate) {
info.lastUpdate = p.getLastUpdate ();
evaluatePrototype(p);
}
}
}
return info == null? null : info.objectPrototype;
}
/**
@ -386,7 +477,7 @@ public final class FesiEvaluator {
*/
public void putPrototype (String protoName, ObjectPrototype op) {
if (protoName != null && op != null)
prototypes.put (protoName, op);
prototypes.put (protoName, new TypeInfo (op, protoName));
}
@ -418,7 +509,7 @@ public final class FesiEvaluator {
// Gotta find out the prototype name to use for this object...
String prototypeName = app.getPrototypeName (e);
ObjectPrototype op = getPrototype (prototypeName);
if (op == null)
@ -430,7 +521,7 @@ public final class FesiEvaluator {
/**
* Get a script wrapper for an implemntation of helma.objectmodel.INode
* Get a script wrapper for an instance of helma.objectmodel.INode
*/
public ESNode getNodeWrapper (INode n) {
// FIXME: should this return ESNull.theNull?
@ -440,6 +531,7 @@ public final class FesiEvaluator {
ESNode esn = (ESNode) wrappercache.get (n);
if (esn == null || esn.getNode() != n) {
String protoname = n.getPrototype ();
ObjectPrototype op = null;
// set the DbMapping of the node according to its prototype.
@ -511,15 +603,11 @@ public final class FesiEvaluator {
updateEvaluator (prototype, reader, es);
}
public synchronized void updateEvaluator (Prototype prototype, Reader reader, EvaluationSource source) {
private synchronized void updateEvaluator (Prototype prototype, Reader reader, EvaluationSource source) {
try {
ObjectPrototype op = getPrototype (prototype.getName());
// do the update, evaluating the file
evaluator.evaluate(reader, op, source, false);
} catch (Throwable e) {
app.logEvent ("Error parsing function file "+source+": "+e);
} finally {
@ -532,4 +620,16 @@ public final class FesiEvaluator {
}
class TypeInfo {
ObjectPrototype objectPrototype;
long lastUpdate = 0;
String protoName;
public TypeInfo (ObjectPrototype op, String name) {
objectPrototype = op;
protoName = name;
}
}
}

View file

@ -1,85 +0,0 @@
// 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 final 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();
}
private FesiEvaluator getEvaluator (RequestEvaluator reval) {
FesiEvaluator fesi = (FesiEvaluator) evaluators.get (reval);
if (fesi == null) {
fesi = new FesiEvaluator (app, reval);
evaluators.put (reval, fesi);
}
return fesi;
}
}