Running Node.js on the JVM

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.

<pre lang="Java">
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 http://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();
}
</pre>

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.

<pre lang="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();
}
</pre>

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).

<pre lang="xml">
<dependency>
  <groupId>com.eclipsesource.j2v8</groupId>
  <artifactId>j2v8_win32_x86_64</artifactId>
  <version>4.4.0</version>
</dependency>
</pre>

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

6 Comments
  • Wolfgang Geck
    Reply
    Posted at 1:02 pm, July 20, 2016

    I’m trying to write a Java based desktop application that uses Electron (atom.io) as UI toolkit. Do you think it’s possible to combine your node.js implementation (I think you bundle a native fork with your plugins?) together with electron? So as far as I understand the concept electron itself is a Node module.
    Writing the entire application in Javascript is no option and a combination of HTML and Java was really great!

  • Ned Twigg
    Reply
    Posted at 5:09 am, August 1, 2016

    I am also extremely interested in this topic. The JavaCEF project has embedded Chrome into SWT https://github.com/wjywbs/javacef, but needs a commercial maintainer. J2V8 is doing a fantastic job with Java / Javascript bridging. It would be great if there was a UI solution here too…

  • Kristopher Haflett
    Reply
    Posted at 6:48 pm, August 25, 2016

    This is great tip, I only use Netbeans because it s integrated quite well with Tomcat. By the way does this setup will also work for eclipse remote debugging how do i get standard and error output into eclipse s console? i tried to use catalina.bat jpda run but this does not help.

  • William
    Reply
    Posted at 12:48 am, September 17, 2016

    I am trying to run a script which uses throng module internally. I get into a infinite loop, giving ‘Error: Could not find or load main class …startup6366016822819869382.js.tmp’

    When i remove the throng module dependency from the start script it works.

    I use j2V8. 4.5.0 version.

    How do i run a script which uses clusterAPIs like throng in j2V8? Any help in this regard would be great.

  • Wolfie Wolf
    Reply
    Posted at 1:39 am, February 14, 2017

    It looks like the API has changed a bit since you posted this. Here’s a re-factored example that works using the latest maven release.

    package com.xyz.j2v8test;

    import com.eclipsesource.v8.JavaCallback;
    import com.eclipsesource.v8.NodeJS;
    import com.eclipsesource.v8.V8Array;
    import com.eclipsesource.v8.V8Function;
    import com.eclipsesource.v8.V8Object;
    import java.io.File;

    public class MainApp {

    public static void main(String[] args) {
    final NodeJS nodeJS = NodeJS.createNodeJS();
    final V8Object jimp = nodeJS.require(new File(“node_modules/jimp”));

    V8Function callback = new V8Function(nodeJS.getRuntime(), new JavaCallback() {
    @Override
    public Object invoke(V8Object receiver, V8Array parameters) {
    final V8Object image = parameters.getObject(1);
    image.executeJSFunction(“posterize”, 7);
    image.executeJSFunction(“greyscale”);
    image.executeJSFunction(“write”, “/Users/supermac/NetBeansProjects/j2v8Test/skynet.jpg”);
    image.release();
    return null;
    }
    });
    jimp.executeJSFunction(“read”, “/Users/supermac/Desktop/skynet.jpg”, callback);
    while(nodeJS.isRunning()) {
    nodeJS.handleMessage();
    }
    //callback.release();
    //jimp.release();
    //nodeJS.release();
    }
    }

Post a Comment

Comment
Name
Email
Website