google-site-verification: google61fe8ba583a51912.html
Java to JavaScript with TeaVM in Java

Introduction

TeaVM is an ahead-of-time compiler for Java bytecode that converts Java applications to JavaScript, allowing Java code to run in web browsers. It provides a way to write web applications in Java while maintaining good performance and compatibility.

TeaVM Setup and Configuration

Maven Configuration

<!-- pom.xml --> <project> <properties> <teavm.version>0.8.0</teavm.version> </properties> <dependencies> <!-- TeaVM core --> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-classlib</artifactId> <version>${teavm.version}</version> </dependency> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-platform</artifactId> <version>${teavm.version}</version> </dependency> <!-- JavaScript interoperability --> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-jso</artifactId> <version>${teavm.version}</version> </dependency> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-jso-apis</artifactId> <version>${teavm.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.teavm</groupId> <artifactId>teavm-maven-plugin</artifactId> <version>${teavm.version}</version> <executions> <execution> <id>web-client</id> <goals> <goal>compile</goal> </goals> <configuration> <targetDirectory>${project.build.directory}/teavm</targetDirectory> <mainClass>com.example.Main</mainClass> <minifying>true</minifying> <sourceMapsGenerated>true</sourceMapsGenerated> <sourceFilesCopied>true</sourceFilesCopied> <debugInformationGenerated>true</debugInformationGenerated> <optimization>FULL</optimization> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>

Gradle Configuration

// build.gradle plugins { id 'java' id 'org.teavm' version '0.8.0' } teavm { version = '0.8.0' all { mainClass = 'com.example.Main' targetDirectory = file('build/teavm') minifying = true sourceMapsGenerated = true sourceFilesCopied = true debugInformationGenerated = true optimization = 'FULL' } } dependencies { implementation 'org.teavm:teavm-classlib:0.8.0' implementation 'org.teavm:teavm-platform:0.8.0' implementation 'org.teavm:teavm-jso:0.8.0' implementation 'org.teavm:teavm-jso-apis:0.8.0' }

Basic TeaVM Application

Simple Web Application

package com.example; import org.teavm.jso.JSBody; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.dom.html.Text; public class Main { public static void main(String[] args) { // Get the document HTMLDocument document = HTMLDocument.current(); // Create a heading HTMLElement heading = document.createElement("h1"); heading.appendChild(document.createTextNode("Hello from Java!")); // Create a button HTMLElement button = document.createElement("button"); button.appendChild(document.createTextNode("Click me!")); // Add click handler button.addEventListener("click", event -> { showAlert("Button clicked from Java!"); updateCounter(); }); // Create counter display HTMLElement counter = document.createElement("div"); counter.setAttribute("id", "counter"); counter.appendChild(document.createTextNode("Count: 0")); // Add everything to body HTMLElement body = document.getBody(); body.appendChild(heading); body.appendChild(button); body.appendChild(counter); } private static int clickCount = 0; private static void updateCounter() { clickCount++; HTMLElement counter = HTMLDocument.current().getElementById("counter"); if (counter != null) { counter.setInnerHTML("Count: " + clickCount); } } @JSBody(params = {"message"}, script = "alert(message);") private static native void showAlert(String message); }

JavaScript Interoperability

Working with JavaScript Objects

package com.example.js; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; import org.teavm.jso.core.JSString; public class JavaScriptInterop { // Interface for JavaScript objects public interface User extends JSObject { @JSProperty String getName(); @JSProperty void setName(String name); @JSProperty int getAge(); @JSProperty void setAge(int age); } // Creating JavaScript objects from Java @JSBody(script = "return {};") public static native User createUser(); // Calling JavaScript functions @JSBody(params = {"user"}, script = "console.log(user);") public static native void logUser(User user); // Working with JSON @JSBody(params = {"json"}, script = "return JSON.parse(json);") public static native User parseUser(String json); @JSBody(params = {"user"}, script = "return JSON.stringify(user);") public static native String stringifyUser(User user); public static void demonstrateInterop() { // Create a JavaScript object User user = createUser(); user.setName("John Doe"); user.setAge(30); // Log to console logUser(user); // Convert to JSON and back String json = stringifyUser(user); System.out.println("JSON: " + json); User parsedUser = parseUser(json); System.out.println("Parsed name: " + parsedUser.getName()); } }

DOM Manipulation

package com.example.dom; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.dom.html.HTMLInputElement; import org.teavm.jso.dom.html.HTMLButtonElement; import org.teavm.jso.dom.events.EventListener; import org.teavm.jso.dom.events.Event; public class DOMExample { public static void createTodoApp() { HTMLDocument document = HTMLDocument.current(); HTMLElement body = document.getBody(); // Create container HTMLElement container = document.createElement("div"); container.setAttribute("class", "container"); // Create input field HTMLInputElement input = (HTMLInputElement) document.createElement("input"); input.setType("text"); input.setAttribute("placeholder", "Enter a todo item"); input.setAttribute("id", "todoInput"); // Create add button HTMLButtonElement addButton = (HTMLButtonElement) document.createElement("button"); addButton.setInnerHTML("Add Todo"); // Create todo list HTMLElement todoList = document.createElement("ul"); todoList.setAttribute("id", "todoList"); // Add event listener addButton.addEventListener("click", event -> { String todoText = input.getValue().trim(); if (!todoText.isEmpty()) { addTodoItem(todoList, todoText); input.setValue(""); // Clear input } }); // Also allow Enter key input.addEventListener("keypress", event -> { if ("Enter".equals(event.getKey())) { String todoText = input.getValue().trim(); if (!todoText.isEmpty()) { addTodoItem(todoList, todoText); input.setValue(""); } } }); // Add everything to container container.appendChild(input); container.appendChild(addButton); container.appendChild(todoList); body.appendChild(container); } private static void addTodoItem(HTMLElement todoList, String text) { HTMLDocument document = HTMLDocument.current(); // Create list item HTMLElement li = document.createElement("li"); li.setAttribute("class", "todo-item"); // Create text span HTMLElement span = document.createElement("span"); span.setInnerHTML(text); // Create delete button HTMLButtonElement deleteBtn = (HTMLButtonElement) document.createElement("button"); deleteBtn.setInnerHTML("Delete"); deleteBtn.setAttribute("class", "delete-btn"); deleteBtn.addEventListener("click", event -> { todoList.removeChild(li); }); // Add elements to list item li.appendChild(span); li.appendChild(deleteBtn); // Add to list todoList.appendChild(li); } }

Advanced TeaVM Features

Working with JavaScript Libraries

package com.example.chart; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; public class ChartJSIntegration { // Chart.js configuration interface public interface ChartConfig extends JSObject { @JSProperty String getType(); @JSProperty void setType(String type); @JSProperty ChartData getData(); @JSProperty void setData(ChartData data); @JSProperty ChartOptions getOptions(); @JSProperty void setOptions(ChartOptions options); } public interface ChartData extends JSObject { @JSProperty JSString[] getLabels(); @JSProperty void setLabels(JSString[] labels); @JSProperty Dataset[] getDatasets(); @JSProperty void setDatasets(Dataset[] datasets); } public interface Dataset extends JSObject { @JSProperty String getLabel(); @JSProperty void setLabel(String label); @JSProperty int[] getData(); @JSProperty void setData(int[] data); @JSProperty String[] getBackgroundColor(); @JSProperty void setBackgroundColor(String[] backgroundColor); } public interface ChartOptions extends JSObject { @JSProperty boolean isResponsive(); @JSProperty void setResponsive(boolean responsive); } public interface Chart extends JSObject { void destroy(); } // Native JavaScript methods for Chart.js @JSBody(script = "return typeof Chart !== 'undefined';") public static native boolean isChartJSAvailable(); @JSBody(params = {"canvas", "config"}, script = "return new Chart(canvas, config);") public static native Chart createChart(HTMLElement canvas, ChartConfig config); public static void createSampleChart() { if (!isChartJSAvailable()) { System.err.println("Chart.js is not available"); return; } HTMLDocument document = HTMLDocument.current(); // Create canvas element HTMLElement canvas = document.createElement("canvas"); canvas.setAttribute("id", "myChart"); canvas.setAttribute("width", "400"); canvas.setAttribute("height", "400"); // Create chart configuration ChartConfig config = createChartConfig(); // Create chart Chart chart = createChart(canvas, config); // Add to document document.getBody().appendChild(canvas); } private static ChartConfig createChartConfig() { ChartConfig config = (ChartConfig) JSObject.create(); config.setType("bar"); ChartData data = (ChartData) JSObject.create(); data.setLabels(new JSString[]{ JSString.valueOf("January"), JSString.valueOf("February"), JSString.valueOf("March"), JSString.valueOf("April"), JSString.valueOf("May") }); Dataset dataset = (Dataset) JSObject.create(); dataset.setLabel("Sales"); dataset.setData(new int[]{65, 59, 80, 81, 56}); dataset.setBackgroundColor(new String[]{ "rgba(255, 99, 132, 0.2)", "rgba(54, 162, 235, 0.2)", "rgba(255, 206, 86, 0.2)", "rgba(75, 192, 192, 0.2)", "rgba(153, 102, 255, 0.2)" }); data.setDatasets(new Dataset[]{dataset}); config.setData(data); ChartOptions options = (ChartOptions) JSObject.create(); options.setResponsive(true); config.setOptions(options); return config; } }

Async Operations with Promises

package com.example.async; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; import org.teavm.jso.core.JSPromise; import org.teavm.jso.typedarrays.ArrayBuffer; public class AsyncOperations { public interface FetchResponse extends JSObject { JSPromise<ArrayBuffer> arrayBuffer(); JSPromise<String> text(); JSPromise<JSObject> json(); } // Fetch API integration @JSBody(params = {"url"}, script = "return fetch(url);") public static native JSPromise<FetchResponse> fetch(String url); public static void demonstrateAsyncOperations() { // Fetch data from API fetch("https://api.example.com/data") .then(response -> response.json()) .then(data -> { System.out.println("Data received: " + data); processData(data); return null; }) .catch(error -> { System.err.println("Error fetching data: " + error); return null; }); } private static void processData(JSObject data) { // Process the received data HTMLDocument document = HTMLDocument.current(); HTMLElement div = document.createElement("div"); div.setInnerHTML("Data processed successfully"); document.getBody().appendChild(div); } // Custom async operations public static JSPromise<String> delayedOperation(String input, int delayMs) { return new JSPromise<>((resolve, reject) -> { setTimeout(() -> { resolve.execute("Processed: " + input); }, delayMs); }); } @JSBody(params = {"callback", "delay"}, script = "setTimeout(callback, delay);") private static native void setTimeout(Runnable callback, int delay); public static void useCustomAsync() { delayedOperation("Hello", 2000) .then(result -> { System.out.println("Async result: " + result); return null; }); } }

Building a Complete Application

Single Page Application (SPA)

package com.example.spa; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.dom.html.HTMLAnchorElement; import org.teavm.jso.dom.html.HTMLDivElement; public class SinglePageApplication { private static HTMLDivElement contentArea; public static void main(String[] args) { HTMLDocument document = HTMLDocument.current(); // Create navigation createNavigation(); // Create content area contentArea = (HTMLDivElement) document.createElement("div"); contentArea.setAttribute("id", "content"); document.getBody().appendChild(contentArea); // Show home page by default showHomePage(); } private static void createNavigation() { HTMLDocument document = HTMLDocument.current(); HTMLElement nav = document.createElement("nav"); nav.setAttribute("class", "navbar"); String[] pages = {"Home", "About", "Contact", "Products"}; for (String page : pages) { HTMLAnchorElement link = (HTMLAnchorElement) document.createElement("a"); link.setInnerHTML(page); link.setAttribute("href", "#"); link.setAttribute("class", "nav-link"); link.addEventListener("click", event -> { event.preventDefault(); showPage(page.toLowerCase()); }); nav.appendChild(link); } document.getBody().appendChild(nav); } private static void showPage(String pageName) { switch (pageName) { case "home": showHomePage(); break; case "about": showAboutPage(); break; case "contact": showContactPage(); break; case "products": showProductsPage(); break; default: showHomePage(); } } private static void showHomePage() { contentArea.setInnerHTML(""" <div class="page"> <h1>Welcome to Our SPA</h1> <p>This is a single page application built with Java and TeaVM.</p> <button id="homeBtn">Click Me!</button> </div> """); HTMLElement button = HTMLDocument.current().getElementById("homeBtn"); if (button != null) { button.addEventListener("click", event -> { showAlert("Hello from Home Page!"); }); } } private static void showAboutPage() { contentArea.setInnerHTML(""" <div class="page"> <h1>About Us</h1> <p>We are a company that uses Java to build web applications.</p> <p>TeaVM allows us to write frontend code in Java.</p> </div> """); } private static void showContactPage() { contentArea.setInnerHTML(""" <div class="page"> <h1>Contact Us</h1> <form id="contactForm"> <div> <label for="name">Name:</label> <input type="text" id="name" name="name"> </div> <div> <label for="email">Email:</label> <input type="email" id="email" name="email"> </div> <div> <label for="message">Message:</label> <textarea id="message" name="message"></textarea> </div> <button type="submit">Send Message</button> </form> </div> """); HTMLElement form = HTMLDocument.current().getElementById("contactForm"); if (form != null) { form.addEventListener("submit", event -> { event.preventDefault(); handleContactForm(); }); } } private static void showProductsPage() { contentArea.setInnerHTML(""" <div class="page"> <h1>Our Products</h1> <div id="productsList"> <p>Loading products...</p> </div> </div> """); // Simulate loading products loadProducts(); } private static void handleContactForm() { String name = getInputValue("name"); String email = getInputValue("email"); String message = getInputValue("message"); if (name.isEmpty() || email.isEmpty() || message.isEmpty()) { showAlert("Please fill in all fields"); return; } showAlert("Thank you for your message, " + name + "!"); clearForm(); } private static String getInputValue(String id) { HTMLElement element = HTMLDocument.current().getElementById(id); return element != null ? element.getAttribute("value") : ""; } private static void clearForm() { setInputValue("name", ""); setInputValue("email", ""); setInputValue("message", ""); } private static void setInputValue(String id, String value) { HTMLElement element = HTMLDocument.current().getElementById(id); if (element != null) { element.setAttribute("value", value); } } private static void loadProducts() { // Simulate API call setTimeout(() -> { String[] products = {"Product A", "Product B", "Product C", "Product D"}; StringBuilder html = new StringBuilder(); html.append("<ul>"); for (String product : products) { html.append("<li>").append(product).append("</li>"); } html.append("</ul>"); HTMLElement productsList = HTMLDocument.current().getElementById("productsList"); if (productsList != null) { productsList.setInnerHTML(html.toString()); } }, 1000); } @org.teavm.jso.JSBody(params = {"callback", "delay"}, script = "setTimeout(callback, delay);") private static native void setTimeout(Runnable callback, int delay); @org.teavm.jso.JSBody(params = {"message"}, script = "alert(message);") private static native void showAlert(String message); }

Testing and Debugging

Unit Testing with TeaVM

package com.example.test; import org.teavm.junit.TeaVMTestRunner; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; @RunWith(TeaVMTestRunner.class) public class BusinessLogicTest { @Test public void testCalculator() { Calculator calc = new Calculator(); assertEquals(5, calc.add(2, 3)); assertEquals(1, calc.subtract(4, 3)); assertEquals(6, calc.multiply(2, 3)); assertEquals(2, calc.divide(6, 3)); } @Test public void testStringUtils() { assertTrue(StringUtils.isNullOrEmpty(null)); assertTrue(StringUtils.isNullOrEmpty("")); assertFalse(StringUtils.isNullOrEmpty("Hello")); assertEquals("HELLO", StringUtils.toUpper("hello")); } } class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } } class StringUtils { public static boolean isNullOrEmpty(String str) { return str == null || str.isEmpty(); } public static String toUpper(String str) { return str != null ? str.toUpperCase() : null; } }

Debugging Configuration

package com.example.debug; import org.teavm.jso.JSBody; import org.teavm.jso.console.Console; public class DebuggingUtils { // Console logging public static void log(String message) { Console.log("LOG: " + message); } public static void warn(String message) { Console.warn("WARN: " + message); } public static void error(String message) { Console.error("ERROR: " + message); } // Performance measurement public static void measureTime(String operationName, Runnable operation) { long start = currentTimeMillis(); operation.run(); long end = currentTimeMillis(); log(operationName + " took " + (end - start) + "ms"); } @JSBody(script = "return Date.now();") private static native long currentTimeMillis(); // Assertions for development public static void assertThat(boolean condition, String message) { if (!condition) { throw new AssertionError("Assertion failed: " + message); } } // Debug information display public static void showDebugInfo() { log("User Agent: " + getUserAgent()); log("Viewport: " + getViewportSize()); } @JSBody(script = "return navigator.userAgent;") private static native String getUserAgent(); @JSBody(script = "return window.innerWidth + 'x' + window.innerHeight;") private static native String getViewportSize(); }

Build and Deployment

HTML Wrapper Template

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Java TeaVM Application</title> <style> body { font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; } .navbar { background-color: #333; padding: 10px; margin-bottom: 20px; } .nav-link { color: white; text-decoration: none; margin-right: 15px; padding: 5px 10px; } .nav-link:hover { background-color: #555; } .page { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .todo-item { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid #eee; } .delete-btn { background-color: #ff4444; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; } </style> </head> <body> <div id="loading">Loading Java application...</div> <!-- TeaVM will replace this with compiled JavaScript --> <script type="text/javascript" src="teavm.js"></script> <script> // Show loading message until Java app is ready window.addEventListener('load', function() { document.getElementById('loading').style.display = 'none'; }); </script> </body> </html>

Build Script for Production

package com.example.build; public class BuildConfiguration { /* * TeaVM Maven plugin configuration for production: * * <configuration> * <mainClass>com.example.Main</mainClass> * <targetDirectory>target/teavm</targetDirectory> * <minifying>true</minifying> * <sourceMapsGenerated>false</sourceMapsGenerated> <!-- Disable for production --> * <debugInformationGenerated>false</debugInformationGenerated> * <optimization>ADVANCED</optimization> * <targetType>JAVASCRIPT</targetType> * <classesToPreserve> * <class>com.example.Main</class> * <class>com.example.**</class> * </classesToPreserve> * <entryPointName>myJavaApp</entryPointName> * </configuration> */ }

Best Practices

Performance Optimization

package com.example.optimization; import org.teavm.jso.JSBody; public class PerformanceTips { // 1. Minimize JSO calls public static class EfficientDOMOperations { // Inefficient: multiple DOM calls public static void inefficientUpdate() { for (int i = 0; i < 100; i++) { appendItem("Item " + i); // 100 DOM calls } } // Efficient: batch DOM operations public static void efficientUpdate() { StringBuilder html = new StringBuilder(); for (int i = 0; i < 100; i++) { html.append("<li>Item ").append(i).append("</li>"); } setInnerHTML("list", html.toString()); // 1 DOM call } @JSBody(params = {"text"}, script = """ var li = document.createElement('li'); li.textContent = text; document.getElementById('list').appendChild(li); """) private static native void appendItem(String text); @JSBody(params = {"id", "html"}, script = """ document.getElementById(id).innerHTML = html; """) private static native void setInnerHTML(String id, String html); } // 2. Use efficient data structures public static class DataOptimization { // Prefer primitive arrays over collections for performance public static int[] processNumbers(int[] input) { int[] result = new int[input.length]; for (int i = 0; i < input.length; i++) { result[i] = input[i] * 2; } return result; } } // 3. Lazy loading for large applications public static class LazyLoading { private static boolean featureLoaded = false; public static void loadFeatureOnDemand() { if (!featureLoaded) { loadFeature(); featureLoaded = true; } } @JSBody(script = """ // Dynamically load additional JavaScript var script = document.createElement('script'); script.src = 'feature.js'; document.head.appendChild(script); """) private static native void loadFeature(); } }

Conclusion

TeaVM provides a powerful way to compile Java to JavaScript, enabling:

  • Full-stack Java development - Use Java for both backend and frontend
  • Type safety - Leverage Java's strong typing in the browser
  • Code reuse - Share business logic between server and client
  • JavaScript interoperability - Seamlessly work with existing JavaScript libraries
  • Good performance - Ahead-of-time compilation produces efficient JavaScript

Key considerations when using TeaVM:

  • Some Java libraries may not be compatible
  • Reflection usage is limited
  • File I/O operations are simulated
  • Focus on business logic and use JSO for DOM manipulation

TeaVM is particularly suitable for:

  • Enterprise web applications
  • Applications with complex business logic
  • Teams with strong Java expertise
  • Projects requiring code sharing between client and server

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper