NetBeans Application With Optional Module

By | November 8, 2017

This is a demonstration of a NetBeans application that can call code in ModuleB if ModuleB is installed/activated but also operate without ModuleB.  No reflection is used. ModuleA can invoke classes defined in ModuleB.

Watch the application in action:

This is a hack to circumvent the NB Module class loading restrictions. I’m not sure what issues this could lead to this is purely for demonstration purposes that might help lead to a better solution.

I created a ModuleSuite. I called it MyVendorSuite and added one Module to it called VendorModule (think of this as ModuleB). VendorModule contains one class with a single method that returns a string (as shown below)

Click to view enlarged image

There’s nothing special about this Module. I packaged it as an NBM that I could install via the plugins page in the UI.

The code from our VendorClass object…

package com.delta.vendor;

public class VendorClass {
    public String sayHello() {
        return "Hello from your vendor";
    }
}

I created an Application Suite called MyApp with a single MyAppMod module (think of this as ModuleA).
MyAppModule contains a single window/TopComponent class from which we will attempt to use ModuleB classes.

TopComponent code snippet:

    public testTopComponent() {
        initComponents();
        setName(Bundle.CTL_testTopComponent());
        setToolTipText(Bundle.HINT_testTopComponent());

        
        setLayout(new BorderLayout(0,0));
        JLabel label = new JLabel();
        add(label, BorderLayout.CENTER);
        
        
        // we'll want a utility method to perform this check
        boolean isMyModuleEnabled  = false;
        Collection<ModuleInfo> modules = (Collection<ModuleInfo>) 
            Lookup.getDefault().lookupAll(ModuleInfo.class);
        for(ModuleInfo module: modules) {
            if(module.getCodeName().equals("com.delta.vendor")) {
                isMyModuleEnabled = module.isEnabled();
                break;
            }
        }

        // isMyModuleEnabled will be false if the module is not installed 
        // OR the module is deactivated! If we just used a try catch block 
        // catching ClassNotFound it would only work when the module is 
        // uninstalled.  If the module is deactivated, the class files can 
        // still be loaded and used
        if(isMyModuleEnabled) {        
            VendorClass instance = new VendorClass();
            label.setText(instance.sayHello());
        } else {
            label.setText("Module B must be deactivated or not intalled!");
        } 
    }

Notice we are using the VendorClass code directly.

I modified the project.xml (Project Metadata) for MyAppModule (ModuleA) as shown:

Click to view enlarged image

            <code-name-base>com.delta.app</code-name-base>
            <suite-component/>
            <module-dependencies>
                <dependency>
                    <code-name-base>com.delta.vendor</code-name-base>
                    <compile-dependency/>
                </dependency>

Basically I removed the Runtime dependency for ModuleB.  This allows me to use the class names in ModuleA at compile time but doesn’t require ModuleB to be installed at runtime.

I added a Class-Path: entry to the Module Manifest for Module A as shown:

Click to view enlarged image

Manifest-Version: 1.0
AutoUpdate-Show-In-Client: true
OpenIDE-Module: com.delta.app
OpenIDE-Module-Localizing-Bundle: com/delta/app/Bundle.properties
OpenIDE-Module-Requires: org.openide.windows.WindowManager
OpenIDE-Module-Specification-Version: 1.0
Class-Path: ../../../../../../Users/redacted/AppData/Roaming/.myapp/dev/modules/com-delta-vendor.jar

The class path entry is a RELATIVE path. For this to work we need to know where our nb userdir will be relative to our application install (and since we can control the userdir location from etc/app.conf that should be something we have control over). The relative path needs to point to the location our vendor-module jar file will be located at once installed!

For this demonstration:

my user dir was: C:\Users\redacted\AppData\Roaming\NetBeans\dev
my app was installed: C:\Users\redacted\Desktop\myapp
my NBM install resulted in: C:\Users\redacted\AppData\Roaming\.myapp\dev\modules\com-delta-vendor.jar

Finally I packaged MyApp as a zip distribution and extracted the files into a directory so I could launch the application.

NOTE:  This solution cannot be run from within the IDE but it does work for an installed application!

Leave a Reply

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