• Feb
    • 17
    • 2012

JavaFX and Maven

Posted by In Uncategorized
This entry is part 9 of 15 in the series Building JEE applications in JavaFX 2.0

For JEE application development, Maven is, in my opinion, a requirement. Sure you can use Ant, and that will give you more ‘control’ over you build, but you’ll be spending a fair chunk of time managing your build scripts – time that you could spend actually building your application. The two big reasons you want to use Maven for your JEE build are:

  1. Dependency management: Maven provides a free, web-hosted central repository which contains just about every major distributable toolkit out there. When you use Maven, you simply specify the toolkits you want to use and the version numbers, and Maven will automatically download the jars you need and setup your classpath.
     
  2. Minimal configuration build: Maven uses “convention over configuration”, which means if you follow its directory structure and naming conventions, you can avoid writing massive amounts of XML config. When assembling WAR or EAR files, which have some funny rules regarding WEB-INF directories and the like, Maven just makes all of this simple.

I’m not going to waste time selling you further on this (a quick web search will find you hundreds of articles comparing Maven to other build tools). What we’ll do here now is step through how to create a Maven project for running JavaFX applications. In this post, I’m only going to get us to the stage where we can run a simple, pure-client JFX app in out IDE, but we’ll grow on this later to build multi-module projects with client and server components, and include ways to build JNLP deployment bundles for web deployment.

Step 1: Download and install Maven

You can get the latest version from here: http://maven.apache.org/download.html. It’s just a zip file and doesn’t need any special ‘installing’. I usually just grab the latest ‘Binary Zip’ and extract it locally. I tend to put all my applications (like Tomcat, Maven, Java, etc) under c:\apps (on windows) as this makes it real easy to type on the command line if I ever need to.So for me, I unzip Maven to in c:\apps\maven\maven-3.0.1 but you can do whatever you like.

Once you have unzipped it, the only thing needed to install it is to add a system variable called ‘M2_HOME‘ that points to the directory you just unzipped to, and then update the PATH variable to include ‘%M2_HOME%\bin’. If you run into any trouble with this, check out this very detailed installation guide:http://www.sonatype.com/books/mvnref-book/reference/installation.html

Step 2: Create an empty project folder

Most IDE’s will generate you a skeleton Maven project these days, and Maven itself has the concept of project templates (called ‘archetypes’) that you can use to quickly generate a ready-to-use project structure but we’ll give these a miss in this round so you can see just how little magic is actually needed.

We need to create a new directory to house our project, Maven doesn’t care where we put this directory.  I tend to use c:\dev for all my projects (again because it is easy from the command line), so if we’re creating a project called ‘MyJfxProject’, I would put it in a folder called c:\dev\myjfxproject.

Step 3: Create an initial POM file

The heart of every Maven project is the POM (or Project Object Model). This is just an XML file called  ’pom.xml‘ that lives in the base directory of your project. It tells Maven what features you want to use in your build (e.g. are you building a JAR or a WAR). Unlike an Ant build script however, this file is generally pretty light however, there’s a lot Maven can work out just from your project structure.

At a minimum we need to tell Maven three pieces of information: our group ID, which is typically your domain name (so I use ‘com.zenjava’), our artifact ID, which is just the name of our project (so in this case we’ll use ‘myjfxproject’) and the version, usually it’s good to start with version 1.0. You can also do some cool stuff with snapshot versions, but we won’t get sidetracked with them right now. Here’s how our initial pom.xmlshould look:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zenjava</groupId>
    <artifactId>myjfxproject</artifactId>
    <version>1.0</version>

</project>

Step 4: Create our source file structure

Maven requires us to follow its convention for structuring our project. It doesn’t matter where our project base folder is, but within this base folder we need to use the Maven hierarchy. There is a lot to this, but we just need a place to store our Java files at the moment, Maven requires us to put these in the directory src/main/java. Any Java files in that directory will be automatically compiled and included in our resulting build.

You can (and should) use sub-directories to get packages like normal. In my case I use the base package com.zenjava, but you should use a domain that suits you (your registered web domain is best, but if you don’t have one, just make up something sensible – not using a package structure is bad form).

We will also want some non-Java resources (such as properties files, images, CSS style sheets and FXML files) included in our project. Maven requires us to put these in the directory src/main/resources. Any files in these directories will be automatically included in our final JAR. You can use whatever directory structure you want within the resources directory, I typically create a directory for each resource type. So our initial directory structure should look something a little like the following.

c:\dev\myjfxproject

- pom.xml
+ src
  + main
    + java
      + com
        + zenjava
          + myjfxproject
            - MyJfxProjectApp.java
    + resources
      + fxml
        -  (FXML files)
      + images
        -  (image files files)
      + styles
        -  (CSS files)

Step 5: Add JavaFX as a dependency 

In the introduction of this blog, I made the bold claim that the Maven repository “contains just about every major distributable toolkit out there”. There’s one notable exception to this however: unfortunately and frustratingly, JavaFX is not in there!

There’s a couple of reasons for this. Firstly, there’s a licencing issue. Somewhat disappointingly Oracle has used a licence that allows us to redistribute JavaFX but only if as part of a bigger application. You can’t just redistribute the JAR on it’s own. Putting the JFX JAR in any public Maven repository would blatantly violate this licence. Oracle’s not likely to change this unfortunately, but once JavaFX is fully open sourced into the OpenJDK project (sometime this year, I believe) we should be able to get around this.

The second reason is a technical one. JavaFX uses a number of native files (e.g. DLLs on windows) and Maven does not handle native files overly well. Native files need to be added to the java system path, whereas Maven’s whole dependency management is focused around the more common java classpath. Other libraries that use native files have found ways around this (such as the JOGL toolkit), but to support this the actual libraries themselves have to load their native files in a certain way, which the JavaFX guys are not doing (and as yet, are not intending to do). JavaFX requires its native files to be in a relative subdirectory (namely rt/bin), and Maven just doesn’t support this.

So this seems like a bit of a show stopper: if we can’t add JavaFX as a Maven dependency we can’t use JavaFX in Maven. Fortunately there are a couple of hack options we can use to cludge around this problem. None of the options are perfect and they all sacrifice some of the niceness that we expect when using Maven, but the cludges are just good enough to get us through and still take advantage of the other great features that Maven offers.

The easiest option, and my recommended approach, for getting a JavaFX dependency into Maven is to use the ‘system‘ scope. If you’re a regular Maven user, you may not even be aware this exists (I wasn’t) as it is very rarely used. This special scope allows you to specify a system path, which can reference the JAR file on your local system, rather than downloading it from the central repository. Maven no longer provides version management and automatic updates for us, but our native issue is resolved because we can reference the jar in its default installation location, which has the natives files relative to it.

To do this you first download and install the JavaFX SDK as per normal. Then you just add a dependency like the following to your POM (change the system path to point to your JFX installation directory):

<dependencies>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>javafx</artifactId>
        <version>2.0</version>
        <scope>system</scope>
        <systemPath>C:/apps/jfx2.0/rt/lib/jfxrt.jar</systemPath>
    </dependency>
</dependencies>

The groupId, artifactId and version are actually arbitrary in this case (you can put whatever you like in there). Normally Maven would use these to find the JAR in the central repository but because we’re specifying the JAR with the ‘systemPath’, Maven will ignore these other values.

Several alternate options exist for getting Maven and JavaFX to play nicely together but the above is the simplest and easiest. Probably the most powerful alternative is to use a “pre-loader” that extracts the native files from a JAR file installed into Maven when your application runs (the JFXtras project has code in it to assist with this). If you’re not wanting to take advantage of the JFX/JNLP deployment tools (more on this in future posts) then this can be a good option for bundling JFX in your final JAR. There’s a fair bit of work involved in assembling and maintaining the native JAR bundles however, so I personally tend not to use this approach.

It’s my hope that through a few of us working with the JFX team on this Maven issue that we’ll end up with a much cleaner solution for this in the future. There’s not a great deal of Maven experience in the JFX team however so it is slow going, and the future plans to co-bundle JFX with the JDK are also an influencing factor in this discussion. If JFX ends up in the JDK then version management and distribution will be intimately linked between these two taking Maven out of the game altogether (adding a whole new bucket of versioning and deployment complexities). There’s work to be done here still.

Step 6: Open your project in your favourite IDE 

We now have a fully operational JavaFX project and we can open this in our IDE. I use IntelliJ, which has fantastic, built-inn support for Maven. I simply go to File -> Open Project and then select the pom.xml file for my project. The result is a ready to run project with the classpath all set to include my source code, resources and third party libraries (in this case we’ve only added JavaFX but you can add as many dependencies as you like). I can also edit my POM file whenever I like (to add dependencies for example) and IntelliJ will automatically download the corresponding JAR files and update the classpath instantly.

I know that Eclipse also has strong support for Maven, as I have often worked with developers using Eclipse while I was using IntelliJ on the same project (which is another advantage of using Maven). I’m not totally up on the steps to getting this working but you should be able to find more info here: http://www.eclipse.org/m2e/

Additionally NetBeans also has support for Maven. Again I am not sure of the exact details, but this wiki seems to provide the right sort of information: http://wiki.netbeans.org/MavenBestPractices

If anyone has experience in using Maven with either Eclipse or NetBeans (or any other IDE out there), please post additional information in the comments below.

Step 7: Build your JAR file

With just this simple POM setup, we can now quickly and easily build the JAR file for our application. You can usually do this via your IDE, but using the command line is just as effective. On windows open a Command Prompt (Start -> Run -> cmd), then change directory into your project home directory (e.g. cd  c:\dev\myjfxproject) and type the following command:


mvn clean package

You will see a whole lot of log that should finish with “BUILD SUCCESS”. Assuming you get to this point, then the jar file containing all of your application code and resources can be found in the newly created ‘target’ directory. You can take this jar and use it as normal.

Beyond the basics

Building a single JAR file is pretty cool, but generally we will want to build a ready to run deployable application. We can use Maven to do all sorts of wonderful things, including building web deployable JNLP based deployment bundles and applets, as well as executable jars containing third party dependencies. Not to mention generating JavaDoc, automatically running unit tests, creating full project documentation and much, much more.

This does get quite a bit more complex however, and this blog post is already too long, so I will leave these more advanced topics to later. We’ll weave some deployment options into our future posts, especially when we look at building WAR files for our JEE-based server.

If you’re desperate for more info on a more complete JNLP-based deployment build process and you’re feeling comfortable with Maven then you can find some workable options buried in this forum discussion. As always, feel free to post further questions in the comment section below, or post on the JFX forum if you have more general questions.

Series Navigation<< JavaFX and MVP – a smörgåsbord of design patternsPorting “First Contact” to Spring >>

10 Comments

  • gajasutra
    February 18, 2012

    > Dependency management: Maven provides a free, web-hosted central repository [...]

    You can use Maven Central with Ant for dependency management (and/or a local repository like Nexus).
    http://ant.apache.org/ivy/

    :)

  • zonski Author
    February 19, 2012

    Yep, nice one. For those of you hooked on Ant and unable/unwilling to make the switch to Maven, I would definitely recommend using Ivy for dependency management.

  • March 1, 2012

    Or use a local maven repositiory. E.g. Artifactory is for free.

  • zonski Author
    March 1, 2012

    A local repository will get around the legal issues but JavaFX won’t run if you just install the JAR in a local repository in the normal way. JFX loads it’s native library files from a relative path, but when you install the jfxrt.jar in your local Maven repo the directory structure required by JavaFX is no longer present (since Maven uses it’s own directory structure organised by group/artifcat/version, etc).

    If you want to use this approach of installing a JAR into a local repository, you need to use the ‘Pre Launcher’ option I mention briefly in the post above. In this case you have to package up all the native files into a JAR file, install both the normal jfxrt.jar file and native jar into your local repository and then have a Pre Launcher (i.e. a bit of Java code that runs when your application starts up) manually extract the native files to the local file system (since the OS can’t load native files from within a JAR file) and edit the Java ‘system path’ to get the native files available for loading. This is quite a complex task, so I’d recommend the ‘System Path’ option, however if you do want to go down this road, the code I link to in JFxtras is a good base to work off.

    There’s some on-going discussions with the JFX team about improving this situation so that Maven will be better supported, however this is taking some time and the longer term goal of getting JavaFX co-bundled into the JRE adds complexity as eventually we probably won’t be defining JFX as a dependency at all because it will be part of the JRE itself (which adds some challenges with version management – another conversation in progress).

  • Alan
    March 7, 2012

    Nice.
    I like the material you’ve been posting on this blog.
    Becoming a big fan of it.
    Thanks for sharing!

  • Rafael A P Nascimento
    October 26, 2012

    Hi, zonsky, great post!

    I tried your solution, but I’m getting “java.lang.NoClassDefFoundError: javafx/application/Application”

    Is there something else to set up to get javaFx running on maven 3 ?

    thanks

  • zonski Author
    October 27, 2012

    Follow ups to this for anyone who is interested.

    Maven plugin:
    https://github.com/zonski/javafx-maven-plugin

    Alternative to System scope:
    http://www.zenjava.com/firstcontact/architecture/setup/install-javafx/

  • February 20, 2013

    I Believe post, “JavaFX and Maven | Zen Java” was just right!
    Icould not agree with u more! At last seems like
    I personallyuncovered a blog site worthy of looking through.

    Thanks for your time, Zane

  • July 8, 2013

    It has been said that desperate times call for desperate measures, and that there were
    more than enough clients to go around. Everyone understands the importance of maintaining a well managed client / vendor relationship.
    If the site is prepared for this and seriously want to
    earn online. However, it is newer customers who are reluctant to buy things online.
    I couldn’t blame Google for not putting me on the first page of search results.

Leave a Comment