I'm trying to work out if there is a definitive, recommended, robust way to work with native libraries in Java and Maven.
I know it's possible to do this by putting libraries in the right places, configuring java.library.path and calling System.loadLibrary etc. - but I want a proper solution that works across the whole development / deployment life-cycle and doesn't involve lots of hacks.
My requirements seem fairly straightforward:
Configuration 100% via Maven
Ability to include native dependencies for different platforms / architectures (mainly Linux and Windows, others would be nice though...)
Ability to create a runnable .jar that doesn't require the user to fiddle with command line options to run it (i.e. it will unpack and load any native dependencies it needs at runtime, preferably as temp files or something like that)
The code works during development (in Eclipse)
The code works during normal Maven builds at the command line
Basically, I want natives to just work both during development and for the user at runtime.
Is this possible? If so what do I need to configure to make it happen?
It certainly is possible. A good example that I came across which solves the same problem is sqlite wrapper at https://bitbucket.org/xerial/sqlite-jdbc/overview
By following a pre defined packaging structure for each architecture, we can programmatically extract the desired native library from class path into a temp folder and can set the desired java.library.path to load the native library.
All native libraries would be packaged in the src/main/resources folder
At runtime there needs to be a bootstrap code which can determine the OS, architecture and choose the right native library to extract
the same bootstrap code can extract the native library into a temp folder and call System.loadLibrary
I have not personally tried executing the above flow but could not think of any other way.
Related
I am planning to use GraalVM's native-image tools to package an enterprise application. I've heard that they get packaged into single executable files which would not suit my use case. My application is a pretty large enterprise application which I would like to package into a one-directory application that contains all the dlls and the libraries packaged in. I want to package it similar to how in pyinstaller we can use the --onedir option to package into a single directory. Could anyone please specify clearly how to achieve this because I do not seem to get my answer even after hours of research.
PS: sorry for my bad english (if it was bad)
GraalVM native image builds a standalone executable or a shared library.
I don't know the details on how the pyinstaller works. When using GraalVM native image your whole application -- your code, the dependencies, the JDK runtime library classes, the VM components like GC (all of which are in the form of the JVM bytecode) analysed together, initialized, and compiled together.
I don't think you can use GraalVN native image to get separate files, at least right now.
Let's imagine, I created a bunch of command line utilities, written in Scala and/or Java, and I'm using SBT to build them. They all use a couple of libraries, some of them pretty big, and in case of Scala, also the (not so small) Scala standard library.
I want to have these utilities in completely built form (runnable .jar files) to be able to instantly launch any of them and, if needed, also easily distribute them. But what I don't want is to include their dependencies in all of them, because they will be taking disk space. Instead I want them to get dependencies from a shared folder at runtime, and the application jar should contain only "my" classes.
The question is, is there a standard way to accomplish this? If so, where must be shared .jars located? Otherwise, what would you recommend to do?
You can set the CLASSPATH for this.
The JRE searched for classes in the .jar files named in the CLASSPATH.
Additionally all .jar files in the directory jre/lib/ext are used.
To find the complete serching in classpathes please consult the official documentation from Oracle.
Something you might like to consider (although it will require slightly changing what you plan to do) is to have a local Maven repository.
You could have SBT publish libraries to it when they're built. Instead of building runnable JARs, you could run your applications via SBT, which would pull libraries from the local repository as/when required.
The benefit of this is that all the plumbing to do this is built into SBT, and it would make distribution trivial.
The downside is that you would have to run your apps via SBT instead of building runnable JARs. Whether that will work for you, I don't know.
What's the best option for packaging and distributing a command line application written in Java, targetting OS/X and Unix?
The executable jar option seems fairly robust, and my distribution doesn't need to be super fancy, so right now I'm just thinking of plonking a bash script next to that with the relevant java invocation and being done with it.
I'm wondering if there's something similar to python's bdist package that would let me easily make nice installers for both target platforms. Or if there's some more obvious way that I'm missing where I can turn the entire distribution in to a executable file that wraps the jar in some way.
Since you are providing a CLI application it may be easiest to just provide the script you already mentioned. I usually try to keep it self-contained, e. g. not referencing external paths / using only relative paths etc. And maybe a readme.txt file.
If you would like to provide a full-blown installer, you might want to take a look at IzPack, a tool for generating installers for Java deliverables. It also allows the wizard to run in console mode, if you do not have a graphical environment available for installation (see the "Features" page in the link above).
I am just starting to learn JNI. I have been following a simple example, and I have created a Java app that calls a Hello World method in a native library. I'd like to target Win32 and Linux x86.
My library resides in a DLL, and I can call it just fine using LoadLibrary when the DLL is added to the root of my Eclipse project.
However, I can't figure out how to get Eclipse to export a runnable JAR that includes the DLL and the .SO file for Linux.
So my question is basically; how would you go about creating a project in Eclipse and include several versions of the same native library?
Thank you,
Martin
For runnable JARs, what you need to do is extract to the temporary directory (maybe in a static { } block) and then load the DLL from that directory (using System.loadLibrary() in the same block). To do this, you need to subclass ClassLoader and override the findLibrary() method to allow libraries to be found in that directory. You can do whatever logic you need to here to load the particular platform libraries. To be honest, the naming for the libraries on the different platforms should be similar anyway -- I believe that you omit the 'lib' part when loading, and the extension. That's the gist of it. Probably easier to use One-JAR as the other poster mentioned :)
You might want to check out the One-JAR project. It lets you package your application and its dependencies (including native libraries) to a single jar file.
my application needs multiple jars to work. Since it is a desktop application i can not hold the user responsible of installing. So in my build script i unzip the jars content in to my build directory delete manifest files, compile my software and jar it again. Everything works as it should my question is are there any long term side effects to this process?
In the past, there were JARs with weird content (like the DB2 driver which contains com.ibm and com.IBM; after decompressing in a Windows filesystem, those two packages would be merged).
The only issue you need to be aware of are signed jars and other files in META-INF which might have the same name in multiple source JARs.
A simple solution for all these issues is to use One-JAR. It allows to wrap several JARs into one without unpacking them, first. And read this answer: Easiest way to merge a release into one JAR file
A simpler solution (IMO) is using Maven's assembly plugin, which is also described in one of the answers to another question which was linked to in a previous Q&A. This is provided you are using Maven (which is a recommended tool by its own right) as a build tool.
If you want a no fuss way for the end user to kick off a program with multiple jar dependencies you may want to look at Launch4j or Jsmooth (I prefer Launch4j). Both are programs that create executables that wrap jar(s) and the JRE together so that to the end user it appears no different then any other executable.
Another great option is ProGuard, which can also shrink and/or obfuscate the code too.
If your primary target platform is Windows desktop, then you could also consider generating an Windows native exe from the jars of your application
If some of the jars are signed you lose the signature by unpacking/repacking it.
Well you're throwing away the MANIFEST of your third party jars, so that could cause you problems. For example you could be causing security issues by throwing away the "Sealed" attribute.
Why not just create a simple installer and a script to launch your application which sets the CLASSPATH correctly?
One-JAR will do the job, and has a new release (0.97) which supports frameworks like Spring and Guice, which users are now packing into One-JAR archives. http://one-jar.sourceforge.net
Ference Hechler also did some great work inside Eclipse with the Eclipse export wizard: we worked together on FatJar/One-JAR from which the Eclipse work grew, and I can recommend that as an approach, though I don't know how well it handles the frameworks.