Running a Polyglot Application with Payara Micro on GraalVM

Photo of Rudy De Busscher by Rudy De Busscher

 

The Java Virtual Machine comes in different flavors. On one hand, you have the OpenJDK based JVMs. Many vendors, including Oracle, RedHat, Amazon, and Azul to name just a few, create builds from the OpenJDK source to bring the JVM to your development machine or server.

Other JVMs do not have OpenJDK as a base. The most known ones are the Eclipse OpenJ9 and GraalVM.

What is GraalVM?

Besides a JVM that runs your Java application, GraalVM has two specific features, the polyglot capabilities and the native compilation possibilities.

In the standard usage of a JVM, the source code is compiled to Java ByteCode which is ‘interpreted’ by the JVM at runtime. This makes it possible to use the same byte code on different operating systems. The JVM can convert some of this byte code into native during runtime when it sees clear benefits from it.

Native compilation goes a step further than that. Here the compiler generates native byte code for the operating system. The application can start up much faster due to the preparation done at compile time. Also, the memory consumption is lower since the binary is optimised for your application and it doesn’t need the general-purpose JVM. But it has also some drawbacks. For example, some well-known features from Java, such as reflection (usually requires manual configuration) and dynamic interceptions, won't work anymore, and performance is a bit lower in general.

GraalVM Offers Interoperability with Other Languages

Another feature which is also very useful is the polyglot support within GraalVM. With polyglot support, it is not only possible to run JVM-based languages, like Scala, Groovy, and Kotlin, which is possible on all JVMs, but you can also have interoperability with other languages like Python, R, and JavaScript.

There is no interfacing or translation of some kind of context, all the languages share the same memory space. Each of the languages can access the variables directly and thus values can be used directly by the different languages.

A Polyglot Application

So what does it mean to develop a polyglot application? There are different variations possible. Your application can be mainly a Java program but you call some JavaScript functions because the functionality you require is already available in a JavaScript library. Or you can develop a JavaScript program and make calls to other languages like Java or R.

To create a polyglot application with Java, you need to add the graal-sdk dependency to your applications. It contains all constructs required to combine the different languages. This also means of course that your application can be run only on the GraalVM as other runtimes like OpenJDK doesn’t have the required classes on board.

val chromaResource = Thread.currentThread().contextClassLoader.getResource("chroma.min.js")
val content = String(Files.readAllBytes(Paths.get(chromaResource!!.toURI())))
val context = Context.create()
context.eval("js", content)
chroma = context.eval("js", "chroma")

The above snippet reads the file containing the Chroma ‘library’ written in JavaScript (https://github.com/gka/chroma.js/), evaluates the file within a Graal Context, capable of handling JavaScipt language by default, and then we execute the JavaScript function to return a reference to the Chroma Object which we keep for further reference.

Oh, and if you think that the above snippet is not Java, you are correct. It is written in Kotlin. (Remember the goal of this blog was to show you how you can combine different languages.)

Later on, we can use the reference to the Chroma value to execute some functions on it, like this statement:

chroma.getMember("darken").execute()

The syntax is not as direct as in Java, you cannot just reference the name of the method. Instead, you have to request a reference to the method by specifying the name as a String parameter and then execute it.

But when you hide this interaction with JavaScript behind some interface, you can easily call JavaScript in a Java-like manner in your application.

The Polyglot Example

The source code of the example can be found in our Github examples demo.

The example is mainly in Kotlin and defines a JAX-RS endpoint. This endpoint calls the Chroma JavaScript library to perform his functionality.

Within the pom file, we have to include the Graal SDK as explained earlier in the article and the kotlin dependency to work with Java 8.

<dependency>
<groupId>org.graalvm</groupId>
<artifactId>graal-sdk</artifactId>
<version>0.30</version>
</dependency>

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>

The maven project file also contains the kotlin maven plugin with his configuration to be able to have a entire project within Kotlin.

The JAX-RS resources within Kotlin is also quite readable for those who are familiar with the Java Version. As an example, I share here the hello world type version. The actual resource is just using that `chroma` variable referring to the JavaScript we discussed earlier.

@Path("/hello")
open class HelloResource {

@GET
@Path("/{name}")
open fun read(@PathParam("name") name: String): String {
return "Hello $name"
}
}

Besides the different syntax for parameters and return types, we should not forget to define the class and methods as open. Since all Kotlin artefacts are final by default, we explicitly need to indicate that they can be extended so that proxies can be created within the Java EE framework.

The JavaScript specific code is already described in the previous section and can be seen in the demo application on GitHub.

After you build the project (mvn clean package), you start Payara Micro the same as always.

java -jar payara-micro-5.193.jar --noCluster target/graalvm-example.war

And testing can be done using the following URL which gives you a darker version of the colour

http://localhost:8080/graalvm/chroma/darken/D4F880

It's also interesting when there are exceptions occurring within the JavaScript code, as with the following URL:

http://localhost:8080/graalvm-example/chroma/darken/xyz

You clearly see the stacktrace joining into the JavaScript code:

org.graalvm.polyglot.PolyglotException: unknown color: xyz
unknown color: xyz
at <js> D(Unnamed:32:16430-16453)
at <js> k.hex(Unnamed:32:16873-16876)
at <js> a(Unnamed:32:3339-3353)
at <js> :anonymous(Unnamed:32:2659-2670)
at <js> t(Unnamed:32:2605-2722)
at org.graalvm.polyglot.Value.newInstance(Value.java:416)

Combine Code from Other Languages with Your Java Code

One of the major benefits of the GraalVM is this Polyglot feature. Using this, you can combine code written in other languages like JavaScript, Python and R, which normally do not run on the JVM, in combination with your Java code. The integration is seamless so that the program code shares the same memory space and thus variables can be accessed from different languages.

This allows you to write polyglot applications which can run on Payara Micro and Payara Platform in general. It allows you to choose the most optimal library providing you the required functionality you have for your application, and it doesn’t need to be a JVM language.

 Payara Platform  Download Here 

 

 

 

Comments