Can I dynamically change the contents of the System Filesystem at runtime?
Yes. This technique is commonly used in platform applications which require the user to log in, to keep menu contents/toolbar actions/keyboard shortcuts/window definitions hidden until the user is authenticated.
There are two ways to do it. The most obvious way is to programmatically write files to disk at runtime (call FileUtil.getConfigRoot().createFolder()
, etc.). This technique is appropriate if you are creating files which should be preserved across restarts (for example, adding folders to the Favorites window). It is completely inappropriate in the case of authentication, or any other case where you do not want the added files to be present after restart. You cannot depend on the application always being shut down normally and having a chance to clean such files up - since they are actually written to disk.
Using Dynamically Added Layers
The alternative is quite simple: Write a subclass of MultiFileSystem
. Put it in the default lookup.
At runtime, when you want to add contents to the system filesystem, simply add additional filesystems to your MultiFileSystem
. To remove the contents, simply remove those filesystems.
A convenient way to do this is to use XMLFileSystem - this is a filesystem created with exactly the same syntax as normal module XML layer files (see DevFaqModulesLayerFile). The following code loads an XML filesystem, which is in an XML file in the same package as the class, called dynamicContent.xml
:
@ServiceProvider(service=FileSystem.class)
public class DynamicLayerContent extends MultiFileSystem {
private static DynamicLayerContent INSTANCE;
public DynamicLayerContent() {
// will be created on startup, exactly once
INSTANCE = this;
setPropagateMasks(true); // permit *_hidden masks to be used
}
static boolean hasContent() {
return INSTANCE.getDelegates().length > 0;
}
static void enable() {
if (!hasContent()) {
try {
INSTANCE.setDelegates(new XMLFileSystem(
DynamicLayerContent.class.getResource(
"dynamicContent.xml")));
} catch (SAXException ex) {
Exceptions.printStackTrace(ex);
}
}
}
static void disable() {
INSTANCE.setDelegates();
}
}
In NetBeans 7.2 and later, the above code can be simplified by using Repository.LayerProvider
. See its Javadoc for a usage example.
If finer grained control of what is added is needed at runtime, there are two possibilities, using this general approach:
-
If the new layer contents are fixed and known, but perhaps correspond to user roles which may be overlaid together, split up the functionality for each role into a separate XML file (hint: define an enum of roles, where each role can point to an XML file URL, use
EnumSet.of()
and process that to decide what to enable) -
Write contents programmatically, but write folders/files to an in-memory filesystem created using
FileUtil.createMemoryFileSystem()
so the contents disappear on VM exit
https://bitbucket.org/jglick/dynamicmenudemo/ tries to automate this kind of task.