I can’t yet review prior versions of JavaFX. I’ve only worked with version 11. In my previous post, I discussed the horrible failures I had getting a JavaFX 11 application, deployable.

But there is greatness in JavaFX. I wanted to cover some of the good things in this article.

Scene Builder

I was using Oracle’s Scene Builder and I found it to be amazing. Scene Builder is a design utility that allows one to drag/drop elements (fields, buttons, etc) and style them within a GUI.

The Scene Builder interface looks like this:

Scene Builder Interface

In the image above, we have our Scene Builder GUI, which has a library of elements to pick from on the left. Our stage in the center and the Inspector on the right.

The library will contain the elements like fields, buttons, charts, 3d elements, etc. The stage is the visual representation of the application. The Inspector has three main sub categories: Properties, Layout and Code – which are used to configure the elements by way of look & feel, or functionality.

Using Scene Builder I was able to create a UI like below:

Initial design of a Desktop Application, using Scene Builder.

Using Scene Builder, I created a main container which was a Grid. This let met snap components into specific spots in the grid. As for styling, I added a glow effect to some labels: The labels “host,” “starting port,” “ending port” and “Open Ports” was given a special effect called “Bloom” which makes it glow.

Some styling examples

Some of the visual effects are listed above. These effects can be further modified with threshold values as well as adding an inner effect. So two effects can be set via this utility and thresholds can be set.

The UI elements have three menu options (PropertiesLayout and Code.) The code option is where the element abilities, names and actions are defined. Some elements can be made draggable here, for example. 

As a simple example, the Submit button I made could be set with the button id, as well as what action it will call in the controller (in this case, “startScan.”)

JDK 11 and JavaFX

With the design complete, the IDE side was done for this project using IntelliJ 2018.3.3. Note there are some bugs I found and reported to IntelliJ, mentioned below. 

JDK 11 removes JavaFX from the standard library. This means if you’re using JDK 11, you’ll need to download JavaFX 11. It can be downloaded from OPENJFX.IO

Once you download JavaFX 11 and JavaFX Mods 11, uncompress to some folder on the disk. 

Setting Up IntelliJ

In the IDE Project Structure window, add the library for JavaFX 11… you’ll go to the uncompressed folder from the download, and point to the lib folder within.

Advice from OpenJFK was to add a Path definition for PATH_TO_FX by going into Preferences and opening up “Appearance & Behavior” and clicking on Path Variables.

Adding a new one called PATH_TO_FX and setting the value to your uncompressed javafx codes lib folder.

Once you have the path variable set, you need to modify the VM options under the Runtime Configuration. Under Run > Edit Runtime Config Here the VM Options field needs to have something like this:

--module-path ${PATH_TO_FX}:mods/production --add-modules=javafx.controls, javafx.fxml


The field for Main class, needs to be updated to your class that has the Main file.

At this point the IntelliJ IDE should recognize JavaFX calls. 

Code

My Main class looks like this:

package portscanner;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("portscanner.fxml"));
        System.out.println(root);
        Controller controller = new Controller();
        FXMLLoader loader = new FXMLLoader();
        loader.setController(controller);

        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);

        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }

}

IntelliJ handled the imports for me and it created the @Override for me. I added created an instance of the Parent to root, using the FXMLLoader to load and get the resource (using getClass()) for my fxml file. 

So this is the fxml made in the fantastic Scene Builder app. I dropped the fxml in the same package location… so I didn’t really have to do anything funny. 

Instantiating the Controller I passed it into the loader. So Main, loads the controller and finally I’m setting the primary stage of the app by creating a scene (from Scene) and setting some initial values. 

The controller class handles all the logic of this simple app:

package portscanner;

import javafx.fxml.FXML;
import javafx.event.Event;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import java.io.DataOutputStream;
import java.net.Socket;

public class Controller {
    @FXML public TextField hostValue;
    @FXML public TextField startingPortValue;
    @FXML public TextField endingPortValue;
    @FXML public TextArea scanResultsValues;

    @FXML
    public void startScan(Event e) {
        System.out.println("Button Clicked");
        System.out.println(hostValue.getText());
        System.out.println(startingPortValue.getText());
        System.out.println(endingPortValue.getText());
        System.out.println("Scanning host: " + hostValue.getText() + " starting at port: " + startingPortValue.getText() + " and ending at port: " + endingPortValue.getText());

        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 idea is that the user has 3 fields to fill out in the app (host name, starting port and ending port.) The user input is taken and then we will make a Java call to connect to the port host on a starting port and increment till we hit the ending port. All open ports will be presented to the user in the UI. 

The fields in question are all public… why is that? Well I had some errors on them being private. I need to really dig deeper to understand why. For now I pass them as public fields. the @FXML annotation shows these are fields in the fxml file. A small snippet of the fxml file is below, showing how these values align:

<TextField fx:id="hostValue" text="127.0.0.1" GridPane.columnIndex="1" GridPane.rowIndex="1">
   <GridPane.margin>
      <Insets left="6.0" right="6.0" />
   </GridPane.margin>
</TextField>

In the above fxml the fx:id has a value that is defined in the Controller. 

startScan Method

I added a method in the Controller for starting the scan. This will take the values in the UI, and when the button is pressed this gets called – passing those values into the code. 

Doing a bit of research I came across using Runnable tasks for concurrency in Java. That’s why I define a runnable task called “task” which has its own run method that calls the core of the code to run in a background thread. Without which, the UI would block from being updated on each run (meaning if you were scanning 2000 ports the UI would lock until all ports were scanned.) 

Values from the UI fields will come in as Strings. Parsing those integers out in the for loop, the a Socket instance is created to connect to that port on the host. 

Reason for the try/catch was that I couldn’t get the Java Socket connection to work without using a try catch and having the catch specifically catch “Exception” vs something very specific. My inexperience here with Java caused me to find this solution. 

After the for loop is complete we append one more value back to the UI – a completion message (a UI graphic would be better.) 

Button

The submit button has an onAction, which calls the startScan method in the Controller.

<Button fx:id="submit" mnemonicParsing="false" onAction="#startScan" opacity="0.76" style="-fx-background-color: #ff3c3c;" text="Start Scan" textFill="#fcf9f9" GridPane.columnIndex="3" GridPane.rowIndex="2" GridPane.valignment="BOTTOM" />

UI – Results Field

The UI textArea is set in the fxml as:

<TextArea fx:id="scanResultsValues" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: #000000; -fx-text-fill: #000000;" GridPane.columnIndex="1" GridPane.rowIndex="2">
   <opaqueInsets>
      <Insets />
   </opaqueInsets>
   <GridPane.margin>
      <Insets left="6.0" top="20.0" />
   </GridPane.margin>
   <padding>
      <Insets top="5.0" />
   </padding>
   <font>
      <Font name="System Bold" size="14.0" />
   </font>
</TextArea>

The Controller updates that field like so:

scanResultsValues.appendText("Port open: " + Integer.toString(i )+"\n");
Fully working example of the JavaFX 11 Desktop App.

Conclusion

JavaFX was a fine way to develop but I ran into a plethora of issues deploying it to a standalone application. After 20+ hours I still can’t make a distribution of this app. But that’s a WHOLE ‘NOTHER STORY!!!! 

Staying on the positive, I have to say I liked using Scene Builder and JavaFX was fine to actually create something. You can also add Scene Builder into IntelliJ so that it can be called within the IDE.

Still, at this point in time, if I were tasked to make a Desktop Application, I would probably prefer Qt/C++. The main reason comes down to deployability. It seems that JavaFX 11/JDK 11 have made deployability such a nuisance that it’s nearly impossible. An alternative would be to revert back to JDK 8 and use the integrated JavaFX libraries to generate a fat JAR.

Leave a Reply

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