I've been studying Maven in my free time over the last several days but can't seem to figure out how to organize a project so that the JOGL libraries are used. I would preferably like the following:
- Automatically download, if necessary, the OS-specific JOGL zip file from here (contains 4 jar files and some native library files (.so/.dll)); or depend on a Maven project which is a wrapper of one of the files.
- Unzip that zip file appropriately, so that:
- the jar files are added to the classpath and deployed as necessary, and
- the native library files are added to the final jar file (would this enable them to be automatically used, or would I need something more involved?)
I think part of my problem is that I don't fully understand the use of JOGL, where to place the native libraries when running the code, etc. I need to go back to the basics and write a JOGL hello world, compile it from the command line and run it from the command line to see exactly what it requires as far as directory placement of the native libraries; I may go do that right now, actually.
With item 1, I've found some OS-specific features; Maven profiles can be activated based on properties of the system, which include the operating system. So then I could activate a Windows profile which has a dependency of the Windows-specific JOGL library, same for the Linux, and both having a 64-bit alter ego. (Activation official docs / unofficial docs.)
I have tried creating a Maven repository based on a JOGL jar file, and then adding the JOGL jar file project as a dependency of my project; the dependency is downloaded, but not used. I have no idea where the jar file goes or how to use it, unpack it, etc. Here is the command I used.
So, in short: JOGL consists of four .jar files and some native libraries. How can I integrate those files into my Maven project so that I can write a JOGL application with Maven handling my build process? Furthermore, how can I use a different set of files depending on the operating system, because of course the native libraries and even the .jar files differ between Windows, Linux and Mac.
Jogamp now contains support for Maven, for the jogl components (jocl and joal support is forthcoming). As of 2.0-rc11, packages are pushed to Maven Central.
Just put this to your pom:
<dependencies>
<dependency>
<groupId>org.jogamp.gluegen</groupId>
<artifactId>gluegen-rt-main</artifactId>
<version>2.0-rc11</version>
</dependency>
<dependency>
<groupId>org.jogamp.jogl</groupId>
<artifactId>jogl-all-main</artifactId>
<version>2.0-rc11</version>
</dependency>
</dependencies>
Maven will pull all of the dependencies the next time you attempt to build the project.
Read more here on the wiki
When dealing with JNI and Maven, Projects With JNI is the reference one should start with. It covers a lot more than your current problem (which is "just" using a library that relies on JNI and native libraries) but, well, he who can do more can do less.
If you read it carefully, you'll see that one solution to use JNI libraries is to bundle them in architecture specific JARs so that you can depend on them like any other dependency from a Maven point of view. This is actually how JOGL version 1.1.1 is packaged in http://download.java.net/maven/2/net/java/dev/jogl/, there is one JAR artifact with the Java classes and several architecture specific JARs artifacts with the native libraries.
JNI library archived within the jar
The solution I ended up using was to
store the compiled jni library in the
jar alongside the class files.
This means either cross-compiling for
all possible architectures, or more
simply, having a different jar for
each architecture. This latter fits
quite well with our setup - where
almost all of our machines are
Linux-i386, with a smattering of win32
boxes.
Sadly System.load()
can't cope with
loading libraries from within a jar,
so we'll therefore need a custom
loader which extracts the library to a
temporary file at runtime; this is
obviously achievable, however.
Then, as explained, the idea is to use a custom library loader to load the native library. The good news is that such a loader is "provided" as explained below.
Library loader
We now have our JNI library on the
class path, so we need a way of
loading it. I created a separate
project which would extract JNI
libraries from the class path, then
load them. Find it at
http://opensource.mxtelecom.com/maven/repo/com/wapmx/native/mx-native-loader/1.2/.
This is added as a dependency to the
pom, obviously.
To use it, call
com.wapmx.nativeutils.jniloader.NativeLoader.loadLibrary(libname)
.
More information is in the javadoc for
NativeLoader
.
I generally prefer to wrap such things
in a try/catch block, as follows:
public class Sqrt {
static {
try {
NativeLoader.loadLibrary("sqrt");
} catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}
}
/* ... class body ... */
}
We should now be at the point where
our junit tests work from maven; a mvn
test should work! It should also work
fine from an IDE.
Now, to answer your questions, how to:
Automatically download, if necessary, the OS-specific JOGL zip file from here (contains 4 jar files and some native library files (.so/.dll)); or depend on a Maven project which is a wrapper of one of the files.
Sadly, JOGL 2.0 jars aren't available in the Maven repository of java.net so you'll have to deal with that and either make them available in a private repository or install them manually in the local repository of each developer. To do so, use mvn install:install-file
as documented in the Guide to installing 3rd party JARs (and not mvn deploy:deploy-file
as you did, this goal is used to install artifacts to a remote repository).
Personally, I would download JOGL 2.0 ZIPs from the URL you provided, package it as they did with JOGL 1.1.1 (one Java JAR and several specific JARs for native libraries) and install the JARs in each local repository for now. Then, declare a standard dependency on the Java artifact and, indeed, use profiles for the architecture specific dependency. Something like this:
<project>
...
<dependencies>
<dependency>
<groupId>net.java.dev.jogl</groupId>
<artifactId>jogl</artifactId>
<version>2.0-beta10</version>
</dependency>
...
</dependencies>
...
<profiles>
<profile>
<id>linux-i586</id>
<activation>
<os>
<arch>i386</arch>
<family>unix</family>
<name>linux</name>
</os>
</activation>
<dependencies>
<dependency>
<groupId>net.java.dev.jogl.jogl-linux-i586</groupId>
<artifactId>jogl-linux-i586</artifactId>
<version>2.0-beta10</version>
</dependency>
</dependencies>
</profile>
...
</profiles>
...
</project>
Don't forget to add the repository required for the custom library loader and the dependency:
<project>
<repositories>
<repository>
<id>opensource.mxtelecom.com</id>
<url>http://opensource.mxtelecom.com/maven/repo</url>
</repository>
...
<repositories>
...
<dependencies>
<dependency>
<groupId>com.wapmx.native</groupId>
<artifactId>mx-native-loader</artifactId>
<version>1.2</version>
</dependency>
...
</dependencies>
...
</project>
Regarding the second part of your question:
Unzip that zip file appropriately, so that (...)
As I explained, you actually won't depend on ZIP files but on JARs and you won't need to uncompress them neither during development nor to distribute your project. For the distribution, you'll just need to create a jar including the dependencies. This can be done with the maven-assembly-plugin. See this answer for example for more details on this.
There is a maven repo for JOGL 2.0 here: http://jogamp.org/deployment/maven/
I use SBT for building my projects. The resolver you need to add to build.sbt
:
resolvers += MavenRepository("jogamp", "http://jogamp.org/deployment/maven")
And the dependency, e.g., for base jogl library:
libraryDependencies += "org.jogamp.jogl" % "jogl-all" % "2.0-rc9"
In maven xml files this would be something like (according to this):
<settings>
<profiles>
<profile>
<id>jogamp</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>jogamp-remote</id>
<name>jogamp test mirror</name>
<url>http://www.jogamp.org/deployment/maven/</url>
<layout>default</layout>
</repository>
</repositories>
</profile>
</profiles>
</settings>
With dependency declared as:
<dependency>
<groupId>org.jogamp.jogl</groupId>
<artifactId>jogl-all</artifactId>
<version>2.0-rc9</version>
</dependency>
To automatically download proper native jar, in sbt I do something like:
sys.props("os.name") match {
case "Linux" => "org.jogamp.jogl" % "jogl-all-natives-linux-i586" % "2.0-rc9"
... etc. ...
I don't know JOGL library but I have experience with Java3d, which has the same installation/build issues. There are two ways to achive this:
tell developers to install JOGL unassisted, then treat JOGL libraries as system dependencies like we do with Java3d
<dependency>
<groupId>javax.java3d</groupId>
<artifactId>j3dcore</artifactId>
<version>1.5.1</version>
<scope>system</scope>
<systemPath>${java.home}/lib/ext/j3dcore.jar</systemPath>
</dependency>
place all jars and system dependent libraries in own repository and create suitable poms for them
If you strongly want to automate with Maven installation of JOGL you can try use maven-antrun-plugin or create own Maven plugin that handles installation (a good example is Cargo that downloads servers and unpack it).
I consider to use first option - tell developers to install JOGL. In our case Java3d application is distributed by Java WebStart so for them installation of Java3d is fully automated by WebStart.
Here, for reference, is the part of my Ant build.xml file that downloads and unzips the JOGL library (2.0 beta 10).
<target name="libraries" depends="libraries.jogl" />
<target name="libraries.jogl.check">
<condition property="libraries.jogl.exists">
<available file="lib/jogl" />
</condition>
</target>
<target name="libraries.jogl" depends="libraries.jogl.check" unless="libraries.jogl.exists">
<condition property="joglostype" value="windows-i586">
<and>
<os family="windows" />
<or>
<os arch="i386" />
<os arch="x86" />
</or>
</and>
</condition>
<condition property="joglostype" value="windows-amd64">
<and>
<os family="windows" />
<os arch="amd64" />
</and>
</condition>
<condition property="joglostype" value="linux-i586">
<and>
<os name="Linux" />
<or>
<os arch="i386" />
<os arch="x86" />
</or>
</and>
</condition>
<condition property="joglostype" value="linux-amd64">
<and>
<os name="Linux" />
<or>
<os arch="AMD64" />
<os arch="x86_64" />
</or>
</and>
</condition>
<echo>Detected operating system: ${joglostype}</echo>
<echo>(if invalid OS, update ant build file)</echo>
<mkdir dir="lib" />
<get src="http://download.java.net/media/jogl/builds/archive/jsr-231-2.0-beta10/jogl-2.0-${joglostype}.zip" dest="lib/jogl.zip" usetimestamp="true" />
<mkdir dir="lib/jogl" />
<unzip src="lib/jogl.zip" dest="lib/jogl">
<patternset>
<include name="**/gluegen-rt.jar" />
<include name="**/jogl.all.jar" />
<include name="**/nativewindow.all.jar" />
<include name="**/newt.all.jar" />
<include name="**/*.so" />
<include name="**/*.dll" />
</patternset>
<mapper type="flatten" />
</unzip>
</target>
this script will download a release from a URL and install it to a local (named) repository.
https://gist.github.com/1624599
example usage: ./install_jogl_maven http://jogamp.org/deployment/v2.0-rc5 path_to_local_repo 2.0-rc5
There is no easy way to accomplish this with. Try if you can configure the maven-assembly-plugin to build a executable jar and package the correct files with your code. You cannot use the maven dependency management to achieve this because you need the ZIP contents not the ZIP itself. You could try the maven-ant-plugin.