This commit was generated by cvs2svn to compensate for changes in r10,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
parent
89c38c6eee
commit
3598f6bd3f
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