Backend Development 14 min read

Integrating Lua with Java: Using LuaJ and LuaJava for Bidirectional Scripting

This article explains how to embed Lua scripts in Java applications with LuaJ, how to call Java code from Lua using LuaJava, and demonstrates dynamic extension, script auto‑upgrade, and bidirectional interaction through comprehensive code examples and detailed explanations.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Integrating Lua with Java: Using LuaJ and LuaJava for Bidirectional Scripting

1. Using Lua in Java

1.1 Using the LuaJ library

LuaJ is a pure‑Java implementation of a Lua interpreter that bridges Java and Lua via JNI. It provides three main components: a Lua compiler that generates bytecode, a Lua virtual machine that executes the bytecode, and a set of Java classes (LuaValue, LuaFunction) that allow Java code to invoke Lua functions and manipulate Lua data types.

(1) Lua language compiler

LuaJ compiles Lua source into Lua bytecode rather than translating it to Java code. The generated bytecode can be executed by the embedded Lua VM.

(2) Lua virtual machine

The VM is built on top of the native Lua C library accessed through JNI, enabling execution of Lua bytecode and handling of Lua data types within a Java process.

(3) Java‑Lua bridge

Key classes such as LuaValue (represents Lua values) and LuaFunction (represents callable Lua functions) let Java code call Lua functions and retrieve results.

(4) Using LuaJ in a Java project

Add the dependency via Maven:

<dependency>
    <groupId>org.luaj</groupId>
    <artifactId>luaj-jse</artifactId>
    <version>3.0.1</version>
</dependency>

Example of executing a simple Lua script from Java:

import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;

public class HelloWorld {
    public static void main(String[] args) {
        LuaValue globals = JsePlatform.standardGlobals();
        LuaValue chunk = globals.load("print('Hello, World!')");
        chunk.call();
    }
}

To pass a Lua function to a Java method, define a subclass of OneArgFunction (e.g., TwoParametersFunction ) and invoke it from Java:

import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;

public class LuaJavaExample {
    public static void main(String[] args) {
        LuaValue globals = JsePlatform.standardGlobals();
        LuaValue luaFunction = LuaValue.valueOf(new TwoParametersFunction());
        invokeJavaMethod(luaFunction);
        globals.set("invokeJavaMethod", new InvokeJavaMethodFunction());
        globals.load("invokeJavaMethod()").call();
    }

    public static void invokeJavaMethod(LuaValue luaFunction) {
        luaFunction.call(LuaValue.valueOf("Hello"), LuaValue.valueOf("World"));
    }

    public static class TwoParametersFunction extends OneArgFunction {
        @Override
        public LuaValue call(LuaValue arg) {
            String first = arg.checkjstring();
            String second = arg.checkjstring(2);
            System.out.println("First parameter: " + first);
            System.out.println("Second parameter: " + second);
            return LuaValue.NIL;
        }
    }

    public static class InvokeJavaMethodFunction extends ZeroArgFunction {
        @Override
        public LuaValue call() {
            invokeJavaMethod(LuaValue.valueOf(new TwoParametersFunction()));
            return LuaValue.NIL;
        }
    }
}

1.2 Implementing dynamic extension and script auto‑upgrade

(1) Dynamic extension

Load and execute additional Lua scripts at runtime without restarting the Java application:

import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;

public class DynamicExtensionExample {
    public static void main(String[] args) {
        LuaValue globals = JsePlatform.standardGlobals();
        globals.loadfile("extension.lua").call();
        LuaValue luaFunction = globals.get("addTwoNumbers");
        LuaValue result = luaFunction.call(10, 20);
        System.out.println("Result: " + result.toint());
    }
}

(2) Script auto‑upgrade

Continuously check for newer Lua script versions and reload them when available:

import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;

public class ScriptAutoUpgradeExample {
    public static void main(String[] args) {
        LuaValue globals = JsePlatform.standardGlobals();
        globals.loadfile("script.lua").call();
        while (true) {
            if (hasNewScriptVersion()) {
                globals.loadfile("new_script.lua").call();
            }
            // other program logic
            sleep(1000);
        }
    }

    private static boolean hasNewScriptVersion() {
        // custom detection logic, e.g., download from server
        return false;
    }

    private static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); }
    }
}

2. Using Java from Lua

2.1 Using the LuaJava library

LuaJava (jnlua) provides a Java‑Lua bridge that lets Lua scripts instantiate Java objects and call their methods. Add the Maven dependency:

<dependency>
    <groupId>com.naef.jnlua</groupId>
    <artifactId>jnlua</artifactId>
    <version>0.9.0</version>
</dependency>

Example of calling a Java method from Lua:

import org.luaj.vm2.*;
import org.luaj.vm2.lib.*;
import com.naef.jnlua.*;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        LuaState luaState = JNLuaUtil.newState();
        luaState.openLibs();
        luaState.pushJavaObject(new Hello());
        luaState.setGlobal("hello");
        luaState.load("hello:sayHello('World')");
        luaState.call(0, 0);
    }

    public static class Hello {
        public void sayHello(String name) {
            System.out.println("Hello, " + name);
        }
    }
}

2.2 Accessing Java classes from Lua

LuaJava also allows pushing a Java class onto the Lua stack and invoking its static methods:

import org.luaj.vm2.*;
import org.luaj.vm2.lib.*;
import com.naef.jnlua.*;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        LuaState luaState = JNLuaUtil.newState();
        luaState.openLibs();
        luaState.pushJavaClass(Hello.class);
        luaState.setGlobal("Hello");
        luaState.load("Hello.sayHello('World')");
        luaState.call(0, 0);
    }

    public static class Hello {
        public static void sayHello(String name) {
            System.out.println("Hello, " + name);
        }
    }
}

3. Summary

This tutorial demonstrated how to embed Lua scripts in Java using LuaJ, how to expose Java objects and classes to Lua via LuaJava, and how to achieve dynamic script extension and automatic upgrades. By combining these two bridges, developers can leverage Lua’s lightweight scripting capabilities alongside Java’s robust ecosystem, enhancing flexibility and extensibility of backend applications.

BackendJavaintegrationscriptingLuaLuaJLuaJava
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.