I'm trying to get require.js to load modules on the server-side with Java 6 and Rhino.
I'm able to load require.js itself just fine. Rhino can see the require()
function. I can tell because Rhino complains that it can't find the function when I change require()
to something else like requireffdkj()
.
But when I try to require even a simple JS, like hello.js
var hello = 'hello';
using either of the following:
require('hello');
require('./hello');
it doesn't work. I get
Caused by: javax.script.ScriptException: sun.org.mozilla.javascript.internal.JavaScriptException: [object Error] (<Unknown source>#31) in <Unknown source> at line number 31
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:167)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)
I have my hello.js
at the top of the Java classpath. That's where I have require.js
as well. I tried moving hello.js
everywhere I could think it might possibly go, including the root of my hard drive, the root of my user directory, the directory from which I'm running my Java app, etc. Nothing works.
I looked at the CommonJS spec (http://wiki.commonjs.org/wiki/Modules/1.0) and it says that top-level IDs (like hello
) are resolved from the "conceptual module name space root", whereas relative IDs (like ./hello
) are resolved against the calling module. I'm not sure where either of those baselines is, and I suspect that's the issue.
Any suggestions? Can I even use require.js from Rhino?
EDIT: Thinking that I need to set the environment up as per Pointy's suggestion in the comment below, I tried evaluating r.js
as well. (I tried evaluating after evaluating require.js
, and then again before require.js
.) In either case I get an error:
Caused by: javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "arguments" is not defined. (<Unknown source>#19) in <Unknown source> at line number 19
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:167)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)
"arguments" appears to be a variable in r.js
. I think it's for command line arguments, so I don't think r.js
is the right path for what I'm trying to do. Not sure though.
require.js works well with rhino. Recently, I used it in a project.
- You have to make sure to use r.js (not require.js) , modified version of require.js for rhino.
- You have to extend
ScritableObject
class to implement load
and print
function. When you call require(["a"])
, the load function in this class will be called, you can tweak this function to load the js file from any location. In the below example, I load from classpath
.
- You have to define the property
arguments
in the sharedscope as shown below in the sample code
- Optionally, you can configure the sub path using
require.config
, to specify the subdirectory inside classpath where js files are located.
JsRuntimeSupport
public class JsRuntimeSupport extends ScriptableObject {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(JsRuntimeSupport.class);
private static final boolean silent = false;
@Override
public String getClassName() {
return "test";
}
public static void print(Context cx, Scriptable thisObj, Object[] args,
Function funObj) {
if (silent)
return;
for (int i = 0; i < args.length; i++)
logger.info(Context.toString(args[i]));
}
public static void load(Context cx, Scriptable thisObj, Object[] args,
Function funObj) throws FileNotFoundException, IOException {
JsRuntimeSupport shell = (JsRuntimeSupport) getTopLevelScope(thisObj);
for (int i = 0; i < args.length; i++) {
logger.info("Loading file " + Context.toString(args[i]));
shell.processSource(cx, Context.toString(args[i]));
}
}
private void processSource(Context cx, String filename)
throws FileNotFoundException, IOException {
cx.evaluateReader(this, new InputStreamReader(getInputStream(filename)), filename, 1, null);
}
private InputStream getInputStream(String file) throws IOException {
return new ClassPathResource(file).getInputStream();
}
}
Sample Code
public class RJsDemo {
@Test
public void simpleRhinoTest() throws FileNotFoundException, IOException {
Context cx = Context.enter();
final JsRuntimeSupport browserSupport = new JsRuntimeSupport();
final ScriptableObject sharedScope = cx.initStandardObjects(browserSupport, true);
String[] names = { "print", "load" };
sharedScope.defineFunctionProperties(names, sharedScope.getClass(), ScriptableObject.DONTENUM);
Scriptable argsObj = cx.newArray(sharedScope, new Object[] {});
sharedScope.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);
cx.evaluateReader(sharedScope, new FileReader("./r.js"), "require", 1, null);
cx.evaluateReader(sharedScope, new FileReader("./loader.js"), "loader", 1, null);
Context.exit();
}
}
loader.js
require.config({
baseUrl: "js/app"
});
require (["a", "b"], function(a, b) {
print('modules loaded');
});
js/app
directory should be in your classpath.