Binary Distribution of Java Apps and Libs which Include Native Libraries... Or How to Package libxine-java?
Loading native libraries in Java
Although any kind of file, and especially native libraries, can be stored in JARs, native libraries cannot be loaded from there by the system dynamic loader.
In Java, there are three ways in which native libs can be found and loaded:
- They are placed in the system library folder (/usr/lib on Mas OS X and Linux, C:\\WINDOWS\\system32 on Win32).
- The location of native libs are specified using the java.library.path property when the Java VM is invoked.
- A native library can be loaded directly from an absolute path.
Similar to these options, various projects use different strategies to load their libraries:
- Extract to /tmp and Load: The SWT project provides its native libraries inside a platform-specific JAR. However, it first tries to load a native library from the system folder. If the library is not found, SWT extract its libraries into the Java temp folder (/tmp on Mac OS X and Linux) and loads them from there. Although this allows to ship only a single JAR, it is not optimal for xine-lib. xine-lib contains more than one hundred plug-ins which all would have to be extracted on the first start after a reboot.
- Startup Script: If the final application is started by startup script, the script can determine its location and start the Java VM with an appropriate java.library.path.
- Self Inspection: Instead of specifying the path to the native libraries at the Java VM invokation, a Java lib can inspect the classpath (java.class.path) for the path to its JAR. From there, it can access the requires libraries using their absolute paths.
JNI and dependent libraries
If a native library is loaded by its absolute path, a problem arises when it is linked against other libraries that are not in the system search path. On Windows, the folder of the native library is automatically added to the search path for dependent libraries. There, if all native libraries are in the same folder, the runtime loader can find and load all libraries correctly. On Linux and Mac OS X, however, extra care has to be taken to allow for dependent libraries that are placed in the same folder as the native library. Let's assume we have a Java library A.jar which loads native library foo, which itself depends on library bar.
- On Linux, the --rpath options of the linker allows to add arbitrary folders to the runtime linker search path. In order to add the path of the native library, the special
$ORIGINcan be used when linking foo. When foo is loaded, dependent libraries are searched in the same folder as foo.
- On Mac OS X, a similar behavior is achieved by using @loader_path/bar.dylib as the install_name of library bar. When linking foo against bar, the install_name of bar is stored in foo. When foo is loaded, @loader_path is set to foo's folder, and bar can be found in the same folder.
An example project with a dummy library that searches for its JAR in the classpath to find its native libraries automatically is here.
What should be in the package?
In addition to the native libraries provided by libxine-java, to actually use libxine-java, the xine-lib and related libs such as libdvdread and libdvdcss are required. If these libraries should be included in a future libxine-java package depends on the deployment platform:
- On Linux, it is safe to assume that xine-lib and related libs can be provided by a package manager (e.g. for Debian/Ubuntu/OpenSuse..). So, a simple test if the libs are installed, and the provision of an error message if they are missing, should be all that is needed.
- On Mac OS X, xine-lib can be provided by Fink or MacPorts. However, requiring the end user to install xine-lib with one of the two is cumbersome. It would be better to just ship the complete xine-lib binaries.
- On Windows, there are AFAIK no package managers for open-source software, especially none that provides xine-lib, so the libraries have to be shipped anyway.
How to build all the required libraries?
Building all the dependent libraries manually would be a tedious task. With a bit of luck, the efforts of the VideoLan team to automatically build all libraries can be directly harvested. xine and vlc most likely share most of their dependent libraries. Have a look at the very helpful/detailed GIT changelog for extras/contrib and the binary contrib packages for OS X Intel/PPC and Win32.
Finally, I've taken a look at the way Java apps are provided by different projects. It looks like there is a common agreement over the best way to deploy a Java application for a particular platform:
- On Mac OS X, a GUI-application consists of a folder (named ApplicationName.app), which contains everything required. All JARs and native libraries can be placed inside this folder. The folder can then be downloaded as a ZIP archive or a DMG disk image.
- On Linux, an application (e.g. Eclipse) is shipped as an archived application folder that contains a startup script. Inside this folder, all required libs are stored similar to the Mac OS X application bundle.
- On Windows, the preferred way to distribute an application is to provide a GUI-installer. Such an installer can create a folder in C:\\Program\ Files\NewAppName and put all JARs and libs in there. A startup script (batch script) can either contain the absolute path to the folder (as it was created by the installer) or can dynamically determine its location and invoke the Java VM accordingly.
Will it work on the web?
In addition to these, Java Web Start allows to store all JARs and other resources, including native libraries, on a web server, and just specify the corresponding URLs in a JNLP XML file.
The libxine-java plan
For libxine-java, I'll try to provide a single folder which contains a JAR and all required libs (including xine-lib). libxine-java should use the Self Inspection to check the classpath and load all required libraries from the folder. By this, all distribution scenarios are fine. If xine-lib (with the one hundred plug-ins) can be provided by Java Web Start is not clear to me yet.