blob: 16004d6a3f2f080f0c6078d2fde47abd213aa16d [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= DevFaqWhenToUseWhatRegistrationMethod
:jbake-type: wiki
:jbake-tags: wiki, devfaq, needsreview
:jbake-status: published
:keywords: Apache NetBeans wiki DevFaqWhenToUseWhatRegistrationMethod
:description: Apache NetBeans wiki DevFaqWhenToUseWhatRegistrationMethod
:toc: left
:toc-title:
:syntax: true
=== When do I use which registration method?
As described in link:DevFaqModulesGeneral.asciidoc[DevFaqModulesGeneral], there are several different declarative registration mechanisms:
* Use the `@ServiceProvider` annotation (or one of the link:DevFaqWaysToRegisterInDefaultLookup.asciidoc[other registration mechanisms]) to register objects in the link:DevFaqLookupDefault.asciidoc[default Lookup]
* Add files to folders in the link:DevFaqSystemFilesystem.asciidoc[system filesystem]
* Run some code on startup by implementing `link:http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/ModuleInstall.html\[ModuleInstall]` and declaring your `ModuleInstall` subclass in your module's `manifest.mf`
If you are implementing some API from another module, that module should tell you what to do. If it tells you something should be in the link:DevFaqLookupDefault.asciidoc[default lookup], that means to use [link:http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/ServiceProvider.html[http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/ServiceProvider.html] `@ServiceProvider`] (see caveats in link:DevFaqWaysToRegisterInDefaultLookup.asciidoc[DevFaqWaysToRegisterInDefaultLookup]).
==== Deciding On A Registration Mechanism For Clients Of Your Module
If you are defining an link:DevFaqApiSpi.asciidoc[SPI] in your module, and other modules will implement it and provide their own classes, provide a declarative (plain text, no code) way to register them.
Define interfaces or abstract classes, and document where subclasses can be registered (typically the link:DevFaqLookupDefault.asciidoc[default Lookup] or some folder in the link:DevFaqSystemFilesystem.asciidoc[system filesystem]).
Starting with NetBeans 6.7, you can link:DeclarativeRegistrationUsingAnnotations.asciidoc[provide annotations] which other modules can use to register their objects - so registration is declarative, but it is visible in the Java source file.
If you can possibly avoid it, don't require your module (or modules that implement your link:DevFaqApiSpi.asciidoc[SPI]) to run code on startup to link:DevFaqModulesDeclarativeVsProgrammatic.asciidoc[programmatically register] their functionality. That slows startup time and does not scale.
Below are typical registration mechanisms and patterns, and when each is useful:
|===
|What |When to Use It |How |Examples
|Define a singleton service class (there should be only one) that should be global to the application |You are defining a service, but another module will provide an implementation of that service |<ul><li>Define your service-provider class. Typically in NetBeans it will have a static method `getDefault()` which tries to find an instance of itself in the default Lookup, and if that fails, returns some sort of non-null mock implementation (which may not really do anything, but is useful in unit test that call code which calls your module)</li><li>Document that it should be registered in the default Lookup and that it is expected to be a singleton.</li><li>Define and document a unique string token which modules can "provide" if they provide an implementation of your API - for example `com.mymodule.MyService` (it can be any string)</li><li>Modify your module's `manifest.mf` file to use that token as follows:<ul><li>*If you provide no implementation of your service, but one is needed at runtime for proper functioning* add the line
`OpenIDE-Module-Requires: com.mymodule.MyService`
to the manifest. If no module is present which _provides_ this token, your module will not be loaded on startup - the user will be offered an option of exiting or disabling your module.</li><li>*If you do provide some mock implementation of your service which is available in the case no other module is providing one* then add the line
`OpenIDE-Module-Recommends: com.mymodule.MyService`
to the manifest. Your module will be loaded, no matter what. If no other module _provides_ this token, a warning will be logged.</li></ul></li><li>Document that modules which implement your service should include `OpenIDE-Module-Provides: com.mymodule.MyService` in their manifest(s). |<ul><li>The link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/overview-summary.html[UI Utilities API] defines `link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/StatusDisplayer.html[StatusDisplayer]`. You can call `StatusDisplayer.getDefault().setStatusText("Hello world")` to change the text in the status bar of the main window.
But the UI Utilities API does not provide the subclass of `StatusDisplayer` which is being called.
In fact, the module `core.windows`, which is responsible for creating NetBeans' main window _injects_ its own subclass into the default lookup, and that is what actually changes the status bar you see on the screen. It is that subclass which you are actually calling when you set the main window's status text. But your module only depends on the API, not the windowing system. Your code doesn't have to care whose subclass of `StatusDisplayer` it is calling. If a new version is created that displays status, say, in a translucent fading popup window, your code will work perfectly with that as well, without any changes or recompiling.</li><li>The link:http://bits.netbeans.org/dev/javadoc/org-openide-io/overview-summary.html[IO API] provides a way to write to the output window. In fact, there are two different output window implementations available for NetBeans - the default one, and a terminal emulator. The I/O API does not care which one is present, but it recommends that one should be, and provides a mock implementation that writes to `System.out` if none is present.</li></ul>
|Define an interface or abstract class and look for multiple instances of it in the link:DevFaqLookupDefault.asciidoc[default Lookup] and do something with those objects |The objects modules will register is are implementations/subclasses of a simple interface or class. Your module only needs to find all such registered objects and use them. Your module will need all of them at the same time. |<ul><li>Define an interface or class for others to implement.</li> <li> Document that there can be multiple ones registered and that they should be registered in the link:DevFaqLookupDefault.asciidoc[default Lookup].</li> <li>In _your_ module, use `Lookup.getDefault().lookup(MyClass.class).allInstances()` to find all registered instances.</li></ul> |`link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/StatusLineElementProvider.html[StatusLineElementProvider]` allows modules to contribute components to the status bar in the main window. All components are needed in order to show the status bar.
|Define an interface or abstract class, and document your strategy for locating these objects in folders in the link:DevFaqModulesLayerFile.asciidoc[system filesystem] |The objects modules will register is are implementations/subclasses of a simple interface or class, *but* not all objects are needed at any given time. At any time, some may be needed, based on what the user is doing (for example, the MIME type of the file the user is editing - MIME types map easily to folder paths, e.g. `Editors/text/x-java/`). |<ul><li>Figure out from context what folder to look in</li><li>Use `link:http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/Lookups.html#forPath(java.lang.String)[Lookups.forPath("path/to/folder").lookupAll(MyType.class)]` to find all registered objects of your type.</li><li>Describe the lookup strategy in the documentation of your link:DevFaqApiSpi.asciidoc[SPI].</li></ul> |If you want to add an action to the popup menu that appears when you right-click in the text editor for a `.java` file, but not other kinds of files, you [[DevFaqRegisterObjectsViaInstanceOrSettingsFiles| register an instance of `javax.swing.Action`]] in the system filesystem (via your module's link:DevFaqModulesLayerFile.asciidoc[layer file]) folder `Editors/text/x-java/Actions`. If the user never actually opens a Java file and right-clicks the editor, your Action will never be created, nor its class loaded.
|Define a single folder in the link:DevFaqModulesLayerFile.asciidoc[system filesystem] where objects should be registered, and optionally a factory method which will create the object. |<ol><li>Other modules are not really registering their own subclasses, they are registering files. You want to read the files and create the objects in your code.</li><li>Other modules are registering objects; however, there is useful metadata that can be used without ever creating the object.</li><li>Other modules are registering objects. Creating those objects requires additional metadata which can be specified declaratively using link:DevFaqFileAttributes.asciidoc[file attributes]</li></ol> |<ul><li>Define a static, public factory method which takes a `Map`.</li><li>Document that all registered files should list this factory method as their `instanceCreate` attribute (e.g. `<attr name=&quot;instanceCreate&quot; methodvalue=&quot;com.XClass.factoryMethod&quot; />`.</li><li>Find registered objects using `Lookups.forPath("path/to/my/folder")`).</li></ul> |Examples for the cases defined under _When To Use It_: <ol><li>The `simple.project.templates` module defines a spec for using `.properties` files to list everything that should be created when the user wants a new project. It does not need a special file type or object instances - it will read the file and make the object it needs.</li><li>The Services tab in the IDE allows objects to be registered, which are shown as nodes in its UI. The icon and localized display name of these nodes can be declaratively specified as file attributes, so no classes need to be loaded until the first time the user selects one of these nodes.</li><li>As mentioned in 1., `simple.project.templates` defines a spec for describing a project template inside a regular `.properties` file. The `javacard.project` module reads defines several template files. But ''it also needs to know what "flavor" of project (applet, web, library, etc.) each file defines, so that it will ask the user the right questions in the New Project Wizard. It defines an additional file attribute to indicate what "flavor" of project a template represents.</li></ol>
|===
==== Why Declarative Registration and Lazy Loading Is Important
For best performance and scalability, avoid actually instantiating the objects other modules register until the first time your code needs to call them. Avoid programmatic registration mechanisms, and delay instantiating declaratively registered objects until they really need to be used. This is for several reasons:
* Object take up memory. Your application will use less memory and be faster if you do not create objects that you do not _know_ you will call.
* Java class loading happens the first time a class is needed, and loading one class can trigger loading many others. It means file I/O happens, blocking whatever thread first needs to load the class.
* If you create objects only when your code really is going to call them, class loading and object creation still happens, but it happens in small chunks of time as things are needed, rather than causing long pauses
If there will potentially be a large number of subclasses of your interface, try to find a way to divide them into context-appropriate categories and use folders in the system filesystem to partition contexts.
==== Why Declarative Icon and Display Name Registration Is Particularly Important
Many pieces of user interface in NetBeans &mdash; almost any tree view &mdash; is a view of a folder on disk, or a folder in the system filesystem. The Services tab is such a view; the Projects tab composes several such views; the left and right sides of the first pages of the New File and New Project wizards are such views.
The need to simply show an icon and a name should not ever be the trigger for loading hundreds or even thousands of classes (bear in mind that loading your class may mean loading many other classes &mdash; and the link:http://www.securingjava.com/chapter-two/chapter-two-6.html[Java Bytecode Verifier] may trigger loading many more classes than you expect).
You can handle this very simply with `.instance` files:
[source,xml]
----
<filesystem>
<folder name="UI">
<folder name="Runtime">
<file name="MyNode.instance">
<attr name="instanceClass" stringvalue=
"org.netbeans.modules.stuff.MyNode"/>
<attr name="iconBase" stringvalue=
"org/netbeans/modules/stuff/root.png"/>
<attr name="displayName" bundlevalue=
"org.netbeans.modules.stuff.Bundle#MyNode"/>
<attr name="position" intvalue="152"/>
</file>
</folder>
</folder>
</filesystem>
----
and in your resource bundle file, define
[source,java]
----
MyNode=My Node
----
This was a serious problem in older versions of the NetBeans IDE - for example, opening the Options dialog (which used to be a tree of Nodes and a property sheet - modules that had settings provided their own Node, and you changed settings by changing properties) - simply trying to paint it for the first time originally triggered loading, literally, thousands of classes from many different JAR files.
=== Apache Migration Information
The content in this page was kindly donated by Oracle Corp. to the
Apache Software Foundation.
This page was exported from link:http://wiki.netbeans.org/DevFaqWhenToUseWhatRegistrationMethod[http://wiki.netbeans.org/DevFaqWhenToUseWhatRegistrationMethod] ,
that was last modified by NetBeans user Jglick
on 2010-06-14T22:27:08Z.
*NOTE:* This document was automatically converted to the AsciiDoc format on 2018-02-07, and needs to be reviewed.