[]
WebAssembly, or Wasm, provides a generalized, lightweight native binary format for any language that can be compiled to it. Once the source language is transformed into WebAssembly, that compact binary can run in a variety of contexts, from cloud virtual machines and desktops to IoT and mobile devices. One of the most interesting environments Wasm supports is the web browser. In this article, we’ll look at how to compile your Java code into WebAssembly, then we’ll run a demo application in a web server and see it in action.
Why use WebAssembly for Java?
Wasm provides a way to execute Java on the front end in the web browser, where your Java API can be called from JavaScript.
WebAssembly compilers generate a binary from the Java source (or from the bytecode) and that binary is executed by the browser using the host operating system capabilities, so you get OS-level performance. The Java code gets turned into a Wasm binary, which exposes hooks you call from JavaScript.
Wasm is available on all the major modern browsers, so you can use it now. It offers superior performance for demanding tasks in the language of your choice. Video encoding, graphics, and other intensive data processing are good candidates for this technology. Some projects are using Wasm to address blockchain’s performance limitations.
If you’ve been around Java for a while, you might think about Java applets when you hear about Java in the browser. Rest assured, Wasm is a completely different animal. It is a modern, high-performance technology, designed from the ground up as a secure sandboxed runtime. With portability, performance, security, and polyglot support, Wasm is a vital piece of the technology landscape today.
Java to Wasm: How it works
Transforming a Java program into WebAssembly is a bit tricky. There is not a fully acceptable golden path that I can recommend. Several projects exist, but they are not mature or well-documented. Java as a language presents a few difficult problems for a WebAssembly transpiler to solve, like garbage collection and reflection. These seem to be the source of the delay in arriving at a first-class Java Wasm solution.
GraalVM currently is building support for outputting to Wasm with the native-image tool (as a target binary in the –features flag). If the proposal succeeds, that likely will become the recommended path. GraalVM already has the capacity to execute Wasm, and generally is an excellent project. Once GraalVM can generate Wasm binaries, it will be well worth exploring.
In the meantime, there are a handful of libraries for handling Java-to-Wasm compilation:
We’re going to use TeaVM for our Java Wasm compilation. I asked TeaVM’s creator, Alexey Andreev, why Java is lagging behind other languages in Wasm implementations. He told me, “In brief: it’s not hard to write your own GC or exception handling. It’s hard to implement your own GC in Wasm due to lack of access to stack.” WebAssembly has limited stack access for security reasons. Instead of looking at ways to allow stack access securely, as Andreev has proposed, Wasm is opting to build garbage collection into Wasm itself. (See the WebAssembly project page on GitHub to follow the progress of garbage collection in Wasm.)
Compiling Java to Wasm with TeaVM
TeaVM is meant to be a lightweight library for creating Wasm out of Java. It draws inspiration from Google Web Toolkit but builds off of the bytecode instead of the source. Because of this, it can handle other JVM languages, like Scala. TeaVM gives us a quick way to look at a Java Wasm project with one of its sample projects, which we will use for the demonstration.
Setting up TeaVM and the sample project
To use TeaVM you’ll need a JDK of Java 8 or higher installed. We’re going to build the latest TeaVM version and install it into our local repository, then build one of the sample projects that uses Wasm to see how it accomplishes the Java-to-Wasm compilation in the browser transformation.
You’ll need Git installed to clone the TeaVM project: Git clone https://github.com/konsoletyper/teavm.git. At the time of writing, TeaVM is on 0.8.0-SNAPSHOT.
Once the project is checked out, you need to build the TeaVM library itself and install it to your local repository. Go to the root directory, and type: /teavm/gradlew. That’ll use the standalone Gradle wrapper executable to build TeaVM and install it locally.
Once that’s done, you can go to the /teavm/samples/pi directory and type: ../../gradlew war. That tells Gradle to build the Pi sample project to a WAR file. Once that completes, there will be a /teavm/samples/pi/build/libs/pi.war file that we can deploy to a servlet container.
In my case, I’m running Ubuntu for this demo, so I installed Tomcat as a service (sudo apt-get install tomcat9) and then copied the pi.war file into the /webapps directory (sudo cp /teavm-wasm/target/pi.war /var/lib/tomcat9/webapps/). Next, I used sudo systemctl start tomcat9 to start Tomcat. Another way is with the start.sh/.bat script in the /bin directory, which works if you’ve downloaded the standalone version of Tomcat.
Note that the Pi sample demonstrates both the JavaScript and Wasm output capabilities of TeaVM. We’ll stick with Wasm.
The Pi demo for Wasm
The application should now be running on localhost:8080/pi. If you visit that URL in the browser, you’ll see a simple user interface with two links, one for JavaScript and one for WebAssembly. Click WebAssembly and you’ll see a view like the one shown below.
IDG
Figure 1. The WebAssembly Pi interface
This interface lets you specify the number of digits to calculate pi to. It uses Java that has been compiled to Wasm for the calculation, and it calls the code from JavaScript. Let’s see how this calculation is accomplished.
If you look at the /teavm/samples/pi project, it is a standard Maven/Gradle layout that includes both Java and web application sources. These are stored at src/main/java and src/main/webapp, respectively. Look at the single Java class in src/main/java/org/teavm/samples/pi/PiCalculator.java and you’ll see that it is a normal Java class called PiCalculator. The class defines a main method, which takes a single argument from args[] as the number of digits to calculate. It uses an inner class named PiDigitSpigot to do the actual crunching of pi, and relies on a single Java library, java.math.BigInteger. Overall, it’s a typical garden-variety Java program. In fact, if you go to /teavm/samples/pi/build/libs/classes/main and type java org/teavm/samples/pi/PiCalculator 50, it will happily compute pi to 50 digits for you on the command line.
Now let’s look at the web application side of things, in teavm/samples/pi/src/main/webapp. The wasm.html is the one we are interested in. Most of the file is basic HTML, but there’s some interesting JavaScript to consider, shown in Listing 1.
Listing 1. The web application for calculating pi