Let's wrap this up quickly

Tutorial: A guide to the Gradle JavaFX Plugin

DannoFerrin
package1

Danno Ferrin on taking the pain out of packaging JavaFX applications using a simple open-source Gradle plugin.

When Oracle released
JavaFX 2.2 they included tools to package JavaFX applications into
native installers and executables. However, much like any cross
platform solution there are many knobs to be turned and switches to
be flipped. When building a JavaFX Native bundle, there are many
places where slight deviations can result in major changes in
functionality and reliability. This is a process that is best
automated.

Oracle has provided a series of Ant tasks to help
in this deployment process, but Ant is starting to show its age.
Many organizations have moved to newer build systems that support
convention over configuration patterns and less verbose
configuration. The Gradle JavaFX Plugin (which is open sourced) is
one such tool that aims to simplify the JavaFX packaging experience
when using the Gradle build system.

The Many Pieces of a JavaFX Deployment

Creating and packaging a JavaFX application isn’t
quite like packaging up a good old Swing application for use by a
customer. When creating Swing applications users were left to their
own devices to create the distribution chain they needed. But this
is keeping in line with when Swing was first developed. There were
very few best practices and no preset processes from Sun to go the
last mile to the user (other than applets). In addition, there are
some aspects of a packaged deployment that are entirely unique to
JavaFX.

The first notable difference in JavaFX is that it
customizes the jar construction and signing process. The principal
reason is that JavaFX prefers that applications use a different
launching mechanism than the old main with args method. This new

Application
class is a refugee from JSR-296 and allows for
the framework to manage threading issues and other framework
specific tasks that the application developer shouldn’t normally
need to worry about. In addition to creating the jar file, JavaFX
offers a different way to sign them. Rather than signing each class
file and resource separately, JavaFX signs the entire jar as a
single blob. With a single signature to verify, the validation and
verification occurs much faster. The same signing keys and
infrastructures are used to create and verify the certificates.

The second notable difference is that JavaFX
provides some packaging tools to speed up the parsing and handling
of CSS styling. These compiled stylesheets are distinguished from
the plain text versions by their ‘bss
extension, where the B stands for binary. The custom jar
application, as well as a stand alone
css2bss
tool, compiles the css file into the binary
format.

Finally, the JavaFX packager takes all of the
various jar files and resources used by the application and creates
a platform-dependent file to distribute, install, and launch the
application. These packages are complete with cross-platform
flair-like start menu integration, dock and tray icons, menubar
integration, and single click icons. However, some of this
integration comes at a cost: if you want high quality integration
you often need to provide extra files such as platform specific
icon formats.

Convention Over Configuration

So that may seem like a daunting list of steps that you need to
perform to assemble your JavaFX application. You may be wondering
just how large the gradle file is that you need to build it is.
Well, let’s look at the build file for one of the classic JavaFX
Samples:
brickbreaker
.

apply from: 'http://dl.bintray.com/content/shemnon/javafx-gradle/javafx.plugin'

The code above is not an abbreviated build file, this is the
entire build file
for the brickbreaker sample app. You can
even
look for yourself
. This is not some magic trick where I am
hiding the real build file somewhere else. This same plugin script
goes into every gradle build where you are building a JavaFX
application. The trick here is that if you follow a standard file
layout and naming convention, the plugin will make all the needed
assumptions for all of the other parts of the build process.

What file conventions are these? The standard Maven
layout conventions are used as a foundation. The src/main/java and
src/main/resources file layout provides appropriate locations for
the source code and the other resources such as CSS files and
images. Tasks that rely on source code or other sources are
configured by default to look into those directories. One example
is the task that compiles CSS into a binary version for JavaFX – if
you make no configuration changes then all of the CSS files in the
src/main/resources file will be compiled into BSS files. The
original CSS files will also be stored in the jar so that any code
not taking advantage of the binary form can still use the CSS
files.  

Directory

Files

Purpose

src/main/java

Standard Java layout

Application source
files

src/main/java

<project
name>/Main.java

Default main
file/Application

src/main/resources

Non-java files

Application resources. (Images,
CSS, etc.)

src/deploy/package

shortcut*.png

Launcher and Dock
icons

src/deploy/package

volume*.png

MacOSX VolumeIcon

src/deploy/package

setup*.png

Windows Installer Corner
Icon

src/deploy/package/linux

*

Installer specific scripts for
Linux

src/deploy/package/macosx

*

Installer specific scripts for
Mac

src/deploy/package/windows

*

Installer specific scripts for
Windows

Gradle JavaFX Plugin File Conventions

 

The file conventions also extend to areas not
normally covered by the standard Maven conventions. Two areas are
currently handled: first, there are some installer specific
customizations that you can make directly with the installer
scripts. These are stored in
src/deploy/package/<platform>
, where each platform
gets their own subdirectory. The second set of files handled by
convention are the launcher and installer icons. While you can
store these icons in a platform-specific manner in the
platform-specific directory, you can also store them as PNG files
in
src/deploy/package
and the plugin will convert them into
platform-specific binaries. The plugin will even convert them into
multi-resolution icons on Windows and MacOSX.

Configuration When Necessary

There are always some aspects of your application
that just cannot be handled via file layout conventions. For that,
there are configuration options inside of the
build.gradle
file itself. The Gradle JavaFX Plugin does its
best to provide sensible defaults, but in some cases there are no
universal defaults. Gradle uses the notion of extensions in the
build file to hold these configuration values. JavaFX is not the
only plugin to provide these extensions – nearly all of the plugins
provide it in some form. The Java, webapp, and Maven plugins each
use the extension mechanism to configure items that just cannot be
done in the file system. Some of the configurations include what
version of the language to use, deployment descriptor information,
and random bits of metadata for the generated Maven poms such as
licensing and source control. As seen below the extension is
basically a block of code with lots of variable assignments.

javafx {
    appID 'SampleApp'
    appName 'Sample Application'
    mainClass 'com.example.sample.Main'

    jvmArgs = ['-XX:+AggressiveOpts', '-XX:CompileThreshold=1']
    systemProperties = [ 'prism.disableRegionCaching':'true' ]
    arguments = ['-l', '--fast']

    embedLauncher = false

    // deploy/info attributes
    category = 'Demos'
    copyright = 'Copyright (c) 2013 Acme'
    description = 'This is a sample configuration, it is not real.'
    licenseType = 'Apache 2.0'
    vendor = 'Acme'
    installSystemWide = true
    menu = true
    shortcut = true

    // app icons
    icons {
        shortcut = ['shortcut-16.png', 'shortcut-32.png', 'shortcut-128.png', 'shortcut-256.png', 'shortcut-16@2x.png', 'shortcut-32@2x.png', 'shortcut-128@2x.png']
        volume = 'javafx-icon.png'
        setup = 'javafx-icon.png'
    }

    // applet and webstart stuff
    debugKey {
        alias = 'debugKey'
        //keyPass = 'password' // provide via command line
        keyStore = file('~/keys/debug.jks')
        //storePass = 'password'  // provide via command line
    }
    releaseKey {
        alias = 'production'
        //keyPass = 'password' // provide via command line
        keyStore = file('/Volumes/ProdThumbDrive/production.jks')
        //storePass = 'password'  // provide via command line
    }
    signingMode 'release'

    width = 800
    height = 600
    embedJNLP = false
    codebase = 'http://example.com/bogus/JNLP/Codebase'

    // arbitrary jnlp icons
    icon {
        href = 'src/main/resources/javafx-icon.png'
        kind = 'splash'
        width = 128
        height = 128
    }
    icon {
        href = 'shortcut-32@2x.png'
        kind = 'selected'
        width = 16
        height = 16
        scale = 1
    }
}

 

This listing is entirely fake and does not reflect any
particular example or sample, but it does show every knob you can
twist and switch you can flip. It is also important to note that
none of these values are required as some sort of a default will be
provided.

The first section contains general information
about the application. The
appName
is the name presented to the user while the

appID
is used internally by the OS.

The next section lists the system properties,
environmental variables, and command line arguments for the
application. The JavaFX packager takes these values and uses them
in appropriate ways based on the packaging choices. They are then
available to the JavaFX application in the expected manner.

The third section of code is entirely
metadata-oriented. These values are not used at runtime but are
used by the platform-specific packages. For example, the Windows
installers use the publisher in the Add/Remove Programs control
panel applet. The type of the license is important to the Linux
packages. The tree booleans at the end adjust where in the system
the package is installed, although not all the packagers pay
attention to those values. For example, the Mac OSX DMG packager
will not create dock shortcuts for you.

Next, we describe the icons. Icons can both be
defined by file convention and via configuration. Why would you
want to define your icons in the build file when a suitable file
convention exists? The main reason is that you may want to re-use
image files across multiple kinds of icons.To set the images used
in a kind of icon, you assign a string value (or a list of string
values) to the kind of icon you are using. These string values are
URLs and if they appear to be relative URLs, they will be resolved
against the
src/deploy/package
directory. When setting a list of images
for an icon, all of the appropriately sized images will be used
when creating a multi-resolution icon for a specific installer.

The final segment of code deals with JNLP and
applet packaging. Most of this is simply passed through to the
JavaFX packager to allow for the generation of the JNLP files. This
includes an alternate layout of the icon format that has more of
the fields required by a JNLP file. The
releaseKey
,
debugKey
, and
signingMode
configurations are used for signing the applet
during the custom signjar step. While the configuration allows you
to store the signing key passwords in the build file, it is
generally a bad idea to do so. There are a myriad of ways to pass
this information to the build system, and your choice of method
will be driven by you and your organizations security requirements.
But seriously, don’t check your signing keys into source control.
For a debug key the Gradle JavaFX Plugin will create a self-signed
debug key for you if one is not specified. You must, however,
provide your own release key.

Future Plans

 My future plans for the Gradle JavaFX plugin
are in general to make it the easiest way to generate native
packages for your JavaFX applications. This applies to whether it
is for commercial projects, internal projects, or your own personal
entertainment.

As with any open source project it is important to
supply a list of “patches welcome” features that the current
committers do not intend to address. For me, this list includes
anything related to JNLP and applet packaging. Since none of the
applications I work on have a real requirement for WebStart
compatibility, I don’t have a real good testbed to say if a
particular feature is working well or not. It would be best if the
code for those parts come from someone with a vested interest in
WebStart deployment. As a consequence of this, I also would welcome
patches (and sample code) relating to the use of the JavaFX
pre-loader and integration with JVMs where the SecurityManager is
enabled.

What features can you expect to see from future
versions that don’t come from patches? First there are some
existing features that need support. The current packager code from
Oracle has support for license texts and click-through licenses.
The problem is that each platform has different requirements, Linux
likes text while Windows refuses to let go of RTF. I would like to
be able to have the developer provide a simple text files and the
plugin will convert to the other formats.

The second feature on the roadmap is a mechanism to
deal with platform-specific values. License file formats are just
the beginning. Windows installers need GUIDs for the app
identifier, whereas Mac bundles need a format that looks more like
a Java package name. The categories in Mac and Linux also need to
come from a finite set of values, and the build ideally would let
you know when you stray from that path. To support this feature,
there will also be a mechanism added to specify the JRE to be
bundled with the installer packages. This would be useful to do
things like build a RaspberryPi distribution in an Ubuntu VM.

Most importantly, the Gradle JavaFX plugin will do
whatever it takes to support JavaFX on mobile platforms, such as
Android and iOS. Ideally all of the ugly details will be already
managed by the JavaFX packager, but whatever steps are not handled
will get some attention from this plugin. I would imagine that
there are features like final packaging for submission to the App
Store and Google Play that are outside of the realm of the core
packager, but there are a lot of steps that can be done before the
files get uploaded to the store servers.

Summary

The Gradle JavaFX Plugin provides high quality
integration of the esoteric pieces of the new JavaFX packaging
tools. It integrates with current file convention best practices,
as well as providing configuration options you may need to create a
high quality deployment package. Whether you have a small toy app
you want to share with friends, or a cross platform application you
need to launch on Mac, Windows, and Linux, the Gradle JavaFX Plugin
can help you do it.

Photo by Beck
Gusler
.

Author
DannoFerrin
Danno Ferrin is a Software Engineer at Fluke Networks by day, where he is enhancing a long-standing application to work on JavaFX and mobile devices. He has worked with Java since the beta release in 1995 and has only had one software job where he hasn't used a JVM on a daily basis. He has contributed to several open source projects including committing to Apache Tomcat, Apache Ant, the Groovy Programming Language and co-founded the Griffon Framework. He sings tenor, used to play the clarinet and piano, and was an award winning debater for his high school speech team. His favorite colors are green and orange.
Comments
comments powered by Disqus