From 4b91011578485eb2b194918748eef3b032fab3cf Mon Sep 17 00:00:00 2001 From: hns Date: Wed, 6 Aug 2003 16:36:49 +0000 Subject: [PATCH] Implement invocation of scripted getChildElement(name) function when resolving a request path also when objects implement IRequestPath interface. The biggest change was induced by the need to have an object representing the request path that is able to grow while the path is being resolved. Previously, this was done by passing an ArrayList to the scripting engine that was then transformed into a JavaScript array. This is now done using a proprietary RequestPath object. --- src/helma/framework/core/Application.java | 37 +++-- src/helma/framework/core/Prototype.java | 13 +- .../framework/core/RequestEvaluator.java | 43 +++--- src/helma/framework/core/RequestPath.java | 134 ++++++++++++++++++ src/helma/scripting/rhino/PathWrapper.java | 134 ++++++++++++++++++ src/helma/scripting/rhino/RhinoCore.java | 8 ++ src/helma/scripting/rhino/RhinoEngine.java | 44 ++---- 7 files changed, 341 insertions(+), 72 deletions(-) create mode 100644 src/helma/framework/core/RequestPath.java create mode 100644 src/helma/scripting/rhino/PathWrapper.java diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 8c961338..d5dd0a0d 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -115,6 +115,10 @@ public final class Application implements IPathElement, Runnable { // the URL-prefix to use for links into this application private String baseURI; + // the name of the root prototype + private String rootPrototype; + + // Db mappings for some standard prototypes private DbMapping rootMapping; private DbMapping userRootMapping; private DbMapping userMapping; @@ -1019,13 +1023,9 @@ public final class Application implements IPathElement, Runnable { * Return a path to be used in a URL pointing to the given element and action */ public String getNodeHref(Object elem, String actionName) { - // Object root = getDataRoot (); - // check optional root prototype from app.properties - String rootProto = props.getProperty("rootPrototype"); - StringBuffer b = new StringBuffer(baseURI); - composeHref(elem, b, rootProto, 0); + composeHref(elem, b, 0); if (actionName != null) { b.append(UrlEncoded.encode(actionName)); @@ -1034,13 +1034,12 @@ public final class Application implements IPathElement, Runnable { return b.toString(); } - private final void composeHref(Object elem, StringBuffer b, String rootProto, - int pathCount) { + private final void composeHref(Object elem, StringBuffer b, int pathCount) { if ((elem == null) || (pathCount > 20)) { return; } - if ((rootProto != null) && rootProto.equals(getPrototypeName(elem))) { + if ((rootPrototype != null) && rootPrototype.equals(getPrototypeName(elem))) { return; } @@ -1050,11 +1049,19 @@ public final class Application implements IPathElement, Runnable { return; } - composeHref(getParentElement(elem), b, rootProto, pathCount++); + composeHref(getParentElement(elem), b, pathCount++); b.append(UrlEncoded.encode(getElementName(elem))); b.append("/"); } + /** + * Returns the baseURI for Hrefs in this application. + */ + public String getBaseURI() { + return baseURI; + } + + /** * This method sets the base URL of this application which will be prepended to * the actual object path. @@ -1077,6 +1084,13 @@ public final class Application implements IPathElement, Runnable { return props.containsKey("baseuri"); } + /** + * Returns the prototype name that Hrefs in this application should start with. + */ + public String getRootPrototype() { + return rootPrototype; + } + /** * Tell other classes whether they should output logging information for this application. */ @@ -1574,8 +1588,9 @@ public final class Application implements IPathElement, Runnable { // debug flag debug = "true".equalsIgnoreCase(props.getProperty("debug")); + String reqTimeout = props.getProperty("requesttimeout", "60"); try { - requestTimeout = Long.parseLong(props.getProperty("requestTimeout", "60")) * 1000L; + requestTimeout = Long.parseLong(reqTimeout) * 1000L; } catch (Exception ignore) { // go with default value requestTimeout = 60000L; @@ -1590,6 +1605,8 @@ public final class Application implements IPathElement, Runnable { baseURI = "/"; } + rootPrototype = props.getProperty("rootprototype"); + // update the XML-RPC access list, containting prototype.method // entries of functions that may be called via XML-RPC String xmlrpcAccessProp = props.getProperty("xmlrpcaccess"); diff --git a/src/helma/framework/core/Prototype.java b/src/helma/framework/core/Prototype.java index 2d6fa701..5fcab75d 100644 --- a/src/helma/framework/core/Prototype.java +++ b/src/helma/framework/core/Prototype.java @@ -186,9 +186,7 @@ public final class Prototype { Prototype p = parent; while ((p != null) && !"hopobject".equalsIgnoreCase(p.getName())) { - if (!handlers.containsKey(p.name)) { - handlers.put(p.name, obj); - } + handlers.put(p.name, obj); p = p.parent; } @@ -267,15 +265,6 @@ public final class Prototype { lastUpdate = System.currentTimeMillis(); } - /** - * Get the time at which this prototype's scripts were checked - * for changes for the last time. - */ - - /* public long getLastCheck () { - return lastCheck; - } */ - /** * Signal that the prototype's scripts have been checked for * changes. diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index c9186684..fab68f17 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -55,7 +55,7 @@ public final class RequestEvaluator implements Runnable { Object[] args; // the object path of the request we're evaluating - List requestPath; + RequestPath requestPath; // the result of the operation Object result; @@ -122,7 +122,7 @@ public final class RequestEvaluator implements Runnable { // object refs to ressolve request path Object currentElement; - requestPath = new ArrayList(); + requestPath = new RequestPath(app); switch (reqtype) { case HTTP: @@ -158,6 +158,9 @@ public final class RequestEvaluator implements Runnable { globals.put("path", requestPath); req.startTime = System.currentTimeMillis(); + // enter execution context + scriptingEngine.enterContext(globals); + if (error != null) { res.error = error; } @@ -186,7 +189,7 @@ public final class RequestEvaluator implements Runnable { } else if ((req.path == null) || "".equals(req.path.trim())) { currentElement = root; - requestPath.add(currentElement); + requestPath.add(null, currentElement); action = getAction(currentElement, null); @@ -210,7 +213,7 @@ public final class RequestEvaluator implements Runnable { pathItems[i] = st.nextToken(); currentElement = root; - requestPath.add(currentElement); + requestPath.add(null, currentElement); for (int i = 0; i < ntokens; i++) { @@ -222,9 +225,6 @@ public final class RequestEvaluator implements Runnable { continue; } - // we used to do special processing for /user and /users - // here but with the framework cleanup, this stuff has to be - // mounted manually. // if we're at the last element of the path, // try to interpret it as action name. if (i == (ntokens - 1)) { @@ -233,13 +233,13 @@ public final class RequestEvaluator implements Runnable { } if (action == null) { - currentElement = app.getChildElement(currentElement, - pathItems[i]); + currentElement = getChildElement(currentElement, + pathItems[i]); // add object to request path if suitable if (currentElement != null) { // add to requestPath array - requestPath.add(currentElement); + requestPath.add(pathItems[i], currentElement); } } } @@ -311,13 +311,10 @@ public final class RequestEvaluator implements Runnable { ///////////////////////////////////////////////////////////////////////////// // beginning of execution section try { - // enter execution context - scriptingEngine.enterContext(globals); - // set the req.action property, cutting off the _action suffix req.action = action.substring(0, action.length() - 7); - // set the application checksum in response to make ETag + // set the application checksum in response to make ETag // generation sensitive to changes in the app res.setApplicationChecksum(app.getChecksum()); @@ -411,7 +408,7 @@ public final class RequestEvaluator implements Runnable { if (app.debug && !(x instanceof ScriptingException)) { x.printStackTrace (); } - + // set done to false so that the error will be processed done = false; error = x.getMessage(); @@ -459,8 +456,8 @@ public final class RequestEvaluator implements Runnable { for (int i = 1; i < cnt; i++) { String next = st.nextToken(); - currentElement = app.getChildElement(currentElement, - next); + currentElement = getChildElement(currentElement, + next); } if (currentElement == null) { @@ -841,6 +838,18 @@ public final class RequestEvaluator implements Runnable { return result; } + private Object getChildElement(Object obj, String name) throws ScriptingException { + if (scriptingEngine.hasFunction(obj, "getChildElement")) { + return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name}, false); + } + + if (obj instanceof IPathElement) { + return ((IPathElement) obj).getChildElement(name); + } + + return null; + } + /** * Stop this request evaluator's current thread. If currently active kill the request, otherwise just * notify. diff --git a/src/helma/framework/core/RequestPath.java b/src/helma/framework/core/RequestPath.java new file mode 100644 index 00000000..c4e99b7e --- /dev/null +++ b/src/helma/framework/core/RequestPath.java @@ -0,0 +1,134 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + +package helma.framework.core; + +import java.util.*; +import helma.util.UrlEncoded; + +/** + * Represents a URI request path that has been resolved to an object path. + * Offers methods to access objects in the path by index and prototype names, + * and to render the path as URI again. + */ +public class RequestPath { + + Application app; + + List objects; + List ids; + + Map primaryProtos; + Map secondaryProtos; + + /** + * Creates a new RequestPath object. + * + * @param app the application we're running in + */ + public RequestPath(Application app) { + this.app = app; + objects = new ArrayList(); + ids = new ArrayList(); + primaryProtos = new HashMap(); + secondaryProtos = new HashMap(); + } + + /** + * Adds an item to the end of the path. + * + * @param id the item id representing the path in the URL + * @param obj the object to which the id resolves + */ + public void add(String id, Object obj) { + ids.add(id); + objects.add(obj); + + Prototype proto = app.getPrototype(obj); + + if (proto != null) { + primaryProtos.put(proto.getName(), obj); + proto.registerParents(secondaryProtos, obj); + } + } + + /** + * Returns the number of objects in the request path. + */ + public int size() { + return objects.size(); + } + + /** + * Gets an object in the path by index. + * + * @param idx the index of the object in the request path + */ + public Object get(int idx) { + if (idx < 0 || idx >= objects.size()) { + return null; + } + + return objects.get(idx); + } + + /** + * Gets an object in the path by prototype name. + * + * @param typeName the prototype name of the object in the request path + */ + public Object getByPrototypeName(String typeName) { + // search primary prototypes first + Object obj = primaryProtos.get(typeName); + + if (obj != null) { + return obj; + } + + // if that fails, consult secondary prototype map + return secondaryProtos.get(typeName); + } + + /** + * Returns the string representation of this path usable for links. + */ + public String href(String action) { + StringBuffer buffer = new StringBuffer(app.getBaseURI()); + + int start = 1; + String rootPrototype = app.getRootPrototype(); + + if (rootPrototype != null) { + Object rootObject = getByPrototypeName(rootPrototype); + + if (rootObject != null) { + start = objects.indexOf(rootObject) + 1; + } + } + + for (int i=start; i= 0 && index < path.size(); + } + + /** + * Returns a list of array indices 0..length-1. + */ + public Object[] getIds() { + Object[] ids = new Object[path.size()]; + + for (int i=0; i