timob / jnigi

Golang Java JNI library
BSD 2-Clause "Simplified" License
163 stars 44 forks source link

Need to dynamically find libjvm.so (linux, darwin) #3

Closed timob closed 5 years ago

timob commented 8 years ago

Currently libjvm.so is dynamically loaded on Linux but the path currently is hard coded in linux.go. Need to dynamically determine this.

timob commented 8 years ago

Best way to do this is to look for java command in PATH and find relative path to this that contains libjvm.so. (this is what Eclipse does). see http://git.eclipse.org/c/equinox/rt.equinox.framework.git/tree/features/org.eclipse.equinox.executable.feature/library/eclipseNix.c

michaeltrobinson commented 7 years ago

Any progress on this? I'm trying to use this library in AWS lambda and it is not able to find libjvm.so.

michaeltrobinson commented 7 years ago

I was able to get it working by doing a really dumb filetree walk in linux.go

func init() {
    libJVMLoc := ""
    if err := filepath.Walk("/usr/lib/jvm", func(path string, f os.FileInfo, err error) error {
        if err != nil {
            panic(err)
        }
        if strings.HasSuffix(path, "libjvm.so") && strings.Contains(path, "java-1.8") {
            libJVMLoc = path
        }
        return nil
    }); err != nil {
        panic(err)
    }
    cs := cString(libJVMLoc)
    log.Infof("using libjvm: %s", libJVMLoc)

    defer free(cs)
    libHandle := uintptr(C.dlopen((*C.char)(cs), C.RTLD_NOW|C.RTLD_GLOBAL))
    if libHandle == 0 {
        panic(errors.Errorf("could not dynamically load libjvm.so: %s", libJVMLoc))
    }
...
timob commented 7 years ago

Glad you got it working. I think this code does a bit too much to find the path, and hard codes /usr/lib/jvm and java-1.8. I think it should be:

  1. Find java executable in $PATH, following symlinks if encountered
  2. Loop through the array of common relative paths, these are in the eclipse source here. (ARCH component of path can be got from golang)
  3. for each relative dir , see if dir of java path plus relative dir contains libjvm.so

So for my ubuntu: java is in: /usr/lib/jvm/java-8-openjdk-amd64/jre/bin relative path of containing libjvm.so: ../lib/amd64/server/

timob commented 7 years ago

Also now we need to do this for darwin as well to find libjvm.dylib .

gnewton commented 5 years ago

This is how I modified linux.go, to use an env LIBJVM (you are not taking pull requests?):

` . . . import ( "os" "unsafe" )

const LIBJVM = "LIBJVM"

func jni_GetDefaultJavaVMInitArgs(args unsafe.Pointer) jint { return jint(C.dyn_JNI_GetDefaultJavaVMInitArgs((unsafe.Pointer)(args))) }

func jni_CreateJavaVM(pvm unsafe.Pointer, penv unsafe.Pointer, args unsafe.Pointer) jint { return jint(C.dyn_JNI_CreateJavaVM((*C.JavaVM)(pvm), (unsafe.Pointer)(penv), (unsafe.Pointer)(args))) }

func init() { libjvm := os.Getenv(LIBJVM) if libjvm == "" { libjvm = "/usr/lib/jvm/default-java/jre/lib/amd64/server/libjvm.so" } cs := cString(libjvm) defer free(cs) libHandle := uintptr(C.dlopen((*C.char)(cs), C.RTLD_NOW|C.RTLD_GLOBAL)) if libHandle == 0 { panic("could not dyanmically load libjvm.so") } . . .

timob commented 5 years ago

I think we can use JAVA_HOME environment variable for all operating systems.

hscells commented 5 years ago

I agree. I think it would also be safe to panic if the variable is not set instead of attempting to load a hard-coded path i.e., https://github.com/timob/jnigi/blob/master/darwin.go#L63

timob commented 5 years ago

@hscells Ok i've made that change.

This commit uses JAVA_HOME to find path:

https://github.com/timob/jnigi/commit/4a6d12459ba7cd175426a185b96cb357338cfe3b