How can I change my TopComponent to not be a singleton (NB version up to 6.9)?
The "New Window Component" wizard in the NetBeans IDE generates a singleton TopComponent. That’s fine for windows that there should only be one of. Particularly if you are creating some kind of editor, you will want to create multiple instances of your TopComponent.
The really easy way
If you have not already used the New Window template to create your TopComponent subclass, don’t.
Instead, use New > JPanel Form. Once the new JPanel is created, switch to the Code tab, and replace extends javax.swing.JPanel
with extends TopComponent
. Then do the following things:
-
Override
getPersistenceType()
. -
If you do not want your components reopened on restart
-
return
PERSISTENCE_NEVER
— that is all you need to do to make sure they are not preserved across restarts. -
If you do want your components reopened on restart, then
-
return
PERSISTENCE_ONLY_OPENED
fromgetPersistenceType()
-
Add the following slightly-cryptic annotation to the class:
@ConvertAsProperties(dtd = "-//com.yourmodule.yourpackage//YourTopComponent//EN", autostore = false)
, replacing the package and class name with your own. This identifies a DTD. You do not need to define the DTD. You just need to give it a unique namespace that nothing else is using. Package and class name work well for that. -
Add two additional methods (you are not overriding anything and they can be package-private, like serialization methods):
-
void writeProperties(Properties p)
- here we will callp.put()
passing enough information to reconstruct your component on restart. If we are editing a file, we might save the path to the file. If we are viewing a URL, we might save the URL. If we want to be particularly fastidious, we might save the scroll position, or what line the editor caret was on , or anything else useful to restore the state of our component. -
void readProperties(Properties p)
- here we will reading whatever keys we wrote out inwriteProperties
and (re)initializing the component to its pre-shutdown state. This method will be called on startup to restore our component to its pre-shutdown state as best can be done. If we were, say, editing a file that no longer exists, the appropriate thing to do is throw an exception.
If you already have a generated singleton TopComponent subclass
The good news is that you won’t have to write any code — you’ll just have to delete some of the code that was generated for you.
In your TopComponent’s .java source file:
-
Delete the static
instance
variable, which ought to be declared a few lines above the constructor. -
Make sure your TopComponent class is
public
-
Make sure your TopComponent has a no-argument constructor which is
public
-
Delete the
getDefault()
method (typically somewhere around the middle of the file) -
Delete the
findInstance()
method (which typically follows thegetDefault()
method) -
Update the persistence code which saves your component’s state on shutdown and restores it on restart to reopen your component as follows
-
Locate the
getPersistenceType
method and change its return value to eitherTopComponent.PERSISTENCE_NEVER
orTopComponent.PERSISTENCE_ONLY_OPENED
(see below for why). -
If you have methods called
writeReplace()`and an inner class called `ResolvableHelper
(NetBeans 6.8 and earlier): -
Delete the
writeReplace()
method (typically towards the end of the file) -
Delete the
ResolvableHelper
inner class (typically towards the end of the file) -
If you do not want persistence across restarts — you are returning PERSISTENCE_NEVER from
getPersistenceType()
-
If you have a
@ConvertAsProperties
annotation andreadProperties(Properties)
andwriteProperties(Properties)
methods, delete the annotation and both methods -
If do want persistence across restarts — you are returning
PERSISTENCE_ONLY_OPENED
from`getPersistenceType()` -
If you already have the
@ConvertAsProperties
annotation andreadProperties(Properties)
andwriteProperties(Properties)
methods just leave them there -
If you do not have the annotation and those methods, implement them as described in the previous section
Next we will need to delete the metadata that registers the component:
-
For version 6.9 of NetBeans:
-
Delete the settings XML file for your component. If your component class is
MyWindow
then that file will be in the same folder and will be calledMyWindowSettings.xml
. -
Delete the wstcrf ("window system TopComponent reference") XML file in that folder. If your component class is
MyWindow
then that file will be namedMyWindowWstcrf.xml
-
Edit your module’s [DevFaqModulesLayerFile| layer.xml file] to
-
Remove any references to either of these files (just use Ctrl-F to search for e.g.
MyWindowSettings.xml
andMyWindowWstcrf.xml
). They will be in<file>
tags. -
If you have removed a
<file>
entry, and it was the only entry in that folder, you can remove the XML for parent folder (and its parent if it is now empty, and so forth) -
Find where an Action is registered for to open your (formerly) singleton TopComponent
-
NetBeans 6.9 and later:
-
Look for an
<file>
registered inActions/Window`in the XML file. It will have an `<attr>
element that refers to your TopComponent class, e.g.<attr name="component" methodvalue="com.foo.MyWindow.findInstance"/>
. Delete the entire<file>
entry. -
Look for
<file>
entry for a.shadow
file inActions/Menu
in the XML, with itsoriginalFile
pointing to the file entry you just deleted. Delete the.shadow
<file>
too. -
NetBeans 6.8 and earlier:
-
There will be an
Action
class in your sources which is registered, e.g.MyWindowAction.java
. Delete the java source file. -
Look for an
<file>
registered inActions/Window`in the XML file. It will be a `<file>
whose name is the munged fully-qualified class name of theAction
you just deleted, e.g.com-foo-MyWindowAction.instance
. Delete the<file>
entry for it -
Look for
<file>
entry for a.shadow
file inActions/Menu
in the XML, with itsoriginalFile
pointing to the file entry you just deleted. Delete the.shadow
<file>
too.
Creating And Opening Your TopComponents
Now that you have deleted the actions for your TopComponent, presumably they will be created some other way (for example, from a file’s popup menu). You can create new instances of your TopComponent, open them and give them focus as follows:
TopComponent win = new MyTopComponent();
win.open();
win.requestActive();
If you wrote your persistence code correctly, your components will magically reopen on restart with no further work.
What About PERSISTENCE_ALWAYS?
There is one other value you can return from TopComponent.getPersistenceType()
. That value is TopComponent.PERSISTENCE_ALWAYS
.
While it is legal to return this value from a non-singleton TopComponent, it is almost never what you want to do. What will happen if you do this is:
-
Every instance of your component that is ever created will be persisted on shutdown, forever
-
Even if it is closed
-
Even if nothing can use it, or it represents a file that was deleted, or is in some other way invalid
-
Even if no code will ever be able to find it and open it again
-
One every restart, forever
-
Every instance of your component that has ever existed will be read back from disk
-
Each one will slow down startup a little bit
-
Each one will be wasting disk space
PERSISTENCE_ALWAYS
is for singleton components that need to be remembered forever across restarts. Don’t use it for non-singletons.
If you do not have any persistence code, but your components are reopening on restart…
You are returning either PERSISTENCE_ONLY_OPENED
or PERSISTENCE_ALWAYS
from getPersistenceType()
. If there is no persistence code, but you are returning one of these values, NetBeans will use plain old Java serialization to store and reload your component.
Either use PERSISTENCE_NEVER
or write persistence code as described above. Serialization is slower and more fragile than proper persistence, and is never a good option for production code.