node-java
node-java copied to clipboard
Issues with jvm_dll_path.json
I have noticed that the postInstall.js does not execute in some environments resulting in the jvm path not being written to jvm_dll_path.json. Yes, we can run the script manually on a dev machine, but this does not work for server-side environments.
Another issue is that the path being written to jvm_dll_path.json reference a specific java version. If the Java version on a server is updated or the location is changed, then the application is broken.
Is there a reason that you need to save the JVM path to a flat file? Why not simply determine the JVM path at runtime?
This is very important for environments like Electron, where you need the running machine's JVM instead of the compiling machine's JVM.
This is still an issue for me in 2020. What is a workaround?
@Petros99 We've been struggling with this issue for quite some time and had finally settled on "You have to install adoptopenjdk-8.jdk in /Library/Java/JavaVirtualMachines to run our application because that's what's on the Mac Xcode image on CircleCI". I finally found a workaround for the problem, at least on OS X for now. I hesitate to share this because it's so hacky, but... it works.
It seems the most intransigent issue here is not jvm_dll_path.json but really nodejavabridge_bindings.node, because the path to the JLI library gets hard-coded into the node file. I'm not familiar enough with how node-gyp works to know if this is absolutely necessary, but it's definitely very brittle, as evidenced by this exact case: it requires every machine on which an application is installed to have its native library dependency installed in exactly the same place. Getting jvm_dll_path.json to have a different path was a bit tricky, but getting nodejavabridge_bindings.node to include a different path required dirty nasty deeds.
Anyway, everything is in this commit, at least the relevant changes. I had previously tried monkey-patching the find_java_libdir.sh script in node-java, which actually ended up not fixing anything (I think because I actually managed to apply the patch after the script had already been run: one of the changes in this commit is applying the patch in postinstall rather than prepublish). The changes here do two things:
- Replaces the path returned from postInstall.js to point to the location of the JRE integrated into the application. This changes the location stored in jvm_dll_path.json, but not the location that gets hard-coded into nodejavabridge_bindings.node.
- Sets the include and library dirs in binding.gyp to point to the location of the JRE integrated into the application. This changes the location that gets hard-coded into nodejavabridge_bindings.node, but not the location stored in jvm_dll_path.json.
I think you need to change both of those locations or the fix won't work. I tested it with just the first and just the second and got different errors in each case.
The really big trick here though is copy a JDK (not just JRE, because you need the header files) into the installation location of the application. I did this manually on my local system while working on this, but it's reflected in the change to the CircleCI build configuration. It copies the JDK installed on the build image to the application install path, /Applications/XNAT-Desktop-Client.app in our case. AdoptOpenJDK, Zulu, Oracle, etc., all have the standard JDK layout (i.e. bin, include, lib, jre folders) in Contents/Home, but the JRE we bundle with the application gets installed to Contents/Resources in the installed application structure. This copies Contents/Home to Contents/Resources so that this resolves properly.
Note that I don't change JAVA_HOME or the path to use this copy of the JDK. Having the include headers in the correct place seems to be adequate. At one point I did actually configure the build to use the copy and had to modify Contents/_CodeSignature/CodeResources to change references from <key>Home/<file></key>
to <key>Resources/<file></key>
but I think I had changed JAVA_HOME and my path to use the JDK copy at that point. Also the JDK on the CircleCI Mac Xcode image doesn't even have the Contents/_CodeSignature folder so there's nothing to do there.
This is admittedly ghastly and it also means that you can't install the application anywhere other than /Applications, but I think that's easier to live with than requiring our users to install a JDK (they may even be able to install a JDK, since many of them don't have administrator permissions on their machines. I haven't even started trying this with our Windows build yet. Hopefully someone else on our team (someone with a working Windows machine maybe 😄 ) will do that instead.
Really, I'd love to have functionality in this module that could locate the JRE/JDK relative to the application structure and/or have the path be configurable so installing the JRE along with the application could be optional. That said, it seems at least in part the issue goes down to the node-gyp level, but again I'm not at all familiar with that so maybe someone who understands it could figure out a better (real) solution. Hopefully this helps though.