hegemon is a set of tools and libraries for running the latest Rhino JavaScript implementation on the JVM.
hegemon currently embeds Rhino 1.7R4, supporting JavaScript 1.8.
For examples in the context of a java project, see the example application.
The essentials of hegemon allow you to run JavaScript functions from Java without boilerplate.
new Script("myscript", "function foo() { return 3 + 4; }").run("foo"); // returns 7
hegemon also introduces python/go inspired module loading to JavaScript. This can be done via a varargs constructor argument:
// The 'util' module is loaded and a variable named 'util' (default naming based on filename) is injected into scope.
new Script("myscript", "function foo() { return util.definedInUtil(); }", LoadPath.defaultPath(), "util");
And also from inside your JavaScript itself with the 'hegemon/core' provided core.load
function:
new Script("let util = core.load('util'); function foo() { return util.definedInUtil(); }", LoadPath.defaultPath());
For more on module loading, see Script.java.
Reloading files can be expensive, so hegemon-core ships with a ScriptCache
.
ScriptCache cache = new ScriptCache(LoadPath.defaultPath());
Script script = cache.get("myScript");
script.run("foo");
// with optional reloading: cache.get("myScript", true);
The default load path loads files with a .js extension from the
resources/javascript
directory. Custom load schemes can be implemented
with the ScriptLocator
interface.
These simple examples show JavaScript evalution out of context - usage at Cue has largely been via HTTP. For example, the sample
appliction includes a jersey resource that maps URLs to pre-packaged javascript files.
This allows requests to /script/example
to run code in resources/javascript/script/example.js
.
Another resource evaluates code that's POST'd to it. This pattern is used primarily to provide a debugging entrypoint.
You can build an interactive console on top of it or bundle code in development and run in against production data
without changing what consumers of the service experience - assuming your eval'd code doesn't do anything malicious.
Take care to understand the implications of an eval endpoint if you follow this pattern.
Add with maven:
<dependencies>
<dependency>
<groupId>com.cueup.hegemon</groupId>
<artifactId>hegemon-core</artifactId>
<version>0.0.2</version>
</dependency>
</dependencies>
When you're writing a lot of JavaScript that interacts with Java, it's useful to have a set of standard libraries
that handles the language bridge gracefully. hegemon-stdlib
in 0.0.2 provides methods to handle Java and JS sequences
seamlessly through it's hegemon/sequence
module.
Add with maven:
<dependencies>
<dependency>
<groupId>com.cueup.hegemon</groupId>
<artifactId>hegemon-stdlib</artifactId>
<version>0.0.2</version>
</dependency>
</dependencies>
If you're going to have production code in JavaScript, you're going to
want to be able to write and run tests. hegemon-testing
provides the
bindings to JUnit so you can write JavaScript tests without the rest of
your project having to care.
Just drop a file in test/resources/javascript
and a binding Java class
like so:
@RunWith(HegemonRunner.class)
@HegemonRunner.TestScript(filename = "myJsTest") // Maps to test/resources/javascript/myJsTest.js
public class MyJsTest {
}
Now any functions prefixed with 'test' in 'myJsTest.js' will be run along with all other JUnit tests.
For example, the tests for this file are bound to a JUnit test case with with a class like this.
Add with maven:
<dependencies>
<dependency>
<groupId>com.cueup.hegemon</groupId>
<artifactId>hegemon-testing</artifactId>
<version>0.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
The disadvantage of connecting JavaScript unit tests to JUnit is that
you've got to recompile for every test cycle. The hegemon-testserver
project speeds up the feedback loop by removing the recompile step when
you're only changing JavaScript. Subclass HegemonTestServer
and tell
it where your source files are, and it'll run tests and reload code via
an http server. You can also run a single test independent from the rest
of the file.
See the example app's implementation for more.
Add with maven:
<dependencies>
<dependency>
<groupId>com.cueup.hegemon</groupId>
<artifactId>hegemon-testserver</artifactId>
<version>0.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
When any of your Java project can be called easily through JavaScript, sometimes JS is the only caller and your IDE misidentifies dead code.
The @ReferencedByJavascript
annotation is a simple way to advertise
that the code is called somewhere. Long term it might be nice to build
tools around this.
Conceivably there will be other annotations in the future, but for now
the hegemon-annotations
project is separated to simplify project
dependencies.
The hegemon-guice
project is for pre-annotated classes useful to
Google Guice users. For now, it's just InjectableScriptCache
- a
singleton that recieves it's LoadPath
via injection.
Add it to your project with maven:
<dependencies>
<dependency>
<groupId>com.cueup.hegemon</groupId>
<artifactId>hegemon-guice</artifactId>
<version>0.0.2</version>
</dependency>
</dependencies>