Running Node.js on the JVM

Gone are the days of single vendor lock-in, where one technology stack is used across an entire organization. Even small organizations and hobbyists will find themselves mixing technologies in a single project. For years, Java reigned king on the server. Today Node.js is everywhere.

Screen Shot 2016-07-19 at 10.45.53 AM

But even with the rise of Node.js and popularity of JavaScript, Java continues to shine. Furthermore, few organizations can afford to migrate their entire platform from the JVM to Node.js. This means organizations must either continue with their current technology stack or run multiple stacks with networked APIs to communicate.

Another option is to run Node.js and the JVM in a single process, and J2V8 finally makes this possible.

J2V8

J2V8-with-no-2-and-J

J2V8 is a set of V8 bindings for Java. J2V8 bundles V8 as a dynamic library and provides a Java API for the engine through the Java Native Interface (JNI). With J2V8 you can execute JavaScript using V8 in a similar fashion to how you would using Rhino or Nashorn.

J2V8 was originally developed to bring highly performant JavaScript to Tabris.js, a cross-platform mobile framework.

Over the past few months I’ve managed to build Node.js as a dynamic library and provide a Java API for it as well. Now you can execute Node scripts directly from Java. Unlike other approaches which try to implement Node.js using other JavaScript engines, this is true Node.js — bug for bug, feature for feature. Node.js runs in the same process as the JVM and all communication is done synchronously through JNI.

Combining Node and the JVM

J2V8 provides an API for executing Node.js scripts, registering Java callbacks, calling JavaScript functions, requiring NPM modules and running the Node.js message loop. The Node.js core modules have also been compiled in.

Running Node.js on the JVM provides an easy migration path for anyone with a large Java stack who wishes to start using Node.js. For example, you could run a Node.js server (such as Express.js) and call existing Java methods to handle certain requests.

static String NODE_SCRIPT = "var http = require('http');\n"
  + ""
  + "var server = http.createServer(function (request, response) {\n"
  + " response.writeHead(200, {'Content-Type': 'text/plain'});\n"
  + " response.end(someJavaMethod());\n"
  + "});\n"
  + ""
  + "server.listen(8000);\n"
  + "console.log('Server running at https://127.0.0.1:8000/');";

public static void main(String[] args) throws IOException {
  final NodeJS nodeJS = NodeJS.createNodeJS();
  JavaCallback callback = new JavaCallback() {
			
    public Object invoke(V8Object receiver, V8Array parameters) {
      return "Hello, JavaWorld!";
    }
  };
		
  nodeJS.getRuntime().registerJavaMethod(callback, "someJavaMethod");
  File nodeScript = createTemporaryScriptFile(NODE_SCRIPT, "example");
		
  nodeJS.exec(nodeScript);
		
  while(nodeJS.isRunning()) {
    nodeJS.handleMessage();
  }
  nodeJS.release();
}

NPM

In addition to calling existing Java methods from Node.js, J2V8 provides the ability to call JavaScript functions (and by extension, NPM modules) directly from Java. With this integration, Java users can now start using NPM modules directly on the JVM. For example, you could use the jimp image processing library from Java.

public static void main(String[] args) {
  final NodeJS nodeJS = NodeJS.createNodeJS();
  final V8Object jimp = nodeJS.require(new File("path_to_jimp_module"));
		
  V8Function callback = new V8Function(nodeJS.getRuntime(), new JavaCallback() {	
    public Object invoke(V8Object receiver, V8Array parameters) {
      final V8Object image = parameters.getObject(1);
      executeJSFunction(image, "posterize", 7);
      executeJSFunction(image, "greyscale");
      executeJSFunction(image, "write",  "path_to_output");
      image.release();
      return null;
    }
  });
  executeJSFunction(jimp, "read", "path_to_image", callback);
		
  while(nodeJS.isRunning()) {
    nodeJS.handleMessage();
  }		
  callback.release();
  jimp.release();
  nodeJS.release();
}

Getting J2V8

Node.js integration is now available in J2V8 (version 4.4.0). You can use it on Windows (32 and 64 bit), MacOS and Linux (64 bit). Use the following pom dependency to get it from Maven Central (this example is for Windows 64 bit — Change the OS / Arch for other platforms).

  com.eclipsesource.j2v8
  j2v8_win32_x86_64
  4.4.0

If you find this useful, please let me know. You can find me on Twitter @ irbull, or give the GitHub repo a star!