JavaFX Dev and Deployment

In my previous articles on the usefulness of Java FX:

And on the horrid deployment of Java FX 11:

I hit a brick wall in deployment. It feels as though this just isn’t yet ready for usage. However, there was a redeeming light… rolling back to JDK 8 I was able to easily and quickly generate executable Jar’s of my applications.

JavaFX8/JDK8

JDK 9 and up is where my problems started. By rolling back to JDK 8, I was able to create an application (still using Scene Builder to create the fxml GUI) and deploy it via Java Artifact Build/Deploy (jar deploy.)

The actual code had to be modified a bit. Below is the link to the Github repo:

https://github.com/continuouscoder/JavaFX8PortScanner

Modification of Code

Where I ran into problems with JDK 8 was in the use of concurrency. I couldn’t use the same Runnable syntax as in JDK 11. JavaFX 8 would complain that the Thread created was not a JavaFX Application thread.

In JDK 11 I could write this and JavaFX 11 took it just fine:

       Runnable task = new Runnable() {  // Concurrency solution provided by: https://examples.javacodegeeks.com/desktop-java/javafx/javafx-concurrency-example/
            @Override
            public void run() {  // Part of the Runnable:
                // My scanner code gets dropped in the run method of the runnable...
                scanResultsValues.clear();
                for (int i = Integer.parseInt(startingPortValue.getText()); i <= Integer.parseInt(endingPortValue.getText()); i++) {
                    try {
                        Socket socket = new Socket(hostValue.getText(), i);
                        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
                        dout.writeUTF("\n\n\n");
                        dout.flush();
                        dout.close();
                        socket.close();
                        System.out.println("Port " + i + " is open...");
                        scanResultsValues.appendText("Port open: " + Integer.toString(i )+"\n");
                    } catch (Exception err) {

                    }
                }
                scanResultsValues.appendText("\n## Scan Completed ##");
            }
        };

        Thread backgroundThread = new Thread(task); // Concurrency solution: defining a new background thread
        backgroundThread.setDaemon(true);           // Concurrency solution: thread will use a daemon
backgroundThread.start(); // Concurrency solution: starting thread

The above JDK 11 / JavaFX 11 code allowed me to update the UI on each result in the for loop.

However, it failed in JavaFX 8 when I downgraded. The failures were about this:

https://stackoverflow.com/questions/21083945/how-to-avoid-not-on-fx-application-thread-currentthread-javafx-application-th

Following the solutions around this issue, I converted the looping Code like so:

new Thread(new Runnable() {
            @Override
            public void run() {
                scanResultsValues.clear();

                for (int i = Integer.parseInt(startingPortValue.getText()); i <= Integer.parseInt(endingPortValue.getText()); i++) {
                    final String port = Integer.toString(i);
                    try {
                        Socket socket = new Socket(hostValue.getText(), i);
                        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
                        dout.writeUTF("\n\n\n");
                        dout.flush();
                        dout.close();
                        socket.close();
                        System.out.println("Port " + i + " is open...");

                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                scanResultsValues.appendText("Port open: " + port +"\n");
                            }
                        });
                    } catch (Exception err) {

                    }
                }
                scanResultsValues.appendText("\n## Scan Completed ##");
            }
}).start();

The key differences above, are that it’s simpler. I have a new Thread that sets up a new Runnable item, which is my for loop. However, at the point of writing out my updated ports found, I put that code in a Platform.runLater method.

Once I call my value in the runLater method I lost my connection to the variable. I would get an error that this variable is in a scope that needs to be declared as final now. The solution is to have the variable defined as final at the top:

final String port = Integer.toString(i);

That’s it.

Building / Deployment

Once all that’s done, all you do to build or deploy out a Jar, is to setup an artifact like so:

(Using IntelliJ) I go to Project Structure > Artifacts. Here I click the “+” sign and choose “JavaFX Application” > [name of your application].

That will create 4 tabs on the right side of the window: output layout, pre-processing, post-processing and Java FX. Click on the Java FX sub tab.

On this tab we want to point to the class using the Main method, click the folder icon in the field for the “Application Class” field and choose your Main method.

Add a title in the Title field. It’s all ready to go now.

Creating a Jar

Now that the Artifact is configured correctly, in IntelliJ we click Build > Build Artifacts and select “Build” from the contextual menu.

A Jar will be created in the /out/artifacts folder of our Project.

If you’re curious about the size of the Jar, in my case it was only 5kb in size! Obviously I have a very slim application, it’s just a port scanner – still that’s pretty damn small!

Leave a Reply

Your email address will not be published. Required fields are marked *