Awesome GraalVM: Create a Java API on top of a JavaScript library
The Graal Polyglot API lets you embed and run code from guest languages in JVM-based host applications. In this article, we will cover how we create a Java API on top of a JavaScript library.
With more than 700 000 packages npmjs is the world’s largest repository of code! You can find literally everything you need (or not).
For our example we will use the excellent chroma.js library.
chroma.js is a tiny JavaScript library (but really useful) for dealing with colors!
Here’s an example for a simple read / manipulate / output chain:
chroma('pink').darken().saturate(2).hex() // "#ff6d93"
Pretty cool right ?
Now let’s see how we can use this library in a Java application that runs on GraalVM.
-
First download and copy chroma.min.js to your project resources.
-
Add a dependency on the GraalVM SDK (that contains the Polyglot API):
maven<dependency> <groupId>org.graalvm</groupId> <artifactId>graal-sdk</artifactId> <version>1.0.0-rc7</version> </dependency>gradlecompile 'org.graalvm:graal-sdk:1.0.0-rc7'If you are using another package manager, follow the instructions on Maven Central.
-
Use the GraalVM Polyglot API to evaluate the chroma library.
Now that we can use theorg.graalvm.polyglotAPI in our code, we are going to create a singleton class namedGraalVMChromain theorg.chromapackage:package org.chroma; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; public class GraalVMChroma { private final Value chroma; private static GraalVMChroma instance = null; private GraalVMChroma() { Context context = Context.create(); URL chromaResource = Thread.currentThread().getContextClassLoader().getResource("chroma.min.js"); String content = new String(Files.readAllBytes(Paths.get(chromaResource.toURI()))); context.eval("js", content); chroma = context.eval("js", "chroma"); } public static GraalVMChroma create() { if (instance == null) { instance = new GraalVMChroma(); } return instance; } public Value getChroma() { return chroma; } }1 Create a GraalVM Context2 Read the file chroma.min.js into a String3 Evaluate the library code 4 Get the chromaobject as a GraalVMValueIn the code above, the file chroma.min.js will be read from the classpath and turned into a
String. Then we evaluate the chroma library code, using theevalmethod on thecontext. And finally, we useeval("js", "chroma")to get thechromaobject.The GraalVM
Valuerepresents a polyglot value that can be accessed using a set of language agnostic operations. Polyglot values have one of the following types:-
Null -
Number -
Boolean -
String -
Host Object -
Proxy Object -
Native Pointer
In addition any value may have one or more of the following traits:
-
Array Elements -
Members -
Executable -
Instantiable
If you want to learn more about GraalVM
Value, please read the official documentation.In our case, the
chromaobject is an Instantiable as we can instantiate a new chroma object withchroma('hotpink'). -
-
Build a Java API.
We create aChromainterface in theorg.chromapackage:package org.chroma; import org.graalvm.polyglot.Value; import java.io.IOException; import java.net.URISyntaxException; import java.util.List; public interface Chroma { Chroma darken(); Chroma saturate(int value); String hex(); List<Integer> rgba(); static Chroma create(String color) { try { Value chroma = GraalVMChroma.create().getChroma(); Value instance = chroma.newInstance(color); return new ChromaInstance(instance); } catch (IOException | URISyntaxException e) { throw new RuntimeException("Unable to instantiate Chroma GraalVM"); } } }1 Get the chromaobject on the singleton2 Instantiate the chromaobject with a color3 Instantiate a Java class named ChromaInstancewith the chroma instance -
Implement the API.
TheChromaInstanceimplements theChromainterface:package org.chroma; import org.graalvm.polyglot.Value; import java.util.ArrayList; import java.util.List; public class ChromaInstance implements Chroma { private Value chromaInstance; public ChromaInstance(Value chromaInstance) { this.chromaInstance = chromaInstance; } @Override public Chroma darken() { return new ChromaInstance(chromaInstance.getMember("darken").execute()); } @Override public Chroma saturate(int value) { return new ChromaInstance(chromaInstance.getMember("saturate").execute(value)); } @Override public String hex() { return chromaInstance.getMember("hex").execute().asString(); } @Override public List<Integer> rgba() { List<Integer> result = new ArrayList<>(); Value value = chromaInstance.getMember("rgba").execute(); if (value.hasArrayElements()) { for (int i = 0; i < value.getArraySize(); i++) { result.add(value.getArrayElement(i).asInt()); } } return result; } }1 Save the reference to the chroma instance 2 Get the member called darkenon the chroma instance and execute the function3 Get the member called saturateon the chroma instance and execute the function with a value4 Get the member called hexon the chroma instance, execute the function and return aString5 Get the member called rgbaon the chroma instance, execute the function and build a JavaListfrom the Array ElementsValue
And now we can use the chroma library in our Java application as follow:
Chroma.create("pink").darken().saturate(2).hex() // "#ff6d93"
Chroma.create("orange").rgba() // List(255, 165, 0, 1)
Written with GraalVM 1.0.0-rc7 and Java 1.8.
The full source code is available on GitHub: github.com/yuzutech/chroma-graalvm