BookNBPlatformCookbookCH01

Contents

Modules

Introduction

For some reason you had to write a simple application. After some time your boss likes it but wants other features in it. All of a sudden the tool turns from an internal application to a company application and more requirements are added to the mix. That simple application is now a nightmare to keep up with and extend. You need something else. Or you are tasked with a well designed and big application with extensibility requirements but don’t want to reinvent the wheel. Sounds familiar?

Welcome to the world of Rich Client Platforms (RCP). RCP provide a framework for building such applications without the hassle of coding simple stuff like windowing and data systems and allows you to focus in what your application really needs to do by avoiding pitfalls that someone else already went through.

In this book you’ll find resources to perform certain tasks with the same benefits a RCP provides since they have been tested and/or utilized in real applications so you don’t have to reinvent the wheel. The following is an excerpt from the NetBeans webpage:

“The NetBeans platform is completely configurable and allows using your custom platform and provides reliable and flexible application architecture. It can save you years of development time. The NetBeans Platform gives you a time-tested architecture for free. An architecture that encourages sustainable development practices. Because the NetBeans Platform architecture is modular, it's easy to create applications that are robust and extensible.” We’ll show you how to do complete that task and more importantly the logic behind it. There’s a saying on my home country, Puerto Rico that goes like this after translation: “Bring him fish and he’ll eat for a day, show him to fish and he will know how to feed himself always”

So get your fishing rod ready and get ready to go fishing! Creating your application is a huge task on its own. But when you encounter an uncommon need you have no idea where to start.

Here we’ll cover the uncommon scenarios for applications using the NetBeans platform. Also we’ll take advantage of useful features for gathering usage data and error reporting important for maintenance and enhancements of your application. Knowledge is power. Uncommon ways of upgrading your application will be covered as well.

Module Installer

Preparation

Most of the time a module installer is not needed at all but when you need to address one of the following:

  • Perform some preparation tasks before the application is started
  • Perform some cleanup after your application is done
  • Perform tasks that might take too long for the user to wait

The platform allows the developer to hook into the module’s different life cycle stages. Most of the uncommon requirements for your application will be handled there. From preparation for your module execution to clean up after your module is closed.

How to

The installer allows us to hook into the module’s different life cycle stages so we can handle our uncommon needs. Let’s start by creating the installer. In the desired package of the module we want to add the installer right click>New>Installer / Activator and click finish. If Installer / Activator is not available use New>Other>Module Development> Installer / Activator instead. This will create a file named Installer in the specified package. Looking at the generated code is nothing special. The real important part of the wizard is the changes to module’s manifest file (Important Files>Module manifest). It has an entry like this:

OpenIDE-Module-Install: <package>/Installer.class

If the file is for some reason moved, renamed or deleted this entry needs to be modified manually. The IDE doesn’t handle this. Here is the list of methods to override and when in the life cycle of the module they occur:

  • void restored(): This occurs when the module is loaded
  • boolean closing(): Called when the application is about to exit. Returning a false will stop it from closing. Good place to decide if it’s valid to close the module now. (i.e. prompt the user about saving or discarding changes to an open file)
  • void close(): Actually closing the application. Any clean up should happen here.
  • boolean uninstalling(): When the module is uninstalling. For example warning the user about the consequences of uninstalling the module (besides the dependencies already handled by the platform).
  • void uninstalled(): Any last minute actions after uninstalling the module.
  • validate(): Called when a module is being considered for loading. (This would be before installed(), restored(), or updated(int, String) are called.) IllegalStateException may be thrown to prevent it from being loaded

For demonstration purposes lets add some dialogs on each of the life cycle phases. On your application just replace those with the appropriate code for your specific situation.

package com.packtpub.nbpcook.module.installer;

import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.modules.ModuleInstall;

/**
 * Manages a module's lifecycle. Remember that an installer is optional and
 * often not needed at all.
 */
public class Installer extends ModuleInstall {

    @Override
    public void restored() {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Restoring module"));
    }

    @Override
    protected boolean clearSharedData() {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Clearing shared data"));
        return super.clearSharedData();
    }

    @Override
    public void close() {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Module closed"));
        super.close();
    }

    @Override
    public boolean closing() {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("IDE closing"));
        NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
                "Select yes to make the module ready for closure or no othewise.",
                NotifyDescriptor.YES_NO_OPTION);
        Object result = DialogDisplayer.getDefault().notify(nd);
        if (NotifyDescriptor.YES_OPTION == result) {
            super.closing();
            return true;
        } else if (NotifyDescriptor.NO_OPTION == result) {
            DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
                    "Notice that selecting no \n"
                    + "will stop the IDE from closing..."));
            super.closing();
            return false;
        }
        return super.closing();
    }

    @Override
    public void uninstalled() {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Module uninstalled"));
        super.uninstalled();
    }

    @Override
    public void updated(int release, String specVersion) {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
                "Module updated by release: " + release + ", spec version: " + specVersion));
        super.updated(release, specVersion);
    }

    @Override
    public void validate() throws IllegalStateException {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Validating module"));
        NotifyDescriptor nd = new NotifyDescriptor.Confirmation("Select yes to accept validation "
                + "and no to make it invalid", NotifyDescriptor.YES_NO_OPTION);
        Object result = DialogDisplayer.getDefault().notify(nd);
        if (NotifyDescriptor.YES_OPTION == result) {
            DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Validation successful"));
        } else if (NotifyDescriptor.NO_OPTION == result) {
            throw new IllegalStateException("Module is invalid. Not loaded!");
        }
        super.validate();
    }
}

Here’s the output of the code:

  • When loading it needs to make sure its valid

Image:5863-01-01.png

  • Select yes to mark the module as valid or no to mark it as invalid.

Image:5863-01-02.png

Note: If you select no you will be prompted to exit or disabling the module. If you choose the later you might need to enable the module from the Plugin menu in your application.

  • Validation Successful!

Image:5863-01-03.png

  • Module is loaded/restored

Image:5863-01-04.png

  • Now, when we stat closing the application…

Image:5863-01-05.png

  • Module is asked to close

Image:5863-01-06.png

  • Selecting no

Image:5863-01-07.png

  • Selecting yes

Image:5863-01-08.png

Let's Explain!

A Module Installer is an extension point in the module’s life cycle provided in the API for this purpose. The code in the previous section will give you an idea of when each method is called in the process so you can decide what’s the best place for that special task in hand.

Note

This is not usually done at the start of development of a module since the special needs are found when the application is mature enough, is enhanced. So you might get back to this section after you work with other sections of this book.

Do I need an installer?

This is a good first question to make. The answer will depend on various factors. For example:

  1. Does the module need to perform a lengthy task before being ready for use?
  2. Does the module need to initialize/load other modules that take long to load?
  3. Does the module interact with other applications (i.e. database) that might take some time to react?

If the issue is that the application seems to “freeze” maybe a simple thread might be the answer. If the module needs to load other modules maybe those other modules need to be of type autoload or eager instead so they are loaded when the application starts. If the module interacts with other applications there’s not much you can do about it. You either have the user wait or do this interaction in the module installer.

The task turns out to be too complicated

If the task turns out to be too complicated you might consider making this a module on its own instead and be a dependency for your module.

Tasks after the GUI is loaded

In some scenarios you need to perform some actions after the GUI is loaded (i.e. manage GUI elements after they have been loaded/created). This is a simple but powerful technique. Getting ready There’s no preparation needed besides an application with tasks to be performed after the GUI is loaded.

How to
  1. Create Installer class (if needed)
  2. Wait for the GUI to be ready
  3. Execute your code

Here’s all the code you’ll need (besides your custom code of course):

WindowManager.getDefault().invokeWhenUIReady(new Runnable() {

            @Override
            public void run() {
                <your code goes here>
            }
        });

Here’s an example of code done before and after the GUI is ready:

@Override
    public void restored() {
        DialogDisplayer.getDefault().notify(
                        new NotifyDescriptor.Message("The GUI is not ready yet..."));
        WindowManager.getDefault().invokeWhenUIReady(new Runnable() {

            @Override
            public void run() {
                DialogDisplayer.getDefault().notify(
                        new NotifyDescriptor.Message("The GUI is ready!"));
            }
        });
    }

In the above example you’ll see the first prompt before the GUI is loaded and the second one after it’s loaded.

Let's Explain!

WindowManager.getDefault().invokeWhenUIReady does all the magic. Like the method name says it’s called when the User Interface is ready.

Using Warm-up tasks

Sometimes you need to do some warm up before showing something in the GUI (i.e. calculating values to be displayed into a form). This differs from the recipeTasks after the GUI is loaded because the GUI is already displayed and this is triggered by an action or other part of your code but the concept is the same, delegate the task to a thread. Getting Ready There’s no preparation needed besides an application with tasks to be performed before the GUI is loaded.

How to

If the task is GUI related we’ll use SwingUtilities.invokeAndWait delegating to the event dispatching thread. Here’s the code:

SwingUtilities.invokeAndWait(new Runnable  () {
                 public void run() {
                     <your code goes here>
                 }
             });

Otherwise using a Tread should be enough.

Let's Explain!

NetBeans platform requires that any GUI related code is performed on the event dispatching thread. As a Swing based application NetBeans and/or anything based on its platform needs to conform to this rule: Swing is single-threaded and cannot be used from other threads than the AWT event dispatch thread

Installing and distribution of the application

With the introduction of NetBeans 6.9, the Platform Installer was added to the NetBeans platform. Although promising, the amount of features is not there yet. While it gets to the level we need there’s another viable option: IzPack.

Preparation

Making the configuration from scratch can be hard unless you are used to using IzPack already. Download and install the IzPack plugin for NetBeans, NIzPack, from: http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6689

How to

Build your application. The NIzPack plugin assumes that the dist folder already exists with the results of the building process. Highlight your application (its module suite) and select IzPack Installer from the from the Tools menu.

Image:5863-01-09.png

Leave the default values and click on generate. This will create a file named nizpack.xml. This will dump the whole application into one package, Core. For most cases this is more than enough. But if you want to divide your installation you need to modify the nizpack.xml file some more. See Dividing Installation in Modules if you want to divide it further and allow the user the option of what to install and what not.

Let's Explain!

IzPack is a configurable installer platform that allows the user to create customized installer for applications. IzPack installers can be greatly customized. Each step of an installation is materialized by a panel. A wide range of panels is available for packagers to use: it is possible to freely choose and order them. Should the choice be too limited, custom panels can be created by application developers. The installer builder is available as a command-line tool. It can be invoked from Apache Ant automated build as well. Finally, IzPack provides a set of utilities:

  • a native application launcher that can be used to first check for a Java Runtime Environment, then launch the installers
  • a tool that wraps installers inside self-extracting Windows executables (based on 7-Zip)
  • a tool that wraps installers inside Mac OS X application bundles.
Dividing Installation in Modules

The IzPack generated configuration is quite simple and basically includes everything in your application. As mentioned before, if you don’t want to give the end user the flexibility to decide which modules to be installed or your application needs all modules installed to work you can avoid this customization. Here’s an example of the generated nizpack.xml file:

<installation version="1.0">
    <info>
        <appname>Module Installer</appname>
        <appversion>1.0</appversion>
        <appsubpath>$APP_NAME $APP_VER</appsubpath>
        <javaversion>1.6</javaversion>
        <authors>
            <author email="javier.ortiz.78@gmail.com" name="Javier A. Ortiz Bultron"/>
        </authors>
    </info>
    <guiprefs height="450" resizable="yes" width="600">
        <modifier key="useFlags" value="no"/>
        <modifier key="langDisplayType" value="native"/>
        <modifier key="allYGap" value="8"/>
        <modifier key="allXGap" value="4"/>
        <modifier key="layoutAnchor" value="NORTHWEST"/>
    </guiprefs>
    <locale>
        <langpack iso3="eng"/>
    </locale>
    <native name="ShellLink.dll" type="izpack">
        <os family="windows"/>
    </native>
    <native stage="both" name="COIOSHelper.dll" type="3rdparty">
        <os family="windows"/>
    </native>
    <listeners>
        <listener installer="RegistryInstallerListener" uninstaller="RegistryUninstallerListener">
            <os family="windows"/>
        </listener>
    </listeners>
    <variables/>
    <resources/>
    <panels>
        <panel classname="CheckedHelloPanel"/>
        <panel classname="TargetPanel"/>
        <panel classname="PacksPanel"/>
        <panel classname="InstallPanel"/>
        <panel classname="ShortcutPanel"/>
        <panel classname="SimpleFinishPanel"/>
    </panels>
    <packs>
        <pack name="Core" required="yes">
            <description>Core Files</description>
            <file targetdir="$INSTALL_PATH" src="dist"/>
        </pack>
    </packs>
</installation>

The important pack is within the packs section of the file. The Core package should hold the modules that your application can’t work without. The required parameter with a “yes” value tells the IzPack installer that the package is not optional, so the user won’t be able to unselect this one from the installer GUI. The preselected parameter with a “yes” value tells the IzPack installer that the package is presented to the user as selected for installation. If it’s not required the user should be able deselect it. Use “no” as value for the parameters mentioned above to have the opposite behaviour. The hidden parameter specifies if the module will be visible in the installation wizard. To divide each module you need to be aware of the module’s architecture. You can get information from the Module’s API (http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html). The contents of the Core package should contain the following at minimum:

<pack name="Core" installGroups="<name>" required="yes">
        <description>Core Files</description>
        <file targetdir="$INSTALL_PATH" src="dist/<app name>/bin"/>
        <file targetdir="$INSTALL_PATH" src="dist/<app name>/etc"/>
        <file targetdir="$INSTALL_PATH" src="dist/<app name>/platform"/>
    </pack>

Depending on your application needs you might need to add more parts of the platform. To make sure you have everything you need right click the module suite and select “Build ZIP Distribution”. This will generate a zip folder in the suite’s dist folder. Extract it and look for all folders not named like your application. In our example module suite it looks like this:

Image:5863-01-10.png

In this example the module’s application name is module_installer so we need to add bin, etc and platform folders to the core package. Also include the entire module’s information of all modules that are not optional for your application to run. Our sample application is made of 3 modules:

  1. Installer
  2. Dummy1
  3. Dummy2

Let’s assume that Installer module is required part of our application. The Core package would be like follows:

<pack name="Core" installGroups="<name>" required="yes">
            <description>Core Files</description>
            <file targetdir="$INSTALL_PATH" src="dist/module_installer/bin"/>
            <file targetdir="$INSTALL_PATH" src="dist/module_installer/etc"/>
            <file targetdir="$INSTALL_PATH" src="dist/module_installer/platform"/>
            <file targetdir="$INSTALL_PATH/module_installer/modules" src="dist/module_installer/module_installer/modules/com-packtpub-nbpcook-module-installer.jar"/>
            <file targetdir="$INSTALL_PATH/module_installer/config/Modules/" src="dist/module_installer/module_installer/config/Modules/ com-packtpub-nbpcook-module-installer.xml"/>
            <file targetdir="$INSTALL_PATH/module_installer/modules/docs" src="dist/module_installer/module_installer/modules/docs/com-packtpub-nbpcook-module-installer.jar"/>
            <file targetdir="$INSTALL_PATH/module_installer/update_tracking" src="dist/module_installer/module_installer/update_tracking/ com-packtpub-nbpcook-module-installer.xml"/>
            <file targetdir="$INSTALL_PATH/module_installer" src="dist/module_installer/module_installer/file.dummy"/>
        </pack>

Notice the file.dummy file which is within the release folder of the module_installer module. Placing files in the module’s release folder is the way to add files to your application like dll’s, data and other executable files. The release folder needs to be created. See the Retrieving files from the release folder section for more details on how to use those files. Now let’s create the packages for the optional modules of our application. The optional packages have the parameter: required="no" and your choice for the preselected parameter (either yes or no). For our example let’s assume that dummy 1 is going to be preselected and dummy2 no.

Retrieving files from the release folder

The files placed on the release folder can be accessed from your application in the following manner:

File result = InstalledFileLocator.getDefault().locate("<file name>", "<module>", false);

Where <file name> is the full file name to be retrieved including its extension and <module> is the value of the OpenIDE-Module entry in the module’s manifest files previous to any dash. If for example your entry is like my.own.module/1 module would be my.own.module.

So if you have this project structure:

Image:InstalledFileLocator Example.png

And assuming the module is example.module, you'll retrieve it like this:

File result = InstalledFileLocator.getDefault().locate("images/deck.jpg", "example.module", false);
Module’s Contents

The important parts of a module structure for this recipe are described below:

  1. Module documentation (Only add if you enabled Java Help for the module)
<file targetdir="$INSTALL_PATH/<app name>/modules/docs" src="dist/< app name >/< app name >/modules/docs/<module package name>.jar"/>
  1. Module jar file
<file targetdir="$INSTALL_PATH/<app name>/modules" src="dist/<app name>/<app name>/modules/<module package name>.jar"/>
  1. Module Configuration
<file targetdir="$INSTALL_PATH/<app name>/config/Modules/" src="dist/<app name>/<app name>/config/Modules/ < module package name>.xml"/>
  1. Module Update Tracking information
<file targetdir="$INSTALL_PATH/<app name>/update_tracking/" src="dist/<app name>/<app name>/update_tracking/<module package name>.xml"/>
  1. Related files (place in the module’s release folder)
<file targetdir="$INSTALL_PATH/<app name>" src="dist/<app name>/<app name>/<file name>"/>

Where:

  • <app name> : the app.name property value in the module’s project.properties file
  • < module package name >: the OpenIDE-Module entry in the module’s manifest.mf file replacing the dots (‘.’) for dashes (‘-‘).
  • <file name>: the file name from the module’s release folder including its extension.
Building the installer

Here you have three options:

  1. Use the IzPack plugin functionality
  2. Add an ant target
  3. Create your own plugin

The plugin works fine but has an issue. It doesn’t run the dist target on its own so you need to do it manually. This option is straight forward so I won’t detail it here. The third option is a lot more interesting but probably too complicated at this time. So we’ll stick with option #3. It’ll have some extra stuff that might not make sense until later chapters (Miscellaneous chapter) so I’m only going to explain the parts relevant to this section.

Here are the targets and a brief explanation:

<target name="getAntContribJar">
        <fileset id="ant-contrib-jar" dir="${suite.dir}/tools">
            <include name="ant-contrib-*.jar" />
        </fileset>
        <pathconvert property="ant-contrib-jar" refid="ant-contrib-jar" pathsep="," />
        <basename property="ant-contrib-filename" file="${ant-contrib-jar}"/>
    </target>

This target locates the ant-contrib-jar file were some extra ant tasks are defined used in the other targets to follow.

<target name="check-env" depends="getAntContribJar">
        <condition property="isNetbeans">
            <not>
                <isset property="Hudson"/>
            </not>
        </condition>
    </target>

This target decides if this is done within NetBeans or Hudson. This will make more sense in later chapters.

<target name="init-netbeans" depends="check-env" if="isNetbeans">
        <echo>Configuring ant-contrib for Netbeans use...</echo>
        <property name="ant-contrib-loc" value="${suite.dir}/tools/${ant-contrib-filename}"/>
        <available file="${ant-contrib-loc}" property="ant-contrib.present"/>
        <fail unless="ant-contrib.present" message="The ant-contrib jar doesn't exist at: ${ant-contrib-loc}, can't build. Check your settings!" />
        <!--We are in not Hudson-->
        <taskdef resource="net/sf/antcontrib/antcontrib.properties">
            <classpath>
                <pathelement location="${ant-contrib-loc}"/>
            </classpath>
        </taskdef>
    </target>

This target defines the tasks within ant-contrib.jar for NetBeans usage.

<target name="init-hudson" depends="check-env" unless="isNetbeans">
        <echo>Configuring ant-contrib for Hudson use...</echo>
        <!--Import Hudson environment variables-->
        <property environment="env"/>
        <property name="ant-contrib-loc" value="${env.ANT_HOME}/lib/${ant-contrib-filename}"/>
        <available file="${ant-contrib-loc}" property="ant-contrib.present"/>
        <fail unless="ant-contrib.present" message="The ant-contrib jar doesn't exist at: ${ant-contrib-loc}, can't build. Check your settings!" />
        <!--Define it. For some reason the approach in init-netbeans doesn't work in Hudson.-->
        <taskdef name="for" classname="net.sf.antcontrib.logic.ForTask">
            <classpath>
                <pathelement location="${ant-contrib-loc}"/>
            </classpath>
        </taskdef>
        <taskdef name="propertyregex" classname="net.sf.antcontrib.property.RegexTask">
            <classpath>
                <pathelement location="${ant-contrib-loc}"/>
            </classpath>
        </taskdef>
        <taskdef name="if" classname="net.sf.antcontrib.logic.IfTask">
            <classpath>
                <pathelement location="${ant-contrib-loc}"/>
            </classpath>
        </taskdef>
        <taskdef name="math" classname="net.sf.antcontrib.math.MathTask">
            <classpath>
                <pathelement location="${ant-contrib-loc}"/>
            </classpath>
        </taskdef>
        <taskdef name="var" classname="net.sf.antcontrib.property.Variable">
            <classpath>
                <pathelement location="${ant-contrib-loc}"/>
            </classpath>
        </taskdef>
    </target>

This target defines the new tasks for Hudson. For some reason Hudson doesn’t work with the easier approach.

<target name="create-launch4j-installer">
        <delete>
            <fileset dir="." includes="*.exe"/>
        </delete>
        <echo file="${suite.dir}/Module Installer.xml"><![CDATA[<launch4jConfig>
        <dontWrapJar>false</dontWrapJar>
        <headerType>gui</headerType>
        <jar>Module Installer-1.0.jar</jar>
        <outfile>]]>${exeName}<![CDATA[.exe</outfile>
    <errTitle/>
    <cmdLine/>
    <chdir/>
    <priority>normal</priority>
    <downloadUrl>http://java.com/download</downloadUrl>
    <supportUrl/>
    <customProcName>false</customProcName>
    <stayAlive>false</stayAlive>
    <manifest/>
    <icon>nizpack.ico</icon>
    <singleInstance>
        <mutexName>NIzPack</mutexName>
        <windowTitle/>
    </singleInstance>
    <jre>
        <path/>
        <minVersion>1.6.0</minVersion>
        <maxVersion/>
        <jdkPreference>jreOnly</jdkPreference>
    </jre>
</launch4jConfig>]]>
        </echo>
        <launch4j configFile="Module Installer.xml"/>
    </target>

This target creates an input file for the IzPack process.

<target name="build-installer" description="Create Installer" depends="init-netbeans, init-hudson, branding">
        <echo message="Setting property and task for installer creation" />
        <property name="nbdist.dir" value="dist"/>
        <property name="nbdist-app.dir" value="${nbdist.dir}/${app.name}"/>
        <property name="izpack-installer" value="Output.jar"/>
        <property name="izpack.dir" location="tools/IzPack"/>
        <property name="launch4j.dir" location="tools/launch4j" />
        <taskdef name="izpack" classpath="${izpack.dir}/lib/compiler.jar"
             classname="com.izforge.izpack.ant.IzPackTask"/>
        <!--Launch 4J task-->
        <taskdef name="launch4j" classname="net.sf.launch4j.ant.Launch4jTask"
             classpath="${launch4j.dir}/launch4j.jar:${launch4j.dir}/lib/xstream.jar" />

        <echo message="Preparing ...." />
        <!--Delete the folder just in case-->
        <delete dir="${app.name}"/>
        <echo message="Creating zip distribution." />
        <antcall target="build-zip"/>
        <unzip src="${nbdist.dir}/${app.name}.zip" dest="${nbdist.dir}"/>

        <replace file="shortcutSpec.xml">
            <replacefilter token="$APP-VERSION" value="${app.version}" />
        </replace>
        <replace file="${nbdist.dir}/${app.name}/etc/${app.name}.conf">
            <replacefilter token="/dev" value="/${app.version}" />
            <replacefilter token="-J-Xms24m -J-Xmx64m" value="${run.args.extra}" />
        </replace>

        <echo message="Makes the installer using IzPack to ${izpack-installer}"/>
        <izpack input="${basedir}/nizpack.xml"
            output="${basedir}/${izpack-installer}"
            installerType="standard"
            basedir="${basedir}"
            izPackDir="${izpack.dir}/"/>

        <antcall target="create-launch4j-installer"/>
        
        <echo message="Cleaning and finalizing release..." />
        <delete dir="${nbdist-app.dir}"/>
        <delete file="${izpack-installer}"/>
    </target>

This target is the one to call and does all the tricks to pull it out. If you pay close attention you can notice references to a tools folder. It contains IzPack (IzPack files used during creation), launch4J (to create an .exe wrapper around the jar created by IzPack) and ReplaceVistaIcon.exe (to replace the icon of the application) Here’s the end result:

Image:5863-01-11.png

Application in the Task Bar

Sometimes you don’t need an application with a GUI or just need the application running in the background. The first case is well documented at The system tray is a specialized area of the desktop where users can access currently running programs. This area may be referred to differently on various operating systems. On Microsoft Windows, the system tray is referred to as the Taskbar Status Area, while on the GNU Network Object Model Environment (GNOME) Desktop it is referred to as the Notification Area. On K Desktop Environment (KDE) this area is referred to as the System Tray. However, on each system the tray area is shared by all applications running on the desktop.

Preparation

The only real preparation is getting the icon to be displayed in the System Tray. It needs to be 16 x 16 pixels.

How to

It might look simple but it’s somewhat hard to find the answer. Basically we add a JPopupMenu with two MenuItems to a Java object of the type SystemTray. Then we assign an action to each MenuItem, one to show and other to hide the application. As discussed earlier, the correct place of the life cycle of the module is in its installer.

How it works...

Yes, that simple. See the code below:

WindowManager.getDefault().getMainWindow().setVisible(false|true);

That single line of code is capable of hiding or showing the whole GUI. We modify the installer’s declaration so it implements ActionListener interface and can be used to catch the actions on the MenuItems. To provide the icon for the System tray just place the icon file in the release folder of the module and retrieve it as discussed earlier on this chapter. See the Task Bar Example source for full code.

In order to create the task bar icon see the code below;

WindowManager.getDefault().invokeWhenUIReady(new Runnable() {

                @Override
                public void run() {
                    //Check the SystemTray is supported
                    if (!SystemTray.isSupported()) {
                        try {
                            throw new Exception("SystemTray is not supported");
                        } catch (Exception ex) {
                            initialized = false;
                            Exceptions.printStackTrace(ex);
                        }
                    } else {
                        try {
                            final PopupMenu popup = new PopupMenu();
                            final TrayIcon trayIcon = new TrayIcon(<get icon>);
                            final SystemTray tray = SystemTray.getSystemTray();

                            showItem.addActionListener(Installer.this);
                            hideItem.addActionListener(Installer.this);

                            //Add components to pop-up menu
                            popup.add(showItem);
                            popup.addSeparator();
                            popup.add(hideItem);

                            trayIcon.setPopupMenu(popup);
                            try {
                                tray.add(trayIcon);
                            } catch (AWTException e) {
                                throw new Exception("TrayIcon could not be added.");
                            }
                            initialized = true;
                        } catch (Exception ex) {
                            initialized = false;
                            Exceptions.printStackTrace(ex);
                        }
                    }
                }
            });

In the example above I add pop up menus so the icon has some functionality. You can ignore those if you only want the icon. The above code is ran within a module installer.

Tasks after the GUI is loaded

Using your own Platform for Building You might end up wanting to reuse a module in various applications or want to keep more control over the platform you use in your application. Also it makes sense to have the platform used to build your application as part of its source in version control. We’ll discuss more on that on the Misc chapter Another option is using a module of the IDE in your application. Both will be covered in this section.

Preparation

Download and install the NetBeans platform to be used in your application. Install any plug-ins that you want to include in your application as well.

How to

Let’s talk about adding modules of the IDE in your application. As discussed in the subsection Module’s Contents, a module is divided in the following:

  1. Documentation
  2. Jar file
  3. Configuration
  4. Update Tracking information
  5. Related files

For example purposes we’ll add the Interactive UI Handler module. First install the plugin in your NetBeans IDE and locate it on your IDE installation folder. In this case its in the netbeans\nb\modules folders within the installation folder. Grab the org-netbeans-modules-uihandler-interactive.jar file from there. Then from the netbeans\nb folder get the netbeans-modules-uihandler-interactive.xml file from netbeans\nb\config\Modules and netbeans\nb\update_tracking respectively. Place those files in a folder of your choosing and place the files as shown below: Folder- - Jar file - config - xml file from config folder - update_tracking - xml file from update_tracking folder Then add this folder as explained here: http://wiki.netbeans.org/DevFaqHowToReuseModules Platform existing modules can be added using the process in the above URL as well.

NetBeans 7.1+

For platform 7.1 and above there's another option to download the platform being used for the application. It'll modify the platform.xml file within the nbproject directory.

The following modified script supports additional update sites to be listed, so your application can download the platform as well as any other plugins it needs:

<?xml version="1.0" encoding="UTF-8"?>
<project name="platform" default="download" basedir="..">
    <import file="../preparation.xml"/>
    
    <fileset dir="${nbplatform.custom.netbeans.dest.dir}" id="platform.path"/>
    <mkdir dir="${nbplatform.custom.netbeans.dest.dir}"/>
    <pathconvert refid="platform.path"
                 property="platform.notempty"
                 setonempty="false"/>
                 
    <condition property="download.enabled">
        <and>
            <isset property="bootstrap.url"/>
            <isset property="autoupdate.catalog.url"/>
            <not>
                <isset property="platform.notempty"/>
            </not>
        </and>
    </condition>
    
    <condition property="download.harness.required">
        <and>
            <not>
                <available file="${harness.dir}/suite.xml"/>
            </not>
            <isset property="download.enabled"/>
        </and>
    </condition>
    
    <target name="download-harness" if="download.harness.required">
        <mkdir dir="${harness.dir}"/>
        <autoupdate installdir="${nbplatform.active.dir}" updatecenter="${autoupdate.catalog.url}">
            <modules includes="org[.]netbeans[.]modules[.]apisupport[.]harness" clusters="harness"/>
        </autoupdate>
    </target>
    
    <target name="download" depends="-init-netbeans, -init-hudson" if="download.enabled">
        <pathconvert pathsep="|" property="download.clusters">
            <mapper type="flatten"/>
            <path path="${cluster.path}"/>
        </pathconvert>
        <property name="disabled.modules" value=""/>
        <pathconvert property="module.includes" pathsep="">
            <mapper type="glob" from="${basedir}${file.separator}*" to="(?!\Q*\E)"/>
            <path>
                <filelist files="${disabled.modules}" dir="."/>
            </path>
        </pathconvert>
        <property file="nbproject/platform.properties" prefix="urls."/>
        <propertyselector property="urls" match="urls.autoupdate.catalog\.(.*)" select="\1"/>

        <property file="nbproject/platform.properties"/>
        <echo message="Downloading clusters ${download.clusters}"/>
        <property name="tasks.jar" location="${java.io.tmpdir}/tasks.jar"/>
        <get src="${bootstrap.url}" dest="${tasks.jar}" usetimestamp="true" verbose="true"/>
        <taskdef name="autoupdate" classname="org.netbeans.nbbuild.AutoUpdate" classpath="${tasks.jar}"/>
        <antcall target="download-harness"/>
        <for list="${urls}" param="url">
            <sequential>
                <echo message="Attempting to download plug-ins from ${autoupdate.catalog.@{url}}" />
                <download-platform url="${autoupdate.catalog.@{url}}"/>
            </sequential>
        </for>
        <echo>Installing plug-ins from ../netbeans</echo>
        <mkdir dir="${nbplatform.active.dir}/extra"/>
        <autoupdate todir="${nbplatform.active.dir}/extra">
            <nbms dir="netbeans">
                <include name="*.nbm"/>
            </nbms>
            <modules includes=".+"/>
        </autoupdate>
    </target>
    
    <macrodef name="download-platform">
        <attribute name="url"/>
        <sequential>
            <autoupdate installdir="${nbplatform.active.dir}" updatecenter="@{url}">
                <modules includes="${module.includes}.*" clusters="${download.clusters}"/>
                <modules includes="org[.]netbeans[.]modules[.]apisupport[.]harness" clusters="harness"/>
            </autoupdate>
        </sequential>
    </macrodef>
</project>

Most of the changes are in the download target.

<for list="${urls}" param="url">
            <sequential>
                <echo message="Attempting to download plug-ins from ${autoupdate.catalog.@{url}}" />
                <download-platform url="${autoupdate.catalog.@{url}}"/>
            </sequential>
        </for>

It gets all properties with the following patter name: autoupdate.catalog.urlx. Where x can be not present or an integer number.

For example:

  • autoupdate.catalog.url=url
  • autoupdate.catalog.url1=url1
  • autoupdate.catalog.url2=url2

The script will try all those URL's to retrieve the modules listed in the cluster.path property.

Let's Explain!

The NetBeans platform is completely configurable and allows using your custom platform seamlessly. The platform is designed with extensibility in mind.


Navigation

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo