merged in changes from "lazy_typing" branch, prototypes
are only initialized on demand.
This commit is contained in:
parent
f1a304f122
commit
aa4c0389d4
10 changed files with 460 additions and 461 deletions
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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+"]";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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 ();
|
||||
} */
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
18
src/helma/scripting/fesi/FesiEngineFactory.java
Normal file
18
src/helma/scripting/fesi/FesiEngineFactory.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue