How can I change my TopComponent to not be a singleton (NB version up to 6.9)?

Apache NetBeans Wiki Index

Note: These pages are being reviewed.

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 from getPersistenceType()

  • 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 call p.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 in writeProperties 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 the getDefault() 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 either TopComponent.PERSISTENCE_NEVER or TopComponent.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 and readProperties(Properties) and writeProperties(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 and readProperties(Properties) and writeProperties(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 called MyWindowSettings.xml.

  • Delete the wstcrf ("window system TopComponent reference") XML file in that folder. If your component class is MyWindow then that file will be named MyWindowWstcrf.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 and MyWindowWstcrf.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 in Actions/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 in Actions/Menu in the XML, with its originalFile 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 in Actions/Window`in the XML file. It will be a `<file> whose name is the munged fully-qualified class name of the Action you just deleted, e.g. com-foo-MyWindowAction.instance. Delete the <file> entry for it

  • Look for <file> entry for a .shadow file in Actions/Menu in the XML, with its originalFile 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.