Webstart Native Issues

Started by smith, March 30, 2016, 19:16:12

Previous topic - Next topic

smith

After dealing with about 50 different issues with Webstart and native libraries (mostly caching / wrong vm being launched etc). I finally managed to get it to work but I have a hack in place that I'm not happy with and was wondering why.

I have to do:
System.loadLibrary("glfw");

prior to:
GLFW.glfwInit()


None of the other native libs need this so I'm at a bit of a loss as to what is going on.
Here is the exception if I don't explicitly load glfw:
java.lang.RuntimeException: Failed to load library: glfw.dll (error code = 0)
	at org.lwjgl.system.windows.WindowsUtil.windowsThrowException(WindowsUtil.java:28)
	at org.lwjgl.system.windows.WindowsLibrary.<init>(WindowsLibrary.java:33)
	at org.lwjgl.system.APIUtil.apiCreateLibrary(APIUtil.java:76)
	at org.lwjgl.system.Library.loadNative(Library.java:141)
	at org.lwjgl.glfw.GLFW.<clinit>(GLFW.java:594)


Any ideas?


Kai

The thing is that this glfw.dll is not a JNI library. And it will not be loaded via System.loadLibrary.
Instead it is loaded via the platform-specific function to load shared libraries, which under Windows is LoadLibrary().
Now, that LoadLibrary does not know the file system folder where WebStart copied the DLL resource files given in the JNLP.
When calling System.loadLibrary() instead, the custom System Classloader under which your WebStart application runs, will be responsible for loading the shared library.
And this ClassLoader knows where WebStart copied the DLLs and uses that path to load the shared library.

The reason why everything works when you manually System.loadLibrary("glfw") is that whilst glfw.dll is not strictly a JNI library (i.e. it does not export function symbols for Java native methods) it still can be loaded as such. Internally, System.loadLibrary() also just does a platform-specific LoadLibrary(). Then when LWJGL internally calls the Win32 LoadLibrary() function the already loaded shared library is being picked up, because the platform's Win32 dynamic loader of the JVM process has already tracked that the glfw.dll was loaded, because the JVM did it in System.loadLibrary().
As a result, the dynamic loader will return the same in-memory reference to the shared library to LWJGL's call to LoadLibrary().

smith

So what is the preferred way to deploy lwjgl 3 using webstart?


Kai

How you did it seems to work. So, I'd say this is the preferred way. :)
You might also want to load jemalloc.dll in the same way.
I don't think that anyone is using Java WebStart currently with LWJGL 3 and it is also kinda not a supported deployment mechanism, meaning that no one has yet put any effort into making LWJGL 3 work with WebStart or is planning to maintain that support.

spasi

Quote from: smith on March 30, 2016, 22:36:09So what is the preferred way to deploy lwjgl 3 using webstart?

Do not use Webstart's nativelib resources. Instead, deploy the native libraries in simple (but OS-specific) JAR resources. LWJGL's SharedLibraryLoader will take care of the rest.

smith

I tried that but instead get:

java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
	at java.lang.ClassLoader.loadLibrary(Unknown Source)
	at java.lang.Runtime.loadLibrary0(Unknown Source)
	at java.lang.System.loadLibrary(Unknown Source)
	at org.lwjgl.system.Library.loadSystem(Library.java:97)
	at org.lwjgl.system.Library.<clinit>(Library.java:48)
	at org.lwjgl.system.MemoryAccess.<clinit>(MemoryAccess.java:22)
	at org.lwjgl.system.Pointer.<clinit>(Pointer.java:22)
	at org.lwjgl.glfw.GLFW.<clinit>(GLFW.java:594)


spasi

Hmm, looks like SharedLibraryLoader does not extract the natives when running under JWS. I'm not sure why that's there. I'll ask Mario, maybe it can be removed.

Kai

I'd say, one reason is that uninstalling a WebStart application via the Java control panel then will not workwill not be complete. The natives will still be around.

spasi

The natives are extracted in temp storage, so I don't think they will interfere with uninstallation.

spasi

Nightly build #63 should now work with JWS.

smith

Sweet, I'll give it a try!

Edit:
So that appears to work fine for the lwjgl libs, is there any way I can use the Library loader to load my own native libs?
I've tried Library.loadNative() but I get:

Caused by: java.lang.RuntimeException: Failed to load library: rxtxSerial.dll (error code = 126)
	at org.lwjgl.system.windows.WindowsUtil.windowsThrowException(WindowsUtil.java:15)
	at org.lwjgl.system.windows.WindowsLibrary.<init>(WindowsLibrary.java:39)
	at org.lwjgl.system.APIUtil.apiCreateLibrary(APIUtil.java:81)
	at org.lwjgl.system.Library.loadNative(Library.java:149)


I can always use the webstart nativelib method but I would rather keep all the native libs in one jar.

spasi

Sorry for not replying earlier, missed your edit.

That's a great idea. I'll refactor the loader to:

a) lazily extract only libraries that are actually used, and
b) work with any library specified as a relative path.

Thanks!

smith


spasi

The above loader improvements are available in build 3.0.0 #66.

spasi

Quote from: Kai on March 31, 2016, 08:02:13The natives will still be around.

I tried to use File.deleteOnExit() for this, without success. The shared libraries cannot be deleted while loaded by the process. In theory, we could track libraries loaded with Library.loadNative, free them with a shutdown hook, then the files will be deleted. The problem is Library.loadSystem, which calls System.loadLibrary, currently used for the lwjgl shared library. Afaict, it can only be freed when the classloader that loaded it is garbage collected.