Skip to content

timob/jnigi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

21c53b5 · Apr 11, 2024
Nov 23, 2023
Apr 11, 2024
Feb 17, 2024
Dec 8, 2023
Sep 21, 2020
Apr 11, 2024
Dec 8, 2023
May 13, 2016
Apr 11, 2024
Apr 11, 2024
Jul 28, 2022
Dec 5, 2021
Dec 4, 2021
Jul 14, 2022
May 13, 2016
Feb 29, 2020
Dec 9, 2020
Dec 5, 2021
Dec 15, 2023
Dec 22, 2022
Apr 11, 2024
Jan 15, 2024
Nov 23, 2023
Apr 11, 2024
Apr 2, 2024
Feb 17, 2024
Apr 2, 2024
Dec 4, 2021
Jan 12, 2024
Dec 7, 2023

Repository files navigation

JNIGI

JNI Go Interface.

A package to access Java from Go code. Can be used from a Go executable or shared library. This allows for Go to initiate the JVM or Java to start a Go runtime respectively.

Go Reference Actions

Module name change

The go module name is renamed to github.com/timob/jnigi in the branch. Checkout v2 if you want to retain the old name.

Install

# In your apps Go module directory
go get github.com/timob/jnigi

# Add flags needed to include JNI header files, change this as appropriate for your JDK and OS
export CGO_CFLAGS="-I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux"

# build your app
go build

The compilevars.sh (compilevars.bat on Windows) script can help setting the CGO_CFLAGS environment variable.

Finding JVM at Runtime

The JVM library is dynamically linked at run time. Use the LoadJVMLib(jvmLibPath string) error function to load the shared library at run time. There is a function AttemptToFindJVMLibPath() string to help to find the library path.

Status

Testing

Most of the code has tests. To run the tests using docker:

# get source
git clone https://github.com/timob/jnigi.git

cd jnigi

# build image
docker build -t jnigi_test .

# run tests
docker run jnigi_test

Uses

Has been used on Linux/Windows/MacOS (amd64) Android (arm64) multi threaded apps.

Note about using on Windows

Because of the way the JVM triggers OS exceptions during CreateJavaVM, which the Go runtime treats as unhandled exceptions, the code for the Go runtime needs to be changed to allow the JVM to handle the exceptions. See #31 (comment) for how to do this.

Example

package main

import (
    "fmt"
    "github.com/timob/jnigi"
    "log"
    "runtime"
)

func main() {
    if err := jnigi.LoadJVMLib(jnigi.AttemptToFindJVMLibPath()); err != nil {
        log.Fatal(err)
    }

    runtime.LockOSThread()
    jvm, env, err := jnigi.CreateJVM(jnigi.NewJVMInitArgs(false, true, jnigi.DEFAULT_VERSION, []string{"-Xcheck:jni"}))
    if err != nil {
        log.Fatal(err)
    }

    hello, err := env.NewObject("java/lang/String", []byte("Hello "))
    if err != nil {
        log.Fatal(err)
    }

    world, err := env.NewObject("java/lang/String", []byte("World!"))
    if err != nil {
        log.Fatal(err)
    }

    greeting := jnigi.NewObjectRef("java/lang/String")
    err = hello.CallMethod(env, "concat", greeting, world)
    if err != nil {
        log.Fatal(err)
    }

    var goGreeting []byte
    err = greeting.CallMethod(env, "getBytes", &goGreeting)
    if err != nil {
        log.Fatal(err)
    }

    // Prints "Hello World!"
    fmt.Printf("%s\n", goGreeting)

    if err := jvm.Destroy(); err != nil {
        log.Fatal(err)
    }
}