Check in current Hop snapshot

This commit is contained in:
hns 2000-12-31 15:10:47 +00:00
parent 09d1fdd4e3
commit 2f40345d3a
14 changed files with 4643 additions and 0 deletions

View file

@ -0,0 +1,112 @@
// Action.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.util.*;
import java.io.*;
import helma.framework.*;
import helma.objectmodel.IServer;
import FESI.Data.*;
/**
* An Action is a JavaScript function that is exposed as a URI. It is
* usually represented by a file with extension .hac (hop action file)
* that contains the pure JavaScript body of the function.
*/
public class Action {
String name;
String functionName;
Prototype prototype;
Application app;
long lastmod;
public Action (File file, String name, Prototype proto) {
this.prototype = proto;
this.app = proto.app;
this.name = name;
update (file);
}
public void update (File f) {
long fmod = f.lastModified ();
if (lastmod == fmod)
return;
try {
FileReader reader = new FileReader (f);
char cbuf[] = new char[(int)f.length ()];
reader.read (cbuf);
reader.close ();
String content = new String (cbuf);
update (content);
} catch (Exception filex) {
IServer.getLogger().log ("*** Error reading template file "+f+": "+filex);
}
lastmod = fmod;
}
public void update (String content) throws Exception {
// IServer.getLogger().log ("Reading text template " + name);
functionName = name+"_hop_action";
try {
app.typemgr.readFunction (functionName, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", content, prototype.getName ());
} catch (Exception x) {
String message = x.getMessage ();
app.typemgr.generateErrorFeedback (functionName, message, prototype.getName ());
}
}
public String getName () {
return name;
}
public String getFunctionName () {
return functionName;
}
}

View file

@ -0,0 +1,552 @@
// Application.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;
import java.rmi.*;
import java.rmi.server.*;
import helma.framework.*;
import helma.objectmodel.*;
import helma.objectmodel.db.NodeManager;
import helma.objectmodel.db.WrappedNodeManager;
import helma.xmlrpc.*;
import FESI.Data.*;
import FESI.Interpreter.*;
import com.sleepycat.db.DbException;
/**
* The central class of a HOP application. This class keeps a pool of so-called
* request evaluators (threads with JavaScript interpreters), waits for
* requests from the Web server or XML-RPC port and dispatches them to
* the evaluators.
*/
public class Application extends UnicastRemoteObject implements IRemoteApp, Runnable {
SystemProperties props;
File appDir, dbDir;
private String name;
protected NodeManager nmgr;
protected static WebServer xmlrpc;
protected XmlRpcAccess xmlrpcAccess;
public boolean debug;
TypeManager typemgr;
RequestEvaluator eval;
private Stack freeThreads;
protected Vector allThreads;
Hashtable sessions;
Hashtable activeUsers;
Hashtable dbMappings;
Thread worker;
long requestTimeout = 60000; // 60 seconds for request timeout.
protected String templateExtension, scriptExtension, actionExtension;
// A transient node that is shared among all evaluators
protected INode appnode;
protected volatile long requestCount = 0;
protected volatile long xmlrpcCount = 0;
protected volatile long errorCount = 0;
private DbMapping rootMapping, userRootMapping, userMapping;
public Application () throws RemoteException {
super ();
}
public Application (String name, File dbHome, File appHome) throws RemoteException, DbException {
this.name = name;
appDir = new File (appHome, name);
if (!appDir.exists())
appDir.mkdirs ();
dbDir = new File (dbHome, name);
if (!dbDir.exists())
dbDir.mkdirs ();
File propfile = new File (appDir, "app.properties");
props = new SystemProperties (propfile.getAbsolutePath (), IServer.sysProps);
nmgr = new NodeManager (this, dbDir.getAbsolutePath (), props);
debug = "true".equalsIgnoreCase (props.getProperty ("debug"));
try {
requestTimeout = Long.parseLong (props.getProperty ("requestTimeout", "60"))*1000l;
} catch (Exception ignore) { }
templateExtension = props.getProperty ("templateExtension", ".hsp");
scriptExtension = props.getProperty ("scriptExtension", ".js");
actionExtension = props.getProperty ("actionExtension", ".hac");
sessions = new Hashtable ();
activeUsers = new Hashtable ();
dbMappings = new Hashtable ();
appnode = new Node ("app");
xmlrpc = IServer.getXmlRpcServer ();
xmlrpcAccess = new XmlRpcAccess (this);
}
public void start () {
eval = new RequestEvaluator (this);
IServer.getLogger().log ("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);
}
File urootp = new File (appDir, "userroot.properties");
SystemProperties p = new SystemProperties (urootp.getAbsolutePath ());
// if no userroot.properties, set values manually
if (!urootp.exists ()) {
p.put ("_subnodes", "user.id");
p.put ("_properties", "user.name");
}
new DbMapping (this, "userroot", p);
typemgr = new TypeManager (this);
typemgr.check ();
IServer.getLogger().log ("Started type manager for "+name);
rootMapping = getDbMapping ("root");
userRootMapping = getDbMapping ("userroot");
userMapping = getDbMapping ("user");
rewireDbMappings ();
worker = new Thread (this, "Worker-"+name);
worker.setPriority (Thread.NORM_PRIORITY+2);
worker.start ();
IServer.getLogger().log ("session cleanup and scheduler thread started");
xmlrpc.addHandler (this.name, new XmlRpcInvoker (this));
typemgr.start ();
}
public void stop () {
// stop all threads, this app is going down
if (worker != null)
worker.stop ();
worker = null;
typemgr.stop ();
xmlrpc.removeHandler (this.name);
if (allThreads != null) {
for (Enumeration e=allThreads.elements (); e.hasMoreElements (); ) {
RequestEvaluator ev = (RequestEvaluator) e.nextElement ();
ev.stop ();
}
}
allThreads.removeAllElements ();
freeThreads = null;
try {
nmgr.shutdown ();
} catch (DbException dbx) {
System.err.println ("Error shutting down embedded db: "+dbx);
}
}
public synchronized RequestEvaluator getEvaluator () {
if (freeThreads == null)
throw new ApplicationStoppedException ();
if (freeThreads.empty ())
throw new RuntimeException ("Maximum Thread count reached.");
RequestEvaluator ev = (RequestEvaluator) freeThreads.pop ();
return ev;
}
public synchronized void releaseEvaluator (RequestEvaluator ev) {
if (ev != null)
freeThreads.push (ev);
}
public ResponseTrans execute (RequestTrans req) {
requestCount += 1;
User u = getUser (req.session);
ResponseTrans res = null;
RequestEvaluator ev = null;
try {
ev = getEvaluator ();
res = ev.invoke (req, u);
} catch (Exception x) {
errorCount += 1;
res = new ResponseTrans ();
res.write ("Error in application: <b>" + x.getMessage () + "</b>");
} finally {
releaseEvaluator (ev);
}
res.close (); // this needs to be done before sending it back
return res;
}
// get raw content from the database, circumventing the scripting framework.
// currently not used/supported.
public ResponseTrans get (String path, String sessionID) {
ResponseTrans res = null;
return res;
}
public void ping () {
// do nothing
}
public INode getDataRoot () {
INode root = nmgr.safe.getNode ("0", null);
root.setDbMapping (rootMapping);
return root;
}
public INode getUserRoot () {
INode users = nmgr.safe.getNode ("1", null);
users.setDbMapping (userRootMapping);
return users;
}
public WrappedNodeManager getWrappedNodeManager () {
return nmgr.safe;
}
public INode getUserNode (String uid) {
if ("prototype".equalsIgnoreCase (uid))
return null;
try {
INode users = getUserRoot ();
return users.getNode (uid, false);
} catch (Exception x) {
return null;
}
}
public Prototype getPrototype (String str) {
if (debug)
IServer.getLogger().log ("retrieving prototype for name "+str);
return typemgr.getPrototype (str);
}
public Prototype getPrototype (INode n) {
IProperty proto = n.get ("prototype", false);
if (proto == null)
return null;
return getPrototype (proto.toString ());
}
public User getUser (String sessionID) {
if (sessionID == null)
return null;
User u = (User) sessions.get (sessionID);
if (u != null) {
u.touch ();
} else {
u = new User (sessionID, this);
sessions.put (sessionID, u);
}
return u;
}
public INode registerUser (String uname, String password) {
// Register a user who already has a user object
// (i.e. who has been surfing around)
if (uname == null)
return null;
uname = uname.toLowerCase ().trim ();
if ("".equals (uname))
return null;
INode unode = null;
try {
INode users = getUserRoot ();
unode = users.getNode (uname, false);
if (unode != null)
return null;
unode = new Node (uname);
unode.setString ("name", uname);
unode.setString ("password", password);
unode.setString ("prototype", "user");
unode.setDbMapping (userMapping);
users.setNode (uname, unode);
return users.getNode (uname, false);
} catch (Exception x) {
IServer.getLogger().log ("Error registering User: "+x);
return null;
}
}
public boolean loginUser (String uname, String password, ESUser u) {
// Check the name/password of a user who already has a user object
// (i.e. who has been surfing around)
if (uname == null)
return false;
uname = uname.toLowerCase ().trim ();
if ("".equals (uname))
return false;
try {
INode users = getUserRoot ();
INode unode = users.getNode (uname, false);
String pw = unode.getString ("password", false);
if (pw.equals (password)) {
// give the user her piece of persistence
u.setNode (unode);
u.user.setNode (unode);
activeUsers.put (unode.getNameOrID (), u.user);
return true;
}
} catch (Exception x) {
return false;
}
return false;
}
public boolean logoutUser (ESUser u) {
if (u.user != null) {
String uid = u.user.uid;
if (uid != null)
activeUsers.remove (uid);
u.user.setNode (null);
u.setNode (u.user.getNode ());
}
return true;
}
public String getNodePath (INode n, String tmpname) {
INode root = getDataRoot ();
INode users = getUserRoot ();
String href = n.getUrl (root, users, tmpname);
return href;
}
public String getNodeHref (INode n, String tmpname) {
boolean linkByQuery = "query".equalsIgnoreCase (props.getProperty ("linkmethod", ""));
INode root = getDataRoot ();
INode users = getUserRoot ();
String connector = linkByQuery ? "?path=" : "/";
String req = props.getProperty ("baseURI", "") + connector;
String href = n.getHref (root, users, tmpname, req);
// add cache teaser
// href = href + "&tease="+((int) (Math.random ()*999));
return href;
}
public void run () {
long cleanupSleep = 60000; // thread sleep interval (fixed)
long scheduleSleep = 60000; // interval for scheduler invocation
long lastScheduler = 0;
IServer.getLogger().log ("Starting scheduler for "+name);
// as first thing, invoke function onStart in the root object
try {
eval.invokeFunction ((INode) null, "onStart", new ESValue[0]);
} catch (Exception ignore) {}
while (Thread.currentThread () == worker) {
// get session timeout
int sessionTimeout = 30;
try {
sessionTimeout = Math.max (0, Integer.parseInt (props.getProperty ("sessionTimeout", "30")));
} catch (Exception ignore) {}
try {
worker.sleep (cleanupSleep);
} catch (InterruptedException x) {
IServer.getLogger().log ("Scheduler for "+name+" interrupted");
Thread.currentThread().interrupt();
}
try {
IServer.getLogger().log ("Cleaning up "+name+": " + sessions.size () + " sessions active");
long now = System.currentTimeMillis ();
Hashtable cloned = (Hashtable) sessions.clone ();
for (Enumeration e = cloned.elements (); e.hasMoreElements (); ) {
User u = (User) e.nextElement ();
if (now - u.touched () > sessionTimeout * 60000) {
if (u.uid != null) {
try {
eval.invokeFunction (u, "onLogout", new ESValue[0]);
} catch (Exception ignore) {
ignore.printStackTrace ();
}
activeUsers.remove (u.uid);
}
sessions.remove (u.getSessionID ());
u.setNode (null);
}
}
IServer.getLogger().log ("Cleaned up "+name+": " + sessions.size () + " sessions remaining");
} catch (Exception cx) {
IServer.getLogger().log ("Error cleaning up sessions: "+cx);
cx.printStackTrace ();
}
long now = System.currentTimeMillis ();
if (now - lastScheduler > scheduleSleep) {
lastScheduler = now;
ESValue val = null;
try {
val = eval.invokeFunction ((INode) null, "scheduler", new ESValue[0]);
} catch (Exception ignore) {}
try {
int ret = val.toInt32 ();
if (ret < 1000)
scheduleSleep = 60000l;
else
scheduleSleep = ret;
} catch (Exception ignore) {}
IServer.getLogger().log ("Called scheduler for "+name+", will sleep for "+scheduleSleep+" millis");
}
}
IServer.getLogger().log ("Scheduler for "+name+" exiting");
}
public void rewireDbMappings () {
for (Enumeration e=dbMappings.elements(); e.hasMoreElements(); ) {
try {
DbMapping m = (DbMapping) e.nextElement ();
m.rewire ();
} catch (Exception x) {
IServer.getLogger().log ("Error rewiring DbMappings: "+x);
}
}
}
public String getName () {
return name;
}
public DbMapping getDbMapping (String typename) {
return (DbMapping) dbMappings.get (typename);
}
public void putDbMapping (String typename, DbMapping dbmap) {
dbMappings.put (typename, dbmap);
}
/**
* Check if a method may be invoked via XML-RPC on a prototype
*/
protected void checkXmlRpcAccess (String proto, String method) throws Exception {
xmlrpcAccess.checkAccess (proto, method);
}
}
//////////////////////////////////////////////////////////////
//// XML-RPC handler class
class XmlRpcInvoker implements XmlRpcHandler {
Application app;
public XmlRpcInvoker (Application app) {
this.app = app;
}
public Object execute (String method, Vector argvec) throws Exception {
app.xmlrpcCount += 1;
Object retval = null;
RequestEvaluator ev = null;
try {
ev = app.getEvaluator ();
retval = ev.invokeXmlRpc (method, argvec);
} finally {
app.releaseEvaluator (ev);
}
return retval;
}
}
//////////////////////////////////////////////////////////////
//// XML-RPC access permission checker
class XmlRpcAccess {
Application app;
Hashtable prototypes;
long lastmod;
public XmlRpcAccess (Application app) {
this.app = app;
init ();
}
public void checkAccess (String proto, String method) throws Exception {
if (app.props.lastModified () != lastmod)
init ();
Hashtable protoAccess = (Hashtable) prototypes.get (proto.toLowerCase ());
if (protoAccess == null)
throw new Exception ("Method "+method+" is not callable via XML-RPC");
if (protoAccess.get (method.toLowerCase ()) == null)
throw new Exception ("Method "+method+" is not callable via XML-RPC");
}
/*
* create internal representation of XML-RPC-Permissions. They're encoded in the app property
* file like this:
* xmlrpcAccess = root.sayHello, story.countMessages, user.login
* i.e. a prototype.method entry for each function callable via XML-RPC.
*/
private void init () {
String newAccessprop = app.props.getProperty ("xmlrpcaccess");
Hashtable newPrototypes = new Hashtable ();
if (newAccessprop != null) {
StringTokenizer st = new StringTokenizer (newAccessprop, ",; ");
while (st.hasMoreTokens ()) {
String token = st.nextToken ().trim ();
int dot = token.indexOf (".");
if (dot > -1) {
String proto = token.substring (0, dot).toLowerCase ();
String method = token.substring (dot+1).toLowerCase ();
Hashtable protoAccess = (Hashtable) newPrototypes.get (proto);
if (protoAccess == null) {
protoAccess = new Hashtable ();
newPrototypes.put (proto, protoAccess);
}
protoAccess.put (method, method);
}
}
}
this.prototypes = newPrototypes;
this.lastmod = app.props.lastModified ();
}
}

View file

@ -0,0 +1,95 @@
// ESAppNode.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import helma.objectmodel.*;
import FESI.Exceptions.*;
import FESI.Data.*;
/**
* ESApp represents the app node of an application, providing an app-wide transient shared
* space as well as access to some app related runtime information.
*/
public class ESAppNode extends ESNode {
private Application app;
private DatePrototype createtime;
public ESAppNode (INode node, RequestEvaluator eval) {
super (eval.esNodePrototype, eval.evaluator, node, eval);
app = eval.app;
createtime = new DatePrototype (eval.evaluator, node.created());
}
/**
* Overrides getProperty to return some app-specific properties
*/
public ESValue getProperty (String propname, int hash) throws EcmaScriptException {
if ("requestCount".equals (propname)) {
return new ESNumber (app.requestCount);
}
if ("xmlrpcCount".equals (propname)) {
return new ESNumber (app.xmlrpcCount);
}
if ("errorCount".equals (propname)) {
return new ESNumber (app.errorCount);
}
if ("upSince".equals (propname)) {
return createtime;
}
return super.getProperty (propname, hash);
}
public String toString () {
return ("AppNode "+node.getNameOrID ());
}
}

View file

@ -0,0 +1,560 @@
// ESNode.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import helma.objectmodel.*;
import helma.util.*;
import FESI.Interpreter.*;
import FESI.Exceptions.*;
import FESI.Data.*;
import java.io.*;
import java.util.*;
/**
* An EcmaScript wrapper around a 'Node' object. This is the basic
* HOP object type that can be stored in the internal or external databases.
* All HOP types inherit from the Node object.
*/
public class ESNode extends ObjectPrototype {
INode node;
INode cache;
// The ID of the wrapped Node. Makes ESNodes comparable without accessing the wrapped node.
String nodeID;
String protoName;
ESObject cacheWrapper;
Throwable lastError = null;
RequestEvaluator eval;
// used to create cache nodes
protected ESNode (INode node, RequestEvaluator eval) {
super (eval.esNodePrototype, eval.evaluator);
this.eval = eval;
this.node = node;
cache = null;
cacheWrapper = null;
nodeID = node.getID ();
}
public ESNode (ESObject prototype, Evaluator evaluator, Object obj, RequestEvaluator eval) {
super (prototype, evaluator);
// IServer.getLogger().log ("in ESNode constructor: "+o.getClass ());
this.eval = eval;
if (obj == null)
node = new Node ();
else if (obj instanceof ESWrapper)
node = (INode) ((ESWrapper) obj).getJavaObject ();
else if (obj instanceof INode)
node = (INode) obj;
else
node = new Node (obj.toString ());
// set nodeID to id of wrapped node
nodeID = node.getID ();
// get transient cache Node
cache = node.getCacheNode ();
cacheWrapper = new ESNode (cache, eval);
}
/**
* Check if the node has been invalidated. If so, it has to be re-fetched
* from the db via the app's node manager.
*/
private void checkNode () {
if (node.getState () == INode.INVALID) try {
setNode (eval.app.nmgr.getNode (node.getID (), node.getDbMapping ()));
} catch (Exception nx) {}
}
public INode getNode () {
checkNode ();
return node;
}
public void setNode (INode node) {
if (node != null) {
this.node = node;
eval.objectcache.put (node, this);
// get transient cache Node
cache = node.getCacheNode ();
cacheWrapper = new ESNode (cache, eval);
}
}
public void setPrototype (String protoName) {
checkNode ();
this.protoName = protoName;
node.setString ("prototype", protoName);
}
public String getESClassName () {
return protoName == null ? "Node" : protoName;
}
public String toString () {
if (node == null)
return "<null>";
return node.toString ();
}
public String toDetailString () {
return "ES:[Object: builtin " + this.getClass().getName() + ":" +
((node == null) ? "null" : node.toString()) + "]";
}
protected void setError (Throwable e) {
lastError = e;
}
public boolean setContent (ESValue what[]) {
checkNode ();
if (what.length > 0) {
if (what[0] instanceof ESString) {
node.setContent (what[0].toString ());
return true;
}
if (what[0] instanceof ESWrapper) {
Object o = ((ESWrapper) what[0]).toJavaObject ();
if (o instanceof INode) {
try {
INode p = (INode) o;
node.setContent (p.getContent (), p.getContentType ());
return true;
} catch (Exception x) {
IServer.getLogger().log ("error in ESNode.setContent: "+x);
}
}
}
if (what[0] instanceof ESNode) {
INode i = ((ESNode) what[0]).getNode ();
try {
node.setContent (i.getContent (), i.getContentType ());
return true;
} catch (Exception x) {
IServer.getLogger().log ("error in ESNode.setContent: "+x);
}
}
}
return false;
}
public Object getContent () {
checkNode ();
if (node.getContentLength () == 0)
return null;
String contentType = node.getContentType ();
if (contentType != null && contentType.startsWith ("text/")) {
return node.getText ();
} else {
return node.getContent ();
}
}
public boolean add (ESValue what[]) {
checkNode ();
for (int i=0; i<what.length; i++)
if (what[i] instanceof ESNode) {
ESNode esn = (ESNode) what[i];
INode added = node.addNode (esn.getNode ());
// only rewrap if a transient node was addet to a persistent one.
if (esn.getNode () instanceof helma.objectmodel.Node &&
!(node instanceof helma.objectmodel.Node))
esn.reWrap (added);
}
return true;
}
public ESValue list () {
checkNode ();
int l = node.numberOfNodes ();
Enumeration e = node.getSubnodes ();
ESObject ap = evaluator.getArrayPrototype();
ArrayPrototype theArray = new ArrayPrototype(ap, evaluator);
if (e != null) {
theArray.setSize(l);
for (int i = 0; i<l; i++) {
theArray.setElementAt (eval.getNodeWrapper ((INode) e.nextElement ()), i);
}
} else {
theArray.setSize (0);
}
return theArray;
}
public boolean addAt (ESValue what[]) throws EcmaScriptException {
checkNode ();
if (what.length < 2)
throw new EcmaScriptException ("Wrong number of arguments");
if (! (what[1] instanceof ESNode))
throw new EcmaScriptException ("Can ony add Node objects as subnodes");
ESNode esn = (ESNode) what[1];
INode added = node.addNode (esn.getNode (), (int) what[0].toInt32 ());
// only rewrap if a transient node was addet to a persistent one.
if (esn.getNode () instanceof helma.objectmodel.Node &&
!(node instanceof helma.objectmodel.Node))
esn.reWrap (added);
return true;
}
/**
* This is necessary to remap ESNodes to their new peers
* when they go from transient to persistent state.
*/
protected void reWrap (INode newnode) {
// IServer.getLogger().log ("rewrapping "+this+" from "+node+" to "+newnode);
if (newnode == null)
throw new RuntimeException ("Non-consistent check-in detected in reWrap ()");
INode oldnode = node;
if (oldnode == newnode) {
// IServer.getLogger().log ("loop detected or new peers unchanged in rewrap");
return;
}
// set node and nodeID to new node
node = newnode;
nodeID = node.getID ();
int l = oldnode.numberOfNodes ();
for (int i=0; i<l; i++) {
INode next = oldnode.getSubnodeAt (i);
ESNode esn = eval.getNodeWrapperFromCache (next);
// IServer.getLogger().log ("rewrapping node: "+next+" -> "+esn);
if (esn != null) {
esn.reWrap (newnode.getSubnodeAt (i));
}
}
for (Enumeration e=oldnode.properties (); e.hasMoreElements (); ) {
IProperty p = (IProperty) e.nextElement ();
if (p.getType () == IProperty.NODE) {
INode next = p.getNodeValue ();
ESNode esn = eval.getNodeWrapperFromCache (next);
if (esn != null) {
esn.reWrap (newnode.getNode (p.getName (), false));
}
}
}
}
/**
* Remove one or more subnodes.
*/
public boolean remove (ESValue args[]) {
checkNode ();
for (int i=0; i<args.length; i++) {
if (args[i] instanceof ESNode) {
ESNode esn = (ESNode) args[i];
node.removeNode (esn.getNode ());
}
}
return true;
}
/**
* Check if node is contained in subnodes
*/
public int contains (ESValue args[]) {
checkNode ();
if (args.length == 1 && args[0] instanceof ESNode) {
ESNode esn = (ESNode) args[0];
return node.contains (esn.getNode ());
}
return -1;
}
/**
* This used to be different from add(), it isn't anymore. It's left here for
* compatibility.
*/
public boolean link (ESValue args[]) {
checkNode ();
for (int i=0; i<args.length; i++) {
if (args[i] instanceof ESNode) {
ESNode esn = (ESNode) args[i];
node.addNode (esn.getNode ());
}
}
return true;
}
public boolean setParent (ESValue[] pval) {
// do a couple of checks: both nodes need to be persistent in order for setParent to make sense.
if (!(node instanceof helma.objectmodel.db.Node))
return false;
if (pval == null || pval.length < 1 || pval.length > 2)
return false;
if (!(pval[0] instanceof ESNode))
return false;
ESNode esn = (ESNode) pval[0];
INode n = esn.getNode ();
if (!(n instanceof helma.objectmodel.db.Node))
return false;
// check if there is an additional string element - if so, it's the property name by which the node is
// accessed, otherwise it will be accessed as anonymous subnode via its id
if (pval.length == 2)
((helma.objectmodel.db.Node) node).setParent ((helma.objectmodel.db.Node) n, pval[1].toString ());
else
((helma.objectmodel.db.Node) node).setParent ((helma.objectmodel.db.Node) n, null);
return true;
}
public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException {
checkNode ();
// IServer.getLogger().log ("put property called: "+propertyName+", "+propertyValue.getClass());
if ("lastmodified".equalsIgnoreCase (propertyName) || "created".equalsIgnoreCase (propertyName) ||
"contentlength".equalsIgnoreCase (propertyName) || "cache".equalsIgnoreCase (propertyName) ||
"prototype".equalsIgnoreCase (propertyName))
throw new EcmaScriptException ("Can't modify read-only property \""+propertyName+"\".");
if ("subnodeRelation".equalsIgnoreCase (propertyName)) {
node.setSubnodeRelation (propertyValue instanceof ESNull ? null : propertyValue.toString ());
return;
}
if ("contenttype".equalsIgnoreCase (propertyName))
node.setContentType (propertyValue.toString ());
else if (propertyValue instanceof ESNull)
node.unset (propertyName);
else if (propertyValue instanceof ESString)
node.setString (propertyName, propertyValue.toString ());
else if (propertyValue instanceof ESBoolean)
node.setBoolean (propertyName, propertyValue.booleanValue ());
else if (propertyValue instanceof ESNumber)
node.setFloat (propertyName, propertyValue.doubleValue ());
else if (propertyValue instanceof DatePrototype)
node.setDate (propertyName, (Date) propertyValue.toJavaObject ());
else if (propertyValue instanceof ESNode) {
// long now = System.currentTimeMillis ();
ESNode esn = (ESNode) propertyValue;
node.setNode (propertyName, esn.getNode ());
if (esn.getNode () instanceof helma.objectmodel.Node &&
!(node instanceof helma.objectmodel.Node)) {
INode newnode = node.getNode (propertyName, false);
esn.reWrap (newnode);
}
// IServer.getLogger().log ("*** spent "+(System.currentTimeMillis () - now)+" ms to set property "+propertyName);
} else {
// IServer.getLogger().log ("got "+propertyValue.getClass ());
// A persistent node can't store anything other than the types above, so throw an exception
// throw new EcmaScriptException ("Can't set a JavaScript Object or Array as property of "+node);
node.setJavaObject (propertyName, propertyValue.toJavaObject ());
}
}
public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException {
checkNode ();
// IServer.getLogger().log ("delete property called: "+propertyName);
if (node.get (propertyName, false) != null) {
node.unset (propertyName);
return true;
}
return super.deleteProperty (propertyName, hash);
}
public ESValue getProperty (int i) throws EcmaScriptException {
checkNode ();
INode n = node.getSubnodeAt (i);
if (n == null) return ESNull.theNull;
return eval.getNodeWrapper (n);
}
public void putProperty(int index, ESValue propertyValue) throws EcmaScriptException {
checkNode ();
if (propertyValue instanceof ESNode) {
ESNode n = (ESNode) propertyValue;
node.addNode (n.getNode (), index);
} else
throw new EcmaScriptException ("Can only add Nodes to Node arrays");
}
public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException {
checkNode ();
// IServer.getLogger().log ("get property called: "+propertyName);
ESValue retval = super.getProperty (propertyName, hash);
if (! (retval instanceof ESUndefined))
return retval;
if ("cache".equalsIgnoreCase (propertyName) && cache != null)
return cacheWrapper;
if ("created".equalsIgnoreCase (propertyName))
return new DatePrototype (evaluator, node.created ());
if ("lastmodified".equalsIgnoreCase (propertyName))
return new DatePrototype (evaluator, node.lastModified ());
if ("contenttype".equalsIgnoreCase (propertyName))
return new ESString (node.getContentType ());
if ("contentlength".equalsIgnoreCase (propertyName))
return new ESNumber (node.getContentLength ());
if ("subnodeRelation".equalsIgnoreCase (propertyName)) {
String rel = node.getSubnodeRelation ();
return rel == null ? (ESValue) ESNull.theNull : new ESString (rel);
}
// this is not very nice, but as a hack we expose the id of a node as node.__id__
if ("__id__".equals (propertyName))
return new ESString (node.getID ());
// this _may_ do a relational query if properties are mapped to a relational type.
IProperty p = (IProperty) node.get (propertyName, false);
if (p != null) {
if (p.getType () == IProperty.STRING) {
String str = p.getStringValue ();
if (str == null)
return ESNull.theNull;
else
return new ESString (str);
}
if (p.getType () == IProperty.BOOLEAN)
return ESBoolean.makeBoolean (p.getBooleanValue ());
if (p.getType () == IProperty.DATE)
return new DatePrototype (evaluator, p.getDateValue ());
if (p.getType () == IProperty.INTEGER)
return new ESNumber ((double) p.getIntegerValue ());
if (p.getType () == IProperty.FLOAT)
return new ESNumber (p.getFloatValue ());
if (p.getType () == IProperty.NODE) {
INode nd = p.getNodeValue ();
if (nd == null)
return ESNull.theNull;
else
return eval.getNodeWrapper (nd);
}
if (p.getType () == IProperty.JAVAOBJECT)
return ESLoader.normalizeObject (p.getJavaObjectValue (), evaluator);
}
// some more internal properties
if ("__parent__".equals (propertyName)) {
INode n = node.getParent ();
if (n == null)
return ESNull.theNull;
else
return eval.getNodeWrapper (n);
}
if ("__name__".equals (propertyName))
return new ESString (node.getName ());
if ("__fullname__".equals (propertyName))
return new ESString (node.getFullName ());
if ("__hash__".equals (propertyName))
return new ESString (""+node.hashCode ());
if ("__node__".equals (propertyName))
return ESLoader.normalizeObject (node, evaluator);
// as last resort, try to get property as anonymous subnode
INode anon = node.getSubnode (propertyName);
if (anon != null)
return eval.getNodeWrapper (anon);
return ESNull.theNull;
}
public Enumeration getAllProperties () {
return getProperties ();
}
public Enumeration getProperties () {
checkNode ();
class PropEnum implements Enumeration {
Enumeration props = node.properties();
public boolean hasMoreElements () {
return props.hasMoreElements ();
}
public Object nextElement () {
IProperty p = (IProperty) props.nextElement ();
return p.getName();
}
}
return new PropEnum ();
}
public String error() {
if (lastError == null) {
return "";
} else {
String exceptionName = lastError.getClass().getName();
int l = exceptionName.lastIndexOf(".");
if (l>0) exceptionName = exceptionName.substring(l+1);
return exceptionName +": " + lastError.getMessage();
}
}
public void clearError() {
lastError = null;
}
/**
* An ESNode equals another object if it is an ESNode that wraps the same INode
* or the wrapped INode itself. FIXME: doesen't check dbmapping/type!
*/
public boolean equals (Object what) {
if (what == null) return false;
if (what == this) return true;
if (what instanceof ESNode) {
return (((ESNode) what).nodeID.equals (this.nodeID));
}
return false;
}
} // class ESNode

View file

@ -0,0 +1,126 @@
// ESRequestData.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import FESI.Data.*;
import FESI.Exceptions.*;
import FESI.Interpreter.Evaluator;
import java.util.*;
import helma.objectmodel.INode;
/**
* An EcmaScript object to access the form data sent with a HTTP request
*/
public class ESRequestData extends ESWrapper {
private Hashtable data;
private RequestEvaluator reval;
public ESRequestData (ESObject prototype, Evaluator evaluator, RequestEvaluator reval) {
super (prototype, evaluator);
this.reval = reval;
}
public void setData (Hashtable data) {
this.data = data;
}
/**
* Overridden to make the object read-only
*/
public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException {
throw new EcmaScriptException ("Can't set property, req.data is read-only");
}
public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException {
throw new EcmaScriptException ("Can't delete property, req.data is read-only");
}
public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException {
if (data == null)
return ESNull.theNull;
Object val = data.get (propertyName);
if (val == null)
return ESNull.theNull;
if (val instanceof String)
return new ESString ((String) val);
else if (val instanceof INode)
return reval.getNodeWrapper ((INode) val);
return ESLoader.normalizeValue(val, evaluator);
}
public Enumeration getAllProperties () {
return getProperties ();
}
public Enumeration getProperties () {
if (data == null)
return new Hashtable().keys();
return data.keys();
}
}

View file

@ -0,0 +1,114 @@
// ESUser.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import helma.objectmodel.*;
import FESI.Interpreter.*;
import FESI.Exceptions.*;
import FESI.Data.*;
/**
* The ESUser is a special kind of Node object that represents a user of
* a HOP application. The actual user session data are kept in class User.
* If the user is logged in as a registered member, the wrapped node represents
* the user object in the database, while for anonymous surfers the node object
* is just a transient node. <p>
* This means that the wrapped node will be swapped when the user logs in or out.
* To save session state across logins and logouts, the
* cache property of the user object stays the same for the whole time the user
* spends on this site.
*/
public class ESUser extends ESNode {
// if the user is online, this is his/her online session object
public User user;
public ESUser (INode node, RequestEvaluator eval) {
super (eval.esUserPrototype, eval.evaluator, node, eval);
user = (User) eval.app.activeUsers.get (node.getNameOrID ());
if (user == null)
user = (User) eval.app.sessions.get (node.getNameOrID ());
if (user != null) {
cache = user.cache;
cacheWrapper = new ESNode (cache, eval);
}
}
/**
* Overrides getProperty to return the uid (which is not a regular property)
*/
public ESValue getProperty (String propname, int hash) throws EcmaScriptException {
if ("uid".equals (propname)) {
if (user == null || user.uid == null)
return ESNull.theNull;
else
return new ESString (user.uid);
}
if ("sessionID".equals (propname)) {
return new ESString (user.getSessionID ());
}
return super.getProperty (propname, hash);
}
public void setUser (User user) {
if (this.user != user) {
this.user = user;
cache = user.cache;
}
cacheWrapper = new ESNode (cache, eval);
}
public String toString () {
return ("UserNode "+node.getNameOrID ());
}
}

View file

@ -0,0 +1,81 @@
// FunctionFile.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.util.*;
import java.io.*;
import helma.framework.*;
/**
* This represents a File containing JavaScript functions for a given Object.
*/
public class FunctionFile {
String name;
Prototype prototype;
Application app;
long lastmod;
public FunctionFile (File file, String name, Prototype proto) {
this.prototype = proto;
this.app = proto.app;
this.name = name;
update (file);
}
public void update (File f) {
long fmod = f.lastModified ();
if (lastmod == fmod)
return;
lastmod = fmod;
app.typemgr.readFunctionFile (f, prototype.getName ());
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,81 @@
// NodeConstructor.java
// Copyright (c) Hannes Wallnöfer 2000
package helma.framework.core;
import FESI.Data.*;
import FESI.Exceptions.*;
import FESI.Interpreter.*;
/**
* A constructor for user defined data types. This first constructs a node, sets its prototype
* and invokes the scripted constructor function on it.
*/
public class NodeConstructor extends BuiltinFunctionObject {
RequestEvaluator reval;
String typename;
public NodeConstructor (String name, FunctionPrototype fp, RequestEvaluator reval) {
super(fp, reval.evaluator, name, 1);
typename = name;
this.reval = reval;
}
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
return doConstruct(thisObject, arguments);
}
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
ESNode node = null;
if ("Node".equals (typename)) {
if (arguments.length == 0) {
node = new ESNode (reval.esNodePrototype, this.evaluator, null, reval);
reval.objectcache.put (node.getNode (), node);
} else {
node = new ESNode (reval.esNodePrototype, this.evaluator, arguments[0], reval);
reval.objectcache.put (node.getNode (), node);
}
} else {
// Typed nodes are instantiated as helma.objectmodel.db.Node from the beginning
// even if we don't know yet if they are going to be stored in a database. The reason
// is that we want to be able to use the specail features like subnode relations even for
// transient nodes.
ObjectPrototype op = reval.getPrototype (typename);
node = new ESNode (op, reval.evaluator, typename, reval);
node.setPrototype (typename);
node.getNode ().setDbMapping (reval.app.getDbMapping (typename));
try {
// first try calling "constructor", if that doesn't work, try calling a function
// with the name of the type.
// HACK: There is an incompatibility problem here, because the property
// constructor is defined as the constructor of the object by EcmaScript.
if (op.getProperty("constructor", "constructor".hashCode()) instanceof ConstructedFunctionObject)
node.doIndirectCall (reval.evaluator, node, "constructor", arguments);
else
node.doIndirectCall (reval.evaluator, node, typename, arguments);
} catch (Exception ignore) {}
}
return node;
}
public ESValue getPropertyInScope(String propertyName, ScopeChain previousScope, int hash) throws EcmaScriptException {
return super.getPropertyInScope (propertyName, previousScope, hash);
}
public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException {
if ("prototype".equals (propertyName))
return reval.getPrototype (typename);
return super.getProperty(propertyName, hash);
}
public String[] getSpecialPropertyNames() {
String ns[] = {};
return ns;
}
} // class NodeConstructor

View file

@ -0,0 +1,172 @@
// Prototype.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.util.*;
import java.io.*;
import helma.framework.*;
import helma.objectmodel.*;
/**
* The Prototype class represents JavaScript prototypes defined in HOP
* applications. This 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 {
String id;
String name;
Application app;
Hashtable templates, functions, actions;
File codeDir;
long lastUpdate;
DbMapping dbmap;
Prototype prototype;
public Prototype (File codeDir, Application app) {
IServer.getLogger().log ("Constructing Prototype "+app.getName()+"/"+codeDir.getName ());
this.codeDir = codeDir;
this.app = app;
this.name = codeDir.getName ();
File propfile = new File (codeDir, "type.properties");
SystemProperties props = new SystemProperties (propfile.getAbsolutePath ());
dbmap = new DbMapping (app, name, props);
lastUpdate = System.currentTimeMillis ();
}
public Action getActionOrTemplate (String aname) {
Action retval = null;
if (aname == null || "".equals (aname))
aname = "main";
retval = (Action) actions.get (aname);
// check if it's allowed to access templates via URL
// this should be cached in the future
if (retval == null && "true".equalsIgnoreCase (app.props.getProperty ("exposetemplates")))
retval = (Action) templates.get (aname);
// if still not found, check if the action is defined for the generic node prototype
if (retval == null && this != app.typemgr.nodeProto && app.typemgr.nodeProto != null)
retval = app.typemgr.nodeProto.getActionOrTemplate (aname);
return retval;
}
public void setPrototype (Prototype prototype) {
this.prototype = prototype;
}
public Prototype getPrototype () {
return prototype;
}
public Template getTemplate (String tmpname) {
return (Template) templates.get (tmpname);
}
public FunctionFile getFunctionFile (String ffname) {
return (FunctionFile) functions.get (ffname);
}
public Action getAction (String afname) {
return (Action) actions.get (afname);
}
public File getCodeDir () {
return codeDir;
}
public synchronized boolean checkCodeDir () {
boolean retval = false;
String[] list = codeDir.list ();
for (int i=0; i<list.length; i++) {
if (list[i].endsWith (app.templateExtension) || list[i].endsWith (app.scriptExtension)) {
File f = new File (codeDir, list[i]);
if (f.lastModified () > lastUpdate) {
lastUpdate = System.currentTimeMillis ();
try {
app.typemgr.updatePrototype (this.name, codeDir, this);
// TypeManager.broadcaster.broadcast ("Finished update for prototype "+name+" @ "+new Date ()+"<br><hr>");
} catch (Exception x) {
IServer.getLogger().log ("Error building function protos in prototype: "+x);
// TypeManager.broadcaster.broadcast ("Error updating prototype "+name+" in application "+app.getName()+":<br>"+x.getMessage ()+"<br><hr>");
}
retval = true;
}
}
}
return retval;
}
public String getName () {
return name;
}
}

View file

@ -0,0 +1,850 @@
// RequestEvaluator.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.util.*;
import java.io.*;
import helma.objectmodel.*;
import helma.objectmodel.db.Transactor;
import helma.framework.*;
import helma.framework.extensions.*;
import helma.xmlrpc.fesi.*;
import helma.util.*;
import Acme.LruHashtable;
import FESI.Data.*;
import FESI.Interpreter.*;
import FESI.Exceptions.*;
/**
* This class does the work for incoming requests. It holds a transactor thread
* and an EcmaScript evaluator to get the work done. Incoming threads are
* blocked until the request has been serviced by the evaluator, or the timeout
* specified by the application has passed. In the latter case, the evaluator thread
* is killed and an error message is returned.
*/
public class RequestEvaluator implements Runnable {
Application app;
RequestTrans req;
ResponseTrans res;
volatile Transactor rtx;
String method;
ESObject current;
User user;
Vector args;
ESValue[] esargs;
ESValue esresult;
Object result;
Exception exception;
private ArrayPrototype reqPath;
private ESRequestData reqData;
// vars for FESI EcmaScript support
protected Evaluator evaluator;
protected ObjectPrototype esNodePrototype;
protected ObjectPrototype esUserPrototype;
protected LruHashtable objectcache;
protected Hashtable prototypes;
GlobalObject global;
HopExtension hopx;
MailExtension mailx;
FesiRpcServer xmlrpc;
ESAppNode appnode;
static String[] extensions = new String[] {
"FESI.Extensions.BasicIO",
"FESI.Extensions.FileIO",
"helma.xmlrpc.fesi.FesiRpcExtension",
"helma.framework.extensions.ImageExtension",
"helma.framework.extensions.FtpExtension",
"helma.framework.extensions.Database",
"FESI.Extensions.JavaAccess",
"FESI.Extensions.OptionalRegExp"};
// the type of request to be serviced
int reqtype;
static final int NONE = 0; // no request
static final int HTTP = 1; // via HTTP gateway
static final int XMLRPC = 2; // via XML-RPC
static final int INTERNAL = 3; // generic function call, e.g. by scheduler
INode root, userroot, currentNode;
/**
* Build a RenderContext from a RequestTrans. Checks if the path is the user home node ("user")
* or a subnode of it. Otherwise, the data root of the site is used. Two arrays are built, one
* that contains the data nodes, and anotherone with the corresponding Prototypes or Prototype.Parts.
*/
public RequestEvaluator (Application app) {
this.app = app;
this.objectcache = new LruHashtable (100, .80f);
this.prototypes = new Hashtable ();
initEvaluator ();
startThread ();
}
// init Script Evaluator
private void initEvaluator () {
try {
evaluator = new Evaluator();
global = evaluator.getGlobalObject();
for (int i=0; i<extensions.length; i++)
evaluator.addExtension (extensions[i]);
hopx = new HopExtension ();
hopx.initializeExtension (this);
mailx = (MailExtension) evaluator.addExtension ("helma.framework.extensions.MailExtension");
mailx.setProperties (this.app.props);
// fake a cache member like the one found in ESNodes
global.putHiddenProperty ("cache", new ESNode (new Node ("cache"), this));
global.putHiddenProperty ("undefined", ESUndefined.theUndefined);
appnode = new ESAppNode (app.appnode, this);
reqPath = new ArrayPrototype (evaluator.getArrayPrototype(), evaluator);
reqData = new ESRequestData (evaluator.getObjectPrototype(), evaluator, this);
} catch (Exception e) {
System.err.println("Cannot initialize interpreter");
System.err.println("Error: " + e);
e.printStackTrace ();
throw new RuntimeException (e.getMessage ());
}
}
private void startThread () {
rtx = new Transactor (this, app.nmgr);
evaluator.thread = rtx;
rtx.start ();
// yield to make sure the transactor thread reaches waiting status before the first
// invocation request comes in
Thread.yield ();
}
public void run () {
int txcount = 0;
while (evaluator.thread == Thread.currentThread () && evaluator.thread == rtx) {
synchronized (this) {
notifyAll ();
try {
wait ();
} catch (InterruptedException ir) {
Thread.currentThread().interrupt();
}
}
if (Thread.currentThread () != rtx)
return;
// IServer.getLogger().log ("got request "+reqtype);
switch (reqtype) {
case HTTP:
int tries = 0;
boolean done = false;
while (!done) {
current = null;
currentNode = null;
reqPath.setSize (0);
try {
String requestPath = app.getName()+"/"+req.path;
// set Timer to get some profiling data
rtx.timer.reset ();
rtx.timer.beginEvent (requestPath+" init");
rtx.begin (requestPath);
if (app.debug) {
IServer.getLogger().log ("request transactor = "+this);
IServer.getLogger().log ("user = "+user);
}
Action action = null;
root = app.getDataRoot ();
ESUser esu = (ESUser) getNodeWrapper (user.getNode ());
esu.setUser (user);
global.putHiddenProperty ("root", getNodeWrapper (root));
global.putHiddenProperty("user", esu);
global.putHiddenProperty ("req", ESLoader.normalizeValue(req, evaluator));
global.putHiddenProperty ("res", ESLoader.normalizeValue(res, evaluator));
global.putHiddenProperty ("path", reqPath);
global.putHiddenProperty ("app", appnode);
// set and mount the request data object
reqData.setData (req.getReqData());
req.data = reqData;
if (app.debug) {
IServer.getLogger().log ("root = "+root);
IServer.getLogger().log ("usernode = "+esu);
}
try {
if (req.path == null || "".equals (req.path.trim ())) {
currentNode = root;
current = getNodeWrapper (root);
reqPath.putProperty (0, current);
Prototype p = app.getPrototype (root);
action = p.getActionOrTemplate (null);
} else {
// march down request path...
// is the next path element a subnode or a property of the last one?
// currently only used for users node
boolean isProperty = false;
StringTokenizer st = new StringTokenizer (req.path, "/");
int ntokens = st.countTokens ();
String next = null;
currentNode = root;
reqPath.putProperty (0, getNodeWrapper (currentNode));
// the first token in the path needs to be treated seprerately,
// because "/user" is a shortcut to the current user session, while "/users"
// is the mounting point for all users.
if (ntokens > 1) {
next = st.nextToken ();
if ("user".equalsIgnoreCase (next)) {
currentNode = user.getNode ();
ntokens -=1;
if (currentNode != null)
reqPath.putProperty (1, getNodeWrapper (currentNode));
} else if ("users".equalsIgnoreCase (next)) {
currentNode = app.getUserRoot ();
ntokens -=1;
isProperty = true;
} else {
currentNode = currentNode.getSubnode (next);
ntokens -=1;
if (currentNode != null)
reqPath.putProperty (1, getNodeWrapper (currentNode));
}
}
for (int i=1; i<ntokens; i++) {
if (currentNode == null)
throw new FrameworkException ("Object not found.");
next = st.nextToken ();
if (isProperty) // get next element as property
currentNode = currentNode.getNode (next, false);
else // get next element as subnode
currentNode = currentNode.getSubnode (next);
isProperty = false;
if (currentNode != null && currentNode.getState() != INode.VIRTUAL) // add to reqPath array
reqPath.putProperty (reqPath.size(), getNodeWrapper (currentNode));
// limit path to < 20 tokens
if (i > 20) throw new RuntimeException ("Path too deep");
}
if (currentNode == null)
throw new FrameworkException ("Object not found.");
Prototype p = app.getPrototype (currentNode);
next = st.nextToken ();
if (p != null)
action = p.getActionOrTemplate (next);
if (p == null || action == null) {
currentNode = currentNode.getSubnode (next);
if (currentNode == null)
throw new FrameworkException ("Object or Action '"+next+"' not found.");
p = app.getPrototype (currentNode);
// add to reqPath array
if (currentNode != null && currentNode.getState() != INode.VIRTUAL)
reqPath.putProperty (reqPath.size(), getNodeWrapper (currentNode));
if (p != null)
action = p.getActionOrTemplate ("main");
}
current = getNodeWrapper (currentNode);
}
if (action == null)
throw new FrameworkException ("Action not found");
} catch (FrameworkException notfound) {
// The path could not be resolved. Check if there is a "not found" action
// specified in the property file.
String notFoundAction = app.props.getProperty ("notFound", "notfound");
Prototype p = app.getPrototype (root);
action = p.getActionOrTemplate (notFoundAction);
if (action == null)
throw new FrameworkException (notfound.getMessage ());
current = getNodeWrapper (root);
}
rtx.timer.endEvent (requestPath+" init");
try {
rtx.timer.beginEvent (requestPath+" execute");
current.doIndirectCall (evaluator, current, action.getFunctionName (), new ESValue[0]);
rtx.timer.endEvent (requestPath+" execute");
done = true;
} catch (RedirectException redirect) {
res.redirect = redirect.getMessage ();
done = true;
}
rtx.commit ();
done = true;
// if (app.debug)
// ((Transactor) Thread.currentThread ()).timer.dump (System.err);
} catch (ConcurrencyException x) {
try { rtx.abort (); } catch (Exception ignore) {}
res.reset ();
if (++tries < 8) {
try {
// wait a bit longer with each try
int base = 500 * tries;
Thread.currentThread ().sleep ((long) (base + Math.random ()*base*2));
} catch (Exception ignore) {}
continue;
}
app.errorCount += 1;
res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>Couldn't complete transaction due to heavy object traffic (tried "+tries+" times).</pre>");
done = true;
} catch (FrameworkException x) {
try { rtx.abort (); } catch (Exception ignore) {}
app.errorCount += 1;
res.reset ();
res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>" + x.getMessage () + "</pre>");
if (app.debug)
x.printStackTrace ();
done = true;
} catch (Exception x) {
try { rtx.abort (); } catch (Exception ignore) {}
app.errorCount += 1;
System.err.println ("### Exception in "+app.getName()+"/"+req.path+": current = "+currentNode);
System.err.println (x);
// Dump the profiling data to System.err
((Transactor) Thread.currentThread ()).timer.dump (System.err);
if (app.debug)
x.printStackTrace ();
// If the transactor thread has been killed by the invoker thread we don't have to
// bother for the error message, just quit.
if (rtx != Thread.currentThread())
return;
res.reset ();
res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>" + x.getMessage () + "</pre>");
done = true;
}
}
break;
case XMLRPC:
try {
rtx.begin (app.getName()+":xmlrpc/"+method);
root = app.getDataRoot ();
global.putHiddenProperty ("root", getNodeWrapper (root));
global.deleteProperty("user", "user".hashCode());
global.deleteProperty ("req", "req".hashCode());
// global.deleteProperty ("res", "res".hashCode());
global.putHiddenProperty ("res", ESLoader.normalizeValue(new ResponseTrans (), evaluator));
global.deleteProperty ("path", "path".hashCode());
global.putHiddenProperty ("app", appnode);
// convert arguments
int l = args.size ();
current = getNodeWrapper (root);
if (method.indexOf (".") > -1) {
StringTokenizer st = new StringTokenizer (method, ".");
int cnt = st.countTokens ();
for (int i=1; i<cnt; i++) {
String next = st.nextToken ();
try {
current = (ESObject) current.getProperty (next, next.hashCode ());
} catch (Exception x) {
throw new EcmaScriptException ("The property \""+next+"\" is not defined in the remote object.");
}
}
if (current == null)
throw new EcmaScriptException ("Method name \""+method+"\" could not be resolved.");
method = st.nextToken ();
}
// check XML-RPC access permissions
String proto = current.getProperty ("prototype", "prototype".hashCode ()).toString ();
if (proto != null)
app.checkXmlRpcAccess (proto, method);
ESValue esa[] = new ESValue[l];
for (int i=0; i<l; i++) {
esa[i] = FesiRpcUtil.convertJ2E (args.elementAt (i), evaluator);
}
result = FesiRpcUtil.convertE2J (current.doIndirectCall (evaluator, current, method, esa));
rtx.commit ();
} catch (Exception wrong) {
try { rtx.abort (); } catch (Exception ignore) {}
// If the transactor thread has been killed by the invoker thread we don't have to
// bother for the error message, just quit.
if (evaluator.thread != Thread.currentThread())
return;
this.exception = wrong;
}
break;
case INTERNAL:
esresult = ESNull.theNull;
// Just a human readable descriptor of this invocation
String funcdesc = app.getName()+":internal/"+method;
try {
rtx.begin (funcdesc);
root = app.getDataRoot ();
global.putHiddenProperty ("root", getNodeWrapper (root));
global.deleteProperty("user", "user".hashCode());
global.deleteProperty ("req", "req".hashCode());
// global.deleteProperty ("res", "res".hashCode());
global.putHiddenProperty ("res", ESLoader.normalizeValue(new ResponseTrans (), evaluator));
global.deleteProperty ("path", "path".hashCode());
global.putHiddenProperty ("app", appnode);
if (current == null) {
if (user == null) {
current = global;
} else {
ESUser esu = (ESUser) getNodeWrapper (user.getNode ());
esu.setUser (user);
current = esu;
}
}
esresult = current.doIndirectCall (evaluator, current, method, new ESValue[0]);
rtx.commit ();
} catch (Throwable wrong) {
try { rtx.abort (); } catch (Exception ignore) {}
// If the transactor thread has been killed by the invoker thread we don't have to
// bother for the error message, just quit.
if (evaluator.thread != Thread.currentThread())
return;
String msg = wrong.getMessage ();
if (msg == null || msg.length () == 0)
msg = wrong.toString ();
IServer.getLogger().log ("Error executing "+funcdesc+": "+msg);
this.exception = new Exception (msg);
}
break;
}
reqtype = NONE;
// create a new Thread every 1000 requests. The current one will fade out
if (txcount++ > 1000) {
rtx.cleanup (); // close sql connections held by this thread
startThread ();
}
}
// IServer.getLogger().log (this+ " exiting.");
}
public synchronized ResponseTrans invoke (RequestTrans req, User user) throws Exception {
checkThread ();
this.reqtype = HTTP;
this.req = req;
this.user = user;
this.res = new ResponseTrans ();
notifyAll ();
wait (app.requestTimeout);
if (reqtype > 0) {
IServer.getLogger().log ("Stopping Thread for Request "+app.getName()+"/"+req.path);
evaluator.thread = null;
rtx.kill ();
res.reset ();
res.write ("<b>Error in application '"+app.getName()+"':</b> <br><br><pre>Request timed out.</pre>");
rtx = new Transactor (this, app.nmgr);
evaluator.thread = rtx;
rtx.start ();
Thread.yield ();
}
return res;
}
public synchronized Object invokeXmlRpc (String method, Vector args) throws Exception {
checkThread ();
this.reqtype = XMLRPC;
this.user = null;
this.method = method;
this.args = args;
result = null;
exception = null;
notifyAll ();
wait (app.requestTimeout);
if (reqtype > 0) {
IServer.getLogger().log ("Stopping Thread");
evaluator.thread = null;
rtx.kill ();
rtx = new Transactor (this, app.nmgr);
evaluator.thread = rtx;
rtx.start ();
Thread.yield ();
}
if (exception != null)
throw (exception);
return result;
}
public synchronized ESValue invokeFunction (INode node, String functionName, ESValue[] args)
throws Exception {
ESObject obj = null;
if (node == null)
obj = global;
else
obj = getNodeWrapper (node);
return invokeFunction (obj, functionName, args);
}
public synchronized ESValue invokeFunction (ESObject obj, String functionName, ESValue[] args)
throws Exception {
checkThread ();
this.reqtype = INTERNAL;
this.user = null;
this.current = obj;
this.method = functionName;
this.esargs = args;
esresult = ESNull.theNull;
exception = null;
notifyAll ();
wait (60000l*15); // give internal call more time (15 minutes) to complete
if (reqtype > 0) {
IServer.getLogger().log ("Stopping Thread");
evaluator.thread = null;
rtx.kill ();
rtx = new Transactor (this, app.nmgr);
evaluator.thread = rtx;
rtx.start ();
Thread.yield ();
}
if (exception != null)
throw (exception);
return esresult;
}
public synchronized ESValue invokeFunction (User user, String functionName, ESValue[] args)
throws Exception {
checkThread ();
this.reqtype = INTERNAL;
this.user = user;
this.current = null;
this.method = functionName;
this.esargs = args;
esresult = ESNull.theNull;
exception = null;
notifyAll ();
wait (app.requestTimeout);
if (reqtype > 0) {
IServer.getLogger().log ("Stopping Thread");
evaluator.thread = null;
rtx.kill ();
rtx = new Transactor (this, app.nmgr);
evaluator.thread = rtx;
rtx.start ();
Thread.yield ();
}
if (exception != null)
throw (exception);
return esresult;
}
/**
* Stop this request evaluator. If currently active kill the request, otherwise just
* notify.
*/
public synchronized void stop () {
evaluator.thread = null;
if (reqtype != NONE) {
rtx.kill ();
} else {
notifyAll ();
}
}
private void checkThread () {
if (rtx == null || !rtx.isAlive()) {
rtx = new Transactor (this, app.nmgr);
evaluator.thread = rtx;
rtx.start ();
Thread.yield ();
}
}
/**
* Returns a node wrapper only if it already exists in the cache table. This is used
* in those places when wrappers have to be updated if they already exist.
*/
public ESNode getNodeWrapperFromCache (INode n) {
return n == null ? null : (ESNode) objectcache.get (n);
}
public ESNode getNodeWrapper (INode n) {
if (n == null)
return null;
ESNode esn = (ESNode) objectcache.get (n);
if (esn == null || esn.getNode() != n) {
ObjectPrototype op = null;
String protoname = n.getString ("prototype", false);
// set the DbMapping of the node according to its prototype.
// this *should* be done on the objectmodel level, but isn't currently
// for embedded nodes since there's not enough type info at the objectmodel level
// for those nodes.
if (protoname != null && protoname.length() > 0 && n.getDbMapping () == null) {
n.setDbMapping (app.getDbMapping (protoname));
}
try {
op = (ObjectPrototype) prototypes.get (protoname);
} catch (Exception ignore) {}
if (op == null)
op = esNodePrototype; // no prototype found for this node.
if ("user".equalsIgnoreCase (protoname))
esn = new ESUser (n, this);
else
esn = new ESNode (op, evaluator, n, this);
objectcache.put (n, esn);
// IServer.getLogger().log ("Wrapper for "+n+" created");
}
return esn;
}
public ObjectPrototype getPrototype (String protoName) {
return (ObjectPrototype) prototypes.get (protoName);
}
public void putPrototype (String protoName, ObjectPrototype op) {
prototypes.put (protoName, op);
}
}

View file

@ -0,0 +1,214 @@
// Template.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.util.*;
import java.io.*;
import helma.framework.*;
import FESI.Data.*;
/**
* This represents a HOP template, i.e. a file with the extension .hsp
* (HOP server page) that contains both parts that are to be evaluated
* as EcmaScript and parts that are to be delivered to the client as-is.
* Internally, templates are regular functions.
* HOP templates are callable via URL, but this is just a leftover from the
* days when there were no .hac (action) files. The recommended way
* now is to have a .hac file with all the logic which in turn calls one or more
* template files to do the formatting.
*/
public class Template extends Action {
public Template (File file, String name, Prototype proto) {
super (file, name, proto);
}
public void update (String content) throws Exception {
// IServer.getLogger().log ("Reading text template " + name);
Vector partBuffer = new Vector ();
int l = content.length ();
char cnt[] = new char[l];
content.getChars (0, l, cnt, 0);
// if last charackter is whitespace, swallow it. this is necessary for some inner templates to look ok.
if (Character.isWhitespace (cnt[l-1]))
l -= 1;
int lastIdx = 0;
for (int i = 0; i < l-1; i++) {
if (cnt[i] == '<' && cnt[i+1] == '%') {
int j = i+2;
while (j < l-1 && (cnt[j] != '%' || cnt[j+1] != '>')) {
j++;
}
if (j > i+2) {
if (i - lastIdx > 0)
partBuffer.addElement (new Part (this, new String (cnt, lastIdx, i - lastIdx), true));
String script = new String (cnt, i+2, (j-i)-2);
partBuffer.addElement (new Part (this, script, false));
lastIdx = j+2;
}
i = j+1;
}
}
if (lastIdx < l)
partBuffer.addElement (new Part (this, new String (cnt, lastIdx, l - lastIdx), true));
StringBuffer templateBody = new StringBuffer ();
int nparts = partBuffer.size();
for (int k = 0; k < nparts; k++) {
Part nextPart = (Part) partBuffer.elementAt (k);
if (nextPart.isStatic || nextPart.content.trim ().startsWith ("=")) {
// check for <%= ... %> statements
if (!nextPart.isStatic) {
nextPart.content = nextPart.content.trim ().substring (1).trim ();
// cut trailing ";"
while (nextPart.content.endsWith (";"))
nextPart.content = nextPart.content.substring (0, nextPart.content.length()-1);
}
StringTokenizer st = new StringTokenizer (nextPart.content, "\r\n", true);
String nextLine = st.hasMoreTokens () ? st.nextToken () : null;
// count newLines we "swallow", see explanation below
int newLineCount = 0;
templateBody.append ("res.write (");
if (nextPart.isStatic) {
templateBody.append ("\"");
}
while (nextLine != null) {
if ("\n".equals (nextLine)) {
// append a CRLF
newLineCount++;
templateBody.append ("\\r\\n");
} else if (!"\r".equals (nextLine)){
StringReader lineReader = new StringReader (nextLine);
int c = lineReader.read ();
while (c > -1) {
if (nextPart.isStatic && ((char)c == '"' || (char)c == '\\')) {
templateBody.append ('\\');
}
templateBody.append ((char) c);
c = lineReader.read ();
}
}
nextLine = st.hasMoreTokens () ? st.nextToken () : null;
}
if (nextPart.isStatic) {
templateBody.append ("\"");
}
templateBody.append ("); ");
// append the number of lines we have "swallowed" into
// one write statement, so error messages will *approximately*
// give correct line numbers.
for (int i=0; i<newLineCount; i++) {
templateBody.append ("\r\n");
}
} else {
templateBody.append (nextPart.content);
if (!nextPart.content.trim ().endsWith (";")) {
templateBody.append (";");
}
}
}
// templateBody.append ("\r\nreturn null;\r\n");
functionName = name;
String fname = name+"_as_string";
String body = templateBody.toString ();
try {
app.typemgr.readFunction (name,
"arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10",
body+"\r\nreturn null;\r\n",
prototype.getName ());
} catch (Exception x) {
String message = x.getMessage ();
app.typemgr.generateErrorFeedback (name, message, prototype.getName ());
}
try {
app.typemgr.readFunction (fname,
"arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10",
"res.pushStringBuffer(); "+body+"\r\nreturn res.popStringBuffer();\r\n",
prototype.getName ());
} catch (Exception x) {
String message = x.getMessage ();
app.typemgr.generateErrorFeedback (fname, message, prototype.getName ());
}
}
class Part {
String content;
Template parent;
boolean isPart;
boolean isStatic;
public Part (Template parent, String content, boolean isStatic) {
isPart = false;
this.parent = parent;
this.content = content;
this.isStatic = isStatic;
}
public String getName () {
return isStatic ? null : content;
}
public String toString () {
return "Template.Part ["+content+","+isStatic+"]";
}
}
}

View file

@ -0,0 +1,536 @@
// TypeManager.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import helma.objectmodel.*;
import helma.util.*;
import FESI.Parser.*;
import FESI.AST.ASTFormalParameterList;
import FESI.AST.ASTStatementList;
import FESI.AST.EcmaScriptTreeConstants;
import FESI.Interpreter.*;
import FESI.Exceptions.*;
import FESI.Extensions.*;
import FESI.Data.*;
import java.util.*;
import java.io.*;
/**
* The type manager periodically checks the prototype definitions for its
* applications and updates the evaluators if anything has changed.
*/
public class TypeManager implements Runnable, EcmaScriptTreeConstants {
Application app;
File appDir;
Hashtable prototypes;
Prototype nodeProto;
long idleSeconds = 120; // if idle for longer than 5 minutes, slow down
boolean rewire;
Thread typechecker;
// The http broadcaster for pushing out parser output
// static WebBroadcaster broadcaster;
// static {
// try {
// broadcaster = new WebBroadcaster (9999);
// } catch (IOException ignore) {}
// }
public TypeManager (Application app) {
this.app = app;
appDir = app.appDir;
File f = new File (appDir, "user");
if (!f.exists())
f.mkdir ();
f = new File (appDir, "root");
if (!f.exists())
f.mkdir ();
f = new File (appDir, "global");
if (!f.exists())
f.mkdir ();
prototypes = new Hashtable ();
nodeProto = null;
}
public void check () {
// long now = System.currentTimeMillis ();
// System.out.print ("checking "+Thread.currentThread ());
try {
String[] list = appDir.list ();
for (int i=0; i<list.length; i++) {
File protoDir = new File (appDir, list[i]);
// cut out ".." and other directories that contain "."
if (isValidTypeName (list[i]) && protoDir.isDirectory ()) {
Prototype proto = getPrototype (list[i]);
if (proto != null) {
// check if existing prototype needs update
// IServer.getLogger().log (protoDir.lastModified ());
updatePrototype (list[i], protoDir, proto);
} else {
// create new prototype
proto = new Prototype (protoDir, app);
registerPrototype (list[i], protoDir, proto);
prototypes.put (list[i], proto);
if ("node".equalsIgnoreCase (list[i]))
nodeProto = proto;
// give logger thread a chance to tell what's going on
Thread.yield();
}
}
}
} catch (Exception ignore) {
IServer.getLogger().log (this+": "+ignore);
}
if (rewire) {
// there have been changes @ DbMappings
app.rewireDbMappings ();
rewire = false;
}
// IServer.getLogger().log (" ...done @ "+ (System.currentTimeMillis () - now)+ "--- "+idleSeconds);
}
private boolean isValidTypeName (String str) {
if (str == null)
return false;
int l = str.length ();
if (l == 0)
return false;
for (int i=0; i<l; i++)
if (!Character.isJavaIdentifierPart (str.charAt (i)))
return false;
return true;
}
public void start () {
stop ();
typechecker = new Thread (this, "Typechecker-"+app.getName());
typechecker.setPriority (Thread.MIN_PRIORITY);
typechecker.start ();
}
public void stop () {
if (typechecker != null && typechecker.isAlive ())
typechecker.interrupt ();
typechecker = null;
}
public Prototype getPrototype (String typename) {
return (Prototype) prototypes.get (typename);
}
public void run () {
while (Thread.currentThread () == typechecker) {
idleSeconds++;
try {
// for each idle minute, add 300 ms to sleeptime until 5 secs are reached.
// (10 secs are reached after 30 minutes of idle state)
// the above is all false.
long sleeptime = 1500 + Math.min (idleSeconds*30, 3500);
typechecker.sleep (sleeptime);
} catch (InterruptedException x) {
// IServer.getLogger().log ("Typechecker interrupted");
break;
}
check ();
}
}
public void registerPrototype (String name, File dir, Prototype proto) {
// IServer.getLogger().log ("registering prototype "+name);
int size = app.allThreads.size ();
for (int i=0; i<size; i++) {
RequestEvaluator reval = (RequestEvaluator) app.allThreads.elementAt (i);
ObjectPrototype op = null;
if ("user".equalsIgnoreCase (name))
op = reval.esUserPrototype;
else if ("global".equalsIgnoreCase (name))
op = reval.global;
else if ("node".equalsIgnoreCase (name))
op = reval.esNodePrototype;
else {
op = new ObjectPrototype (reval.esNodePrototype, reval.evaluator);
try {
op.putProperty ("prototypename", new ESString (name), "prototypename".hashCode ());
} catch (EcmaScriptException ignore) {}
}
reval.putPrototype (name, op);
// Register a constructor for all types except global.
// This will first create a node and then call the actual (scripted) constructor on it.
if (!"global".equalsIgnoreCase (name)) {
try {
FunctionPrototype fp = (FunctionPrototype) reval.evaluator.getFunctionPrototype();
reval.global.putHiddenProperty (name, new NodeConstructor (name, fp, reval));
} catch (EcmaScriptException ignore) {}
}
}
// show the type checker thread that there has been type activity
idleSeconds = 0;
String list[] = dir.list();
Hashtable ntemp = new Hashtable ();
Hashtable nfunc = new Hashtable ();
Hashtable nact = new Hashtable ();
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);
ntemp.put (tmpname, t);
} catch (Throwable x) {
IServer.getLogger().log ("Error creating prototype: "+x);
// broadcaster.broadcast ("Error creating prototype "+list[i]+":<br>"+x+"<br><hr>");
}
} else if (list[i].endsWith (app.scriptExtension) && tmpfile.length () > 0) {
try {
FunctionFile ff = new FunctionFile (tmpfile, tmpname, proto);
nfunc.put (tmpname, ff);
} catch (Throwable x) {
IServer.getLogger().log ("Error creating prototype: "+x);
// broadcaster.broadcast ("Error creating prototype "+list[i]+":<br>"+x+"<br><hr>");
}
} else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) {
try {
Action af = new Action (tmpfile, tmpname, proto);
nact.put (tmpname, af);
} catch (Throwable x) {
IServer.getLogger().log ("Error creating prototype: "+x);
// broadcaster.broadcast ("Error creating prototype "+list[i]+":<br>"+x+"<br><hr>");
}
}
}
proto.templates = ntemp;
proto.functions = nfunc;
proto.actions = nact;
}
public void updatePrototype (String name, File dir, Prototype proto) {
// IServer.getLogger().log ("updating prototype "+name);
String list[] = dir.list();
Hashtable ntemp = new Hashtable ();
Hashtable nfunc = new Hashtable ();
Hashtable nact = new Hashtable ();
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)) {
Template t = proto.getTemplate (tmpname);
try {
if (t == null) {
t = new Template (tmpfile, tmpname, proto);
idleSeconds = 0;
} else if (t.lastmod != tmpfile.lastModified ()) {
t.update (tmpfile);
idleSeconds = 0;
}
} catch (Throwable x) {
IServer.getLogger().log ("Error updating prototype: "+x);
// broadcaster.broadcast ("Error updating prototype "+list[i]+":<br>"+x+"<br><hr>");
}
ntemp.put (tmpname, t);
} else if (list[i].endsWith (app.scriptExtension) && tmpfile.length () > 0) {
FunctionFile ff = proto.getFunctionFile (tmpname);
try {
if (ff == null) {
ff = new FunctionFile (tmpfile, tmpname, proto);
idleSeconds = 0;
} else if (ff.lastmod != tmpfile.lastModified ()) {
ff.update (tmpfile);
idleSeconds = 0;
}
} catch (Throwable x) {
IServer.getLogger().log ("Error updating prototype: "+x);
// broadcaster.broadcast ("Error updating prototype "+list[i]+":<br>"+x+"<br><hr>");
}
nfunc.put (tmpname, ff);
} else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) {
Action af = proto.getAction (tmpname);
try {
if (af == null) {
af = new Action (tmpfile, tmpname, proto);
idleSeconds = 0;
} else if (af.lastmod != tmpfile.lastModified ()) {
af.update (tmpfile);
idleSeconds = 0;
}
} catch (Throwable x) {
IServer.getLogger().log ("Error updating prototype: "+x);
// broadcaster.broadcast ("Error updating prototype "+list[i]+":<br>"+x+"<br><hr>");
}
nact.put (tmpname, af);
} else if ("type.properties".equalsIgnoreCase (list[i])) {
try {
if (proto.dbmap.read ()) {
idleSeconds = 0;
rewire = true;
}
} catch (Exception ignore) {
IServer.getLogger().log ("Error updating db mapping for type "+name+": "+ignore);
}
}
}
proto.templates = ntemp;
proto.functions = nfunc;
proto.actions = nact;
}
protected void readFunctionFile (File f, String protoname) {
EvaluationSource es = new FileEvaluationSource(f.getPath(), null);
FileReader fr = null;
int size = app.allThreads.size ();
for (int i=0; i<size; i++) {
RequestEvaluator reval = (RequestEvaluator) app.allThreads.elementAt (i);
try {
fr = new FileReader(f);
ObjectPrototype op = reval.getPrototype (protoname);
reval.evaluator.evaluate(fr, op, es, false);
} catch (IOException e) {
IServer.getLogger().log ("*** Error reading function file "+f+": "+e);
} catch (EcmaScriptException e) {
IServer.getLogger().log ("*** Error reading function file "+f+": "+e);
} finally {
if (fr!=null) {
try {
fr.close();
} catch (IOException ignore) {}
}
}
}
}
protected void readFunction (String funcname, String params, String body, String protoname)
throws EcmaScriptException {
// ESObject fp = app.eval.evaluator.getFunctionPrototype();
ConstructedFunctionObject function = null;
ASTFormalParameterList fpl = null;
ASTStatementList sl = null;
if (body == null || "".equals (body.trim()))
body = ";\r\n";
else
body = body + "\r\n";
if (params == null) params = "";
else params = params.trim ();
String fullFunctionText = "function "+funcname+" (" + params + ") {\n" + body + "\n}";
EcmaScript parser;
StringReader is;
// Special case for empty parameters
if (params.length()==0) {
fpl = new ASTFormalParameterList(JJTFORMALPARAMETERLIST);
} else {
is = new java.io.StringReader(params);
parser = new EcmaScript(is);
try {
fpl = (ASTFormalParameterList) parser.FormalParameterList();
is.close();
} catch (ParseException x) {
throw new EcmaScriptParseException (x, new StringEvaluationSource(fullFunctionText, null));
}
}
// this is very very very strange: without the toString, lots of obscure exceptions
// deep inside the parser...
is = new java.io.StringReader(body.toString ());
try {
parser = new EcmaScript (is);
sl = (ASTStatementList) parser.StatementList();
is.close();
} catch (ParseException x) {
x.printStackTrace ();
throw new EcmaScriptParseException (x, new StringEvaluationSource(fullFunctionText, null));
} catch (Exception x) {
x.printStackTrace ();
throw new RuntimeException (x.getMessage ());
}
FunctionEvaluationSource fes = new FunctionEvaluationSource (
new StringEvaluationSource(fullFunctionText,null), funcname);
DbMapping dbmap = null;
int size = app.allThreads.size ();
for (int i=0; i<size; i++) {
RequestEvaluator reval = (RequestEvaluator) app.allThreads.elementAt (i);
ObjectPrototype op = reval.getPrototype (protoname);
EcmaScriptVariableVisitor vdvisitor = reval.evaluator.getVarDeclarationVisitor();
Vector vnames = vdvisitor.processVariableDeclarations(sl, fes);
FunctionPrototype fp = ConstructedFunctionObject.makeNewConstructedFunction(reval.evaluator, funcname, fes, fullFunctionText, fpl.getArguments(), vnames, sl);
op.putHiddenProperty (funcname, fp);
}
}
protected void generateErrorFeedback (String funcname, String message, String protoname)
throws EcmaScriptException {
int size = app.allThreads.size ();
for (int i=0; i<size; i++) {
RequestEvaluator reval = (RequestEvaluator) app.allThreads.elementAt (i);
ObjectPrototype op = reval.getPrototype (protoname);
FunctionPrototype fp = (FunctionPrototype) reval.evaluator.getFunctionPrototype ();
FunctionPrototype func = new ThrowException (funcname, reval.evaluator, fp, message);
op.putHiddenProperty (funcname, func);
}
}
class ThrowException extends BuiltinFunctionObject {
String message;
ThrowException (String name, Evaluator evaluator, FunctionPrototype fp, String message) {
super (fp, evaluator, name, 1);
this.message = message == null ? "No error message available" : message;
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
throw new EcmaScriptException (message);
}
public ESObject doConstruct (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
throw new EcmaScriptException (message);
}
}
}

View file

@ -0,0 +1,106 @@
// User.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
import java.io.*;
import java.util.*;
import java.net.URLEncoder;
import helma.objectmodel.*;
/**
* This represents a user who is currently using the HOP application. This does
* not just comprend registered users, but anybody who happens to surf the site.
*/
public class User implements Serializable {
Application app;
String sessionID;
String uid, nid;
long onSince, lastTouched;
Node cache;
DbMapping umap;
public User (String sid, Application app) {
this.uid = null;
this.nid = null;
this.app = app;
setNode (null);
cache = new Node (sid);
cache.setString ("prototype", "user");
sessionID = sid;
onSince = System.currentTimeMillis ();
lastTouched = onSince;
}
/**
* This is used to turn an anonymous user into a registered or known one.
* The user object remains the same, but she gets some persistent storage.
*/
public void setNode (INode n) {
// IServer.getLogger().log ("esn = "+esn);
if (n == null) {
nid = null;
uid = null;
} else {
uid = n.getNameOrID ();
nid = n.getID ();
umap = n.getDbMapping ();
}
}
public INode getNode () {
if (uid == null) {
return cache;
} else {
return app.nmgr.safe.getNode (nid, umap);
}
}
public String getSessionID () {
return sessionID;
}
public void touch () {
lastTouched = System.currentTimeMillis ();
}
public long touched () {
return lastTouched;
}
}