I was honoured to present last weekend at the Java 9 and Women in Tech Unconference in Sandton, South Africa. The topic of the presentation was a JavaFX makeover for the NetBeans Platform – get the slides here.

Today I want to share the details of the process of transforming the GUI of an existing NetBeans Platform Application from Swing to JavaFX. There was not enough time to discuss all the details during the presentation, so I prepared the project before I started. However, here I will describe all of the steps that are required. I am using JDK 8, NetBeans 8.1 (the Java SE bundle has everything we need) and Scene Builder 2.0.

1 – Create the sample application

The application that I will be giving a makeover is the Sample CRUD Application that ships with the NetBeans IDE. From the File menu, choose New Project… Browse to the Samples > NetBeans modules category and choose Sample CRUD Application. On the next page of the wizard, specify a location and click Finish.

Creating the Sample CRUD Application

At this point, the sample application will not compile – please read my earlier post about Module Dependencies and Java 8 for more information. Here is a brief summary of the two steps that are required:

Add a dependency on the Explorer & Property Sheet API for the CustomerEditor module.

Remove the Command-line Serviceability module from the application.

Run the application. It should look something like this:

Running CRUD Application

2 – Create a new module

Lets create a new module to house our JavaFX code. Right-click on the Modules node under the CRUD Customber DB Manager project, and choose Add New… Follow the steps of the wizard – I called my project JavaFXWindowSystem and I chose za.co.pellissier.javafxwindowsystem as my code name base. If you are following step by step, I suggest that you keep at least the code name base the same.

Creating a new module

3 – Find the right class to replace

It is possible to replace the Window System of the NetBeans Platform because it was designed right from the start in a very modular way. (Reading the platform source code, you might spot cases where there are specialized mock classes in the unit tests that are loaded just like the normal implementations, except during test execution.)

Before continuing, you will have to download and configure the source code of the NetBeans platform if you want to follow the steps. On the NetBeans download page, you will find a link referring to ZIP files for that build. (For the latest version, that link points here.) Download the file ending in platform-src.zip, and extract its contents. In the NetBeans IDE, access Tools > NetBeans Platforms. Under the Sources tab, choose the folder where the extracted source code lives, and close the dialog box.

If you have worked with the NetBeans Window System before, you will probably have encountered the class WindowManager before. This is the most important class when it comes to, well, managing windows. So lets find a spot where we can debug into that class to see what is going on. The easiest spot to put the code is in module CustomerViewer, org.netbeans.modules.customerviewer.CustomerTopComponent, in the method componentOpened(). The framework will call this method when the window is opened. Put these two lines of code into that method:

You will notice that WindowManager is in fact an abstract class. So we will need to locate the concrete implementation that needs replacing. The easiest way to do this in a very modular system like this is to debug. So put a breakpoint on the first line that we inserted (by clicking in the left margin) and start the application in debug mode by clicking the Debug Project button on the main toolbar.

When the breakpoint is hit, step over the first line (F8). And then step into the getMainWindow() call on the second line (F7). Take a look at the class that you encounter…

When you fix imports, make sure that you import java.awt.event.WindowEvent and NOT the JavaFX equivalent!

Note that I added a call to the NetBeans Platform’s LifecycleManager when the application is closed. This ensures that the normal process will be followed for shutting the application down, just like the original Window System would have done.

5 – Fix the issues caused by replacing the WindowManager

Clean and build, and then run the application, and have a look at the exception that is raised by the framework:

[text]java.lang.ClassCastException: za.co.pellissier.javafxwindowsystem.JavaFXWindowManager cannot be cast to org.netbeans.core.windows.WindowManagerImpl
at org.netbeans.core.windows.WindowManagerImpl.getInstance(WindowManagerImpl.java:148)
at org.netbeans.core.windows.WindowSystemImpl.load(WindowSystemImpl.java:78)
at org.netbeans.core.GuiRunLevel$InitWinSys.run(GuiRunLevel.java:229)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
…
[/text]

So we see that there is another class that is involved – WindowSystemImpl. Let us replace it with a new class as well – create a class called JavaFXWindowSystem in the same package:

This time adding the required dependencies is more difficult. We need a dependency on the Core module – click the Show Non-API Module checkbox to even see it on the list of dependencies.

Add a dependency on Core

Once it is added, edit the dependency and set it to use implementation version.

Editing dependency

Setting implementation version

If you do not do this, you will see this error message:

[text]The module za.co.pellissier.javafxwindowsystem is not a friend of org-netbeans-core.jar[/text]

Do take note that this means that you are setting a dependency on a very specific version of the Core module – should you ever change the version of the NetBeans Platform that you build against, you would have to fix this dependency!

You will have to stop the previous execution from the IDE before running the application again. Running it again now shows a very minimal JFrame:

Empty JFrame

6 – Including Branding

A NetBeans Platform Application includes branding information – application icons, splash screen image and so forth. To improve the look of our very basic JFrame, we can use some of these elements:

Now the main window title and application icons are set just like would be done for a standard NetBeans Platform application. So you can configure these elements in the normal branding window in the IDE!

With Branding

7 – Building a new GUI

All the difficult parts are now done – from this point on, we can develop a normal JavaFX GUI using SceneBuilder and the JavaFX infrastructure in the NetBeans IDE. Here is the contents of my crudwindow.fxml file:

I recently decided to use the Sample CRUD Application that is available in the NetBeans IDE as the basis for a demonstration that I did at a conference. (More about that in a future post.) It is a very useful sample that illustrates how to get up and running with database access from a NetBeans Platform Application.

Side note: The sample is an Ant-based application, which is in my opinion better for a demonstration than a Maven-based one. And that is simply because it won’t want to download dependencies at the worst possible time during the demonstration, making everybody wait. 🙂

Running CRUD Application

So I installed JDK 8 and NetBeans 8.1, and without further delay created the application.

This appears on the surface to be a really strange error… ExplorerManager is not even mentioned on that line! I decided to just add a dependency on the Explorer & Property Sheet API for the CustomerEditor module, and the code compiled. Now I could use the sample for my demonstration, and I didn’t give it any more thought. But when I sat down this morning to write about the demonstration, I realised that I need to find an explanation for this issue.

Project structure after creation by the wizard

So let us consider the structure of the sample project, as it stands just after creation by the project wizard. It is a very modular design, since that is partially what the sample is meant to demonstrate. The CustomerEditor (the module that is causing the compile error) depends on the CustomerViewer, which is perfectly sensible. And the CustomerViewer does have a dependency on the Explorer & Property Sheet API module from the NetBeans Platform since it uses classes that live there. Again, a perfectly sensible arrangement.

And then it hit me – I have seen this issue before in my own code! When compiling a NetBeans Platform Application on JDK 8, it is sometimes necessary to include direct dependencies on modules that are used by classes in dependent modules. Dependencies that are definitely NOT required when compiling with JDK 7. The sample is not broken, it is simply JDK 8 that is stricter in its requirements when compiling the code!

Side note: Looking at the code in the sample application, we can form an idea of how old the code is. This still has the Settings.xml and Wstcref.xml files for each TopComponent, which means that it dates back to before the introduction of annotations to register TopComponents. 🙂

One last issue did arise when I ran the application:

[text]Warning – could not install some modules:
Command-line Serviceability – The module named
org.netbeans.modules.autoupdate.services was needed and not found.[/text]

This must be due to new modules introduced since the creation of the sample source code. In an Ant-based NetBeans Platform Application, new modules that are introduced in clusters or projects that the application depends on will automatically get included. This is because the way the dependencies are specified is by listing the EXCLUDED modules from each cluster.

I could safely click Disable and Continue without affecting functionality, so I removed the Command-line Serviceability module from the application (CRUD Customer DB Manager project properties > libraries tab) and thus permanently resolved the last issue.