BookNBPlatformCookbookCH03

Contents

NetBeans Platform Cookbook Chapter 03

Lookup

Introduction

Lookup is the heavily used principle and mechanism in the NetBeans Platform. It is very smart and clever software box like a multimap where pairs class-instances are stored. It was created to break inter-component binding and dependencies. Other behaviour of the Lookup API is next useful exploatation of this base purpose.

The main principle is simple: one component uses a business object (e.g. the Customer instance is selected in the list). It stores the object instance into its Lookup by Customer.class as a key and does not care who, how and when (any other component) uses it and how its interface looks like - it does not call the other component's interface and the foreign component does not need call my interface - my Lookup will be collected into global context or some singleton or it can call getLookup() only.

The other component (object, even in other module) asks the Lookup object if it contains any Customer.class instance. If yes it uses it - e.g. shows it in the form.

This principle allows to create very abstract and general solution for some use cases. Some example: you are edit some data (document, planned tasks). The editation is your job. But the user can choose different ways how and where are data loaded from, stored in, imported, exported and in what format. You define an interface ExportMyData or StoreMyData and for these tasks you asks which formats and which storages are available (installed). Other components register HtmlExportMyData, RtfExportMyData, XMLExportMyData as a special implementation of the ExportMyData services.

Good example of such an abstract solution and dependency decoupling are the Project API and actions global context. We will deal with the first and the second one is described bellow.

The Project API module manages Project instances. The Project interface is described very simply:

FileObject getProjectDirectory();
Lookup getLookup();     // from Lookup.Provider

Are you surprised? I was, too. The interface is very simple. However, read the getLookup() method documentation - it recomends to store many objects instances into its Lookup, e.g. Project itself, ProjectInformation, LogicalViewProvider, CustomizerProvider, Sources, ActionProvider, AuxiliaryConfiguration, AuxiliaryProperties, ProjectOpenedHook. Here is the treasure. Who knows these classes it uses them. If you do not provide some instances in the Lookup their functionality is not supported - the appropriate module does not find it. Simple, isn’t it?

To recognize, load and store each project-type you must provide and register the ProjectFactory interface implementation provider (how, see the Registering your service recipe) with these methods:

boolean isProject(FileObject projectDirectory);

Project loadProject(FileObject projectDirectory, ProjectState state); 

void saveProject(Project project); 	

The isProject() method tests whether a given directory probably refers to a project recognized by this factory without actually trying to create it.

Your project type can have several specific methods in its interface, e.g. findMyProperty(), getMainFile(). If you need a Project object then ask the lookup of the active (selected) Project object e.g.

Project projlkp = selected-project-node.getLookup().lookup(Project.class);

Do not cast it to your type, rathter ask for your instance type:

MyProjectType project = projlkp.lookup(MyProjectType.class);   

It seems a bit complicated at the first look but you will consider it is useful do not dirty the Project interface by other behaviour. It contains methods needed only for projects managing. You, even the NetBeans creator, can add new functionality later without touching the interface. Aren't you excited?

You can provide an extension point of your application and you can install many concerete providers.

There are two kinds of the Lookup:

  • Global Lookup is a singleton and works in central registry role. There are two important ones:
    • Default Lookup available by Lookup.getDefault() is a central registry of services. It implements standard JDK JAR service mechanism by META-INF/services folder in the JAR file (see Registering your service and Find implementation tips for more information).
    • Actions Global Context available by Utilities.actionsGlobalContext() is context for actions performing. Here important active objects are stored which other components can use for its work - active (selected) Node, TopComponent, active ActionMap etc. The standard implementation is provided by Windows module API (note: next information are bellow) and uses Lookup object of the active TopComponent. Note - there is an example for dependency decoupling described bellow.
  • Local Lookup is Lookup where object can store objects which could be interesting for other objects. E.g. each TopComponent or Node provides its lookup by getLookup() of the Lookup.Provider interface. No matter who uses it and how - it simply provides list of active objects.

We have spoken about Action global context. The entry to retrieve this special Lookup is in the Utilities API by Utilities.actionsGlobalContext() method. It is no matter who provides it. The defining module can provide some default implementation to not corrupt running application. This Lookup instance is provided by Windows system API module which takes care about windows and selection managing. Isn't it well decoupled? The both modules can know only one interface - the Lookup.

The lookup() and lookupResult() methods return a Lookup.Result<T> instance - list of found instances. It enables to add a LookupListener that is informed about content changes. We will illustrate it later.

Standard Services Registration

The Lookup API module (it was part of the Utilities module before NetBeans 6.9 version) uses the standard JDK extension mechanism installed services. It explores the META-INF/services directory of each JAR file. If it contains file like the.package.SomeClass and registers all classes listed in this file as a the.package.SomeClass service provider. NetBeans Lookup uses some improvements - see this example content of such file:

#-the.vendor.package.OriginalImplementation
some.package.Implementation
#position=0

Lookup API registers some.package.Implementation class as a service provider of the the.package.SomeClass The #position paramete r is advanced NetBeans extension to declare order of instances returned by the lookup() method. The class must have default constructor. The NetBeans #- extension informs NetBeans that the.vendor.package.OriginalImplementation class have to be suppressed. You are able to replace others implementations, even them of the NetBeans (e.g. DialogDisplayer, IOProvider). The both advanced parameters are hidden into comment (prefixed by # letter) because to keep compatibility of JDK.

Service Registration by layer.xml

As you can learn in the chapter 4 (Files and Data) NetBeans uses the System Filesystem as registry of configuration data, data-types, actions, content of main menu, context menu according to data-type, options, used TopComponents, binding for editor etc. This filesystem is built-up of layer.xml file of all modules. Order of each entry is given by module dependencies.

To register any class as a service provider by this way create an .instance file under the Services folder in the layer.xml file.

   <folder name="Services">
       <folder name="MyDataFactory-service">

           <file name="MyDataFactory-implementation.instance">
               <attr name="instanceClass" 
                     stringvalue="com.packtpub.nbpcook.service.view.api.dftdata.DefaultMyDataFactory"/>
               <!-- if instance is created by FactoryCreator.create() method replace "instanceClass"
                    attribute by this "instanceCreate" attribute
               <attr name="instanceCreate"
                     stringvalue="com.packtpub.nbpcook.service.view.api.dftdata.FactoryCreator.create"/>
                 -->
               <attr name="instanceOf"
                     stringvalue="com.packtpub.nbpcook.service.view.api.MyDataFactory"/>
               <attr name="position" intvalue="999999"/>
           </file>
       </folder>

       <!-- to hide other service append _hidden suffix
            or place it to "Services/Hidden" folder   -->
       <file name="we.want.hide.other.Service.instance_hidden"/> 

   </folder>

Name of the folder and of the .instance file is not strictly determined so you can use some logical descriptional names. The instanceClass attribute declares which class you use. It has to have the default constructor. If the provider class is created by some factory method replace the instanceClass attribute by instaceCreate one where class and method is declared. The position attribute has the same meaning.

You can hide some other provider also. Declare it in the Services folder and add the suffix “_hidden”.

Service Registration by an Annotation

NetBeans provides service registration by an annotation from 6.9 version. Add an annotation before class declaration like this:

@ServiceProvider(service=MyViewFactory.class, position=9999999)
public class DefaultMyViewFactory implements MyViewFactory {

The NetBeans IDE extracts these annotations during build time and registers such a class in the standard META-INF/services directory of the module.

Find Service Providers

When you need use the implementation instance ask the default Lookup.getDefault:

        MyDataFactory fac = Lookup.getDefault().lookup(MyDataFactory.class);
        this.myData = fac.createMyData();

In this case the any implementation is useful. If you need know all installed providers look them all:

Result<MyViewFactory> lookupResultView =
                Lookup.getDefault().lookupResult(MyViewFactory.class);

or

Collection<? extends MyViewFactory> lookupAll =
                 Lookup.getDefault().lookupAll(MyViewFactory.class);

The lookupResult() method returns an instance of Lookup.Result. You can retrieve the same collection. Even you can listen to changes by LookupListener.


Text and sources were created under NB 6.8 and will be upgraded.


Sections by topic

Sources

Example sources media:Nbpcook_03_Lookup.zip

Navigation

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo