Friday Apr 11, 2008

Injecting a Layer File into a NetBeans Platform Application

I tried the How to change menus, etc. after login? scenario today: "Since version 7.1 there is a way to change the content of system file system in a dynamic way. As system file systems contains various definitions (in NetBeans Platform menus, toolbars, layout of windows, etc.) it de-facto allows global change to these settings for example when user logs into some system."

The outline of what you need to do is described there, nothing I can really add. It is one new and cool way in which one module can contribute to an application. You start by creating a new module, add the XML layer defining the items you want to register in the system, then implement FileSystem, and export it to META-INF/services.

One scenario is that of a user logging into an application. On successful login, the layer file is injected and whatever is defined in the layer file is added to the application. I added a small touch—whatever the user sets as their user name determines the text in a new menu item that is added via the injected layer file. That's possible via the NbPreferences class, which can store preferences in the application's user directory.

Here's the whole module:

In the Installer, the login dialog is shown (as described elsewhere in this blog):

package org.yourorghere.addedsfs;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.JButton;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.modules.ModuleInstall;
import org.openide.util.Exceptions;
import org.openide.util.NbPreferences;
import org.xml.sax.SAXException;

public class Installer extends ModuleInstall {

    JButton ok = new JButton();
    JButton cancel = new JButton();
    NotifyDescriptor.InputLine msg;

    public Installer() {
        ok.addActionListener(new OkActionListener());
    }

    private class OkActionListener implements ActionListener {
        //In real life, you'd have some login validation here!
        public void actionPerformed(ActionEvent e) {
            try {
                //Create a preference "USER_KEY", with value set to the input text:
                NbPreferences.forModule(Installer.class).put("USER_KEY", msg.getInputText());
                //Specify the XML file that defines the layer you want to inject:
                URL u = new URL("nbresloc:/org/yourorghere/addedsfs/newLayer.xml");
                //Pass the URL to the LoginFileSystem:
                LoginFileSystem.assignURL(u);
            } catch (SAXException ex) {
                Exceptions.printStackTrace(ex);
            } catch (MalformedURLException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }

    @Override
    public void restored() {
        ok.setText("OK");
        cancel.setText("Cancel");
        msg = new NotifyDescriptor.InputLine("Login:", "User name: ",
                NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.QUESTION_MESSAGE);
        msg.setOptions(new Object[]{ok, cancel});
        DialogDisplayer.getDefault().notifyLater(msg);
    }

}

And here's the LoginFileSystem (which is called by means of LoginFileSystem.assignURL(u) above):

package org.yourorghere.addedsfs;

import java.net.URL;
import org.openide.filesystems.MultiFileSystem;
import org.openide.filesystems.XMLFileSystem;
import org.xml.sax.SAXException;

public class LoginFileSystem extends MultiFileSystem {

    private static LoginFileSystem INSTANCE;

    public LoginFileSystem() {
        // let's create the filesystem empty, because the user
        // is not yet logged in
        INSTANCE = this;
    }

    public static void assignURL(URL u) throws SAXException {

        //   Alternatively:
        //   Lookup lookup = Lookup.getDefault();
        //   LoginFileSystem INSTANCE = lookup.lookup(LoginFileSystem.class);

        INSTANCE.setDelegates(new XMLFileSystem(u));
    }
    
}

So then the URL that points to the newLayer.xml is used to create a new XMLFileSystem. The above class should be exported to META-INF/services, following the JDK 6 java.util.ServiceLoader approach.

And what about the preference that the user entered? Here you see the definition of the TestAction.getName method:

public String getName() {
    Preferences pref = NbPreferences.forModule(TestAction.class);
    String name = pref.get("USER_KEY", "");
    return NbBundle.getMessage(TestAction.class, "CTL_TestAction") + name + "!";
}

So first some text (like "Hello") is retrieved from the bundle file, appended with the name that the user entered, appended with an exclamation mark. And the above menu item only appears if the login succeeds, which you'd need to make possible via hooking up your database of users to the module so that whatever the user enters can be verified against your database.

In other news. What technology evangelists really do for a living.

About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2008 »
SunMonTueWedThuFriSat
  
2
4
5
13
18
19
23
30
   
       
Today