Aug 092016
 

The Swing-based window system in the NetBeans Platform has the concept of a window that is loaded at runtime. These windows derive from the class TopComponent and are registered using annotations, and are created using a handy wizard in the NetBeans IDE. Today I am going to show you how to do something similar with JavaFX components, using the default lookup of the NetBeans platform. My aim is to load an FXML file and display it on a tabbed pane.

I am using the source code from my previous blog entry as a starting point because I already had it handy. That main fxml file already had a tabPane, which is what I require in the GUI.

Window with basic menu

Application from the previous blog entry

I created a package called za.co.pellissier.javafxwindowsystem.api where the public API of my JavaFXWindowSystem will be located. To be able to add a component to the tabbed pane, the text to be displayed on the tab as well as a javafx.scene.Node object is needed. So I created an interface that windows will need to implement:

[java]package za.co.pellissier.javafxwindowsystem.api;

import javafx.scene.Node;

public interface IJavaFXWindow {

public Node load();

String getWindowName();
}[/java]

There is a class called FXMLLoader that is able to load an fxml file and return a Node object representing its content. To save future implementers of this API some effort, here is an abstract class that handles the loading:

[java]package za.co.pellissier.javafxwindowsystem.api;

import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;

public abstract class AJavaFXWindow implements IJavaFXWindow {

private final String fxmlFileName;

public AJavaFXWindow(String fxmlName) {
fxmlFileName = fxmlName;
}

@Override
public Node load() {
try {
URL resource = getClass().getResource(fxmlFileName);
return (Node) FXMLLoader.load(resource);
} catch (IOException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}[/java]

That is all that there is to the API, for now. ūüôā

Note: Remember to export the package (set it to public) if you want it to be visible to other modules!

So lets look at how it is used by the window system. In my MainWindowController class (the JavaFX controller for the main fxml file), I added this method:

[java] private void loadWindows() {
Collection windows =
Lookup.getDefault().lookupAll(IJavaFXWindow.class);
for (IJavaFXWindow window : windows) {
Node win = window.load();
String name = window.getWindowName();
tabPane.getTabs().add(new Tab(name, win));
}
}[/java]

I called this method in the initialize() method after the menu items are loaded.

All that remains is to see how a new window can be added to the system. First, lets create a new module called JavaFXTestWindow to contain the new window. It needs dependencies on Lookup API and JavaFXWindowSystem (the module where the API is defined).

In the JavaFXTestWindow module, I created a new fxml file called testwindow.fxml using SceneBuilder, and generated a controller class using the NetBeans IDE. Now for the most important step… implementing that brand new API!

[java]package za.co.pellissier.testwindow;

import org.openide.util.lookup.ServiceProvider;
import za.co.pellissier.javafxwindowsystem.api.AJavaFXWindow;
import za.co.pellissier.javafxwindowsystem.api.IJavaFXWindow;

@ServiceProvider(service = IJavaFXWindow.class)
public class MyWindow extends AJavaFXWindow implements IJavaFXWindow {

public MyWindow() {
super(“testwindow.fxml”);
}

@Override
public String getWindowName() {
return “Test Me!”;
}
}[/java]

The most important line is the @ServiceProvider annotation – if you leave that out, the default lookup will not be able to find the window!

After cleaning and building, here is the result!

FXML file loaded

FXML file loaded

There are certainly still improvements to be made to this implementation. It only emulates a single mode in the NetBeans Platform Window System. And it requires the user to manually create an additional class. But it is a good start! ūüôā

Jul 262016
 

I was asked some very good questions in response my post about the JavaFX makeover for the NetBeans Platform! Questions with long and interesting answers, questions that sparked more questions in my mind. So here is the first post in response to those questions. ūüôā

Is it possible to reuse normal NetBeans Platform actions?

For the purpose of this exploration, I decided to create a simple NetBeans Platform application to work with. A newly created one, with all of the latest infrastructure available (including annotations for registration). With one action for use today, and one window for another post to follow. So, we start off with this little application:

Simple application to transform

Simple application to transform

The action that I created is registered using annotations:

[java]
@ActionID(
category = “Edit”,
id = “za.co.pellissier.someaction.SomeAction”
)
@ActionRegistration(
iconBase = “za/co/pellissier/someaction/clickme.png”,
displayName = “#CTL_SomeAction”
)
@ActionReferences({
@ActionReference(path = “Menu/File”, position = 1300),
@ActionReference(path = “Toolbars/File”, position = 200)
})
@Messages(“CTL_SomeAction=Action!”)
public final class SomeAction implements ActionListener {
[/java]

At compile time, these annotations are transformed into entries in the generated layer file. And at run time that gets loaded into the System FileSystem. Most days, you would not need to know any of that to work with actions. But today is not most days… ūüôā

If you look at the module project where the action was created in Files view (after cleaning and building the module), you will see the generated-layer.xml file:

Generated layer file

Generated layer file

Looking at the content of the generated layer file, we can see where the action will be registered at runtime:

[xml]
































[/xml]

An internet search for the system file system reveals this wiki page which gives us the hint that the FileUtil class is going to be a useful one. And finding usages of that class reveals the following interesting method in org.netbeans.core.windows.view.ui.MainWindow:

[java]
private static JMenuBar getCustomMenuBar() {
try {
String fileName = Constants.CUSTOM_MENU_BAR_PATH;
if (fileName == null) {
return null;
}
FileObject fo = FileUtil.getConfigFile(fileName);
if (fo != null) {
DataObject dobj = DataObject.find(fo);
InstanceCookie ic = (InstanceCookie)dobj.getCookie(InstanceCookie.class);
if (ic != null) {
return (JMenuBar)ic.instanceCreate();
}
}
} catch (Exception e) {
Exceptions.printStackTrace(e);
}
return null;
}
[/java]

This method is a great and simple example of how to call instanceCreate() for an item registered on the file system. So together with the information from last time, we now have the pieces of information to start implementing the solution. This time I created a file called mainwindow.fxml (remember to update JavaFXWindowManager to load this file):

[xml]









[/xml]

The easiest place to call the code that loads the menu items is in the initialize() method of the controller class. Note that some of the menus, like Window, causes a StackOverflowException when being loaded in this context. It happens because it in turn calls on the Window System that is being loaded. So for now, lets exclude those menus. I also created a layer file and removed some of the File menu actions (see below) for the same reason.

[xml]















[/xml]

So here is the controller class that does the basics of loading the actions and adding them to the JavaFX GUI, after a lot of debugging and hunting for ways to access the user friendly names for the actions.

You will notice several TODOs in the code, and there are probably more things to implement that I haven’t thought of yet. But at least it will point you in the right direction.

[java]
public class MainWindowController implements Initializable {

@FXML
private MenuBar menuBar;
@FXML
private TabPane tabPane;

/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
FileObject systemConfigFile = FileUtil.getSystemConfigFile(“Menu”);
FileObject[] menus = systemConfigFile.getChildren();
for (FileObject menu : menus) {
// skip the menus that cause a StackOverflowException
if (menu.getName().equals(“Window”) || menu.getName().equals(“GoTo”)
|| menu.getName().equals(“Edit”) || menu.getName().equals(“View”)) {
continue;
}
Menu newMenu = new Menu(menu.getName());
menuBar.getMenus().add(0, newMenu);
FileObject[] items = menu.getChildren();
// TODO Load sub-menus with their own menu items
for (FileObject item : items) {
try {
DataObject dobj = DataObject.find(item);
InstanceCookie ic = (InstanceCookie) dobj.getCookie(InstanceCookie.class);
if (ic != null) {
Object instance = ic.instanceCreate();
if (instance instanceof ActionListener) {
ActionedMenuItem menuItem = new ActionedMenuItem((ActionListener)instance);
newMenu.getItems().add(menuItem);
} else if (instance instanceof JSeparator) {
// TODO Add logic to avoid two consecutive separators
newMenu.getItems().add(new SeparatorMenuItem());
}
}
} catch (IOException | ClassNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}

private static class ActionedMenuItem extends MenuItem
{
private final ActionListener listener;

private ActionedMenuItem(ActionListener instance) {
String displayName = “”;
if (instance instanceof AbstractAction) {
displayName = ((AbstractAction) instance).getValue(“displayName”).toString();
} else if (instance instanceof SystemAction) {
displayName = ((SystemAction) instance).getName();
}
// TODO Deal with & and mnemonics
setText(displayName);

listener = instance;

setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent e) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
// TODO Create proper action
listener.actionPerformed(null);
}
});
} catch (InterruptedException | InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
}
}
[/java]

Window with basic menu

Window with basic menubar

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:

[java]WindowManager winMngr = WindowManager.getDefault();
winMngr.getMainWindow();[/java]

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! ūüôā

[java]public abstract class WindowManager extends Object implements Serializable {[/java]

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…

[java]package org.netbeans.core.windows;

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

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:

[java]@org.openide.util.lookup.ServiceProvider(service=org.openide.windows.WindowManager.class,
supersedes = “org.netbeans.core.windows.WindowManagerImpl”)
public class JavaFXWindowManager extends WindowManager {[/java]

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:

[java]@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;
}[/java]

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:

[java]@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() {
}
}[/java]

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:

[java]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());
}[/java]

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]







[/xml]

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

[java]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 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 resultList = query.getResultList();
List wrappersList = new ArrayList<>();
for (Customer customer : resultList) {
CustomerWrapper w = new CustomerWrapper();
w.setCustomer(customer);
w.setDisplayName(customer.getName());
wrappersList.add(w);
}
ObservableList items = FXCollections.observableArrayList(wrappersList);
list.setItems(items);
}
});

list.getSelectionModel().selectedItemProperty().addListener(
new ChangeListener() {
@Override
public void changed(ObservableValue ov,
CustomerWrapper old_val, CustomerWrapper new_val) {
txtName.setText(new_val.getDisplayName());
txtCity.setText(new_val.getCustomer().getCity());
}
});
}
}[/java]

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:

[java] 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);
}[/java]

The complete project structure now looks like this:

Completed Project

Completed Project

And the running application:

The new JavaFX GUI

The new JavaFX GUI

Jun 102016
 

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. ūüôā

CRUD Application

Running CRUD Application

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

Creating the Sample CRUD Application

Creating the Sample CRUD Application

But when I tried to run the application, I got this compile error:

[text]SampleCRUDApp2\CustomerEditor\src\org\netbeans\modules\customereditor\EditorTopComponent.java:362:
error: cannot access ExplorerManager
CustomerTopComponent.refreshNode();
class file for org.openide.explorer.ExplorerManager not found[/text]

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

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.

Removing Command-line Serviceability

Removing Command-line Serviceability

Now we are finally ready for the demonstration code… coming soon!

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!
Sep 022013
 

Creating a wizard in a NetBeans Platform Application is quite easy since there is a NetBeans IDE new file wizard that creates all of the scaffolding for you. Have a look at the NetBeans Wizard Module Tutorial for a step by step description of how to work with the normal, modal wizards.

In the above tutorial, the DemoWizardAction class contains the sample code for calling the wizard. The relevant code for displaying the modal wizard is duplicated below for easy reference:

[java]WizardDescriptor wiz = new WizardDescriptor(
new WizardDescriptor.ArrayIterator(panels));
if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) {
// do something
}[/java]

I recently ran into a use case where it was required that the wizard must be non-modal. Whether this is a desirable behaviour for a wizard is a topic all of its own, so lets just assume that we want to be able to do this. The code to display the wizard as non-modal is shown below:

[java]WizardDescriptor wiz = new WizardDescriptor(
new WizardDescriptor.ArrayIterator(panels));
Dialog wizardDialog = DialogDisplayer.getDefault().createDialog(wiz);
wizardDialog.setModal(false);
wizardDialog.setVisible(true);[/java]

Now the remaining complication is being able to catch the event that indicates that the wizard completed with the option WizardDescriptor.FINISH_OPTION as in the original code. I noticed that the WizardDescriptor allows for the registration of property change listeners. And so by debugging into the propertyChange method, I was able to determine what must be handled to catch this particular event:

[java]WizardDescriptor wiz = new WizardDescriptor(
new WizardDescriptor.ArrayIterator(panels));
wiz.addPropertyChangeListener(new PropertyChangeListener() {

@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() != null && “org.openide.WizardDescriptor.FinishAction”.
equals(evt.getNewValue().getClass().getCanonicalName())) {
// do something
}
}
});

Dialog wizardDialog = DialogDisplayer.getDefault().createDialog(wiz);
wizardDialog.setModal(false);
wizardDialog.setVisible(true);[/java]

Jun 212013
 

In the previous blog post, some areas remained unexplored. Specifically, how to observe the values of variables with versions of the IDE that don’t include debug information. In this guest post by Robert Bracko¬†(robert [dot] bracko [at] vip [dot] hr), he describes his experiences with building the NetBeans IDE source code to overcome this obstacle.

Step 1: Building NB sources

  1. Create local disk folder C:\nb6_9_1

  2. Download NB platform sources (netbeans-6.9.1-201007282301-src.zip) and extract it into C:\nb6_9_1\src

  3. Download NB binaries (netbeans-6.9.1-201007282301-ml-javase.zip). This step is necessary for getting the appropriate Ant builder. Extract only netbeans\java\ant into C:\nb6_9_1\ant. Result of that operation should be that C:\nb6_9_1\ant contains ant subfolders bin, etc, extra, lib

  4. Create batch file (buildenv.bat) inside the folder C:\nb6_9_1. Put the following lines (adjust JAVA_HOME to match your Java JDK path!):
    set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_12
    set ANT_HOME=C:\nb6_9_1\ant
    set PATH=%JAVA_HOME%\bin;%ANT_HOME%\bin;%PATH%
    set ANT_OPTS=-Xmx1g

  5. Open command shell and change directory to C:\nb6_9_1. Execute buildenv.bat. Then change directory to NetBeans source which is to be built (C:\nb6_9_1\src). Execute ant (without any cmd-line params). Please be patient; depending on your machine power, the process can take up to hour!

As a result of above-described procedure, new folder is created: C:\nb6_9_1\src\nbbuild.

General notes

  • For some reason, path of Ant installation directory must not contain spaces!
  • If building for the first time,¬†disable¬†Windows Firewall because for some reason it prevents Ant from downloading jars from web.
  • Possible build error “java.lang.OutOfMemoryError” can be overridden by following batch command: set ANT_OPTS=-Xmx1g

Note on building NB 6.9.1 sources:

During build process, you will probably encounter the following error:
C:\nb6_9_1\src\o.jruby.distro\unpatched_source\jruby-1.5.0\build.xml:1141: Java returned: 1

Please check https://netbeans.org/bugzilla/show_bug.cgi?id=186736 for a workaround.

Step 2: Debugging specific NB platform and/or NB module that was developed for that platform

Open your development IDE (in my case, it’s NB 7.3).
There are two possible ways to debug NB module that was developed for specific NB target-platform (in our case, it’s for NB 6.9.1). I prefer the first one ("Adding target platform through NetBeans Platform Manager") because it’s not necessary to install NBM inside target-IDE.

  1. Adding target platform through NetBeans Platform Manager

    1. Choose from main menu Tools > NetBeans Platforms. Press button “Add Platform…”. Choose the folder¬†C:\nb6_9_1\src\nbbuild\netbeans. Let NB decide on the name of platform (“nb691”).

    2. Choose tab “Sources” and add the following folder:¬†C:\nb6_9_1\src

    3. Choose tab “Harness” and check whether radio-button “Harness supplied with Platform” is selected

    4. Open your NBM project.
      The following is important! Open Project Properties > Libraries and adjust the field “NetBeans Platform” to hold the value nb691!

    5. Set the breakpoint inside your NBM code.

    6. Optionally, you can set the breakpoint inside NB platform source code. For instance, let’s imagine that your NBM uses functionality of NB class AbsoluteLayoutSupport and you want to check the interaction. Make a Windows file-search inside the folder¬†C:\nb6_9_1\src. That results in containing folder¬†C:\nb6_9_1\src\form\src\org\netbeans\modules\form\layoutsupport\delegates. So open the folder¬†C:\nb6_9_1\src\form¬†as another project¬†in your development-IDE(uncheck the checkbox “Open Required Projects:”). Expand Projects window node Form Editor > Source Packages > org.netbeans.modules.form.layoutsupport.delegates. Double click on AbsoluteLayoutSupport.java and set the breakpoint.

    7. Inside Projects window, right click on your NBM and choose Debug from the context menu. That makes target-IDE (6.9.1) to launch. Choose Tools > Plugins¬†from target-IDE main menu, then choose tab “Installed”. You should see your NBM inside the list of installed plugins. Nice, isn’t it?

    8. Make some action that affects your NBM and enjoy debugging ūüôā

  2. Opening src\nbbuild as a project

    1. Open your NBM project and set the breakpoint anywhere inside the code.

    2. Open the following folder as a project:¬†C:\nb6_9_1\src\nbbuild. The node “NetBeans Build System” appears inside Projects window.

    3. Right-click the subnode build.xml and choose the following from context menu:¬†Run Target > tryme-debug. That makes¬†target-IDE (6.9.1) to launch. Choose Tools > Plugins¬†from target-IDE main menu, then choose tab Downloaded. Press button “Add Plugins…”, find NBM on disk and then press button “Install”. Finish the installation process, then close Plugins window.

    4. Make some action that affects your NBM and enjoy debugging ūüôā

    5. Optionally, you can set the breakpoint inside NB platform source code. Follow the same approach as given in¬†“Adding target platform through NetBeans Platform Manager”¬†step 6.
Jun 072013
 

Much has been written about the NetBeans Platform. But the ultimate resource still remains the source code of the Platform itself! I have used plenty of open source libraries over the years. And I have ventured into the source code numerous times. Quite often the experience is one that leaves one dazed and confused. But I have found that the NetBeans source code is well structured and easy to follow. So even on your very first adventure, you can gain much wisdom! ūüôā

Reading source code is like reading a Physics text book. You can try to keep all of that information in your head, or you can experience it in practice. So in most cases, the easy way to find an answer is to debug into the source instead of just perusing it.

How to set up the source code to be able to debug into it, depends on what type of project you are working with. For Ant-based module projects, download the source code of the same version as the platform that you are building on, and configure the platform to use it in the NetBeans Platform Manager (Tools > NetBeans Platforms). This gives you access to all of the source code in all of the modules.

For Maven-based projects, you have to attach the source code for each of the dependencies. In project view, right-click on one of the NetBeans module dependencies and choose Download Sources.

Now put a breakpoint somewhere in your own module to start from. So for example, if you wanted to explorer the behaviour of the TopComponent class, put a breakpoint into the constructor of a TopComponent subclass (created using the Window wizard). And step into for example the setName() method.

Using this methodology, you can explore how the platform loads and uses things like nodes and actions and windows. That will provide you with much deeper insight into how these things work!

Robert Bracko (robert [dot] bracko [at] vip [dot] hr) explored the behaviour of an old third party IDE module from the 6.9.1 days. He sent me the following points, describing his fascinating first adventure!

  1. Downloaded NB 6.9.1 (Build Type: Release) from https://netbeans.org/downloads/6.9.1/zip.html:
    1. netbeans-6.9.1-201007282301-ml-javase.zip (description: NetBeans 6.9.1 Java SE OS Independent Zip/English)
      Extracted (not installed!) into directory netbeans-6.9.1-201007282301-ml-javase
    2. netbeans-6.9.1-201007282301-src.zip
      Extracted into directory netbeans-6.9.1-201007282301-src
  2. Inside Development IDE (NB 7.3), added platform nb691 as new NB platform, based on directory netbeans-6.9.1-201007282301-ml-javase\netbeans.
    Sources linked for that platform, based on directory netbeans-6.9.1-201007282301-src
    So far so good¬†ūüôā
  3. Project opened inside Development IDE: KoalaLayoutSupport. Project properties changed: NetBeans platform set to nb691
    Attempt to build the project fails:
    The following error occurred while executing this line:
    X:\netbeans\netbeans-6.9.1-201007282301-ml-javase\netbeans\harness\testcoverage.xml:22: C:\Program Files\NetBeans 7.3\harness\testcoverage\cobertura does not exist.What is strange here is that error message refers to C:\Program Files\NetBeans 7.3 directory. Why does it look into NB 7.3 installation directory?
    Next step was to investigate exactly what’s going on during build process. So I’ve selected project’s node Important Files > Build Script and from the pop-up menu chose Debug Targer > netbeans. I found out that file¬†nbproject/build-impl.xml¬†refers to file¬†nbproject/private/platform-private.properties¬†which further defines property¬†user.properties.file¬†with value¬†C:\\Documents and Settings\\Administrator\\Application Data\\NetBeans\\7.3\\build.properties. Nothing wrong with that, right? Because at the same time debugger’s variables-window shows that property¬†nbplatform.active¬†contains value¬†nb691, so the whole process of building should be (at least I think so) controlled by NB 6.9.1. platform.
    But then one suspicious thing:¬†build-impl.xml¬†tries to define the property¬†harness.dir¬†with value¬†nbplatform.${nbplatform.active}.harness.dir. After having executed that line, debugger’s variables-window shows the value¬†C:\Program Files\NetBeans 7.3/harness¬†for that variable! And that’s obviously wrong.
    So the next step was to change harness dir for platform nb691. I have done that through NB Platform Manager: inside tab Harness, choosing “Harness supplied with Platform” instead of “Harness supplied with IDE”. And then the build passed OK!¬†ūüôā
  4. Next thing, module debugging. Breakpoint set inside of module’s class works OK, also a breakpoint set inside of a class of NB 6.9.1 source. But… For the latter one, debugger’s variable window shows the error message “variable information not available,¬†source compiled without -g option”. I know what it means but how to resolve it?….. For me it’s rather important to see how NB Form Editor source code interact with my module…

Thanks for sharing, Robert! Some questions still remain unanswered in the steps above. Stay tuned for more about his next adventure!

Dec 192012
 

The open space on the menu bar of a NetBeans Platform application is often wasted. In the NetBeans IDE, the quick search bar is positioned in that handy location. And your application can have that too of course. However, the question was raised on the forums recently how one would go about adding your own component there. Here is the promised post explaining the steps in detail. The result will look like the screenshot below, with the new User label introduced in the space where the quick search would have been.

Screenshot showing the standalone module running in the IDE

Screenshot showing the standalone module running in the IDE

Step 1: The Module. We need a NetBeans Platform module to work with. So if you don’t have one yet, create one now. It can be either a standalone module or exist as a part of a suite. I created a standalone module. When you run a standalone module, it basically gets installed into an instance of the IDE. That is why the screenshot shows all the bits of the IDE.

Step 2: The Component. Create a new JPanel form (I called mine CustomComponent) and add a JLabel to it. Specify and icon for the label if you wish. The important point to take note of is that you need to specify a size for the panel (resize it in the GUI builder) and then specify that same size as the minimum and maximum size of the panel too. Otherwise the size of the panel will not be what you expected.

Custom component in the GUI builder

Custom component in the GUI builder

Step 3: The Action. Just as the NetBeans IDE 7.2 Plugin Quick Start tutorial describes, create a new Action. Just as the tutorial describes, choose Global Toolbar Button on the GUI Registration page (this will be changed later). Change the action to extend from AbstractAction and implement Presenter.Toolbar, all still as in the tutorial. And in the getToolbarPresenter() method return an instance of CustomComponent.

Then change the @ActionReference annotation’s path to just be “Menu” and position to be 10000.

So at the end of this step we now have the action class below.

[java]import java.awt.Component;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.Presenter;

@ActionID(category = “Build”,
id = “za.co.pellissier.componentinmenubar.CustomComponentActionListener”)
@ActionRegistration(
displayName = “#CTL_CustomComponentActionListener”)
@ActionReference(path = “Menu”, position = 10000)
@Messages(“CTL_CustomComponentActionListener=CustomComponent”)
public final class CustomComponentActionListener
extends AbstractAction implements Presenter.Toolbar {

@Override
public void actionPerformed(ActionEvent e) {
}

@Override
public Component getToolbarPresenter() {
return new CustomComponent();
}
}[/java]

Step 4. Alignment. In order to force the component to be right-aligned, we can add a spacer just to the left of it. Here the NetBeans Quick Search Integration Tutorial has a handy example (in the very last section). Create an XML Layer file in your module. The file should be opened on creation. Now add a spacer like in the tutorial, resulting in the layer.xml file looking like this:

[xml]






[/xml]

Note the position of 9999, which is just to the left of the 10000 of the component.

Step 5. Hiding Quick Search. In practice you could have the quick search and your own component next to one another. However, for the purpose of this tutorial I decided to hide the quick search feature. Modify the layer file once more, so that it looks like this:

[xml]









[/xml]

That is it. Now you have a custom component being displayed in the empty space on the menu bar.