|
From: <tre...@us...> - 2007-08-31 19:43:28
|
Revision: 311
http://ogoglio.svn.sourceforge.net/ogoglio/?rev=311&view=rev
Author: trevorolio
Date: 2007-08-31 12:43:28 -0700 (Fri, 31 Aug 2007)
Log Message:
-----------
Fixed the nastiness in the script engine in which we mangled javascript function calls using Strings. Now we use Rhino's API to find and call the functions with java argument arrays.
Modified Paths:
--------------
maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/script/SpaceScriptEngine.java
maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/site/SimServlet.java
Modified: maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/script/SpaceScriptEngine.java
===================================================================
--- maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/script/SpaceScriptEngine.java 2007-08-31 19:41:31 UTC (rev 310)
+++ maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/script/SpaceScriptEngine.java 2007-08-31 19:43:28 UTC (rev 311)
@@ -15,8 +15,6 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.util.Map;
import java.util.Vector;
@@ -24,6 +22,7 @@
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
@@ -51,22 +50,16 @@
private ScriptSpace scriptSpace = null;
- public static final String CONSTRUCT_SCRIPT_PREFIX = "if(typeof construct == \"function\") { construct(";
+ public static final String CONSTRUCT_FUNCTION_NAME = "construct";
- public static final String CONSTRUCT_SCRIPT_SUFFIX = "); }";
+ public static final String ONSERVICE_FUNCTION_NAME = "onService";
- public static final String ONCLICK_SCRIPT_PREFIX = "if(typeof onClick == \"function\") { onClick(";
+ private static final String ONCLICK_FUNCTION_NAME = "onClick";
- public static final String ONCLICK_SCRIPT_SUFFIX = "); }";
+ private static final String ONCONTEXTCLICK_FUNCTION_NAME = "onContextClick";
- public static final String ONCONTEXTCLICK_SCRIPT_PREFIX = "if(typeof onContextClick == \"function\") { onContextClick(";
+ private static final String ONCONTEXTMENUITEMCHOSEN_FUNCTION_NAME = "onContextMenuItemChosen";
- public static final String CONTEXT_ITEM_SELECTED = "if(typeof onContextMenuItemChosen == \"function\") { onContextMenuItemChosen(";
-
- public static final String SERVICE_SCRIPT_PREFIX = "if(typeof onService == \"function\") { onService(";
-
- public static final String SERVICE_SCRIPT_SUFFIX = "); }";
-
public SpaceScriptEngine(SpaceSimulator spaceSimulator) {
ArgumentUtils.assertNotNull(spaceSimulator);
this.spaceSimulator = spaceSimulator;
@@ -121,14 +114,23 @@
spaceSimulator.log("Constructed scriptless thing " + thing.getThingID());
return;
}
- script += CONSTRUCT_SCRIPT_PREFIX + thing.getThingID() + CONSTRUCT_SCRIPT_SUFFIX;
-
Context context = Context.enter();
try {
ScriptableObject thingScope = createThingScope(context);
- String result = evaluateScript(context, thingScope, script);
+ Object scriptResult = context.evaluateString(thingScope, script, "<cmd>", 1, null);
+ //the scriptResult will usually be the Function object for the last function defined in the template script
+ //which we usually don't care about
+ spaceSimulator.log("Initialized thing script: " + thing.getThingID());
+
+ Object[] functionArgs = { new Double(thing.getThingID()) };
+ Object callResult = callJavascriptFunction(context, thingScope, CONSTRUCT_FUNCTION_NAME, functionArgs);
+
thingScopes.put(new Long(thing.getThingID()), thingScope);
- spaceSimulator.log("Constructed thing " + thing.getThingID() + ": " + result);
+ spaceSimulator.log("Constructed thing " + thing.getThingID() + ": " + callResult);
+ } catch (EcmaError e) {
+ spaceSimulator.log("Error initializing or constructing thing " + thing.getThingID() + " at line " + e.lineNumber() + ": " + e.getErrorMessage());
+ } catch (Throwable e) {
+ spaceSimulator.log("Error initializing or constructing thing " + thing.getThingID() + ": " + e);
} finally {
Context.exit();
}
@@ -139,7 +141,7 @@
}
public void handleSpaceEvent(SpaceEvent event) {
- if ((SpaceEvent.THING_CLICKED_EVENT.equals(event.getName())) || (SpaceEvent.THING_CONTEXT_CLICKED_EVENT.equals(event.getName()))) {
+ if (SpaceEvent.THING_CLICKED_EVENT.equals(event.getName())) {
Long thingID = event.getLongProperty(SpaceEvent.THING_ID);
if (thingID == null) {
return;
@@ -149,31 +151,75 @@
return;
}
+ String functionName = ONCLICK_FUNCTION_NAME;
+ Object[] functionArgs = new Object[2];
+ functionArgs[0] = event.getStringProperty(SpaceEvent.USERNAME);
+ functionArgs[1] = event.getStringProperty(SpaceEvent.SHAPE_NAME);
+ Context context = Context.enter();
+ try {
+ Object result = callJavascriptFunction(context, thingScope, functionName, functionArgs);
+ spaceSimulator.log("Click script returned: " + Context.toString(result));
+ } finally {
+ Context.exit();
+ }
+ } else if (SpaceEvent.THING_CONTEXT_CLICKED_EVENT.equals(event.getName())) {
+ Long thingID = event.getLongProperty(SpaceEvent.THING_ID);
+ if (thingID == null) {
+ return;
+ }
+ ScriptableObject thingScope = getThingScope(thingID.longValue());
+ if (thingScope == null) {
+ return;
+ }
+
String shapeName = event.getStringProperty(SpaceEvent.SHAPE_NAME);
- String script = null;
- String prefix = ONCLICK_SCRIPT_PREFIX;
+ String username = event.getStringProperty(SpaceEvent.USERNAME);
+ long nonce = event.getLongProperty(SpaceEvent.NONCE).longValue();
- if (SpaceEvent.THING_CONTEXT_CLICKED_EVENT.equals(event.getName())) {
- prefix = ONCONTEXTCLICK_SCRIPT_PREFIX;
- }
- String username = event.getStringProperty(SpaceEvent.USERNAME);
- if (shapeName != null) {
- script = prefix + "\"" + username + "\", \"" + event.getStringProperty(SpaceEvent.SHAPE_NAME) + "\"" + ONCLICK_SCRIPT_SUFFIX;
- } else {
- script = prefix + "\"" + username + "\", null" + ONCLICK_SCRIPT_SUFFIX;
- }
+ String functionName = ONCONTEXTCLICK_FUNCTION_NAME;
+
+ Object[] functionArgs = new Object[2];
+ functionArgs[0] = username;
+ functionArgs[1] = shapeName;
+
+ SpaceEvent resultEvent = new SpaceEvent(SpaceEvent.CONTEXT_MENU_DATA_EVENT);
+ resultEvent.setProperty(SpaceEvent.NONCE, new Long(nonce));
+
Context context = Context.enter();
try {
- //the context click is called for value, the regular click for effect
- if (SpaceEvent.THING_CONTEXT_CLICKED_EVENT.equals(event.getName())) {
- String errorToClient = processScriptForContextMenuInfo(thingScope, username, event.getLongProperty(SpaceEvent.NONCE).longValue(), script, context);
+ Object callResult = callJavascriptFunction(context, thingScope, functionName, functionArgs);
+ if (callResult == null) { //no function or logged function error
+ spaceSimulator.log("No context on " + thingID);
+ spaceSimulator.getListener().generatedSpaceEventForUser(username, resultEvent, spaceSimulator);
+ return;
+ }
- if (errorToClient != null) {
- spaceSimulator.log("Error in contextmenuclick handler:" + errorToClient);
+ Object[] resultArray = convertScriptResultToArray(callResult);
+ if (resultArray == null) {
+ spaceSimulator.log("No context array on " + thingID + ": " + callResult);
+ spaceSimulator.getListener().generatedSpaceEventForUser(username, resultEvent, spaceSimulator);
+ return;
+ }
+
+ for (int i = 0; i < resultArray.length; ++i) {
+ ScriptableObject candidate = (ScriptableObject) resultArray[i];
+ if (!(candidate.getClassName().equals(ScriptContextMenuInfo.JS_CLASS_NAME))) {
+ spaceSimulator.log("Javascript Error: onContextClick(...) returned an array with things other than ContextMenuInfos: " + thingID);
+ spaceSimulator.getListener().generatedSpaceEventForUser(username, resultEvent, spaceSimulator);
+ return;
}
- } else {
- spaceSimulator.log("Click script : " + evaluateScript(context, thingScope, script));
}
+
+ //this is so we can use the same types on the client side
+ Vector changedTypeObjects = new Vector();
+ for (int i = 0; i < resultArray.length; ++i) {
+ ScriptContextMenuInfo infoWithJSBaggage = (ScriptContextMenuInfo) resultArray[i];
+ ContextMenuInfo infoNoJS = new ContextMenuInfo();
+ infoNoJS.setValues(infoWithJSBaggage.getUserVisibleString(), infoWithJSBaggage.getEnabled(), infoWithJSBaggage.getId());
+ changedTypeObjects.add(infoNoJS);
+ }
+ resultEvent.setContextMenu(changedTypeObjects);
+ spaceSimulator.getListener().generatedSpaceEventForUser(username, resultEvent, spaceSimulator);
} finally {
Context.exit();
}
@@ -188,16 +234,14 @@
}
Context context = Context.enter();
+
String username = event.getStringProperty(SpaceEvent.USERNAME);
- String script = CONTEXT_ITEM_SELECTED + "\"" + username + "\", \"" + event.getStringProperty(SpaceEvent.CONTEXT_MENU_DATA_ITEM_ID) + "\"" + ONCLICK_SCRIPT_SUFFIX;
- String result = evaluateScript(context, thingScope, script);
- spaceSimulator.log("Item selected script : " + result);
- try {
- new URL(result); //for effect of parsing it to determine if ok
- spaceSimulator.showLinkToUser(username, result, result);
- } catch (MalformedURLException e) {
- //not a URL, so we are done, assume the call was for effect
- }
+ String dataItemID = event.getStringProperty(SpaceEvent.CONTEXT_MENU_DATA_ITEM_ID);
+ Object[] functionArgs = { username, dataItemID };
+ String functionName = ONCONTEXTMENUITEMCHOSEN_FUNCTION_NAME;
+ Object callResult = callJavascriptFunction(context, thingScope, functionName, functionArgs);
+ spaceSimulator.log("Item selected script returned " + Context.toString(callResult));
+
} else if (SpaceEvent.ADD_THING_EVENT.equals(event.getName())) {
try {
Thing thing = spaceSimulator.getSpace().getThing(event.getLongProperty(SpaceEvent.THING_ID).longValue());
@@ -211,40 +255,6 @@
}
}
- private String processScriptForContextMenuInfo(ScriptableObject thingScope, String username, long nonce, String script, Context context) {
- Vector arrayOfObjects = new Vector();
- String result = evaluateScriptForArray(context, thingScope, script, arrayOfObjects);
- String errorToClient = null;
- if (result != null) {
- spaceSimulator.log("Error in script:" + result);
- errorToClient = result;
- } else {
- for (int i = 0; i < arrayOfObjects.size(); ++i) {
- ScriptableObject candidate = (ScriptableObject) arrayOfObjects.get(i);
- if (!(candidate.getClassName().equals(ScriptContextMenuInfo.JS_CLASS_NAME))) {
- errorToClient = "Should be an array of ContextMenuInfo items but had " + candidate.getClassName() + " in the array1";
- }
- }
- }
- SpaceEvent event = new SpaceEvent(SpaceEvent.CONTEXT_MENU_DATA_EVENT);
- event.setProperty(SpaceEvent.NONCE, new Long(nonce));
- if (errorToClient != null) {
- event.setProperty(SpaceEvent.CONTEXT_MENU_DATA_GENERATION_ERROR, errorToClient);
- } else {
- //this is so we can use the same types on the client side
- Vector changedTypeObjects = new Vector();
- for (int i = 0; i < arrayOfObjects.size(); ++i) {
- ScriptContextMenuInfo infoWithJSBaggage = (ScriptContextMenuInfo) arrayOfObjects.get(i);
- ContextMenuInfo infoNoJS = new ContextMenuInfo();
- infoNoJS.setValues(infoWithJSBaggage.getUserVisibleString(), infoWithJSBaggage.getEnabled(), infoWithJSBaggage.getId());
- changedTypeObjects.add(infoNoJS);
- }
- event.setContextMenu(changedTypeObjects);
- }
- spaceSimulator.getListener().generatedSpaceEventForUser(username, event, spaceSimulator);
- return errorToClient;
- }
-
private ScriptableObject createThingScope(Context context) {
ScriptableObject scriptScope = (ScriptableObject) context.newObject(globalScope);
scriptScope.setPrototype(globalScope);
@@ -252,54 +262,41 @@
return scriptScope;
}
- private String evaluateScript(Context context, ScriptableObject scriptScope, String script) {
- try {
- Object result = context.evaluateString(scriptScope, script, "<cmd>", 1, null);
- if (result == null) {
- return "null";
- } else if (result instanceof Undefined) {
- return "undefined";
- } else {
- return result.toString();
- }
- } catch (EcmaError error) {
- return getMessage(error);
- } catch (Throwable e) {
- return "Error: " + e;
+ private Object callJavascriptFunction(Context context, ScriptableObject thingScope, String functionName, Object[] functionArgs) {
+ Object functionObject = thingScope.get(functionName, thingScope);
+ if (!(functionObject instanceof Function)) {
+ return null;
+ } else {
+ Function function = (Function) functionObject;
+ return function.call(context, thingScope, thingScope, functionArgs);
}
}
- // dodgy: this returns an error message or null if things are ok
- private String evaluateScriptForArray(Context context, ScriptableObject scriptScope, String script, Vector valuesFound) {
- try {
- Object result = context.evaluateString(scriptScope, script, "<cmd>", 1, null);
- if (result == null) {
- return "null returned by javascript";
- } else if (result instanceof Undefined) {
- return "undefined value returned from javascript";
- } else if (!(result instanceof ScriptableObject)) {
- return "unexpected return type from javascript";
- }
+ private Object[] convertScriptResultToArray(Object result) {
+ if (result == null) {
+ return null;
+ } else if (result instanceof Undefined) {
+ return null;
+ } else if (!(result instanceof ScriptableObject)) {
+ return null;
+ }
- ScriptableObject obj = (ScriptableObject) result;
- if (!(obj.getClassName().equals("Array"))) {
- return "Expected Array result but got " + obj.getClassName();
- }
- Object length_raw = obj.get("length", obj);
- if ((length_raw == null) || (!(length_raw instanceof java.lang.Number))) {
- return "Internal error! Can't understand length of array in javascript!";
- }
- Number length = (Number) length_raw;
- for (int i = 0; i < length.intValue(); ++i) {
- valuesFound.add(obj.get(i, obj));
- }
+ ScriptableObject obj = (ScriptableObject) result;
+ if (!(obj.getClassName().equals("Array"))) {
+ spaceSimulator.log("Javascript Error: cannot convert " + obj.getClassName() + " to Array");
return null;
- } catch (EcmaError error) {
- return "Javascript Error:" + error.getMessage();
- } catch (Throwable e) {
- spaceSimulator.log("Caught a throwabe when evaluating script for array:" + e.getMessage());
- return "(Internal) Error:" + e.getMessage();
}
+ Object length_raw = obj.get("length", obj);
+ if ((length_raw == null) || (!(length_raw instanceof java.lang.Number))) {
+ spaceSimulator.log("Javascript error: Can't understand length of array in javascript: " + obj);
+ return null;
+ }
+ int length = ((Number) length_raw).intValue();
+ Object[] resultArray = new Object[length];
+ for (int i = 0; i < length; ++i) {
+ resultArray[i] = obj.get(i, obj);
+ }
+ return resultArray;
}
public ScriptHTTPResponse callThingService(long thingID, String method, Map parameterMap) {
@@ -311,20 +308,35 @@
}
//TODO actually pass the parameter map
- String script = SERVICE_SCRIPT_PREFIX + SERVICE_SCRIPT_SUFFIX;
+ String[] parameterNames = (String[]) parameterMap.keySet().toArray(new String[0]);
+ String[] parameterValues = new String[parameterNames.length];
+ for (int i = 0; i < parameterValues.length; i++) {
+ Object value = parameterMap.get(parameterNames[i]);
+ if (value instanceof String) {
+ parameterValues[i] = (String) parameterMap.get(parameterNames[i]);
+ } else if(value instanceof String[]){
+ String[] array = (String[])value;
+ for (int j = 0; j < array.length; j++) {
+ System.out.println(parameterNames[i] + ": " + array[j]);
+ }
+ } else {
+ System.err.println("Got an unhandled parameter type: " + parameterMap.get(parameterNames[i]).getClass());
+ }
+ }
+ Object[] functionArgs = { method, parameterNames, parameterValues };
Context context = Context.enter();
try {
- Object result = context.evaluateString(thingScope, script, "<cmd>", 1, null);
- if (result == null || result instanceof Undefined) {
+ Object callResult = callJavascriptFunction(context, thingScope, ONSERVICE_FUNCTION_NAME, functionArgs);
+ if (callResult == null || callResult instanceof Undefined) {
ScriptHTTPResponse response = new ScriptHTTPResponse();
response.jsConstructor(200, "", "text/plain");
return response;
- } else if (result instanceof ScriptHTTPResponse) {
- return (ScriptHTTPResponse) result;
+ } else if (callResult instanceof ScriptHTTPResponse) {
+ return (ScriptHTTPResponse) callResult;
} else {
- spaceSimulator.log("Javascript Error: onService returned " + result);
+ spaceSimulator.log("Javascript Error: onService returned " + callResult);
ScriptHTTPResponse response = new ScriptHTTPResponse();
- response.jsConstructor(500, "Javascript Error: onService returned " + result, null);
+ response.jsConstructor(500, "Javascript Error: onService returned " + callResult, null);
return response;
}
} catch (EcmaError error) {
Modified: maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/site/SimServlet.java
===================================================================
--- maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/site/SimServlet.java 2007-08-31 19:41:31 UTC (rev 310)
+++ maven/trunk/ogoglio-server/src/main/java/com/ogoglio/sim/site/SimServlet.java 2007-08-31 19:43:28 UTC (rev 311)
@@ -786,6 +786,10 @@
}
Map parameterMap = request.getParameterMap();
+ //Tomcat is f'ing broken in that if there are no parameters it actually adds one with the key String "null" and a "" value: TFS
+ if(parameterMap.size() == 1 && (parameterMap.containsKey("null"))){
+ parameterMap = new HashMap();
+ }
ScriptHTTPResponse scriptResponse = simulator.callThingHTTPService(thingID, request.getMethod(), parameterMap);
response.setStatus(scriptResponse.getStatus());
if(scriptResponse.getStatus() != 200){
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|