1. Overview
TeaVM is a powerful tool for translating Java bytecode into JavaScript, enabling Java applications to run directly in the browser. This allows us to maintain a Java-based codebase while targeting web environments.
In this tutorial, we’ll explore how to bridge the gap between Java and JavaScript using TeaVM’s ability to interact with the DOM and call Java methods from JavaScript.
2. Main Uses of TeaVM
When we have complex Java-based logic, it’s not practical to rewrite the same functionality for the web. TeaVM allows us to avoid redundancy by efficiently compiling our Java code into JavaScript, optimizing the size and performance of the final script.
Either way, let’s remember that our Java code must fit within the confines of the Java Class Library (JCL) emulation. For example, Swing and JavaFX aren’t supported.
2.1. Single-Page Application (SPA)
TeaVM can be the right solution for creating a Single-Page Application (SPA) from scratch almost entirely in Java and CSS, minimizing the need to write JavaScript and HTML code. This is because TeaVM has these web-specific features:
- Call Java methods directly from JavaScript, as we’ll see in the upcoming examples
- Create native Java methods implemented in JavaScript using the @JSBody annotation
- Manipulate web elements with Java using the JSO library
However, TeaVM doesn’t completely replace the need for JavaScript or HTML, especially with regard to layout and some browser-specific logic. In addition, TeaVM doesn’t handle CSS, which we have to write manually or manage through external frameworks.
2.2. Existing Website
TeaVM is also great for adding features written in Java to an existing website developed with a traditional CMS like WordPress or Joomla. For example, we can create a PHP plugin for our CMS that exposes functionality to TeaVM via a REST API and include a JavaScript compiled by TeaVM on a page of our website.
Such a script, taking advantage of our REST API, can convert JSON to Java objects and vice versa, and execute our business logic written in Java. It can also modify the DOM to create a user interface or integrate into an existing one.
This use case makes sense when the client-side business logic is so complex that we are more comfortable, skilled, and familiar with Java code than with JavaScript.
2.3. Other Cases
In addition, TeaVM has WebAssembly support under active development, with a new version in the works that will allow our JavaScript-targeted applications to be ported to WebAssembly with minimal changes. However, this feature is still in an experimental phase and not yet ready.
TeaVM can also transpile Java to C. We can use this functionality as an ultra-lightweight subset of GraalVM native images. Even if this functionality is stable and ready to use, we won’t go into it because it’s beyond the main use of TeaVM.
3. Maven Setup and TeaVM Configuration
First, the current 0.10.x version of TeaVM supports Java bytecode up to JDK 21 and requires at least Java 11 to run its Java-to-JavaScript compiler. Of course, these requirements may change in later versions.
We’ll use Maven and keep the pom.xml minimal by configuring TeaVM programmatically within a Java class. This approach allows us to dynamically change the configuration based on the parameters passed to the main(…) method via Maven’s -Dexec.args option. This helps to generate different JavaScript outputs from the same project for different purposes, all while sharing the same codebase.
If we prefer an alternative method, or if Maven isn’t in use, the official TeaVM Getting Started guide provides further instructions.
3.1. pom.xml
After checking what the latest version of TeaVM is in the Maven repository, let’s add the dependencies:
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-tooling</artifactId>
<version>0.10.2</version>
</dependency>
Adding these three dependencies automatically adds other TeaVM transitive dependencies. The teavm-core includes libraries such as teavm-interop, teavm-metaprogramming-api, relocated ASM libraries (for bytecode manipulation), HPPC, and Rhino (JavaScript engine). In addition, teavm-classlib brings in teavm-jso, commons-io, jzlib and joda-time. The teavm-tooling dependency also includes a relocated version of commons-io.
These transitive dependencies provide essential functionality without the need to add them manually.
3.2. TeaVMRunner.java
This class configures TeaVM, we need to specify it using Maven’s -Dexec.mainClass option:
public class TeaVMRunner {
public static void main(String[] args) throws Exception {
TeaVMTool tool = new TeaVMTool();
tool.setTargetDirectory(new File("target/teavm"));
tool.setTargetFileName("calculator.js");
tool.setMainClass("com.baeldung.teavm.Calculator");
tool.setTargetType(TeaVMTargetType.JAVASCRIPT);
tool.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED);
tool.setDebugInformationGenerated(false);
tool.setIncremental(false);
tool.setObfuscated(true);
tool.generate();
}
}
Let’s take a closer look:
- setTargetDirectory(…) → Output directory where TeaVM will place the generated files
- setTargetFileName(…) → Name of the generated JavaScript file
- setMainClass(…) → Fully qualified name of the main class of the application
- setTargetType(…) → JavaScript, WebAssembly, or C
- setOptimizationLevel(…) → The ADVANCED level produces the smallest JavaScript files, even smaller than the FULL level
- setDebugInformationGenerated(…) → Only useful for creating a debug information file for the TeaVM Eclipse plugin
- setIncremental(…) → If enabled, compiles faster, but reduces optimizations, so it’s not recommended for production
- setObfuscated(…) → It reduces the size of the generated JavaScript by two or three times when enabled, so it should be preferred in most cases
There are also other options documented in the Tooling section of the official guide, e.g. for generating source maps.
The most important thing to note is that using setMainClass(…) creates a JavaScript main() function with global scope that executes the translated version of the Java main(String[]). We’ll see an example of this later.
3.3. HTML Page
While simple, this is a complete example of how to include the Javascript file generated by TeaVM. We’ve left out meta tags to optimize for mobile devices, indexing, and other requirements unrelated to TeaVM:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TeaVM Example</title>
<script type="text/javascript" src="calculator.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
// Call JS functions here
});
</script>
</head>
<body>
<h1>TeaVM Example</h1>
<div id="calculator-container"></div>
</body>
</html>
In this example, calculator.js is the file we specified earlier in setTargetFileName(…). We’ll see what to put in place of the “Call JS functions here” comment in a moment. Finally, <div id=”calculator-container”></div> is a placeholder that we’ll use to create a sample calculator.
4. Calculator Example
Let’s take a simple addition example.
4.1. Calling a Java Method From Javascript
This is the class we previously specified in setMainClass(…):
public class Calculator {
public static void main(String[] args) {
}
@JSExport
public static int sum(int a, int b) {
return a + b;
}
}
The @JSExport annotation in TeaVM is used to make Java methods, fields, or classes accessible to JavaScript, allowing them to be called directly from JavaScript code. So, after compiling the code, let’s call the sum(…) function in our example HTML page:
[...]
document.addEventListener('DOMContentLoaded', function() {
// Call JS functions here
let result = sum(51, 72);
console.log("Sum result: " + result);
[...]
This is the output of the browser’s JavaScript console:
Sum result: 123
The result is as expected.
4.2. Java-Based DOM Manipulation
Now let’s implement the main(…) function. Let’s notice that document.getElementById(“calculator-container”) goes to select the <div id=”calculator-container”></div> tag that we previously inserted in the example HTML file:
public static void main(String[] args) {
HTMLDocument document = HTMLDocument.current();
HTMLElement container = document.getElementById("calculator-container");
// Create input fields
HTMLInputElement input1 = (HTMLInputElement) document.createElement("input");
input1.setType("number");
container.appendChild(input1);
HTMLInputElement input2 = (HTMLInputElement) document.createElement("input");
input2.setType("number");
container.appendChild(input2);
// Create a button
HTMLButtonElement button = (HTMLButtonElement) document.createElement("button");
button.appendChild(document.createTextNode("Calculate Sum"));
container.appendChild(button);
// Create a div to display the result
HTMLElement resultDiv = document.createElement("div");
container.appendChild(resultDiv);
// Add click event listener to the button
button.addEventListener("click", (evt) -> {
try {
long num1 = Long.parseLong(input1.getValue());
long num2 = Long.parseLong(input2.getValue());
long sum = num1 + num2;
resultDiv.setTextContent("Result: " + sum);
} catch (NumberFormatException e) {
resultDiv.setTextContent("Please enter valid integer numbers.");
}
});
}
The code is self-explanatory. In a nutshell, it dynamically creates input fields, a button, and a result display area within the web page, all using Java. The button listens for a click event, retrieves the numbers entered in the input fields, calculates their sum, and displays the result. If invalid input is provided, an error message is shown instead.
Let’s remember to call the main() function:
document.addEventListener('DOMContentLoaded', function() {
// Call JS functions here
main();
});
Here is the result:
Starting with a simple example like this, we can modify the DOM as we like to create any layout, even with the help of CSS.
5. Conclusion
In this article, we explored how TeaVM facilitates the translation of Java bytecode into JavaScript, allowing Java applications to run directly in web browsers. We covered key features such as how to call Java methods from JavaScript, perform Java-based DOM manipulation, and implement simple web applications without having to write extensive JavaScript code. Through a practical calculator example, we demonstrated the ease of bridging Java and web development using TeaVM.
As always, the full source code is available over on GitHub.