Accessing and sharing modules' resources
$Revision: 1.3 $
Changes: available in
CVS
Abstract:
Description of problems that we have to face when converting settings from a memory storage and using them in
modules is given here. Sample usecases of expected usage of the settings for communication are described and
common mistakes and usual problems investigated.
Known problems
- Scalability is an obvious task. User should pay as less as possible for the amount of installed modules.
The current style of storing settings (
SharedClassObject) does not scale bad,
but still influences startup more than needed.
- Duplication of ugly code. there is a bunch of ugly code duplicating our the codebase that tries to access objects
on system filesystem. We need a solution to kill code like
PersistenceManager.java line 408,
FileEntityResolver.java line 242 method findObject
and the bunch of similar code that would have to be introduced in
openidex/commands, openidex/looks and other modules.
- Simplicity. The current API for accessing composable settings - the
FolderInstance -
is not easy to deal with. One has to subclass and override a set of methods to do what he wants and still not
receive everything. The Window System guys decided to not use FolderInstance because
of not beying able to control the synchronization between own modification of settings (component written to disk)
and external changes (module installed, session switched). That is why they copied part of the functionality and got into
deep problems with stabilization of their solution. Their code should be refactored to use a common solution thus
preventing copy-paste spread of bugs.
- Common inteface. There is a lot of code that wants to access settings and interact with user and other
modules (popup menu creation, URL providers, BeanInfo and
PropertyEditor searchers) and a lot of them wants to stay far way from Filesystems and Datasystems API needed for
the current solution. There is a need to have a simple API accessible to everyone (means packaged in openide-util or
JDK itself).
- Lazy initialization. In the inter-modules communication we are searching for a solution to that will allow lazy
registration and lookup. Modules should not execute any code in order to register, instead they should provide a declarative
description of what they register that will be converted into the live object when needed.
Usage examples
This section tries to describe expected usecases without implementation details. It uses method
Object find (String)
to say that we are looking for a resource with specified name and
Enumeration findAll (String) if we are looking
for all objects registered under the given name. Also consider a method
register (String, Object) that will allow
any module to associate new object with the given name.
Please check discussion summary for more
opinions on this subject.
Example 1: Looking up an implementation of URLHandler
The goal is to find proper java.net.URLHandler for nbdocs:/mydocs.html URL. Because the set of
supported URLs is pluggable and we want the lazy registration, we will use the method find to
request the handler. First of all we choose a root namespace ("/Providers/URLHandlers") and then locate the
correct handler by:
find ("/Providers/URLHandlers/nbdocs");
Example 2: Looking up actions
Imagine
Java module trying to find out which actions to show in popup menu of a node representing a java source.
Right now it just displays the actions associated with the loader, but that works really badly for other modules that want to
extend the Java object. The right solution would be to specify common name for the loader and read actions registered by
different modules there.
Enumeration en = findAll ("/Loaders/Actions/org/netbeans/modules/java/");
while (en.hasMoreElements ()) {
Object obj = en.nextElement ();
if (obj instanceof Action) {
popupMenu.add ((Action)obj);
}
}
Example 3: Update on changes
Right now the window system listens on changes in
"Windows/Workspaces" directory
and updates the actual screen content by changes it recieves from filesystem and datasystem layers.
The code is very complicated, because it spans around three different subsystems (fs, ds, instance convertion) and
thus potentially (and actually) buggy. We need to take divide and conquer approach and simplify it by
splitting the implementation out of the window system.
The window system shall deal just with instances of Workspace, Mode and TopComponent, exactly as described
in example one and two. Plus it needs a notification mechanism that allows it to update when ever a change is
made.
Example 4: Own changes
Let's demonstrate another problem on the example of window system. As described in example 3, the window
system has to react to external changes made on the instances pool (module uninstall, programmatic changes, etc.).
But it also needs to reflect the changes made by a user like component opening or closing to the persistent
instances pool. For it there shall be a update method
register ("Windows/Workspaces/Mode5", mode5);
that will store the object
mode5 under the desired name.
Connected to this is another problem - the ability to recognize its own changes from changes made by somebody else.
Because own changes can be ignored, because they were already made (btw. current FolderInstance is
very weak exactly in this).
Suggested solution
In order to solve the above given problems we should define an interface that supports queries based on
names, lazy initialization, update and change notification. Morever it shall be relatively independent
on the rest of NetBeans API to be usable from any module in NetBeans.
Well, we can design such, but there seems to already exist such general purpose one - JNDI. It has
methods to lookup object or set of objects by a name and provides the register/unregister facility.
Also it has interface to a voluntary extension - the EventContext that provides
the ability to be notified when an object is registered/unregistered.
Here is the rewrite of the general examples in the terms of JNDI.
Example 1: Looking up an implementation of URLHandler
InitialContext ic = new InitialContext ();
ic.lookup ("/Providers/URLHandlers/nbdocs");
Example 2: Looking up actions
Enumeration en = new InitialContext ().list ("/Loaders/Actions/org/netbeans/modules/java/");
while (en.hasMoreElements ()) {
Object obj = en.nextElement ();
if (obj instanceof Action) {
popupMenu.add ((Action)obj);
}
}
Example 3: Update on changes
Context c = new InitialContext ().lookup ("/Windows/Workspaces");
if (c instanceof EventContext) {
EventContext ev = (EventContext)c;
ev.addNamingListener (myNamespaceListener);
}
Example 4: Own changes
Context c = new InitialContext ();
Object o = c.lookup ("Windows/Workspaces");
if (!o instanceof Context) {
return;
}
c = (Context)o;
c.bind ("Mode5", mode5);
// and in the namespace listener use
if (event.getChangeInfo () == c)
// to recognize changes made thru the context c
Comments? Send them to
nbdev!