Closed mlaggner closed 1 year ago
Sorry for taking time to reply, real life issues.
I'm not quite sure what you are trying to do, are you running a Go program, that calls a Java method to start your Java GUI app?
Could you build a Go shared lib, and link that using native method, from Java?
Re: changes to darwin.go, if this is useful, we could add it as special funcs just for macos, last PR was changing other platforms too.
My intention is to build a launcher and patcher for a Java Swing application - and like the original java binary I want to embed the JVM in the same process as the launcher as been triggered (for several reasons).
With jnigi everything worked as expected, except launching the UI (which needs to have the fixes in darwin.go to launch the Java main method in a separate thread and "park" the main thread in the CFRunLoop which is provided by the CoreFoundation Framework).
Can you post the darwin.go that you are using?
the repo with all fixes I committed is at: https://github.com/mlaggner/jnigi
especially the code for darwin.go: https://github.com/mlaggner/jnigi/blob/master/darwin.go - this contains basically the same as you removed from your PR.
Below is what I think darwin.go should be, I don't have a mac to test this. If this looks good would be good if someone can go test
with this file and do a PR:
// Copyright 2016 Tim O'Brien. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build darwin
// +build darwin
package jnigi
/*
#cgo LDFLAGS:-ldl -framework CoreFoundation
#include <dlfcn.h>
#include <jni.h>
#include <CoreFoundation/CoreFoundation.h>
typedef jint (*type_JNI_GetDefaultJavaVMInitArgs)(void*);
type_JNI_GetDefaultJavaVMInitArgs var_JNI_GetDefaultJavaVMInitArgs;
jint dyn_JNI_GetDefaultJavaVMInitArgs(void *args) {
return var_JNI_GetDefaultJavaVMInitArgs(args);
}
typedef jint (*type_JNI_CreateJavaVM)(JavaVM**, void**, void*);
type_JNI_CreateJavaVM var_JNI_CreateJavaVM;
jint dyn_JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) {
return var_JNI_CreateJavaVM(pvm, penv, args);
}
// call back for dummy source used to make sure the CFRunLoop doesn't exit right away
// This callback is called when the source has fired.
void jnigiCFRSourceCallBack ( void *info ) {
}
void jnigiRunCFRLoop(void) {
CFRunLoopSourceContext sourceContext;
//Create a a sourceContext to be used by our source that makes
//sure the CFRunLoop doesn't exit right away
sourceContext.version = 0;
sourceContext.info = NULL;
sourceContext.retain = NULL;
sourceContext.release = NULL;
sourceContext.copyDescription = NULL;
sourceContext.equal = NULL;
sourceContext.hash = NULL;
sourceContext.schedule = NULL;
sourceContext.cancel = NULL;
sourceContext.perform = &jnigiCFRSourceCallBack;
// Create the Source from the sourceContext
CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate (NULL, 0, &sourceContext);
// Use the constant kCFRunLoopCommonModes to add the source to the set of objects
// monitored by all the common modes
CFRunLoopAddSource (CFRunLoopGetCurrent(),sourceRef,kCFRunLoopCommonModes);
// Park this thread in the runloop
CFRunLoopRun();
}
*/
import "C"
import (
"errors"
"log"
"os"
"path/filepath"
"unsafe"
)
const (
JLI_LOAD_ENV = "LIBJLI_LOAD"
JLI_LOAD_YES = "yes"
JLI_LOAD_FORCE = "force"
)
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)))
}
// LoadJVMLib loads libjvm.dyo as specified in jvmLibPath
func LoadJVMLib(jvmLibPath string) error {
// On MacOS we need to preload libjli.dylib to workaround JDK-7131356
// "No Java runtime present, requesting install".
// If envar LIBJLI_LOAD; = "yes": load but just log error if load fails, =
// "force": load and exit with error if load fails.
if jliLoadEnv := os.Getenv(JLI_LOAD_ENV); jliLoadEnv == JLI_LOAD_YES || jliLoadEnv == JLI_LOAD_FORCE {
libjliPath := filepath.Join(filepath.Dir(jvmLibPath), "..", "libjli.dylib")
clibjliPath := cString(libjliPath)
defer func() {
if clibjliPath != nil {
free(clibjliPath)
}
}()
// Do not close JLI library handle until JVM closes
handlelibjli := C.dlopen((*C.char)(clibjliPath), C.RTLD_NOW|C.RTLD_GLOBAL)
if handlelibjli == nil {
if jliLoadEnv == JLI_LOAD_YES {
log.Printf("WARNING could not dynamically load %s", libjliPath)
} else if jliLoadEnv == JLI_LOAD_FORCE {
log.Fatalf("ERROR could not dynamically load %s", libjliPath)
}
}
}
cs := cString(jvmLibPath)
defer func() {
if cs != nil {
free(cs)
}
}()
libHandle := uintptr(C.dlopen((*C.char)(cs), C.RTLD_NOW|C.RTLD_GLOBAL))
if libHandle == 0 {
return errors.New("could not dynamically load libjvm.dylib")
}
cs2 := cString("JNI_GetDefaultJavaVMInitArgs")
defer free(cs2)
ptr := C.dlsym(unsafe.Pointer(libHandle), (*C.char)(cs2))
if ptr == nil {
return errors.New("could not find JNI_GetDefaultJavaVMInitArgs in libjvm.dylib")
}
C.var_JNI_GetDefaultJavaVMInitArgs = C.type_JNI_GetDefaultJavaVMInitArgs(ptr)
cs3 := cString("JNI_CreateJavaVM")
defer free(cs3)
ptr = C.dlsym(unsafe.Pointer(libHandle), (*C.char)(cs3))
if ptr == nil {
return errors.New("could not find JNI_CreateJavaVM in libjvm.dylib")
}
C.var_JNI_CreateJavaVM = C.type_JNI_CreateJavaVM(ptr)
return nil
}
func RunCFRLoop() {
C.jnigiRunCFRLoop()
}
the unit tests pass with your code. This is basically the same as I had in my fork (except for the last two methods, but this should not matter)
# go test
Exception in thread "main" java.lang.NoClassDefFoundError: java/foo/bar
Caused by: java.lang.ClassNotFoundException: java.foo.bar
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
Exception in thread "main" java.lang.NoClassDefFoundError: java/foo/bar
Caused by: java.lang.ClassNotFoundException: java.foo.bar
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
PASS
ok github.com/timob/jnigi 0.177s
I can test this along with my launcher to check everything else. If that works, I will create a PR (probably this weekend)
I could test everything within my app and your code works as expected. Thanks!
I am just trying to use this lib in my golang launcher for my Java Swing application. I managed to start up the JVM and load the first few lines of code before the EventDispatchThread starts. My Java program looks like
In the logs of the application I just see the lines before the EDT start but afterwards nothing happens and the launcher is just "hung".
I found the issues #35 and the PR #36 and #43 but I did not find the right solution for the problem. According to the example of OpenJDK (https://github.com/AdoptOpenJDK/openjdk-jdk/blob/master/src/java.base/macosx/native/libjli/java_md_macosx.m#L354) I see that we need to rund main in a new thread and "park" the first thread - OpenJDK uses CoreFoundation here...
Is this still needed for using this lib? Why has the corresponding code been removed from PR #43?
I have a full setup to help here, but my knowledge with CGO is very limited (I am a Java developer :) ).