* Handle all HTTP methods (all the same way, this is temporary behaviour
until we find a way to deal with them) * Add req.getMethod() that returns method name as string * Add req.getServletRequest() that returns the HttpServletRequest object for HTTP requests * Set up req, path, session... objects for all types of requests * Restructured helma.framework.core.RequestEvaluator to allow for more cleanup in the future
This commit is contained in:
parent
255d6e9812
commit
026dd7faad
4 changed files with 344 additions and 331 deletions
|
@ -45,6 +45,15 @@ public class RequestBean implements Serializable {
|
||||||
return req.get(name);
|
return req.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the method of the request. This may either be a HTTP method or
|
||||||
|
* one of the Helma pseudo methods defined in RequestTrans.
|
||||||
|
*/
|
||||||
|
public String getMethod() {
|
||||||
|
return req.getMethod();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,6 +18,8 @@ package helma.framework;
|
||||||
|
|
||||||
import helma.util.Base64;
|
import helma.util.Base64;
|
||||||
import helma.util.SystemMap;
|
import helma.util.SystemMap;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -25,12 +27,26 @@ import java.util.*;
|
||||||
* A Transmitter for a request from the servlet client. Objects of this
|
* A Transmitter for a request from the servlet client. Objects of this
|
||||||
* class are directly exposed to JavaScript as global property req.
|
* class are directly exposed to JavaScript as global property req.
|
||||||
*/
|
*/
|
||||||
public class RequestTrans implements Externalizable {
|
public class RequestTrans implements Serializable {
|
||||||
|
|
||||||
static final long serialVersionUID = 5398880083482000580L;
|
static final long serialVersionUID = 5398880083482000580L;
|
||||||
|
|
||||||
public final static byte GET = 0;
|
// HTTP methods
|
||||||
public final static byte POST = 1;
|
public final static String GET = "GET";
|
||||||
|
public final static String POST = "POST";
|
||||||
|
public final static String DELETE = "DELETE";
|
||||||
|
public final static String HEAD = "HEAD";
|
||||||
|
public final static String OPTIONS = "OPTIONS";
|
||||||
|
public final static String PUT = "PUT";
|
||||||
|
public final static String TRACE = "TRACE";
|
||||||
|
// Helma pseudo-methods
|
||||||
|
public final static String XMLRPC = "XMLRPC";
|
||||||
|
public final static String EXTERNAL = "EXTERNAL";
|
||||||
|
public final static String INTERNAL = "INTERNAL";
|
||||||
|
|
||||||
|
// the servlet request, may be null
|
||||||
|
HttpServletRequest request;
|
||||||
|
|
||||||
// the uri path of the request
|
// the uri path of the request
|
||||||
public String path;
|
public String path;
|
||||||
|
|
||||||
|
@ -40,8 +56,8 @@ public class RequestTrans implements Externalizable {
|
||||||
// the map of form and cookie data
|
// the map of form and cookie data
|
||||||
private Map values;
|
private Map values;
|
||||||
|
|
||||||
// the request method - 0 for GET, 1 for POST
|
// the HTTP request method
|
||||||
private byte httpMethod = GET;
|
private String method;
|
||||||
|
|
||||||
// timestamp of client-cached version, if present in request
|
// timestamp of client-cached version, if present in request
|
||||||
private long ifModifiedSince = -1;
|
private long ifModifiedSince = -1;
|
||||||
|
@ -60,16 +76,18 @@ public class RequestTrans implements Externalizable {
|
||||||
/**
|
/**
|
||||||
* Create a new Request transmitter with an empty data map.
|
* Create a new Request transmitter with an empty data map.
|
||||||
*/
|
*/
|
||||||
public RequestTrans() {
|
public RequestTrans(String method) {
|
||||||
this(GET);
|
this.method = method;
|
||||||
|
this.request = null;
|
||||||
values = new SystemMap();
|
values = new SystemMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new request transmitter with the given data map.
|
* Create a new request transmitter with the given data map.
|
||||||
*/
|
*/
|
||||||
public RequestTrans(byte method) {
|
public RequestTrans(HttpServletRequest request) {
|
||||||
httpMethod = method;
|
this.method = request.getMethod();
|
||||||
|
this.request = request;
|
||||||
values = new SystemMap();
|
values = new SystemMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +116,14 @@ public class RequestTrans implements Externalizable {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Servlet request represented by this RequestTrans instance.
|
||||||
|
* Returns null for internal and XML-RPC requests.
|
||||||
|
*/
|
||||||
|
public HttpServletRequest getServletRequest() {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hash code is computed from the session id if available. This is used to
|
* The hash code is computed from the session id if available. This is used to
|
||||||
* detect multiple identic requests.
|
* detect multiple identic requests.
|
||||||
|
@ -121,42 +147,26 @@ public class RequestTrans implements Externalizable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the method of the request. This may either be a HTTP method or
|
||||||
|
* one of the Helma pseudo methods defined in this class.
|
||||||
|
*/
|
||||||
|
public String getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if this object represents a HTTP GET Request.
|
* Return true if this object represents a HTTP GET Request.
|
||||||
*/
|
*/
|
||||||
public boolean isGet() {
|
public boolean isGet() {
|
||||||
return httpMethod == GET;
|
return GET.equalsIgnoreCase(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if this object represents a HTTP GET Request.
|
* Return true if this object represents a HTTP GET Request.
|
||||||
*/
|
*/
|
||||||
public boolean isPost() {
|
public boolean isPost() {
|
||||||
return httpMethod == POST;
|
return POST.equalsIgnoreCase(method);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom externalization code for quicker serialization.
|
|
||||||
*/
|
|
||||||
public void readExternal(ObjectInput s) throws ClassNotFoundException, IOException {
|
|
||||||
path = s.readUTF();
|
|
||||||
session = s.readUTF();
|
|
||||||
values = (Map) s.readObject();
|
|
||||||
httpMethod = s.readByte();
|
|
||||||
ifModifiedSince = s.readLong();
|
|
||||||
etags = (Set) s.readObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom externalization code for quicker serialization.
|
|
||||||
*/
|
|
||||||
public void writeExternal(ObjectOutput s) throws IOException {
|
|
||||||
s.writeUTF(path);
|
|
||||||
s.writeUTF(session);
|
|
||||||
s.writeObject(values);
|
|
||||||
s.writeByte(httpMethod);
|
|
||||||
s.writeLong(ifModifiedSince);
|
|
||||||
s.writeObject(etags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -46,7 +46,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
Object thisObject;
|
Object thisObject;
|
||||||
|
|
||||||
// the method to be executed
|
// the method to be executed
|
||||||
String method;
|
String functionName;
|
||||||
|
|
||||||
// the session object associated with the current request
|
// the session object associated with the current request
|
||||||
Session session;
|
Session session;
|
||||||
|
@ -108,7 +108,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
Transactor localrtx = (Transactor) Thread.currentThread();
|
Transactor localrtx = (Transactor) Thread.currentThread();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
do {
|
do { // while (localrtx == rtx);
|
||||||
// initialize scripting engine, in case it hasn't been initialized yet
|
// initialize scripting engine, in case it hasn't been initialized yet
|
||||||
initScriptingEngine();
|
initScriptingEngine();
|
||||||
// update scripting prototypes
|
// update scripting prototypes
|
||||||
|
@ -124,46 +124,37 @@ public final class RequestEvaluator implements Runnable {
|
||||||
|
|
||||||
requestPath = new RequestPath(app);
|
requestPath = new RequestPath(app);
|
||||||
|
|
||||||
switch (reqtype) {
|
int tries = 0;
|
||||||
case HTTP:
|
boolean done = false;
|
||||||
|
String error = null;
|
||||||
|
|
||||||
int tries = 0;
|
while (!done) {
|
||||||
boolean done = false;
|
currentElement = null;
|
||||||
String error = null;
|
|
||||||
|
|
||||||
while (!done) {
|
try {
|
||||||
currentElement = null;
|
// TODO: transaction names are not set for internal/xmlrpc/external requests
|
||||||
|
// used for logging
|
||||||
|
StringBuffer txname = new StringBuffer(app.getName());
|
||||||
|
txname.append("/");
|
||||||
|
txname.append((error == null) ? req.path : "error");
|
||||||
|
|
||||||
try {
|
// begin transaction
|
||||||
// used for logging
|
localrtx.begin(txname.toString());
|
||||||
StringBuffer txname = new StringBuffer(app.getName());
|
|
||||||
|
|
||||||
txname.append("/");
|
String action = null;
|
||||||
txname.append((error == null) ? req.path : "error");
|
|
||||||
|
|
||||||
// begin transaction
|
root = app.getDataRoot();
|
||||||
localrtx.begin(txname.toString());
|
|
||||||
|
|
||||||
String action = null;
|
req.startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
root = app.getDataRoot();
|
initGlobals(root);
|
||||||
|
|
||||||
HashMap globals = new HashMap();
|
if (error != null) {
|
||||||
|
res.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
globals.put("root", root);
|
switch (reqtype) {
|
||||||
globals.put("session", new SessionBean(session));
|
case HTTP:
|
||||||
globals.put("req", new RequestBean(req));
|
|
||||||
globals.put("res", new ResponseBean(res));
|
|
||||||
globals.put("app", new ApplicationBean(app));
|
|
||||||
globals.put("path", requestPath);
|
|
||||||
req.startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// enter execution context
|
|
||||||
scriptingEngine.enterContext(globals);
|
|
||||||
|
|
||||||
if (error != null) {
|
|
||||||
res.error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session.message != null) {
|
if (session.message != null) {
|
||||||
// bring over the message from a redirect
|
// bring over the message from a redirect
|
||||||
|
@ -179,7 +170,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
// do not reset the requestPath so error handler can use the original one
|
// do not reset the requestPath so error handler can use the original one
|
||||||
// get error handler action
|
// get error handler action
|
||||||
String errorAction = app.props.getProperty("error",
|
String errorAction = app.props.getProperty("error",
|
||||||
"error");
|
"error");
|
||||||
|
|
||||||
action = getAction(currentElement, errorAction);
|
action = getAction(currentElement, errorAction);
|
||||||
|
|
||||||
|
@ -187,7 +178,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
throw new RuntimeException(error);
|
throw new RuntimeException(error);
|
||||||
}
|
}
|
||||||
} else if ((req.path == null) ||
|
} else if ((req.path == null) ||
|
||||||
"".equals(req.path.trim())) {
|
"".equals(req.path.trim())) {
|
||||||
currentElement = root;
|
currentElement = root;
|
||||||
requestPath.add(null, currentElement);
|
requestPath.add(null, currentElement);
|
||||||
|
|
||||||
|
@ -199,7 +190,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
} else {
|
} else {
|
||||||
// march down request path...
|
// march down request path...
|
||||||
StringTokenizer st = new StringTokenizer(req.path,
|
StringTokenizer st = new StringTokenizer(req.path,
|
||||||
"/");
|
"/");
|
||||||
int ntokens = st.countTokens();
|
int ntokens = st.countTokens();
|
||||||
|
|
||||||
// limit path to < 50 tokens
|
// limit path to < 50 tokens
|
||||||
|
@ -229,12 +220,12 @@ public final class RequestEvaluator implements Runnable {
|
||||||
// try to interpret it as action name.
|
// try to interpret it as action name.
|
||||||
if (i == (ntokens - 1)) {
|
if (i == (ntokens - 1)) {
|
||||||
action = getAction(currentElement,
|
action = getAction(currentElement,
|
||||||
pathItems[i]);
|
pathItems[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
currentElement = getChildElement(currentElement,
|
currentElement = getChildElement(currentElement,
|
||||||
pathItems[i]);
|
pathItems[i]);
|
||||||
|
|
||||||
// add object to request path if suitable
|
// add object to request path if suitable
|
||||||
if (currentElement != null) {
|
if (currentElement != null) {
|
||||||
|
@ -269,7 +260,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
|
|
||||||
String notFoundAction = app.props.getProperty("notfound",
|
String notFoundAction = app.props.getProperty("notfound",
|
||||||
"notfound");
|
"notfound");
|
||||||
|
|
||||||
currentElement = root;
|
currentElement = root;
|
||||||
action = getAction(currentElement, notFoundAction);
|
action = getAction(currentElement, notFoundAction);
|
||||||
|
@ -285,7 +276,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
int l = requestPath.size();
|
int l = requestPath.size();
|
||||||
Prototype[] protos = new Prototype[l];
|
Prototype[] protos = new Prototype[l];
|
||||||
|
|
||||||
for (int i=0; i<l; i++) {
|
for (int i = 0; i < l; i++) {
|
||||||
|
|
||||||
Object obj = requestPath.get(i);
|
Object obj = requestPath.get(i);
|
||||||
|
|
||||||
|
@ -301,7 +292,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
// in a second pass, we register path objects with their indirect
|
// in a second pass, we register path objects with their indirect
|
||||||
// (i.e. parent prototype) names, starting at the end and only
|
// (i.e. parent prototype) names, starting at the end and only
|
||||||
// if the name isn't occupied yet.
|
// if the name isn't occupied yet.
|
||||||
for (int i=l-1; i>=0; i--) {
|
for (int i = l - 1; i >= 0; i--) {
|
||||||
if (protos[i] != null) {
|
if (protos[i] != null) {
|
||||||
protos[i].registerParents(macroHandlers, requestPath.get(i));
|
protos[i].registerParents(macroHandlers, requestPath.get(i));
|
||||||
}
|
}
|
||||||
|
@ -326,11 +317,11 @@ public final class RequestEvaluator implements Runnable {
|
||||||
// calling the actual action
|
// calling the actual action
|
||||||
try {
|
try {
|
||||||
if (scriptingEngine.hasFunction(currentElement,
|
if (scriptingEngine.hasFunction(currentElement,
|
||||||
"onRequest")) {
|
"onRequest")) {
|
||||||
scriptingEngine.invoke(currentElement,
|
scriptingEngine.invoke(currentElement,
|
||||||
"onRequest",
|
"onRequest",
|
||||||
new Object[0],
|
new Object[0],
|
||||||
ScriptingEngine.ARGS_WRAP_DEFAULT);
|
ScriptingEngine.ARGS_WRAP_DEFAULT);
|
||||||
}
|
}
|
||||||
} catch (RedirectException redir) {
|
} catch (RedirectException redir) {
|
||||||
throw redir;
|
throw redir;
|
||||||
|
@ -341,235 +332,212 @@ public final class RequestEvaluator implements Runnable {
|
||||||
|
|
||||||
// do the actual action invocation
|
// do the actual action invocation
|
||||||
scriptingEngine.invoke(currentElement, action,
|
scriptingEngine.invoke(currentElement, action,
|
||||||
new Object[0],
|
new Object[0],
|
||||||
ScriptingEngine.ARGS_WRAP_DEFAULT);
|
ScriptingEngine.ARGS_WRAP_DEFAULT);
|
||||||
} catch (RedirectException redirect) {
|
} catch (RedirectException redirect) {
|
||||||
// res.redirect = redirect.getMessage ();
|
|
||||||
// if there is a message set, save it on the user object for the next request
|
// if there is a message set, save it on the user object for the next request
|
||||||
if (res.message != null) {
|
if (res.message != null) {
|
||||||
session.message = res.message;
|
session.message = res.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
done = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we're still the one and only or if the waiting thread has given up on us already
|
// check if we're still the one and only or if the waiting thread has given up on us already
|
||||||
commitTransaction();
|
commitTransaction();
|
||||||
done = true;
|
done = true;
|
||||||
} catch (ConcurrencyException x) {
|
|
||||||
res.reset();
|
|
||||||
|
|
||||||
if (++tries < 8) {
|
|
||||||
// try again after waiting some period
|
|
||||||
abortTransaction(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// wait a bit longer with each try
|
|
||||||
int base = 800 * tries;
|
|
||||||
|
|
||||||
Thread.sleep((long) (base + (Math.random() * base * 2)));
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
abortTransaction(false);
|
|
||||||
|
|
||||||
if (error == null) {
|
|
||||||
app.errorCount += 1;
|
|
||||||
|
|
||||||
// set done to false so that the error will be processed
|
|
||||||
done = false;
|
|
||||||
error = "Couldn't complete transaction due to heavy object traffic (tried " +
|
|
||||||
tries + " times)";
|
|
||||||
} else {
|
|
||||||
// error in error action. use traditional minimal error message
|
|
||||||
res.write("<b>Error in application '" +
|
|
||||||
app.getName() + "':</b> <br><br><pre>" +
|
|
||||||
error + "</pre>");
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception x) {
|
|
||||||
abortTransaction(false);
|
|
||||||
|
|
||||||
// If the transactor thread has been killed by the invoker thread we don't have to
|
|
||||||
// bother for the error message, just quit.
|
|
||||||
if (localrtx != rtx) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.reset();
|
|
||||||
|
|
||||||
// check if we tried to process the error already
|
|
||||||
if (error == null) {
|
|
||||||
app.errorCount += 1;
|
|
||||||
app.logEvent("Exception in " +
|
|
||||||
Thread.currentThread() + ": " + x);
|
|
||||||
|
|
||||||
// Dump the profiling data to System.err
|
|
||||||
if (app.debug && !(x instanceof ScriptingException)) {
|
|
||||||
x.printStackTrace ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set done to false so that the error will be processed
|
|
||||||
done = false;
|
|
||||||
error = x.getMessage();
|
|
||||||
|
|
||||||
if ((error == null) || (error.length() == 0)) {
|
|
||||||
error = x.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error == null) {
|
|
||||||
error = "Unspecified error";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// error in error action. use traditional minimal error message
|
|
||||||
res.write("<b>Error in application '" +
|
|
||||||
app.getName() + "':</b> <br><br><pre>" +
|
|
||||||
error + "</pre>");
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XMLRPC:
|
|
||||||
case EXTERNAL:
|
|
||||||
|
|
||||||
try {
|
|
||||||
localrtx.begin(app.getName() + ":ext-rpc:" + method);
|
|
||||||
|
|
||||||
root = app.getDataRoot();
|
|
||||||
|
|
||||||
HashMap globals = new HashMap();
|
|
||||||
|
|
||||||
globals.put("root", root);
|
|
||||||
globals.put("res", new ResponseBean(res));
|
|
||||||
globals.put("app", new ApplicationBean(app));
|
|
||||||
|
|
||||||
scriptingEngine.enterContext(globals);
|
|
||||||
|
|
||||||
currentElement = 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();
|
|
||||||
|
|
||||||
currentElement = getChildElement(currentElement,
|
|
||||||
next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentElement == null) {
|
|
||||||
throw new FrameworkException("Method name \"" +
|
|
||||||
method +
|
|
||||||
"\" could not be resolved.");
|
|
||||||
}
|
|
||||||
|
|
||||||
method = st.nextToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reqtype == XMLRPC) {
|
|
||||||
// check XML-RPC access permissions
|
|
||||||
String proto = app.getPrototypeName(currentElement);
|
|
||||||
|
|
||||||
app.checkXmlRpcAccess(proto, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset skin recursion detection counter
|
|
||||||
skinDepth = 0;
|
|
||||||
|
|
||||||
result = scriptingEngine.invoke(currentElement, method, args,
|
|
||||||
ScriptingEngine.ARGS_WRAP_XMLRPC);
|
|
||||||
commitTransaction();
|
|
||||||
} catch (Exception x) {
|
|
||||||
abortTransaction(false);
|
|
||||||
|
|
||||||
app.logEvent("Exception in " + Thread.currentThread() + ": " +
|
|
||||||
x);
|
|
||||||
|
|
||||||
// If the transactor thread has been killed by the invoker thread we don't have to
|
|
||||||
// bother for the error message, just quit.
|
|
||||||
if (localrtx != rtx) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.exception = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
case INTERNAL:
|
|
||||||
|
|
||||||
// Just a human readable descriptor of this invocation
|
|
||||||
String funcdesc = app.getName() + ":internal:" + method;
|
|
||||||
|
|
||||||
// if thisObject is an instance of NodeHandle, get the node object itself.
|
|
||||||
if ((thisObject != null) && thisObject instanceof NodeHandle) {
|
|
||||||
thisObject = ((NodeHandle) thisObject).getNode(app.nmgr.safe);
|
|
||||||
|
|
||||||
// see if a valid node was returned
|
|
||||||
if (thisObject == null) {
|
|
||||||
reqtype = NONE;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// avoid going into transaction if called function doesn't exist.
|
case XMLRPC:
|
||||||
boolean functionexists = true;
|
case EXTERNAL:
|
||||||
|
|
||||||
// this only works for the (common) case that method is a plain
|
try {
|
||||||
// method name, not an obj.method path
|
currentElement = root;
|
||||||
if (method.indexOf('.') < 0) {
|
|
||||||
functionexists = scriptingEngine.hasFunction(thisObject, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!functionexists) {
|
if (functionName.indexOf('.') > -1) {
|
||||||
// function doesn't exist, nothing to do here.
|
StringTokenizer st = new StringTokenizer(functionName, ".");
|
||||||
reqtype = NONE;
|
int cnt = st.countTokens();
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
localrtx.begin(funcdesc);
|
|
||||||
|
|
||||||
root = app.getDataRoot();
|
for (int i = 1; i < cnt; i++) {
|
||||||
|
String next = st.nextToken();
|
||||||
|
|
||||||
HashMap globals = new HashMap();
|
currentElement = getChildElement(currentElement,
|
||||||
|
next);
|
||||||
|
}
|
||||||
|
|
||||||
globals.put("root", root);
|
if (currentElement == null) {
|
||||||
globals.put("res", new ResponseBean(res));
|
throw new FrameworkException("Method name \"" +
|
||||||
globals.put("app", new ApplicationBean(app));
|
functionName +
|
||||||
|
"\" could not be resolved.");
|
||||||
|
}
|
||||||
|
|
||||||
scriptingEngine.enterContext(globals);
|
functionName = st.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
// reset skin recursion detection counter
|
if (reqtype == XMLRPC) {
|
||||||
skinDepth = 0;
|
// check XML-RPC access permissions
|
||||||
|
String proto = app.getPrototypeName(currentElement);
|
||||||
|
|
||||||
result = scriptingEngine.invoke(thisObject, method, args,
|
app.checkXmlRpcAccess(proto, functionName);
|
||||||
ScriptingEngine.ARGS_WRAP_DEFAULT);
|
}
|
||||||
commitTransaction();
|
|
||||||
} catch (Exception x) {
|
|
||||||
abortTransaction(false);
|
|
||||||
|
|
||||||
app.logEvent("Exception in " + Thread.currentThread() +
|
// reset skin recursion detection counter
|
||||||
": " + x);
|
skinDepth = 0;
|
||||||
|
|
||||||
// If the transactor thread has been killed by the invoker thread we don't have to
|
result = scriptingEngine.invoke(currentElement, functionName, args,
|
||||||
// bother for the error message, just quit.
|
ScriptingEngine.ARGS_WRAP_XMLRPC);
|
||||||
if (localrtx != rtx) {
|
commitTransaction();
|
||||||
return;
|
} catch (Exception x) {
|
||||||
|
abortTransaction(false);
|
||||||
|
|
||||||
|
app.logEvent("Exception in " + Thread.currentThread() + ": " +
|
||||||
|
x);
|
||||||
|
|
||||||
|
// If the transactor thread has been killed by the invoker thread we don't have to
|
||||||
|
// bother for the error message, just quit.
|
||||||
|
if (localrtx != rtx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.exception = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.exception = x;
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INTERNAL:
|
||||||
|
|
||||||
|
// TODO: transaction names are not set for internal/xmlrpc/external requests
|
||||||
|
// Just a human readable descriptor of this invocation
|
||||||
|
// String funcdesc = app.getName() + ":internal:" + functionName;
|
||||||
|
|
||||||
|
// if thisObject is an instance of NodeHandle, get the node object itself.
|
||||||
|
if ((thisObject != null) && thisObject instanceof NodeHandle) {
|
||||||
|
thisObject = ((NodeHandle) thisObject).getNode(app.nmgr.safe);
|
||||||
|
|
||||||
|
// see if a valid node was returned
|
||||||
|
if (thisObject == null) {
|
||||||
|
reqtype = NONE;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid going into transaction if called function doesn't exist.
|
||||||
|
boolean functionexists = true;
|
||||||
|
|
||||||
|
// this only works for the (common) case that method is a plain
|
||||||
|
// method name, not an obj.method path
|
||||||
|
if (functionName.indexOf('.') < 0) {
|
||||||
|
functionexists = scriptingEngine.hasFunction(thisObject, functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!functionexists) {
|
||||||
|
// function doesn't exist, nothing to do here.
|
||||||
|
reqtype = NONE;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// reset skin recursion detection counter
|
||||||
|
skinDepth = 0;
|
||||||
|
|
||||||
|
result = scriptingEngine.invoke(thisObject, functionName, args,
|
||||||
|
ScriptingEngine.ARGS_WRAP_DEFAULT);
|
||||||
|
commitTransaction();
|
||||||
|
} catch (Exception x) {
|
||||||
|
abortTransaction(false);
|
||||||
|
|
||||||
|
app.logEvent("Exception in " + Thread.currentThread() +
|
||||||
|
": " + x);
|
||||||
|
|
||||||
|
// If the transactor thread has been killed by the invoker thread we don't have to
|
||||||
|
// bother for the error message, just quit.
|
||||||
|
if (localrtx != rtx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.exception = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
} // switch (reqtype)
|
||||||
|
} catch (ConcurrencyException x) {
|
||||||
|
res.reset();
|
||||||
|
|
||||||
|
if (++tries < 8) {
|
||||||
|
// try again after waiting some period
|
||||||
|
abortTransaction(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// wait a bit longer with each try
|
||||||
|
int base = 800 * tries;
|
||||||
|
|
||||||
|
Thread.sleep((long) (base + (Math.random() * base * 2)));
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
abortTransaction(false);
|
||||||
|
|
||||||
|
if (error == null) {
|
||||||
|
app.errorCount += 1;
|
||||||
|
|
||||||
|
// set done to false so that the error will be processed
|
||||||
|
done = false;
|
||||||
|
error = "Couldn't complete transaction due to heavy object traffic (tried " +
|
||||||
|
tries + " times)";
|
||||||
|
} else {
|
||||||
|
// error in error action. use traditional minimal error message
|
||||||
|
res.write("<b>Error in application '" +
|
||||||
|
app.getName() + "':</b> <br><br><pre>" +
|
||||||
|
error + "</pre>");
|
||||||
|
done = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Throwable x) {
|
||||||
|
abortTransaction(false);
|
||||||
|
|
||||||
break;
|
// If the transactor thread has been killed by the invoker thread we don't have to
|
||||||
|
// bother for the error message, just quit.
|
||||||
|
if (localrtx != rtx) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.reset();
|
||||||
|
|
||||||
|
// check if we tried to process the error already
|
||||||
|
if (error == null) {
|
||||||
|
app.errorCount += 1;
|
||||||
|
app.logEvent("Exception in " +
|
||||||
|
Thread.currentThread() + ": " + x);
|
||||||
|
|
||||||
|
// Dump the profiling data to System.err
|
||||||
|
if (app.debug && !(x instanceof ScriptingException)) {
|
||||||
|
x.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set done to false so that the error will be processed
|
||||||
|
done = false;
|
||||||
|
error = x.getMessage();
|
||||||
|
|
||||||
|
if ((error == null) || (error.length() == 0)) {
|
||||||
|
error = x.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == null) {
|
||||||
|
error = "Unspecified error";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// error in error action. use traditional minimal error message
|
||||||
|
res.write("<b>Error in application '" +
|
||||||
|
app.getName() + "':</b> <br><br><pre>" +
|
||||||
|
error + "</pre>");
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit execution context
|
// exit execution context
|
||||||
|
@ -657,10 +625,7 @@ public final class RequestEvaluator implements Runnable {
|
||||||
*/
|
*/
|
||||||
public synchronized ResponseTrans invokeHttp(RequestTrans req, Session session)
|
public synchronized ResponseTrans invokeHttp(RequestTrans req, Session session)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
this.reqtype = HTTP;
|
initObjects(req, session);
|
||||||
this.req = req;
|
|
||||||
this.session = session;
|
|
||||||
this.res = new ResponseTrans(req);
|
|
||||||
|
|
||||||
app.activeRequests.put(req, this);
|
app.activeRequests.put(req, this);
|
||||||
|
|
||||||
|
@ -701,22 +666,18 @@ public final class RequestEvaluator implements Runnable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param method ...
|
* @param functionName ...
|
||||||
* @param args ...
|
* @param args ...
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return ...
|
||||||
*
|
*
|
||||||
* @throws Exception ...
|
* @throws Exception ...
|
||||||
*/
|
*/
|
||||||
public synchronized Object invokeXmlRpc(String method, Object[] args)
|
public synchronized Object invokeXmlRpc(String functionName, Object[] args)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
this.reqtype = XMLRPC;
|
initObjects(XMLRPC, RequestTrans.XMLRPC);
|
||||||
this.session = null;
|
this.functionName = functionName;
|
||||||
this.method = method;
|
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.res = new ResponseTrans();
|
|
||||||
result = null;
|
|
||||||
exception = null;
|
|
||||||
|
|
||||||
checkThread();
|
checkThread();
|
||||||
wait(app.requestTimeout);
|
wait(app.requestTimeout);
|
||||||
|
@ -740,22 +701,18 @@ public final class RequestEvaluator implements Runnable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param method ...
|
* @param functionName ...
|
||||||
* @param args ...
|
* @param args ...
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return ...
|
||||||
*
|
*
|
||||||
* @throws Exception ...
|
* @throws Exception ...
|
||||||
*/
|
*/
|
||||||
public synchronized Object invokeExternal(String method, Object[] args)
|
public synchronized Object invokeExternal(String functionName, Object[] args)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
this.reqtype = EXTERNAL;
|
initObjects(EXTERNAL, RequestTrans.EXTERNAL);
|
||||||
this.session = null;
|
this.functionName = functionName;
|
||||||
this.method = method;
|
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.res = new ResponseTrans();
|
|
||||||
result = null;
|
|
||||||
exception = null;
|
|
||||||
|
|
||||||
checkThread();
|
checkThread();
|
||||||
wait();
|
wait();
|
||||||
|
@ -823,14 +780,10 @@ public final class RequestEvaluator implements Runnable {
|
||||||
public synchronized Object invokeInternal(Object object, String functionName,
|
public synchronized Object invokeInternal(Object object, String functionName,
|
||||||
Object[] args, long timeout)
|
Object[] args, long timeout)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
reqtype = INTERNAL;
|
initObjects(INTERNAL, RequestTrans.INTERNAL);
|
||||||
session = null;
|
|
||||||
thisObject = object;
|
thisObject = object;
|
||||||
method = functionName;
|
this.functionName = functionName;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.res = new ResponseTrans();
|
|
||||||
result = null;
|
|
||||||
exception = null;
|
|
||||||
|
|
||||||
checkThread();
|
checkThread();
|
||||||
wait(timeout);
|
wait(timeout);
|
||||||
|
@ -850,6 +803,65 @@ public final class RequestEvaluator implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init this evaluator's objects from a RequestTrans for a HTTP request
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param session
|
||||||
|
*/
|
||||||
|
private void initObjects(RequestTrans req, Session session) {
|
||||||
|
this.req = req;
|
||||||
|
this.reqtype = HTTP;
|
||||||
|
this.session = session;
|
||||||
|
res = new ResponseTrans(req);
|
||||||
|
// result = null;
|
||||||
|
// exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init this evaluator's objects for an internal, external or XML-RPC type
|
||||||
|
* request.
|
||||||
|
*
|
||||||
|
* @param reqtype
|
||||||
|
* @param reqtypeName
|
||||||
|
*/
|
||||||
|
private void initObjects(int reqtype, String reqtypeName) {
|
||||||
|
this.reqtype = reqtype;
|
||||||
|
this.req = new RequestTrans(reqtypeName);
|
||||||
|
session = new Session(functionName, app);
|
||||||
|
res = new ResponseTrans(req);
|
||||||
|
result = null;
|
||||||
|
exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the globals in the scripting engine for the current request.
|
||||||
|
*
|
||||||
|
* @param root
|
||||||
|
* @throws ScriptingException
|
||||||
|
*/
|
||||||
|
private void initGlobals(Object root) throws ScriptingException {
|
||||||
|
HashMap globals = new HashMap();
|
||||||
|
|
||||||
|
globals.put("root", root);
|
||||||
|
globals.put("session", new SessionBean(session));
|
||||||
|
globals.put("req", new RequestBean(req));
|
||||||
|
globals.put("res", new ResponseBean(res));
|
||||||
|
globals.put("app", new ApplicationBean(app));
|
||||||
|
globals.put("path", requestPath);
|
||||||
|
|
||||||
|
// enter execution context
|
||||||
|
scriptingEngine.enterContext(globals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the child element with the given name from the given object.
|
||||||
|
*
|
||||||
|
* @param obj
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
* @throws ScriptingException
|
||||||
|
*/
|
||||||
private Object getChildElement(Object obj, String name) throws ScriptingException {
|
private Object getChildElement(Object obj, String name) throws ScriptingException {
|
||||||
if (scriptingEngine.hasFunction(obj, "getChildElement")) {
|
if (scriptingEngine.hasFunction(obj, "getChildElement")) {
|
||||||
return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name},
|
return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name},
|
||||||
|
@ -864,8 +876,8 @@ public final class RequestEvaluator implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop this request evaluator's current thread. If currently active kill the request, otherwise just
|
* Stop this request evaluator's current thread.
|
||||||
* notify.
|
* If currently active kill the request, otherwise just notify.
|
||||||
*/
|
*/
|
||||||
public synchronized void stopThread() {
|
public synchronized void stopThread() {
|
||||||
Transactor t = rtx;
|
Transactor t = rtx;
|
||||||
|
|
|
@ -89,7 +89,7 @@ public abstract class AbstractServletClient extends HttpServlet {
|
||||||
abstract Application getApplication();
|
abstract Application getApplication();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a GET request.
|
* Handle a request.
|
||||||
*
|
*
|
||||||
* @param request ...
|
* @param request ...
|
||||||
* @param response ...
|
* @param response ...
|
||||||
|
@ -97,28 +97,10 @@ public abstract class AbstractServletClient extends HttpServlet {
|
||||||
* @throws ServletException ...
|
* @throws ServletException ...
|
||||||
* @throws IOException ...
|
* @throws IOException ...
|
||||||
*/
|
*/
|
||||||
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
protected void service (HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException {
|
|
||||||
execute(request, response, RequestTrans.GET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a POST request.
|
|
||||||
*
|
|
||||||
* @param request ...
|
|
||||||
* @param response ...
|
|
||||||
*
|
|
||||||
* @throws ServletException ...
|
|
||||||
* @throws IOException ...
|
|
||||||
*/
|
|
||||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
execute(request, response, RequestTrans.POST);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void execute(HttpServletRequest request, HttpServletResponse response,
|
RequestTrans reqtrans = new RequestTrans(request);
|
||||||
byte method) {
|
|
||||||
RequestTrans reqtrans = new RequestTrans(method);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// get the character encoding
|
// get the character encoding
|
||||||
|
|
Loading…
Add table
Reference in a new issue