Leverage NPM JavaScript Module from Java application using GraalVM image 128

Leverage NPM JavaScript Module from Java application using GraalVM

imageInteroperability from Java to JavaScript has been an objective for the Java community for quite a while. With GraalVM, there is great way to run JavaScript code from within Java applications. The interaction itself is faster, more robust and more ‘native’ (rather than bolt-on) than earlier mechanisms. For developers, the interaction is easy to implement. And this opens up great opportunities for leveraging from Java many of the great community resources in the JavaScript community – for example many of the modules available from NPM.

This article shows how the NPM Validator Module – which implements dozens of very useful data validation algorithms – can be hooked into a Java application. With little effort, the Java developer tasked with implementing and endlessly testing several advanced validations is able to make use of what his JavaScript brothers and sisters have produced and shared. Of course the Validator module is just an example – thousands of NPM modules can be woven into Java applications through the polyglot capabilities of GraalVM.

Note: what we can do from Java to JavaScript can also be done to any other language that the GraalVM runtime can handle – including R, Ruby, Python and LLVM (C, C++, Rust, Swift and others). So our Java application can benefit from more than just the JavaScript community. And vice versa: any language that can run on GraalVM can call out to any other language. So the mutual benefit is not restricted to Java making use of other language resources – it works in all directions.

By picking GraalVM 19.2.1 (based on JDK 8) as the runtime environment you enable the interoperability from Java to any of the languages GraalVM can run. No additional setup is required to interact with JavaScript. On November 19th 2019 we will see the release of GraalVM 19.3 with support for Java 11.

In an earlier article, I have given an introduction to the interoperability from Java to JavaScript. I will now build on that article as my foundation, so I will assume the reader knows about GraalVM polyglot, how to evaluate JavaScript code snippets in Java, how to load JavaScript sources from separate files and invoke functions defined in them and how to exchange data and objects back and forth between Java and JavaScript. With that knowledge in place, what we are about to do in this article is a piece of cake or a cup of peanuts.

Sources for this article are in GitHub: https://github.com/AMIS-Services/jfall2019-graalvm/tree/master/polyglot/java2js

The Challenge

I am developing a Java application. I need to perform validations on input data: Postal Code (various countries), Mobile Phone Numbers (many countries), Email Address, Credit Card Number etc.

In simplified pseudo code:

imageimage

I need to implement (or get my hands on) the postalCode Validator – for starters.

The NPM Module Validator offers most of these OOTB (Out of the Box)

image

image

But… it is written in JavaScriptimage

How could that possibly help me?

GraalVM to the rescue.

The Solution

Spoiler alert: here comes the end result. This is the final code, after integrating NPM Module Validator into my Java application:

image

The major changes are: I retrieve an implementation for the postalCodeValidator from somewhere and I can invoke it. I have not written any code to do the validation of postal codes in 27 different countries. And there is this new package called org.graalvm.polyglot that I import and from which I use classes Context and Value. And finally, there is a resource called validator_bundled.js loaded from file. That resource happens to be the Web Packed bundle created create from all JavaScript resources in NPM module Validator. It is that simple.

Running this code gives me:

image

Implementation Steps

The most important thing I had to figure out was: how to make GraalJS – the JavaScript implementation on GraalVM – work with the module structure in the NPM Validator module. GraalJS does not support require() or CommonJS. In order to make it work with NPM modules – they have to be turned into ‘flat’ JavaScript resources – self-contained JavaScript source file. This can be done using one of the many popular open-source bundling tools such as Parcel, Browserify and Webpack. Note: ECMAScript modules can be loaded in a Context simply by evaluating the module sources. Currently, GraalVM JavaScript loads ECMAScript modules based on their file extension. Therefore, any ECMAScript module must have file name extension .mjs.

The steps to turn an NPM module into a self contained bundle dat GraalVM can process are these:

  • check GraalVM compatibility of NPM module
  • install npx (executable runner – complement to npm which is not included with GraalVM platform)
  • install webpack and webpack-cli
  • install validator module with npm
  • produce self contained bundle for validator module with webpack

When this is done, loading and using validator in Java is the same as with any other JavaScript source – as we will see.

1. Check GraalVM compatibility of NPM module with the GraalVM compatibility check:

image

2. install npx – executable runner – complement to npm which is not included with GraalVM platform

image

3. install webpack and webpack-cli

image

4. install validator module with npm

image

5. produce self contained bundle for validator module with webpack

image

image

#install npx
npm install -g npx 

#install webpack
npm install webpack webpack-cli

#install validator module
npm install validator

#create single bundle for valudator module
/usr/lib/jvm/graalvm-ce-19.2.1/jre/languages/js/bin/npx  webpack-cli --entry=./node_modules/validator/index.js --output=./validator_bundled.js --output-library-target=this --mode=development

#Argument: output-library-target, Choices are : "var", "assign", "this", "window", "self", "global", "commonjs", "commonjs2", "commonjs-module", "amd", "umd", "umd2", "jsonp"

Call Validator Module from Java application

With the Validator module turned into a single self-contained file without non-supported module constructs, we can load this resource into a GraalVM Polyglot context in our Java application running on the GraalVM runtime engine, and invoke any top level function in that context. In order to validate postal codes in Java – here is a very simple code snippet that does just that. Note: the validator_bundled.js is located in the root of our classpath.

image

package nl.amis.java2js;

import java.io.File;
import java.io.IOException;
import org.graalvm.polyglot.*;

public class ValidateThroughNPMValidator {

	private Context c;

	public ValidateThroughNPMValidator() {
		// create Polyglot Context for JavaScript and load NPM module validator (bundled as self contained resource)
		c = Context.create("js");
		try {
			// load output from WebPack for Validator Module - a single bundled JS file
			File validatorBundleJS = new File(
					getClass().getClassLoader().getResource("validator_bundled.js").getFile());
			c.eval(Source.newBuilder("js", validatorBundleJS).build());
			System.out.println("All functions available from Java (as loaded into Bindings) "
					+ c.getBindings("js").getMemberKeys());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Boolean isPostalCode(String postalCodeToValidate, String country) {
		// use validation function isPostalCode(str, locale) from NPM Validator Module to validate postal code
		Value postalCodeValidator = c.getBindings("js").getMember("isPostalCode");
		Boolean postalCodeValidationResult = postalCodeValidator.execute(postalCodeToValidate, country).asBoolean();
		return postalCodeValidationResult;
	}

	public static void main(String[] args) {
		ValidateThroughNPMValidator v = new ValidateThroughNPMValidator();
		System.out.println("Postal Code Validation Result " + v.isPostalCode("3214 TT", "NL"));
		System.out.println("Postal Code Validation Result " + v.isPostalCode("XX 27165", "NL"));
	}

}

The resulting output:

image

Resources

GitHub Repository with sources for this article: https://github.com/AMIS-Services/jfall2019-graalvm/tree/master/polyglot/java2js

NPM Module Validator

GitHub for GraalJS – https://github.com/graalvm/graaljs

Bringing Modern Programming Languages to the Oracle Database with GraalVM

Presentation at HolyJS 2019 (St Petersburg, Russia): Node.js: Just as fast, higher, stronger with GraalVM

Docs on GraalJS and Interoperability with Java – https://github.com/graalvm/graaljs/blob/master/docs/user/NodeJSVSJavaScriptContext.md

Why the Java community should embrace GraalVM – https://hackernoon.com/why-the-java-community-should-embrace-graalvm-abd3ea9121b5

Multi-threaded Java ←→JavaScript language interoperability in GraalVM  https://medium.com/graalvm/multi-threaded-java-javascript-language-interoperability-in-graalvm-2f19c1f9c37b

#WHATIS?: GraalVM – RieckPIL – https://rieckpil.de/whatis-graalvm/

GraalVM: the holy graal of polyglot JVM? – https://www.transposit.com/blog/2019.01.02-graalvm-holy/

JavaDocs for GraalVM Polyglot – https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/package-summary.html

GraalVM Docs – Polyglot – https://www.graalvm.org/docs/reference-manual/polyglot/

Mixing NodeJS and OpenJDK – Language interop and vertical architecture -Mike Hearn – https://blog.plan99.net/vertical-architecture-734495f129c4

Enhance your Java Spring application with R data science Oleg Šelajev – https://medium.com/graalvm/enhance-your-java-spring-application-with-r-data-science-b669a8c28bea

Awesome GraalVM: Create a Java API on top of a JavaScript library

GraalVM Archives on Medium – https://medium.com/graalvm/archive

GraalVM GitHub Repo – https://github.com/oracle/graal

GraalVM Project WebSite – https://www.graalvm.org/

2 Comments

  1. kshitij khanal June 9, 2022
    • Lucas Jellema July 22, 2022