Skip to content

Cue/hegemon

Repository files navigation

Hegemon

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.

hegemon-core

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>

hegemon-stdlib

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>

hegemon-testing

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>

hegemon-testserver

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>

hegemon-annotations

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.

hegemon-guice

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>