Lookup library API

History: Available in CVS

This document describes usage of the API provided by the Lookup Library. In this document we assume that someone has already provided us with a lookup implementation (for those seeking how to write a lookup implementation please check the SPI document).

Getting the lookup

The first question you might ask is this: how can I get hold of a lookup instance? There are basically two ways how you can get it.

Global lookup

As you can see in the Lookup Javadoc there is a static method
public static Lookup getDefault()
The object returned from this method is a global lookup that can serve as a central place for registering services. The default implementation is a lookup that implements the JDK JAR services mechanism and delegates to META-INF/services/name.of.Class files.

If you want to add your class to this lookup just create a file in your jar file under the META-INF directory (e.g. META-INF/services/com.my.APIClass) and let the file contain only one line of text

com.foo.impl.ImplOfTheAPI
The following code will return you a newly created instance of com.foo.impl.ImplOfTheAPI:
   import org.openide.util.Lookup;
   return (com.my.APIClass)Lookup.getDefault().lookup(com.my.APIClass.class);

Local lookup

This is just a reminder that whenever you find a method called getLookup or similar returning a lookup instance, the provided lookup is not the general lookup described in the previous paragraph. Rather, it is a private lookup implementation that is usually bound to the object you invoked the method on.

Use of Lookup.Template and Lookup.Result

There are more ways how you can ask lookup besides the variant with one class parameter. If you want more functionality, you have to implement the interface Lookup.Template and pass an instance of such object to the lookup call.

Note: If you use Lookup.Template, the object returned from the lookup is not the object you are looking for but rather a result object (Lookup.Result). You can call methods on such a result object to get the actual results.

Let's examine following example:

        import org.openide.util.Lookup;

        Lookup lookup = ...;
        Lookup.Template template = new Lookup.Template(MyService.class);
        Lookup.Result result = lookup.lookup(template);
        Collection c = result.allInstances();
        for (Iterator i = c.iterator(); i.hasNext(); ) {
            MyService s = (MyService)i.next();
            s.callMyService();
        }
In this example the call to method lookup(...) returns immediately because the result object can be constructed even without real results. The first time you ask for the result object by calling r.allInstances(), the lookup has to supply you the real results and this method can block until the required data are really available.

If you are not interested in all objects as in the previous example, you can use the template to ask for one resulting object (wrapped in special Item instance):

        import org.openide.util.Lookup;

        Lookup lookup = ...;
        Lookup.Template template = new Lookup.Template(MyService.class);
        Lookup.Item item = lookup.lookupItem(template);
        MyService s = (MyService)item.getInstance();
        s.callMyService();
Again, the Item object can construct the real instance only if you call getInstance. The item can be useful even without calling getInstance - you can get its display name or an unique id. You can use this information, for example, for constructing menu items without the need to instantiate (or even load!) the class implementing the functionality. Only when the real functionality is needed (e.g. the user has selected the menu item) you can call getInstance and call the real meat of the implementation.

Listenning on lookup changes

There is one additional piece of functionality bound to the Lookup.Result object worth mentioning: you can attach a listener to it and be informed about any changes in the lookup. This might be extremly usefull when the lookup dynamically changes (from other threads). The listener can keep state of your object up-to-date even in cases where the lookup changes asynchronously.

So here is some sample code using the listenner:

        import org.openide.util.Lookup;
        import org.openide.util.LookupListener;
        import org.openide.util.LookupEvent;

        Lookup lookup = ...;
        Lookup.Template template = new Lookup.Template(MyService.class);
        final Lookup.Result result = lookup.lookup(template);
        result.addLookupListener(new LookupListener() {
            public void resultChanged(LookupEvent e) {
                reaction(result);
            }
        });
        reaction(result);
    }
    private static void reaction(Lookup.Result r) {
        for (Iterator i = r.allInstances().iterator(); i.hasNext(); ) {
            MyService s = (MyService)i.next();
            s.callMyService();
        }
    }
Please note that we first attach a listener and then call the reaction method. This ensures that we always get the newest possible state. Also you must be careful in the reaction method since it can be called from two different threads simultaneously (your code has to be prepared for this).