Check in current Hop snapshot
This commit is contained in:
parent
09d1fdd4e3
commit
2f40345d3a
14 changed files with 4643 additions and 0 deletions
112
src/helma/framework/core/Action.java
Normal file
112
src/helma/framework/core/Action.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
552
src/helma/framework/core/Application.java
Normal file
552
src/helma/framework/core/Application.java
Normal 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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
95
src/helma/framework/core/ESAppNode.java
Normal file
95
src/helma/framework/core/ESAppNode.java
Normal 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 ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
560
src/helma/framework/core/ESNode.java
Normal file
560
src/helma/framework/core/ESNode.java
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
126
src/helma/framework/core/ESRequestData.java
Normal file
126
src/helma/framework/core/ESRequestData.java
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
114
src/helma/framework/core/ESUser.java
Normal file
114
src/helma/framework/core/ESUser.java
Normal 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 ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
81
src/helma/framework/core/FunctionFile.java
Normal file
81
src/helma/framework/core/FunctionFile.java
Normal 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 ());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1044
src/helma/framework/core/HopExtension.java
Normal file
1044
src/helma/framework/core/HopExtension.java
Normal file
File diff suppressed because it is too large
Load diff
81
src/helma/framework/core/NodeConstructor.java
Normal file
81
src/helma/framework/core/NodeConstructor.java
Normal 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
|
||||
|
||||
|
||||
|
172
src/helma/framework/core/Prototype.java
Normal file
172
src/helma/framework/core/Prototype.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
850
src/helma/framework/core/RequestEvaluator.java
Normal file
850
src/helma/framework/core/RequestEvaluator.java
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
214
src/helma/framework/core/Template.java
Normal file
214
src/helma/framework/core/Template.java
Normal 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+"]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
536
src/helma/framework/core/TypeManager.java
Normal file
536
src/helma/framework/core/TypeManager.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
106
src/helma/framework/core/User.java
Normal file
106
src/helma/framework/core/User.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue