The basic operations now use the IPathElement interface instead

of INode: Path resolution in RequestEvaluator, skin rendering,
and href() function.
Application will check the rootObject property in the
app.properties file and try to create a custom root object, if set.
This commit is contained in:
hns 2001-08-13 13:10:41 +00:00
parent 1b0c4329e5
commit a2dcc29cec
7 changed files with 186 additions and 83 deletions

View file

@ -75,6 +75,11 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
private DbMapping rootMapping, userRootMapping, userMapping;
// the root of the website, if a custom root object is defined.
// otherwise this is managed by the NodeManager and not cached here.
IPathElement rootObject = null;
String rootObjectClass;
boolean checkSubnodes;
String charset;
@ -131,6 +136,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
debug = "true".equalsIgnoreCase (props.getProperty ("debug"));
checkSubnodes = !"false".equalsIgnoreCase (props.getProperty ("subnodeChecking"));
// get class name of root object if defined. Otherwise native Helma objectmodel will be used.
rootObjectClass = props.getProperty ("rootObject");
try {
requestTimeout = Long.parseLong (props.getProperty ("requestTimeout", "60"))*1000l;
} catch (Exception ignore) { }
@ -333,7 +341,20 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
// do nothing
}
public INode getDataRoot () {
public IPathElement getDataRoot () {
if (rootObjectClass != null) {
// create custom root element.
// NOTE: This is but a very rough first sketch of an implementation
// and needs much more care.
if (rootObject == null) try {
Class c = Class.forName (rootObjectClass);
rootObject = (IPathElement) c.newInstance ();
} catch (Throwable x) {
System.err.println ("ERROR CREATING ROOT OBJECT: "+x);
}
return rootObject;
}
// no custom root object is defined - use standard helma objectmodel
INode root = nmgr.safe.getNode ("0", rootMapping);
root.setDbMapping (rootMapping);
return root;
@ -365,7 +386,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
* Return a prototype for a given node. If the node doesn't specify a prototype,
* return the generic hopobject prototype.
*/
public Prototype getPrototype (INode n) {
public Prototype getPrototype (IPathElement n) {
String protoname = n.getPrototype ();
if (protoname == null)
return typemgr.getPrototype ("hopobject");
@ -444,7 +465,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
if (pw != null && pw.equals (password)) {
// give the user her piece of persistence
u.setNode (unode);
activeUsers.put (unode.getNameOrID (), u.user);
activeUsers.put (unode.getName (), u.user);
return true;
}
@ -477,29 +498,69 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
return pwfile.authenticate (uname, password);
}
public String getNodePath (INode n, String tmpname) {
INode root = getDataRoot ();
/* public String getNodePath (INode n, String tmpname) {
// FIXME: will fail for non-node roots
INode root = (INode) getDataRoot ();
INode users = getUserRoot ();
String siteroot = props.getProperty ("rootPrototype");
String href = n.getUrl (root, users, tmpname, siteroot);
return href;
}
} */
public String getNodeHref (INode n, String tmpname) {
INode root = getDataRoot ();
/**
* Return a path to be used in a URL pointing to the given element and action
*/
public String getNodeHref (IPathElement elem, String actionName) {
// FIXME: will fail for non-node roots
IPathElement root = getDataRoot ();
INode users = getUserRoot ();
// check base uri and optional root prototype from app.properties
String base = props.getProperty ("baseURI");
String siteroot = props.getProperty ("rootPrototype");
String rootproto = props.getProperty ("rootPrototype");
if (base != null || baseURI == null)
setBaseURI (base);
String href = n.getUrl (root, users, tmpname, siteroot);
// String href = n.getUrl (root, users, tmpname, siteroot);
String divider = "/";
StringBuffer b = new StringBuffer ();
IPathElement p = elem;
int loopWatch = 0;
while (p != null && p.getParentElement () != null && p != root) {
if (rootproto != null && rootproto.equals (p.getPrototype ()))
break;
b.insert (0, divider);
// users always have a canonical URL like /users/username
if ("user".equals (p.getPrototype ())) {
b.insert (0, UrlEncoder.encode (p.getElementName ()));
p = users;
break;
}
b.insert (0, UrlEncoder.encode (p.getElementName ()));
p = p.getParentElement ();
if (loopWatch++ > 20)
break;
}
if (p == users) {
b.insert (0, divider);
b.insert (0, "users");
}
String href = b.toString()+UrlEncoder.encode (actionName);
return baseURI + href;
}
public void setBaseURI (String uri) {
if (uri == null)
this.baseURI = "/";

View file

@ -130,7 +130,7 @@ public class ESAppNode extends ESNode {
public String toString () {
return ("AppNode "+node.getNameOrID ());
return ("AppNode "+node.getElementName ());
}
}

View file

@ -412,7 +412,11 @@ public class ESNode extends ObjectPrototype {
public void clearError() {
lastError = null;
}
public Object toJavaObject () {
return getNode ();
}
/**
* 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!

View file

@ -5,6 +5,7 @@ package helma.framework.core;
import helma.objectmodel.*;
import helma.util.*;
import helma.framework.IPathElement;
import FESI.Interpreter.*;
import FESI.Exceptions.*;
import FESI.Extensions.*;
@ -76,7 +77,7 @@ public class HopExtension {
reval.esNodePrototype.putHiddenProperty ("editor", new NodeEditor ("editor", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("chooser", new NodeChooser ("chooser", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("multiChooser", new MultiNodeChooser ("multiChooser", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("path", new NodePath ("path", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("path", new NodeHref ("path", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("setParent", new NodeSetParent ("setParent", evaluator, fp));
reval.esNodePrototype.putHiddenProperty ("invalidate", new NodeInvalidate ("invalidate", evaluator, fp));
@ -574,12 +575,12 @@ public class HopExtension {
this.global = global;
this.asString = asString;
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
public ESValue callFunction (ESObject thisObj, ESValue[] arguments) throws EcmaScriptException {
if (arguments.length < 1 || arguments.length > 2 || arguments[0] ==null || arguments[0] == ESNull.theNull)
throw new EcmaScriptException ("renderSkin must be called with one Skin argument and an optional parameter argument");
try {
Skin skin = null;
ESNode handlerNode = global ? null : (ESNode) thisObject;
Skin skin = null;
ESObject thisObject = global ? null : thisObj;
ESObject paramObject = null;
if (arguments.length > 1 && arguments[1] instanceof ESObject)
paramObject = (ESObject) arguments[1];
@ -592,11 +593,11 @@ public class HopExtension {
}
if (skin == null)
skin = reval.getSkin (handlerNode, arguments[0].toString ());
skin = reval.getSkin (thisObject, arguments[0].toString ());
if (asString)
reval.res.pushStringBuffer ();
if (skin != null)
skin.render (reval, handlerNode, paramObject);
skin.render (reval, thisObject, paramObject);
else
reval.res.write ("[Skin not found: "+arguments[0]+"]");
if (asString)
@ -916,7 +917,7 @@ public class HopExtension {
}
class NodePath extends BuiltinFunctionObject {
/* class NodePath extends BuiltinFunctionObject {
NodePath (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
@ -925,7 +926,7 @@ public class HopExtension {
String tmpname = arguments[0].toString ();
return new ESString (app.getNodePath (n, tmpname));
}
}
} */
class NodeSetParent extends BuiltinFunctionObject {
NodeSetParent (String name, Evaluator evaluator, FunctionPrototype fp) {
@ -943,33 +944,33 @@ public class HopExtension {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
INode n = ((ESNode) thisObject).getNode ();
IPathElement elem = (IPathElement) thisObject.toJavaObject ();
String tmpname = arguments.length == 0 ? "" : arguments[0].toString ();
String basicHref =app.getNodeHref (n, tmpname);
String basicHref =app.getNodeHref (elem, tmpname);
String hrefSkin = app.props.getProperty ("hrefSkin");
// FIXME: we should actually walk down the path from the object we called href() on
// instead we move down the URL path.
if (hrefSkin != null) {
// we need to post-process the href with a skin for this application
// first, look in the object href was called on.
INode sn = n;
IPathElement skinElem = elem;
Skin skin = null;
while (skin == null && sn != null) {
Prototype proto = app.getPrototype (sn);
while (skin == null && skinElem != null) {
Prototype proto = app.getPrototype (skinElem);
if (proto != null)
skin = proto.getSkin (hrefSkin);
if (skin == null)
sn = sn.getParent ();
skinElem = skinElem.getParentElement ();
}
if (skin != null) {
ESNode esn = reval.getNodeWrapper (sn);
return renderSkin (skin, basicHref, esn);
ESObject eso = reval.getElementWrapper (skinElem);
return renderSkin (skin, basicHref, eso);
}
}
return new ESString (basicHref);
}
private ESString renderSkin (Skin skin, String path, ESNode obj) throws EcmaScriptException {
private ESString renderSkin (Skin skin, String path, ESObject obj) throws EcmaScriptException {
reval.res.pushStringBuffer ();
ESObject param = new ObjectPrototype (null, reval.evaluator);
param.putProperty ("path", new ESString (path), "path".hashCode ());
@ -1040,7 +1041,7 @@ public class HopExtension {
buffer.append ("<input type=radio name=\"");
buffer.append (name);
buffer.append ("\" value=\"");
buffer.append (next.getNameOrID ()+"\"");
buffer.append (next.getElementName ()+"\"");
if (target == next)
buffer.append (" checked");
buffer.append (">");
@ -1062,7 +1063,7 @@ public class HopExtension {
buffer.append ("<input type=checkbox name=\"");
buffer.append (name);
buffer.append ("\" value=");
buffer.append (next.getNameOrID ());
buffer.append (next.getElementName ());
if (target.contains (next) > -1)
buffer.append (" checked");
buffer.append (">");

View file

@ -77,7 +77,7 @@ public class RequestEvaluator implements Runnable {
static final int XMLRPC = 2; // via XML-RPC
static final int INTERNAL = 3; // generic function call, e.g. by scheduler
INode root, userroot, currentNode;
// INode root, currentNode;
INode[] skinmanagers;
/**
@ -99,6 +99,7 @@ public class RequestEvaluator implements Runnable {
private void initEvaluator () {
try {
evaluator = new Evaluator();
evaluator.reval = this;
global = evaluator.getGlobalObject();
for (int i=0; i<extensions.length; i++)
evaluator.addExtension (extensions[i]);
@ -140,7 +141,7 @@ public class RequestEvaluator implements Runnable {
try {
do {
// app.logEvent ("got request "+reqtype);
IPathElement root, currentElement;
// reset skinManager
skinmanagers = null;
@ -152,7 +153,7 @@ public class RequestEvaluator implements Runnable {
while (!done) {
current = null;
currentNode = null;
currentElement = null;
reqPath.setSize (0);
// delete path objects-via-prototype
for (Enumeration en=reqPath.getAllProperties(); en.hasMoreElements(); ) {
@ -176,7 +177,7 @@ public class RequestEvaluator implements Runnable {
ESUser esu = (ESUser) getNodeWrapper (user);
// esu.setUser (user);
global.putHiddenProperty ("root", getNodeWrapper (root));
global.putHiddenProperty ("root", getElementWrapper (root));
global.putHiddenProperty("user", esu);
global.putHiddenProperty ("req", new ESWrapper (req, evaluator));
global.putHiddenProperty ("res", new ESWrapper (res, evaluator));
@ -197,8 +198,8 @@ public class RequestEvaluator implements Runnable {
if (error != null) {
// there was an error in the previous loop, call error handler
currentNode = root;
current = getNodeWrapper (root);
currentElement = root;
current = getElementWrapper (root);
reqPath.putProperty (0, current);
reqPath.putHiddenProperty ("root", current);
Prototype p = app.getPrototype (root);
@ -208,8 +209,8 @@ public class RequestEvaluator implements Runnable {
throw new RuntimeException (error);
} else if (req.path == null || "".equals (req.path.trim ())) {
currentNode = root;
current = getNodeWrapper (root);
currentElement = root;
current = getElementWrapper (root);
reqPath.putProperty (0, current);
reqPath.putHiddenProperty ("root", current);
Prototype p = app.getPrototype (root);
@ -233,38 +234,38 @@ public class RequestEvaluator implements Runnable {
for (int i=0; i<ntokens; i++)
pathItems[i] = st.nextToken ();
currentNode = root;
current = getNodeWrapper (root);
currentElement = root;
current = getElementWrapper (root);
reqPath.putProperty (0, current);
reqPath.putHiddenProperty ("root", current);
for (int i=0; i<ntokens; i++) {
if (currentNode == null)
if (currentElement == null)
throw new FrameworkException ("Object not found.");
// 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 (i == 0 && "user".equalsIgnoreCase (pathItems[i])) {
currentNode = user.getNode ();
if (currentNode != null) {
current = getNodeWrapper (currentNode);
// currentElement = user.getNode ();
if (currentElement != null) {
current = getElementWrapper (currentElement);
reqPath.putProperty (1, current);
reqPath.putHiddenProperty ("user", current);
}
} else if (i == 0 && "users".equalsIgnoreCase (pathItems[i])) {
currentNode = app.getUserRoot ();
isProperty = true;
if (currentNode != null) {
current = getNodeWrapper (currentNode);
// currentElement = app.getUserRoot ();
// isProperty = true;
if (currentElement != null) {
current = getElementWrapper (currentElement);
reqPath.putProperty (1, current);
}
} else {
if (i == ntokens-1) {
Prototype p = app.getPrototype (currentNode);
Prototype p = app.getPrototype (currentElement);
if (p != null)
action = p.getActionOrTemplate (pathItems[i]);
}
@ -273,23 +274,25 @@ public class RequestEvaluator implements Runnable {
if (pathItems[i].length () == 0)
continue;
if (isProperty) // get next element as property
currentNode = currentNode.getNode (pathItems[i], false);
currentElement = currentElement.getChildElement (pathItems[i]);
/* if (isProperty) // get next element as property
currentElement = currentElement.getNode (pathItems[i], false);
else {
// try to get next element as subnode first, then fall back to property
INode nextNode = currentNode.getSubnode (pathItems[i]);
INode nextNode = currentElement.getSubnode (pathItems[i]);
if (nextNode == null)
nextNode = currentNode.getNode (pathItems[i], false);
currentNode = nextNode;
nextNode = currentElement.getNode (pathItems[i], false);
currentElement = nextNode;
}
isProperty = false;
isProperty = false; */
// add object to request path if suitable
if (currentNode != null) {
if (currentElement != null) {
// add to reqPath array
current = getNodeWrapper (currentNode);
current = getElementWrapper (currentElement);
reqPath.putProperty (reqPath.size(), current);
String pt = currentNode.getPrototype ();
String pt = currentElement.getPrototype ();
if (pt != null) {
// if a prototype exists, add also by prototype name
reqPath.putHiddenProperty (pt, current);
@ -300,13 +303,13 @@ public class RequestEvaluator implements Runnable {
}
}
if (currentNode == null)
if (currentElement == null)
throw new FrameworkException ("Object not found.");
else
current = getNodeWrapper (currentNode);
current = getElementWrapper (currentElement);
if (action == null) {
Prototype p = app.getPrototype (currentNode);
Prototype p = app.getPrototype (currentElement);
if (p != null)
action = p.getActionOrTemplate (null);
}
@ -328,7 +331,7 @@ public class RequestEvaluator implements Runnable {
action = p.getActionOrTemplate (notFoundAction);
if (action == null)
throw new FrameworkException (notfound.getMessage ());
current = getNodeWrapper (root);
current = getElementWrapper (root);
}
localrtx.timer.endEvent (requestPath+" init");
@ -430,7 +433,7 @@ public class RequestEvaluator implements Runnable {
root = app.getDataRoot ();
global.putHiddenProperty ("root", getNodeWrapper (root));
global.putHiddenProperty ("root", getElementWrapper (root));
global.deleteProperty("user", "user".hashCode());
global.deleteProperty ("req", "req".hashCode());
global.putHiddenProperty ("res", ESLoader.normalizeValue(res, evaluator));
@ -439,7 +442,7 @@ public class RequestEvaluator implements Runnable {
// convert arguments
int l = args.size ();
current = getNodeWrapper (root);
current = getElementWrapper (root);
if (method.indexOf (".") > -1) {
StringTokenizer st = new StringTokenizer (method, ".");
int cnt = st.countTokens ();
@ -491,7 +494,7 @@ public class RequestEvaluator implements Runnable {
root = app.getDataRoot ();
global.putHiddenProperty ("root", getNodeWrapper (root));
global.putHiddenProperty ("root", getElementWrapper (root));
global.deleteProperty("user", "user".hashCode());
global.deleteProperty ("req", "req".hashCode());
global.putHiddenProperty ("res", ESLoader.normalizeValue(res, evaluator));
@ -642,13 +645,13 @@ public class RequestEvaluator implements Runnable {
return result;
}
public synchronized ESValue invokeFunction (INode node, String functionName, ESValue[] args)
public synchronized ESValue invokeFunction (IPathElement node, String functionName, ESValue[] args)
throws Exception {
ESObject obj = null;
if (node == null)
obj = global;
else
obj = getNodeWrapper (node);
obj = getElementWrapper (node);
return invokeFunction (obj, functionName, args);
}
@ -741,8 +744,14 @@ public class RequestEvaluator implements Runnable {
Prototype proto = null;
if (thisObject == null)
proto = app.typemgr.getPrototype ("global");
else
proto = app.getPrototype (((ESNode) thisObject).getNode ());
else {
try {
IPathElement elem = (IPathElement) thisObject.toJavaObject ();
proto = app.getPrototype (elem);
} catch (ClassCastException wrongClass) {
throw new RuntimeException ("Can't render a skin on something that is not a path element: "+wrongClass);
}
}
return getSkin (proto, skinname);
}
@ -829,6 +838,21 @@ public class RequestEvaluator implements Runnable {
return (ESNode) objectcache.get (n);
}
public ESObject getElementWrapper (IPathElement e) {
if (e instanceof INode)
return getNodeWrapper ((INode) e);
String protoname = e.getPrototype ();
ObjectPrototype op = (ObjectPrototype) prototypes.get (protoname);
return new ESGenericObject (op, evaluator, e);
}
/**
* Get a script wrapper for an implemntation of helma.objectmodel.INode
*/
public ESNode getNodeWrapper (INode n) {
if (n == null)

View file

@ -72,12 +72,24 @@ public class Skin {
return source;
}
public void render (RequestEvaluator reval, ESNode thisNode, ESObject paramObject) throws RedirectException {
public void render (RequestEvaluator reval, ESObject thisObject, ESObject paramObject) throws RedirectException {
if (parts == null)
return;
IPathElement elem = null;
if (thisObject != null) {
try {
elem = (IPathElement) thisObject.toJavaObject ();
} catch (ClassCastException wrongClass) {
throw new RuntimeException ("Can't render a skin on something that is not a path element: "+wrongClass);
}
}
for (int i=0; i<parts.length; i++) {
if (parts[i] instanceof Macro)
((Macro) parts[i]).render (reval, thisNode, paramObject);
((Macro) parts[i]).render (reval, thisObject, elem, paramObject);
else
reval.res.write (parts[i]);
}
@ -193,7 +205,7 @@ public class Skin {
}
public void render (RequestEvaluator reval, ESNode thisNode, ESObject paramObject) throws RedirectException {
public void render (RequestEvaluator reval, ESObject thisObject, IPathElement elem, ESObject paramObject) throws RedirectException {
if (notallowed) {
String h = handler == null ? "global" : handler;
@ -223,32 +235,33 @@ public class Skin {
if ("currentuser".equalsIgnoreCase (handler)) {
// as a special convention, we use "currentuser" to access macros in the current user object
handlerObject = reval.getNodeWrapper (reval.user.getNode ());
} else if (thisNode != null) {
} else if (elem != null) {
// not a global macro - need to find handler object
// was called with this object - check it or its parents for matching prototype
if (!handler.equalsIgnoreCase ("this") && !handler.equalsIgnoreCase (thisNode.getPrototypeName ())) {
if (!handler.equalsIgnoreCase ("this") && !handler.equalsIgnoreCase (elem.getPrototype ())) {
// the handler object is not what we want
INode n = thisNode.getNode();
IPathElement n = elem;
// walk down parent chain to find handler object
while (n != null) {
if (handler.equalsIgnoreCase (n.getPrototype())) {
handlerObject = reval.getNodeWrapper (n);
handlerObject = reval.getElementWrapper (n);
break;
}
n = n.getParent ();
n = n.getParentElement ();
}
} else {
// we already have the right handler object
handlerObject = thisNode;
handlerObject = thisObject;
}
}
if (handlerObject == null) {
// eiter because thisNode == null or the right object wasn't found in the targetNode path
// eiter because thisObject == null or the right object wasn't found in the object's parent path
// go check request path for an object with matching prototype
int l = reval.reqPath.size();
for (int i=l-1; i>=0; i--) {
if (handler.equalsIgnoreCase (((ESNode) reval.reqPath.getProperty(i)).getPrototypeName())) {
IPathElement pathelem = (IPathElement) reval.reqPath.getProperty (i).toJavaObject ();
if (handler.equalsIgnoreCase (pathelem.getPrototype ())) {
handlerObject = (ESNode) reval.reqPath.getProperty(i);
break;
}

View file

@ -52,7 +52,7 @@ public class User implements Serializable {
nhandle = null;
uid = null;
} else {
uid = n.getNameOrID ();
uid = n.getElementName ();
nhandle = ((helma.objectmodel.db.Node) n).getHandle ();
}
}