Jun 102016
 

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

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:

CRUD Application

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

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:

WindowManager winMngr = WindowManager.getDefault();
winMngr.getMainWindow();

Remember to fix any imports that are missing (Ctrl + Shift + I on Windows).

With the platform source code set up, you can Ctrl + Left Click on the name of the WindowManager class to access the source code of the platform. Go ahead and do so – be brave! 🙂

public abstract class WindowManager extends Object implements Serializable {

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…

package org.netbeans.core.windows;

...

@org.openide.util.lookup.ServiceProvider(service=org.openide.windows.WindowManager.class)
public final class WindowManagerImpl extends WindowManager implements Workspace {

We have found it – WindowManagerImpl is the class that we need to replace.

4 – Create a basic new WindowManager implementation

In the new za.co.pellissier.javafxwindowsystem package of the new module, create a class called JavaFXWindowManager and add the service provider annotation:

@org.openide.util.lookup.ServiceProvider(service=org.openide.windows.WindowManager.class,
supersedes = "org.netbeans.core.windows.WindowManagerImpl")
public class JavaFXWindowManager extends WindowManager {

The annotation indicates what type of service the class provides, and it also (very important!) indicates that this new implementation will supersede the existing one.

Now we need to add some dependencies in order to resolve all the imports.

Adding a dependency

Adding a dependency

Adding Window System API

Adding Window System API dependency

Add the following dependencies:

  • Lookup API
  • Nodes API
  • Utilities API
  • Window System API

Implement all abstract methods (hint in the margin) and fix imports again if necessary.

Quite a long list of methods will be generated, but thankfully there is only one that we are interested in implementing right now – getMainWindow(). So lets add the basics of creating a window:

@ServiceProvider(service = WindowManager.class, 
                  supersedes = "org.netbeans.core.windows.WindowManagerImpl")
public class JavaFXWindowManager extends WindowManager {

    public static JFrame mMainWindow = new JFrame();

    public JavaFXWindowManager() {

        mMainWindow.setSize(new Dimension(640, 480));
        mMainWindow.addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent evt)
            {
               LifecycleManager.getDefault().exit();
            }
        }
        );
    }

    @Override
    public Frame getMainWindow() {
        return mMainWindow;
    }

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:

@ServiceProvider(service=WindowSystem.class, supersedes = "org.netbeans.core.windows.WindowSystemImpl")
public class JavaFXWindowSystem implements WindowSystem {

    @Override
    public void init() {
    }

    @Override
    public void show() {
        JavaFXWindowManager.getDefault().getMainWindow().setVisible(true);
    }

    @Override
    public void hide() {
        JavaFXWindowManager.getDefault().getMainWindow().setVisible(false);
    }

    @Override
    public void load() {
    }

    @Override
    public void save() {
    }
}

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

Add a dependency on Core

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

Editing dependency

Editing dependency

Setting implementation version

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

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:

public JavaFXWindowManager() {

    mMainWindow.setSize(new Dimension(640, 480));
    mMainWindow.addWindowListener(new WindowAdapter()
    {
        @Override
        public void windowClosing(WindowEvent evt)
        {
            LifecycleManager.getDefault().exit();
        }
    }
    );

    String title = NbBundle.getBundle("org.netbeans.core.windows.view.ui.Bundle").getString("CTL_MainWindow_Title_No_Project"); //NOI18N
    if (!title.isEmpty())
    {
        mMainWindow.setTitle(title);
    }
    mMainWindow.setIconImages(Arrays.asList(
        ImageUtilities.loadImage("org/netbeans/core/startup/frame.gif", true),
        ImageUtilities.loadImage("org/netbeans/core/startup/frame32.gif", true),
        ImageUtilities.loadImage("org/netbeans/core/startup/frame48.gif", true)));
    mMainWindow.setLayout(new java.awt.BorderLayout());
}

Add a dependency on the Base Utilities API module and fix imports.

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

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:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="276.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="za.co.pellissier.javafxwindowsystem.CrudwindowController">
   <children>
      <ListView fx:id="list" prefHeight="400.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
      <Label layoutX="220.0" layoutY="18.0" text="Name:" />
      <Label layoutX="220.0" layoutY="52.0" text="City:" />
      <TextField fx:id="txtName" layoutX="269.0" layoutY="14.0" prefHeight="25.0" prefWidth="233.0" />
      <TextField fx:id="txtCity" layoutX="269.0" layoutY="48.0" prefHeight="25.0" prefWidth="233.0" />
   </children>
</AnchorPane>

And the CrudwindowController controller class, which contains bits and pieces copied from the sample code to make the DB access work:

package za.co.pellissier.javafxwindowsystem;

import demo.Customer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.swing.SwingUtilities;
import org.netbeans.modules.customerdb.JavaDBSupport;

/**
 * FXML Controller class
 *
 * @author Hermien Pellissier
 */
public class CrudwindowController implements Initializable {

    @FXML
    private ListView<CustomerWrapper> list;
    @FXML
    private TextField txtName;
    @FXML
    private TextField txtCity;
    
    private static class CustomerWrapper
    {
        private String displayName;
        private Customer customer;

        public String getDisplayName() {
            return displayName;
        }

        public void setDisplayName(String displayName) {
            this.displayName = displayName;
        }

        public Customer getCustomer() {
            return customer;
        }

        public void setCustomer(Customer customer) {
            this.customer = customer;
        }

        @Override
        public String toString() {
            return displayName;
        }
    }

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        JavaDBSupport.ensureStartedDB();
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("CustomerDBAccessPU");
        if (factory == null) {
            // XXX: message box?
            return ;
        }
        EntityManager entityManager = null;
        try {
            entityManager = factory.createEntityManager();
        } catch (RuntimeException re) {
            // XXX: message box?
            return ;
        }
        final Query query = entityManager.createQuery("SELECT c FROM Customer c");
        SwingUtilities.invokeLater(new Runnable () {
            @Override
            public void run() {
                @SuppressWarnings("unchecked")
                List<Customer> resultList = query.getResultList();
                List<CustomerWrapper> wrappersList = new ArrayList<>();
                for (Customer customer : resultList) {
                    CustomerWrapper w = new CustomerWrapper();
                    w.setCustomer(customer);
                    w.setDisplayName(customer.getName());
                    wrappersList.add(w);
                }
                ObservableList<CustomerWrapper> items = FXCollections.observableArrayList(wrappersList);
                list.setItems(items);
            }
        });
        
        list.getSelectionModel().selectedItemProperty().addListener(
                new ChangeListener<CustomerWrapper>() {
            @Override
            public void changed(ObservableValue<? extends CustomerWrapper> ov,
                    CustomerWrapper old_val, CustomerWrapper new_val) {
                txtName.setText(new_val.getDisplayName());
                txtCity.setText(new_val.getCustomer().getCity());
            }
        });
    }    
}

Note that you will need a dependency on the CustomerDBAccessLibrary module from the sample app.

The last step is add the code to display the JavaFX scene to the end of the constructor of the JavaFXWindowManager class:

        try {
            JFXPanel fxPanel = new JFXPanel();
            Parent root = FXMLLoader.load(JavaFXWindowManager.class.getResource("crudwindow.fxml"));
            Scene scene = new Scene(root);
            fxPanel.setScene(scene);
            mMainWindow.add(fxPanel, BorderLayout.CENTER);
        }
        catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }

The complete project structure now looks like this:

Completed Project

Completed Project

And the running application:

The new JavaFX GUI

The new JavaFX GUI

Aug 012014
 

Beans binding makes life so much easier when designing GUIs with the NetBeans IDE. It is well integrated into the GUI builder, and usually it just works out of the box.

Last week I developed a new options panel for a NetBeans Platform Application. Creating the framework for the new panel took maybe a minute using the file wizard that sets up everything for you to link the panel to the Options infrastructure of the Platform. I added a text field or two, and bound their text properties to a bean that I had created for this purpose. When I ran the application, I saw that the binding didn’t do anything at all!

So I dived into the source code of the beansbinding library. I debugged through lots and lots of source code – first in a plain old Java application where the binding did work as expected, and then in the platform application that was giving me trouble. I spent maybe an hour and a half deep down in the depths of that library. And then suddenly I found the source of my problem! If you look carefully at the source code in the NetBeans Options Window Module Tutorial, and knew what you were looking for, this like would jump out at you:

The cause of my trouble is that this panel class is NOT public! In retrospect, this makes a lot of sense. Beans binding does a lot of operations via reflection, and if the class is not public the library won’t have access to it!

Tip of the month
For beans binding to work with any GUI panel, that panel must be public!
Nov 302013
 

The NetBeans IDE has some great refactoring tools. It makes lots of tasks so much less error prone than doing them manually. In this article, I describe some of my favourite refactoring tools. (These tools can be accessed from the main menu bar (Refactor) or from the context menu of a Java file open in the editor.)

#1 – Rename

Anything that has a name can be renamed. But my favourite example is renaming a class.

refactor01

 

When a class is renamed, not only the source code of the class (optionally including comments) is changed, but also the name of the file where it lives. And also any usages of the class elsewhere in your open projects!

#2 – Encapsulate Fields

This feature allows you to generate getters, setters and property change support for fields in a class. It allows you to specify various parameters, such as the visibility of the accessors. So lets take a very simple class, and apply the Encapsulate Fields tool:

refactor02

 

… and lets look at the generated code:

refactor03

 #3 – Extract Interface

If you have an existing class, and you want to extract some of its methods into an interface, the NetBeans IDE has just the tool for you!

refactor04

 

Not only does the interface get generated, but your class is automatically changed to implement the new interface too!

refactor05

#4 – Introduce Method

Sometimes one wants to extract some lines of code into a separate method. Maybe because the initial method is becoming too long to be readable, or because that part of the functionality will be reused elsewhere. Whatever the motivation, the NetBeans IDE has just the tool for the job! So lets look at a silly example where an identical piece of code occurs twice in the same class:

refactor06

And then lets look at the result of the refactoring:

refactor07

Tip: Experiment with extracting large, complicated blocks of code – the NetBeans IDE is capable of much more than this simple example!

#5 – Change Method Parameters

Say we decide to now introduce a new parameter for the extractedMethod() created above. If we did this manually, it would mean changing each place where the method is called manually too. (Sometimes this is desirable, but it can be a lot of effort.) With the Change Method Parameters feature, this task becomes much easier, since it allows you to specify a default value for the new parameter.

refactor08

Now our class looks like this:

refactor09

Tip: Also try out the Create New Method and Delegate from Existing Method option!

Nov 152013
 

The NetBeans IDE has so many wonderful features that makes a Java developer’s life easier. And many of these features have keyboard shortcuts attached to them. So I decided to make a list of the shortcuts that I think every developer should know.

  • Ctrl+O : Quickly go to a class
    This shows a dialog that allows you to start typing the name of the class that you are looking for. You can then choose the class from a list of matched names.
  • Ctrl+Shift+I : Fix imports
    Add any new imports that are necessary and remove unused ones at the same time. If a class name matches multiple possibilities, a dialog is displayed that allows you to choose which class to import.
  • Alt+Shift+F : Format selection
    Format the selected code according to the rules that can be changed in the IDE Options > Editor > Formatting. This is also a handy way to format XML in a useful way.
  • Alt+F7 : Find usages
    Find where the currently selected type is used.
  • Ctrl+R : Rename
    Rename the selected variable or class, refactoring if required.
  • Alt+Insert : Generate code
    Choose from various context sensitive items that the IDE can generate, for example generating getters and setters.
  • Ctrl+Space : Auto complete
    Context sensitive auto complete.
  • Ctrl+/ : Toggle comment lines
    Adds // to the start of each of the selected lines. Or removes these if present.
  • Ctrl+Shift+Up/Down : Copy lines up/down
    Make a copy of the currently selected lines, without going through the clipboard.
  • Alt+Shift+Up/Down : Move lines up/down
    Move the currently selected lines, without going through the clipboard.

For a much more comprehensive list of keyboard shortcuts, have a look at Highlights of NetBeans IDE 7.4 Keyboard Shortcuts & Code Templates.

Apr 242013
 

The NetBeans IDE’s debugging tools is one of maybe the top 5 features that I simply cannot live without! And as you can imagine, debugging is something I do quite regularly. So I was rather surprised when I only recently discovered an AWESOME feature that has been around for a long, long time. 🙂 This feature is Variable Formatters.

To explain what a Variable Formatter is, some context is required. So lets have a look at the Variables window while debugging a very simple little Java application.

Variables Window

Variables Window

That is a pretty useful view. I can see all the variables that I could possibly need at a glance. (Of course, that is easy with an application this simple.) Of interest to this particular discussion, however, is the Value column. Notice how the various types have different string representations that are displayed. Now lets create a simple class called SomeObject:

public class SomeObject {

    private String var1;
    private String var2;

    public SomeObject(String var1, String var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public String getVar2() {
        return var2;
    }
}

and create an ArrayList filled with SomeObjects, to inspect in the debugger:

ArrayList in the Variables Window

ArrayList in the Variables Window

Each of the elements in the list can of course be expanded to see the details. But what if you were looking for a specific element in a very long list? The great news is, that Variable Formatters allows us to customise how these items are displayed! From the main menu, select Tools > Options. Choose the Java page, and then the Java Debugger tab, Variable Formatters category.

Variable Formatters options

Variable Formatters options

A number of formatters are already specified, but we can add a custom one:

Custom Formatter

Custom Formatter

And very impressively, it gets applied immediately when you click OK on the options dialog! Even while the debugger is running! Now the display looks like this:

Customised Display

Customised Display

Suddenly ANY type of variable can have a user friendly, and customised, presentation in the variables window!

Sep 112012
 

Another cool new feature in the latest development builds of the NetBeans IDE: a new source navigation view! At the bottom of the editor window, there is now a new panel showing you where you are in the source code. It shows you which class, method and block you are in. And by clicking on one of the elements on the bar, you can quickly navigate to other locations. Very handy indeed!

image

Sep 042012
 

I discovered another cool feature in the latest development builds of the NetBeans IDE. When viewing a pom.xml file in any Maven-based project, you can now see the effective pom. This means that the configuration inherited from one or more parent pom.xml files is shown in this view too. Very useful indeed!

image