Bug 845

Summary: Allow (discouraged) use of one big-fat jar file [java classes plus all native 'os.and.arch' libraries]
Product: [JogAmp] Gluegen Reporter: Sven Gothel <sgothel>
Component: coreAssignee: Sven Gothel <sgothel>
Status: RESOLVED FIXED    
Severity: enhancement CC: gouessej, ruckc
Priority: ---    
Version: 2   
Hardware: All   
OS: all   
Type: --- SCM Refs:
4aa1478b2e4f1401b08d093461b37a14c9501c29 b05f716cbcbc379588050c8f3d91579b3a14ec88
Workaround: ---

Description Sven Gothel 2013-10-01 13:08:46 CEST
I discourage this design, since such deployment removes our artifacts as stored in
the jar's manifest file, which helps identifying the jogamp modules for bug reports etc.

Further more, adding all native library files for all supported platforms
will add-up to +3M of _compressed_ jar data!

However, since we don't want to patronize our user base, 
we shall add this capability to our native JAR lib loading mechanism.
Comment 1 Sven Gothel 2013-10-01 13:17:50 CEST
http://jogamp.org/git/?p=gluegen.git;a=commit;h=4aa1478b2e4f1401b08d093461b37a14c9501c29

    JNILibLoaderBase.addNativeJarLibsImpl(..):
    
    If the modules's jar file contains the folder 'natives/<os.and.arch>/'
    we assume a big-fat jar and attempt to load all native libraries from the same.
    
    The test for above folder is performed via the class ClassLoader's getResource(..)
    and is considered inexpensive.
    
    If the folder exists and native libraries could be loaded, the method returns successfull.
    
    Otherwise, the 'slim' jar file is attempted to be loaded, even if such folder exist.
Comment 2 Sven Gothel 2013-10-01 13:38:06 CEST
.. doesn't work w/ a JOGL test case .. 


NativeLibrary.findLibrary(<nativewindow_x11>) (TempJarCache): /tmp/jogamp_0000/file_cache/jln7213615344288012373/jln7163851605135055114/natives/linux-amd64/libnativewindow_x11.so
JNILibLoaderBase: loadLibraryInternal(nativewindow_x11), TempJarCache: /tmp/jogamp_0000/file_cache/jln7213615344288012373/jln7163851605135055114/natives/linux-amd64/libnativewindow_x11.so
JNILibLoaderBase: System.load(/tmp/jogamp_0000/file_cache/jln7213615344288012373/jln7163851605135055114/natives/linux-amd64/libnativewindow_x11.so) - mode 2
JNILibLoaderBase: loadLibraryInternal(nativewindow_x11): OK - mode 2
JNILibLoaderBase: Loaded Native Library: nativewindow_x11
JNILibLoaderBase: loaded nativewindow_x11
main - DynamicLibraryBundle.init start with: jogamp.opengl.x11.glx.X11GLXDynamicLibraryBundleInfo
NativeLibrary.findLibrary(<libGL.so.1>) (TempJarCache): null
NativeLibrary.findLibrary(<libGL.so.1>, sun.misc.Launcher$AppClassLoader@13de6be9) (CL): null
NativeLibrary.open(global true): Trying to load libGL.so.1

...


Stack: [0x00007f14ab43d000,0x00007f14ab53e000],  sp=0x00007f14ab5396b0,  free space=1009k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [ld-linux-x86-64.so.2+0x907b]  _dl_rtld_di_serinfo+0xa7b
C  0x00007f14ab57a548

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.lang.ClassLoader$NativeLibrary.find(Ljava/lang/String;)J+0
j  java.lang.ClassLoader.findNative(Ljava/lang/ClassLoader;Ljava/lang/String;)J+49
v  ~StubRoutines::call_stub
j  jogamp.common.os.UnixDynamicLinkerImpl.dlopen(Ljava/lang/String;I)J+0
j  jogamp.common.os.UnixDynamicLinkerImpl.openLibraryImpl(Ljava/lang/String;IZ)J+6
j  jogamp.common.os.PosixDynamicLinkerImpl.openLibraryGlobal(Ljava/lang/String;Z)J+6
j  com.jogamp.common.os.NativeLibrary.open(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/ClassLoader;Z)Lcom/jogamp/common/os/NativeLibrary;+103
j  com.jogamp.common.os.NativeLibrary.open(Ljava/lang/String;Ljava/lang/ClassLoader;Z)Lcom/jogamp/common/os/NativeLibrary;+6
j  com.jogamp.common.os.DynamicLibraryBundle.loadFirstAvailable(Ljava/util/List;Ljava/lang/ClassLoader;Z)Lcom/jogamp/common/os/NativeLibrary;+27
j  com.jogamp.common.os.DynamicLibraryBundle.loadLibraries()V+77
j  com.jogamp.common.os.DynamicLibraryBundle.access$000(Lcom/jogamp/common/os/DynamicLibraryBundle;)V+1
j  com.jogamp.common.os.DynamicLibraryBundle$1.run()V+4
j  com.jogamp.common.util.RunnableExecutor$CurrentThreadExecutor.invoke(ZLjava/lang/Runnable;)V+1
j  com.jogamp.common.os.DynamicLibraryBundle.<init>(Lcom/jogamp/common/os/DynamicLibraryBundleInfo;)V+250
j  jogamp.opengl.GLDynamicLookupHelper.<init>(Ljogamp/opengl/GLDynamicLibraryBundleInfo;)V+2
j  jogamp.opengl.DesktopGLDynamicLookupHelper.<init>(Ljogamp/opengl/DesktopGLDynamicLibraryBundleInfo;)V+2
j  jogamp.opengl.x11.glx.X11GLXDrawableFactory$1.run()Ljogamp/opengl/DesktopGLDynamicLookupHelper;+11
j  jogamp.opengl.x11.glx.X11GLXDrawableFactory$1.run()Ljava/lang/Object;+1
Comment 3 Sven Gothel 2013-10-01 15:37:57 CEST
    Bug 845: Fix JNILibLoaderBase.addNativeJarLibsImpl(..) fat-jar case.
    
    Always use the jar-basename when calling TempJarCache.addNativeLibs(..),
    otherwise it is mapped and loaded multiple times leading to different native libraries.
    
    Simplify addNativeJarLibsImpl(..) argument semantics by passing complete jarBasename
    and nativeJarBasename (w/ suffix).
    
    Added manual test scripts validating [gluegen + jogl] usage
    with multi (Bug 843) and fat (Bug 845) jar configurations.
Comment 4 Julien Gouesse 2013-10-02 16:33:59 CEST
(In reply to comment #0)
> I discourage this design, since such deployment removes our artifacts as
> stored in
> the jar's manifest file, which helps identifying the jogamp modules for bug
> reports etc.
> 
> Further more, adding all native library files for all supported platforms
> will add-up to +3M of _compressed_ jar data!
> 
> However, since we don't want to patronize our user base, 
> we shall add this capability to our native JAR lib loading mechanism.

Moreover, some web browsers may modify the content of JARs when downloading them which de facto prevents from "running" them. The default archivers under GNU Linux and Windows often open them as ZIP archives which prevents from running them once again.

A fat JAR should only be used in the following cases:
- with Java Web Start (and JARs signed with a "trusted" certificate), when the OCSP requests take a very long time because of an high number of JARs and/or slow OCSP servers. Oracle plans to replace OCSP by another mechanism
- with another installer in order to improve the native integration of an application based on JOGL, for example with IzPack. Using a native installer is very helpful to work around the limitations of the deployment of applications as JARs as double-clicking on them is not guaranteed to work as I've just explained above.

Fat JARs can be created with some build tools including Ant. For example, you can use the "zip" task with the "zipgroupfileset" task to merge JARs but the raw result is a bit messy.
Comment 5 Julien Gouesse 2014-01-24 16:36:46 CET
At first, there is a risk of crash Ant when updating a JAR/ZIP with duplicate entries, I advise to use at least Ant 1.9.2:
https://issues.apache.org/bugzilla/show_bug.cgi?id=54967

This is an example of macro that merges jogl-all.jar, gluegen-rt.jar, gluegen-rt-natives-windows-i586.jar and jogl-all-natives-windows-i586.jar into a single fat jar:

<macrodef name="create_jogamp_fatjar">
    <attribute name="src" />
    <attribute name="dest" />
    <sequential>
        <delete file="@{dest}" failonerror="false" />
        <unzip src="@{src}/gluegen-rt-natives-windows-i586.jar" dest="@{src}/natives/windows-i586">
            <patternset>
                <include name="*.dll" />
            </patternset>
        </unzip>
        <unzip src="@{src}/jogl-all-natives-windows-i586.jar" dest="@{src}/natives/windows-i586">
            <patternset>
                <include name="*.dll" />
            </patternset>
        </unzip>
        <zip destfile="@{dest}">
            <zipgroupfileset dir="@{src}" includes="*.jar" excludes="*-natives-*.jar" />
            <zipfileset dir="@{src}" includes="**/*.dll" />
            <zipfileset dir="@{src}" includes="**/*.so" />
        </zip>
        <delete dir="@{src}/natives" />
    </sequential>
</macrodef>
Comment 6 Curtis Ruck 2014-05-01 06:56:22 CEST
Any chance of supporting loading all libraries from / as a fall-back if not found ind /natives/<os.and.arch>/?

When using maven-shade-plugin to generate a fat-jar, it squashes them all into where they are located in their original jars.

Another idea might be to nest them into their natives/<os.and.arch>/ in their native jars instead of at the root level.
Comment 7 Julien Gouesse 2014-11-18 22:36:37 CET
(In reply to comment #6)
> Any chance of supporting loading all libraries from / as a fall-back if not
> found ind /natives/<os.and.arch>/?
> 
> When using maven-shade-plugin to generate a fat-jar, it squashes them all
> into where they are located in their original jars.
> 
> Another idea might be to nest them into their natives/<os.and.arch>/ in
> their native jars instead of at the root level.

Maybe you could use a post-treatment rather than asking us to modify JOGL JAR file handling. We won't support tens of different conventions. You can use Maven Ant plugin and modify the resulting JARs by yourself.