Java and JavaScript Interoperability
Learn how to enable Java and JavaScript communication using CheerpJ
A Java application running in CheerpJ can call JavaScript, and JavaScript can call the Java application.
This tutorial sets up two-way communication between a Swing application and the web page that hosts it. Java sends text to the page through a native method implemented in JavaScript, and the page sends text to Java by calling a method on a Java object.
To let JavaScript call into Java at any time, the application keeps a Java thread waiting inside a native method. For calls into Java made while a native method runs, see calling back into Java from JavaScript in the native methods guide.
Prerequisites
- Node.js
(>= 18)
- A copy of the tutorial repository
git clone https://github.com/leaningtech/cheerpj-meta.git- Go to the
projectTemplatedirectory of the Interoperability example:
cd examples/Interoperability/projectTemplateProject structure
The template contains these files:
projectTemplate/βββ com/example/β βββ Example.classβ βββ Example$1.classβ βββ Example.javaβ βββ manifest.txtβββ Makefileβββ example.jarβββ index.htmlexample.jar is the compiled application. In this tutorial you implement its native methods in JavaScript in index.html.
The finished example is on GitHub.
Java source code: Example.java
The Java class opens a small Swing window. It sends the text typed in the window to the page, and shows the text it receives from the page.
package com.example;
import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JTextField;
import java.awt.event.ActionEvent;import java.awt.event.ActionListener;
public class Example extends JFrame {
// Native method to set the Java application instance in JavaScript public static native void nativeSetApplication(Example myApplication);
// Native method to send text to the HTML UI public static native void sendToHTML(String s);
private JTextField javaTextField; // Input text field private JTextField javaTextField2; // Output display field
public Example() { // Set up the JFrame (Java window) setTitle("Example App"); setSize(400, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(null);
// Create a text field for sending input to JavaScript javaTextField = new JTextField(""); javaTextField.setBounds(50, 50, 300, 30); add(javaTextField);
// Create a read-only text field for displaying input from JavaScript javaTextField2 = new JTextField(""); javaTextField2.setEditable(false); // Disable editing javaTextField2.setBounds(50, 200, 300, 30); add(javaTextField2);
// Create a button to send input to JavaScript JButton sendButton = new JButton("Send to HTML"); sendButton.setBounds(100, 100, 200, 50);
sendButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String textToSend = javaTextField.getText(); sendToHTML(textToSend); // Call the native method } }); add(sendButton); }
// Example method to process input from JavaScript public String processInput(String input) { System.out.println("Received input from JavaScript: " + input); javaTextField2.setText(input); return "Java received: " + input; // Send response back to JavaScript }
public static void main(String[] args) { Example app = new Example();
// Start a thread for initializing the Java application instance in JavaScript new Thread(() -> { nativeSetApplication(app); System.out.println("Starting Thread"); }).start();
app.setVisible(true); }}The class has these methods:
sendToHTML: a static native method implemented in JavaScript. The Send to HTML button calls it with the text of the first text field.processInput: called from JavaScript. It prints the input to the console, shows it in the read-only text field and returns a response string.nativeSetApplication: a static native method implemented in JavaScript.maincalls it from a new thread to hand theExampleobject to JavaScript.
NoteThere is no need to recompile the Java code for this tutorial: the template includes the compiled
example.jar. If you change the Java code, recompile it as described in Running the example.
JavaScript native method implementation
For Java and JavaScript to communicate, implement the Java native methods in JavaScript.
Add the code in this section to the <script> tag in the <body> of index.html. The comments in the template mark where each part goes.
1. Define JavaScript native methods
For each native method, create an async function named Java_<fully-qualified-class-name>_<method-name>. Its first parameter is lib, which gives access to the Java classes. For an instance method the second parameter is the Java object the method was called on. Both methods here are static, so the methodβs own parameters follow lib. See the native method guide for details.
You can also move these functions to a separate .js file and include it with a <script> tag.
async function Java_com_example_Example_sendToHTML(lib, str) { document.getElementById("javaOutput").innerText = "JavaScript received: " + str; console.log("Received input from Java: " + str);}
async function Java_com_example_Example_nativeSetApplication( lib, myApplication) { window.myApplication = myApplication; console.log("Java application instance set on JavaScript side."); // Make the inputDiv visible after initialization document.getElementById("inputDiv").style.display = "block"; return new Promise(() => {}); // Keeps the function from returning}The Java_com_example_Example_nativeSetApplication function:
- Stores the Java
Exampleobject inwindow.myApplication. - Shows the input field and button on the page.
- Returns a promise that never resolves, so the Java thread that called it stays inside the native call. This is what keeps
window.myApplicationusable from JavaScript for as long as the page is open.
The Java_com_example_Example_sendToHTML function:
- Receives a string from Java.
- Shows it on the page and logs it to the console.
2. Implement the sendInputToJava function
Add the function that the Send to Java button calls:
async function sendInputToJava() { const inputText = document.getElementById("inputText").value; const response = await window.myApplication.processInput(inputText);}The sendInputToJava function:
- Reads the text from the input box on the page.
- Calls the Java
processInputmethod onwindow.myApplicationwith it. Calls into Java are asynchronous, so the result is awaited.responseholds the stringprocessInputreturns; this example does not use it.
3. Initialise CheerpJ
Add the CheerpJ loader to the <head> of index.html:
<script src="https://cjrtnc.leaningtech.com/5.0/loader.js"></script>Then initialise CheerpJ with the native methods and run the JAR:
(async () => { await cheerpjInit({ version: 8, natives: { Java_com_example_Example_sendToHTML, Java_com_example_Example_nativeSetApplication, }, }); cheerpjCreateDisplay(400, 300); await cheerpjRunJar("/app/example.jar");})();What this does:
cheerpjInitinitialises CheerpJ with these options:cheerpjCreateDisplaycreates a 400x300 display for the Swing window.cheerpjRunJarrunsexample.jar, whose manifest namescom.example.Exampleas the main class.
Running the example
- There is no need to recompile the Java code, because only the JavaScript changed and the template includes the compiled
example.jar. If you change the Java code, recompile it with the providedMakefile(requires a Java installation):
makeAlternatively, compile and package it manually from the projectTemplate directory:
javac -d . com/example/Example.javajar cvfm example.jar com/example/manifest.txt com/example/*.class- Start a local server: serve the
projectTemplatedirectory with an HTTP server such ashttp-server:
npx http-server -p 8080- Run the example: open
http://localhost:8080/index.htmlin the browser. Type in the pageβs input box and click Send to Java to send text to Java, or type in the Java window and click Send to HTML to send text to the page.