1

I am trying to create an executable jar file for a project which has a java library resource folder as dependency within the project itself. I want to deploy that jar without the need for the user to download that dependency separately. So I added that java library as a resource to my project and built it. Inside the project source-code, I have the following code to try and set "java.library.path" to include that resource.

Here is what my code looks like:

URL url = Staple.class.getResource("/path/to/java_library_name");
String stringUrl = url.toString();
System.setProperty( "java.library.path", stringUrl );  
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );  
fieldSysPath.setAccessible( true );  
fieldSysPath.set( null, null ); 

However, when building the code and trying to execute the jar itself, this doesn't work and I am getting the following error:

no java_library_name in java.library.path: [jar, file, /path/to/project/target/jar_name-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/path/to/java_library_name]
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2660)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
at java.base/java.lang.System.loadLibrary(System.java:1867)
.....

What is the problem with the above code? It looks like it got the whole path wrong and composed something like /path/to/project/target/jar_name-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/path/to/java_library_name
How otherwise can I programmatically retrieve the path which includes the java library folder after jar is created, and during runtime?

GuyTal
  • 207
  • 5
  • 17

1 Answers1

3

native resources (the ones you are trying to load with loadLibrary) need to be FILES, and the resources returned by MyClass.class.getResource are URLs; on purpose, as they need not be files. In fact, they usually aren't, as is the case right here: Your resource isn't a file. It's a zip entry in a jar file, which is not a file.

There's no way around this; OS restriction.

I know, I know. Sucks. But you gotta do it: Unpack the DLL into a temporary location (you CAN do this; use getResourceAsStream instead, and copy it to a file using the new Files API; it's a one-liner these days (Files.write), then load that.

Of course, writing executable code into a temp dir and then executing it is very security sensitive; I have no solutions for that dilemma.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Thanks I will try that. What if I want to make the jar compatible to different operation system? Say I want to pack inside the jar .dll, .jnilib as well as .so ? Do you think this is possible? What would you suggest doing in order for this to work? – GuyTal Jan 08 '20 at 00:02
  • It’s actually [Files.copy](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/file/Files.html#copy%28java.io.InputStream,java.nio.file.Path,java.nio.file.CopyOption...%29), not Files.write. – VGR Jan 08 '20 at 02:12
  • @GuyTal you need to ship the DLL/so/jnilib file for each arch/os combo you want to run on, then you need a strategy to find the appropriate file, presumably by inspecting some of the stuff in System.getProperty (such as System.getProperty("os.name")), then unpack the right file, save it in the right place (and 'right place' probably also depends on OS), and then load it. And that's assuming you manage to compile the native code to all these platforms. So, doable – just.. hard. – rzwitserloot Jan 08 '20 at 17:06