Hi all. In this blog we will see how we can do polyglot implementations with GraalVm. The pre-requisites for this are:
- Understanding of GraalVm.
- Knowledge of features GraalVm provides.
- Local Setup of GraalVm.
- A bit of java and python(Optional).
So let’s start.
What’s GraalVm?
GraalVm is an Open Source project from Oracle which aims to target the JVM so that many langauges can co-exist in the same application. GraalVm is not JVM, instead it’s a UVM (Universal Virtual Machine). It supports all languages that have a compiler which generates bytecode; this means that anybody working with Java, Scala, etc., can already use GraalVM. Thus all these langauges share the same VM and its capabilities. At the time of writing this post, GraalVm supports both Java 8 and 11 version.
Another key feature of GraalVm is the ability to create native images of our JVM based applications. This makes distributing and installing Java programs easier, eliminating the need for an additional JRE installation.
Furthermore, a native binary has the advantage of starting faster. Why? Because, it has already undergone translation to machine code form before execution (AOT compilation) and does not need a translation at runtime by the JIT compiler. Thus, the performance behavior of native binaries is constant and predictable. Why? Because the code no longer needs optimization at runtime by the JIT compiler in interaction with the profiler. Native binaries also need much less memory because the JVM infrastructure for the JIT compiler does not need to run.
We will talk about the advantages of native images in the next post. In this post let’s see how to run a polyglot service.
Setup GraalVm
Oracle provides two versions of the GraalVm to use, first the community eddition & second the enterprise edition. We’ll be using the community edition in this post. Setting up GraalVm on your local is quite easy. Install it from the official GraalVm site: https://www.graalvm.org/getting-started/#install-graalvm. Download the zip file supported for your OS. Next, do the following:
- Set the JAVA_HOME in your .bashrc to point to the location where you have extracted the graalvm zip file.
- Set the PATH variable to point to $JAVA_HOME/bin
We can then run the application using graalVm support, after setting these variables. Setting GraalVM to the PATH
and JAVA_HOME
environment variables during the installation should be enough to run an application with GraalVM.
Polyglot with GraalVm
As per the official documentation and polyglot reference from GraalVm.org, it states:
"GraalVM allows you to write polyglot applications with a seamless way to pass values from one language to another. With GraalVM there is no copying or marshalling necessary as it is with other polyglot systems. This lets you achieve high performance when language boundaries are crossed. Most of the time there is no additional cost for crossing a language boundary at all."
As a result, with GraalVm’s inherent support for polyglot functionality, developers can focus on the logic itself rather being concerned with what language they should write it in. Let’s see how we can achieve this. Assume that, we are working on a normal java application and suddenly there is a need for some random code in javascrip. To keep things simple lets just try to see how to get an element of the js array in java program. So, here’s an example showing how we can do that…
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; public class PolyglotJavaAndJs { public static void main(String[] args) { Context context = Context.newBuilder().allowIO(true).build(); Value array = context.eval("js", "[1,2,42,4]"); int result = array.getArrayElement(2).asInt(); System.out.println(result); } }
I have created a class PolyglotJavaAndJs, in it we have used the polyglot.context and polyglot.vaue components from GraalVm Sdk. With the help of context we can pass in a different language of choice and see it working within our Java application. To run and evaluate the above java application use the following:
Open the terminal and go to the src folder of the application and execute the following javac PolyglotJavaAndJs.java java PolyglotJavaAndJs
You will see that the output on the screen is 42.
Let’s move ahead and see how we can continue with Java and Python using Polyglot approach. Let’s try passing values from Java program to Python code to make things interesting.
Polyglot Java to Python
Let suppose we want to calculate the square of a number. However, we want to run the logic for squaring in Python but we want to capture the number from Java app.
So, here’s the scenario where you will pass variables between two different languages in the same application. Let’s go through the below code snippet:
import org.graalvm.polyglot.*; import java.util.Scanner; public class PolyglotJavaAndPython { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("Enter the number to calculate its square..."); int input = scanner.nextInt(); Context context = Context.newBuilder().allowIO(true).build(); context.eval("python", "print('Hello From Python')"); // Use the getBindings and putMember methods on the context for passing the value from Java to Python app. context.getBindings("python").putMember("number", input); Value result = context.eval("python", "number * number"); System.out.println(result); } }
And, here’s the output of the above program:
Execute the statements: $ javac PolyglotJavaAndPython.java $ java PolyglotJavaAndPython Enter the number to calculate its square… 10 Hello From Python 100
Takeaways
Simple right? We created a small convenience java class. In it we are just asking for an input from the user and are responding back with the square of the input. Though, it looks simple there are a few important things to note here:
- For passing the dynamic input from Java class to your Python app we need a polyglot.Context instance, that has IO ops enabled.
- We will need to capture the bindings for the target language.
- The java app then updates this binding providing a dynamic input from the user. For that we use the putMember() method on the bindings.
- Once the member(s) addition is successful we can access them from the target language code block. We do so by directly using their property name. As a result, of using the putMember() these variables become environment variables for the target language. Consequently, they are now available within the context.
That is all for this blog, I hope you understood the concept of polyglot with GraalVm. The complete code is available here: https://github.com/knoldus/GraalVmPolyglot. For other instructions like how to setup graalVm locally, and how to run this application please refer to the readme.md file in the repo. However, in case you find yourself stuck, feel free to comment here. I will be more than happy to help and resolve your concerns. Until next time, keep learning. 🙂
References:
- https://www.graalvm.org/docs/reference-manual/polyglot/#polyglot-options
- https://medium.com/@jponge/the-graalvm-frenzy-f54257f5932c
- https://rieckpil.de/whatis-graalvm/
- https://hackernoon.com/why-the-java-community-should-embrace-graalvm-abd3ea9121b5
