javafx-gradle-plugin

After tweeting my blog posts on Twitter, I got a reply from the cofounder of Gluon no less (Eugene Ryzhikov) who suggested I take a look at the javafx-gradle-plugin. I’m glad I did – it has cleaned up my gradle file nicely so that it looks less abnormal.

I think I did it right.

Old Gradle Dependencies

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile name: "javafx.fxml" // WEIRD
compile name: "javafx.controls" // WEIRD
compile name: "javafx.base" // WEIRD
compile name: "javafx.graphics" // WEIRD
compile "com.google.guava:guava:27.1-jre"

testCompile "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION"
testCompile "org.assertj:assertj-core:3.11.1"
testCompile "org.testfx:testfx-core:4.0.15-alpha"
testCompile "org.testfx:testfx-junit:4.0.15-alpha"
}

New Gradle Dependencies

javafx {
modules = [ 'javafx.controls', 'javafx.fxml' ]
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "com.google.guava:guava:27.1-jre"
// WEIRDNESS GONE
testCompile "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION"
testCompile "org.assertj:assertj-core:3.11.1"
testCompile "org.testfx:testfx-core:4.0.15-alpha"
testCompile "org.testfx:testfx-junit:4.0.15-alpha"
}

The full gradle file is here.

Application Number 1

I started this blog after spending several years of my private time building Kotlin and JavaFX desktop applications for fun and very little profit. My day job has nothing to do with either. Along the way, I have picked up hard-won experience with Kotlin and JavaFX.  I often struggled with getting answers for my issues, and having collected lots of private repos in bitbucket, thought… “Why not blog about my experiences?”

Nothing sharpens your skills like creating demos and explaining things, if only to avoid writing something stupid. 

Anyway, enough crap about me. On with the show.

I plan to blog about issues that I came across developing Kotlin desktop applications such as Drag and Drop, Custom Controls, Multiple Controllers and so on. Each post will discuss a specific, working application on GitHub.

This first post is mostly about showing how to get started with a basic Kotlin/JavaFX/Gradle setup.

I have created a GitHub repo with fully working example:

https://github.com/SergeMerzliakov/javafx-app-1

Here is a screen shot. Nothing fancy, just a basic demo.

Up and Running in 30 Seconds

If you have Java 11 and OpenJDK 12:

git clone git@github.com:SergeMerzliakov/javafx-app-1.git

<edit build.gradle. Update JFX_INSTALL to point to JFX 12>

gradlew clean build

gradlew runApp1

JavaFX is now a Separate Library

Duh. It’s 2019 and all of us with more than 30 seconds of Googling behind us will know this. JavaFX is now OpenFX and its downloaded as a plain old zip file. So you can install it anywhere. This has implications for your gradle config.

My build.gradle repositories block looks like this now.

repositories {
mavenCentral()
flatDir {
dirs "${JFX_INSTALL}/lib"
}
}

A variable called JFX_INSTALL now defines where it’s installed. This will come in handy later. The install contains a lib directory with all the jars you need.

I don’t know if this is the best way to do it, but it satisfies rule 1 – Keeping Things Simple.

Given that as of today, we have Java 11 and 12, with OpenJFX 11 and 12, what are the allowed permutations? Well I know that:

  • Java 11 + OpenJFX 11 ==> Works
  • Java 11 + OpenJFX 12 ==> Works
  • Java 12 + OpenJFX 12 ==> Works
  • Java 12 + OpenJFX 11 ==> Not sure ++

++ The OpenJFX website tells you to use Java 11 and that’s about it. I quote:

If you have newer or older versions of Java installed along with JDK 11, you need to make sure that the JAVA_HOME environment variable points to JDK 11

Some basic tests with Java 11 and switching between OpenJFX 11 and 12 has shown zero difference in behaviour so far.

Gradle Oddities

The dependencies section references the OpenJFX install location rather than the Maven repository details. That’s because the Maven artefact JFX jars are all empty. WTF? No obvious reason after some research. I ploughed on, and did this:

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile name: 'javafx.fxml'
compile name: 'javafx.controls'
compile name: 'javafx.base'
compile name: 'javafx.graphics'
}

Inelegant, but I am nothing if not goal oriented. Stints as a pre-sales consultant really drilled that into me.

I suspect that this may be due to the use of Java Modules, which I have zero experience with. No one at my day job has ever used them either. So I will blithely coast over this and worry about it ‘real soon now’.

Running The Application

Since we need to use modules, starting an OpenJFX 11+ app requires some extra VM parameters, basically telling the JVM where you installed OpenJFX and which modules you require loaded. Weird. Why can’t this be worked out some other way? Surely as part of the compile/link/resolve dependencies process?

Below is the gradle task for running App1. Hopefully the first of many.

task runApp1(type: JavaExec) {
group = "Application"
description = "Runs Application 1"
classpath sourceSets.main.runtimeClasspath
main = 'org.epistatic.app1.Main'
doFirst {
jvmArgs = [
'--module-path', "${JFX_INSTALL}/lib",
'--add-modules', 'javafx.fxml,javafx.controls'
]
}
}

Summary

Well that’s it for the first post. Nothing revolutionary here, but hopefully the code repository works out of the box. If you cannot get the demo running, let me know so I can fix the bastard.