Monday, October 26, 2020

A native Scala JavaFX application via GraalVM

After successful attempts with Java, JavaFX and GraalVM I get closer to my goal to compile SudokuFX to a native binary. A milestone towards this goal is to compile a trivial Scala JavaFX application to native code, that is to say to a single, dependency free binary. Source code and binaries for windows and MacOsX for this blog post are available here.

03 - Sydney Harbour Bridge Construction
 Sydney Harbour Bridge Construction

JavaFX Logboard

As a vehicle for using JavaFX and FXML I chose to implement a simple log visualisation tool. I'll call it javafx-logboard. It is a JavaFX application which displays green, red, yellow and gray rectangles in a FlowPane, symbolizing INFO, SEVERE, WARNING and TRACE messages. It is nothing fancy, but helps identifying swiftly if there are problems or certain patterns in an application log. With this visualisation, it is easier to detect patterns. Try it! Honestly, I'm wondering why common log viewers don't have such a 'birds eye' feature. 

A screenshot will make things clearer: 

The application helps to get a quick idea about what is going on in a log file. It even sports Tooltips for red rectangles (SEVERE entries) and Drag'n Drop. I always wanted to have such a tool, well I wrote it in 10 minutes (*). It reminds me visually a little of XCopy, an ancestor of XCode (nerd joke). 

Motivation

I started this project and blog post with following promise:

If javafx-logboard compiles to a native binary, I'll be a true believer in GraalVM and will AOT compile my way through various applications in the future. (at least try it)

Side-note: I already successfully compiled an application with JavaCV for MacOsX with GraalVM, this dependency involves more native code I'll ever want to write in my life. But Seeing a Scala JavaFX app running with my own eyes, that should be the the mission for this blog post. 

The motivation for having a native binary is primarily an easier way for distributing software in my mind, in regard of speed and memory consumption it may help twitter but is not really relevant for me ... or is it? 

I believe GraalVM produces more efficient Scala runtime code, it would be great to experience a perceptible difference for my own applications as well.  There is no other way, you have to try it out. 

Some takeaways after spending far too much time on this endeavour until everything worked


Scala Converters

I have experienced some errors with GraalVM compilation which I traced back to scala.jdk.CollectionConverters, but I think it has something to do with implicit resolution of Scala methods. I'm not quite sure what is the issue with it, my solution was to use Java Collections just to get it to run. I'm sure GraalVM has no problem consuming Scala Collections via some extra configuration maybe (will have to solve / tackle this again in future projects anyway ... )

FXML References 

It took some time until I realised that classes won't get automatically included via GraalVM if you use UI elements in FXML without referencing them in your Controller. You have to explicitly add them in your reflection list, otherwise they get lost and you'll get weird runtime behaviour. Or reference them in your controllers. 

fat-jar

Antother thing I've learned is that you don't need to modularise your Java/Scala application in order to compile it with GraalVM. Just provide a shaded jar and there you go.

main-class not found with scala javafx apps?

I experienced something rather intriguing - if I tried to call GraalVM via maven client plugin in the same project like I compiled my Scala sources, I always would get an error - GraalVM compilation would not find the entry point to the appilation, it's main class (which was written in Scala).

Not thinking long about it, I created a multi module maven project which contains all relevant Scala source code, and one with a minimalistic Java main class which can be found by the maven client plugin. I don't know if I did something wrong here (again), it seems rather odd. But with a small Java bootstrapper it works fine.

multimodule maven project - mvn install

Since I use a trick to compile my Scala stuff to a shaded jar to trick client-maven-plugin to compile it as 'normal' byte code I have to use mvn install - otherwise it would not find the dependency (for example with mvn package). In my opinion this should not make any difference, but at the moment it does. Somebody an idea what's going on here?

long compile times - again

This multi-module approach is more or less inevitable anyways since compile times for GraalVM are long. For application development it is not really feasible to wait so long for testing features and such. 

As such it is good practice to make sure your application runs on a VM (in a shaded fat jar for example) and only then try to compile it to native code. Beware: The added complexity for application developers not only to implement a feature but also keep control of the whole build chain doesn't really simplify application development. This is a huge problem in my opinion, and a hurdle for adoption. :( 

Remember however that a little waiting and listening to the noise of the fan yields finally an AOT compiled binary, everything included, no JRE download, all in one package. Which is just mind-blowing and well worth the effort!

scala-native - the other way 

Speaking of which - I want to mention Scala Native here as well, which is a project to compile Scala directly (via LLVM) to native code. With a tighter integration in the Scala compiler more specific optimisations are possible and hence faster, smaller binaries (compared to GraalVM) are possible. ... In theory. It is a shame that this project doesn't get as much attention, love and money it would need to reach a more mature state and really compete with GraalVM. For example, I doubt it would be able to compile JavaFX apps at the moment. 

file sizes acceptable

Back to GraalVM: It is also interesting how big a Scala JavaFX application can be at minimum without any magic - at about 65 MB for MacOsX, which is not much larger than a Helloworld JavaFX (FXML) application directly from gluon samples project. 

I tried my little toy project this time also with windows, and after following instructions from here it compiled without much hassle literally on the first try. Binary size on windows is roughly the same, at about 60 MB. 

Depending on your background or internet access speed those sizes are negligible or a major drama, decide for yourself. If you are keen on minimal file sizes, you should stop by at pouet.net and ask yourself if you have made the right choices as a programmer in your life. 

low hanging fruits

In retrospect it turns out that configuring native compilation with Maven, JavaFX client plugin, GraalVM and Scala is easier than it may sound. It heavily depends on a proper configuration of the toolchain, and this has to be learned. Since this whole process includes compiling your code with GraalVM, I should mention that normal compilation is said to be faster with GraalVM as well (for Scala code). Another reason to consider using it (just as compile vehicle, without creating native images).

For the time being, I'm happy with my GraalVM experiments and try to migrate codebases as soon as I can to be consumable by client-maven-plugin. Some of those journeys I may document here in the future. 

Source code and binaries for windows and MacOsX for this blog post are available here.

(*) implementing Drag'n Drop took me 10 minutes, but debugging why it would not work the way I expected took hours. It had to do with GraalVM very aggressively stripping away JavaFX containers and no log output was given. Drag'n drop works fine, you just have to be cautious about making sure that everything is really contained in your exe file - afterwards it is always easy ;-)




No comments:

Post a Comment