1

I am up with implementing my Java Native Interface functions in Golang using the golang C lib.
Now I want to convert a jstring to an UTF-8 string using the JNI function GetStringUTFChars but I get an error when doing it. These are the steps I have done:

In my Java class (called MyClass) where I have defined the JNI method, I have:

public static native void print(String msg);

Using javah, I have generated the .h-file with the function defined in C language:

JNIEXPORT void JNICALL Java_com_mypackage_MyClass_print
  (JNIEnv *, jclass, jstring);

Then, in my Go code I have the following code:

package main

// #cgo CFLAGS: -I/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include
// #cgo CFLAGS: -I/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin
/*
#include <jni.h>
*/
import "C"

//export Java_com_mypackage_MyClass_print
func Java_com_mypackage_MyClass_print(env *C.JNIEnv, clazz C.jclass, str C.jstring) {

    _ = C.GetStringUTFChars(env, str, 0)

}

When I build the go file using:
go build -buildmode=c-shared -o libmyclass.dylib libmyclass.go
then I get the following error:

could not determine kind of name for C.GetStringUTFChars

How should I call the GetStringUTFChars defined in the JNI spec so I then can print the string with fmt.println?

EDIT 2
Removed "edit 1" since the procedure above was correct, it was just the LD_LIBRARY_PATH variable that was not set.

Rox
  • 2,647
  • 15
  • 50
  • 85
  • 1
    As far as I know, CGO doesn't support calling through C function pointers. So you'd have to write a small wrapper library in C with functions that take a `JNIEnv*` and then calls `*(env)->TheActualJniFunction(env, ...` – Michael Nov 11 '18 at 21:31

1 Answers1

1

JNI functions like GetStringUTFChars are function pointers and cannot be called directly from Go. You have to wrap the functions you need in a separate C file. e.g.

jx.c

#include <jni.h>

const char* jx_GetStringUTFChars(JNIEnv *env, jstring str, jboolean *isCopy) {
    return (*env)->GetStringUTFChars(env, str, isCopy);
}

After creating a library from the C file, your Go file will look something like this:

package main

/*
#cgo CFLAGS: -I/usr/java/jdk1.8.0_162/include/ -I/usr/java/jdk1.8.0_162/include/linux/
#cgo LDFLAGS: -L${SRCDIR}/ -ljx

#include "jx.h"
*/
import "C"
import (
    "fmt"
)

//export Java_com_mypackage_MyClass_print
func Java_com_mypackage_MyClass_print(env *C.JNIEnv, clazz C.jclass, str C.jstring) {
    s := C.jx_GetStringUTFChars(env, str, (*C.jboolean)(nil))
    fmt.Println(C.GoString(s))
}

func main() {}

The reason why there is a separate C file just for the wrapper function is because of this clause in the documentation:

Using //export in a file places a restriction on the preamble: since it is copied into two different C output files, it must not contain any definitions, only declarations.

ssemilla
  • 3,900
  • 12
  • 28
  • Thanks! So the steps I need to to is first build a shared library of jx.c using `gcc -shared -fPIC -I/.../libjx.so` and then point to that shared library using LDFLAGS in the Go file? Correct? I have tried that and it compiles when I do `go build...` but when running my Java program that uses my JNI method, then I get `Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/rox/jnijava/target/libjnijava.so: libjx.so: cannot open shared object file: No such file or directory` but both libjnijava.so (created by go build) and libjx.so (created by gcc -shared...) exist on that path. – Rox Nov 13 '18 at 08:26
  • Yes, build the C file first and then you can create a library using `ar q libjx.a jx.c`. Then build your go shared object, e.g. `go build -buildmode=c-shared -o gox.so so.go`. You might need to add `libjx.so` in your `LD_LIBRARY_PATH`. I suggest https://stackoverflow.com/questions/13428910/how-to-set-the-environmental-variable-ld-library-path-in-linux – ssemilla Nov 13 '18 at 11:59
  • Please have a look at my edit above where I added the steps to reproduce the error on Linux with Java 11. I have set `LD_LIBRARY_PATH` to point at the folder with the shared objects. I have also tried with setting the Java option `-Djava.library.path` but with no luck. So I am still stuck :-( – Rox Nov 13 '18 at 15:01
  • This: `/home/rox/gojni/target/libgojni.so` is not = to this: `/home/rox/jnijava/target/libjnijava.so`. – ssemilla Nov 13 '18 at 15:59
  • Thanks! I was testing with some different projects on different platforms and copy-pasted from wrong project. :-) Got it working now though. It turned out that the LD_LIBRARY_PATH wasn´t set for the session I used. – Rox Nov 13 '18 at 16:44