The final part of the Maven JavaFX build is getting NetBeans to correctly run the application. This, it turns out, is more difficult than I expected. This discussion covers a the problems quite well and is worth a read. I will admit up front that the solution I give here is not pretty, in fact it’s a down right hack, but it works for me. If you have a better solution I’d love to hear it, please leave a comment below.
First things first, I said back in part one that the best way to deal with the dependency on the JavaFX runtime jar was to use a system scoped dependency in your pom. I still like that method but it turns out it’s only any good for compiling the application. The problem is that when you run the application in NetBeans it uses the runtime scope to build the classpath. This scope excludes the system scope so you end up without JavaFX on your classpath and a ClassNotFoundException.
To get around this the simplest thing to do is place the runtime in your local Nexus repository (you’re running Nexus right?) and make it a standard dependency of your application. Now you can build and fire up your application quite happily but there’s still a problem…
JavaFX 2.0 uses a load of native DLLs and it looks for them at ../bin. There’s currently no option to make it look anywhere else or do anything even remotely clever. You’re application will most likely be issuing an exception that looks something like this:
java.lang.UnsatisfiedLinkError: Can't load library: C:\Users\Username\.m2\repository\com\oracle\javafx\bin\mat.dll
When Maven runs your application it loads the jars from the .m2 local cache. Since this is where it’s getting the JavaFX runtime jar it’s also where it will look for the DLLs.
Hack Alert!
To get around this problem (for now) I’ve instigated what feels like one of the biggest hacks of my programming career. I’ve copied the bin folded from the JavaFX install into the location given in the error message in the path above.
This is bad because the .m2 cache is under Maven control so just dumping things in there could lead to problems. In my experience though the cache seems robust so as long as I don’t create a JavaFX entry with a version of “bin” I think I should be ok.
The image on the right shows the file structure I have in my Maven cache. The main downside of this is that I have three versions of JavaFX in my Nexus repository but I can only have one bin folder version.
Pressing the big green run button now fires up my application in NetBeans. I’m back to having the problem of the JavaFX dependency being copied across but I’ll cross that bridge when I come to it.
Fixing the Runtime Copy Issue
Turns out I needed to fix the runtime copy issue sooner than I thought. Because the scope of the JavaFX library is now compile it is being copied into the dist/lib directory and then included by the resources element of the jfxjar task in the jars classpath. The end result of this is that when you double click the jar it fails because it expects the DLLs to be in dist/lib/bin. The quick and simple solution to this is to just exclude the JavaFX jar from the built jars classpath like this:
<jfxjar destfile="${project.build.directory}/dist/${project.build.finalName}"> <fileset dir="${project.build.directory}/classes"/> <application name="${project.name}" mainClass="[MAIN-CLASS]/> <resources> <fileset dir="${project.build.directory}/dist/" includes="lib/*.jar" excludes="lib/javafx-*.jar"/> </resources> <manifest> <attribute name="JavaFX-Fallback-Class" value="com.javafx.main.NoJavaFXFallback"/> </manifest> </jfxjar>
That’s it I think, as far as I can see everything is working. I look forward to the day when I don’t have to hack things like this though. From what I’ve read the release of Java 8 should sort these problems out as JavaFX will be built in then.