diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index a7793514..9602d0bd 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -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 = "/"; diff --git a/src/helma/framework/core/ESAppNode.java b/src/helma/framework/core/ESAppNode.java index a22d26d9..aa25c049 100644 --- a/src/helma/framework/core/ESAppNode.java +++ b/src/helma/framework/core/ESAppNode.java @@ -130,7 +130,7 @@ public class ESAppNode extends ESNode { public String toString () { - return ("AppNode "+node.getNameOrID ()); + return ("AppNode "+node.getElementName ()); } } diff --git a/src/helma/framework/core/ESNode.java b/src/helma/framework/core/ESNode.java index 3bf44edf..fb1ef031 100644 --- a/src/helma/framework/core/ESNode.java +++ b/src/helma/framework/core/ESNode.java @@ -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! diff --git a/src/helma/framework/core/HopExtension.java b/src/helma/framework/core/HopExtension.java index a957b1ad..20d3848a 100644 --- a/src/helma/framework/core/HopExtension.java +++ b/src/helma/framework/core/HopExtension.java @@ -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 (""); @@ -1062,7 +1063,7 @@ public class HopExtension { buffer.append (""); diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index df070d39..a3573d4d 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -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 -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) diff --git a/src/helma/framework/core/Skin.java b/src/helma/framework/core/Skin.java index abdc84f5..45288de5 100644 --- a/src/helma/framework/core/Skin.java +++ b/src/helma/framework/core/Skin.java @@ -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=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; } diff --git a/src/helma/framework/core/User.java b/src/helma/framework/core/User.java index e8299e2c..44bdffa0 100644 --- a/src/helma/framework/core/User.java +++ b/src/helma/framework/core/User.java @@ -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 (); } }