Custom Lookup Provider For NetBeans Platform CRUD Tutorial

For a long time I've been planning to rewrite the second part of the NetBeans Platform CRUD Application Tutorial to integrate the loosely coupled capabilities introduced in a seperate series of articles based on articles by Antonio Vieiro (a great series, by the way). Nothing like getting into the Lookup stuff right from the get go (rather than as an afterthought)!

The question, of course, is how to integrate the loosely coupled capabilities in a logical way within that tutorial. Today I worked through the tutorial from scratch, up until the point where the prototype is completed, i.e., there's a JTextArea displaying data pulled from a database. That brought me to the place where I needed to be. In fact, as soon as the prototype is completed, i.e., the database connection has been shown to work, the whole story about Lookup.Provider and InstanceContent should be introduced, so that all the subsequent sections, i.e., everything within "Integrating CRUD Functionality" will be done by adding new capabilities to the Lookup.Provider.

However, before I perform open heart surgery on that tutorial, I'd like to run the scenario by all those reading this blog who understand what I'm trying to do! (I.e., probably anyone who has read this far into this blog entry.)

So, this is what I propose should happen and in this order:

  1. Point out the fact that right now the database access code is found directly within our TopComponent. Not good. Because you're mixing view code with data code and, ideally, the developers creating the user interface wouldn't need to know anything about the data access layer. Better to separate out the data access code into a separate class, within the CustomerLibrary module, i.e., far away from the module providing the user interface, with this content:
    public class CustomerDataAccess {
    
        public List<Customer> getAllCustomers() {
            return Persistence.createEntityManagerFactory("CustomerLibraryPU").
                    createEntityManager().createNamedQuery("Customer.findAll").getResultList();
        }
    
    }
  2. Point out the fact that there is a concept of "Lookup" (which readers of the tutorial should know about since they should have followed the NetBeans Platform Quick Start), which is a registry into which objects can be published and to which other objects can be listening. In the same way as a TopComponent provides a Lookup, as demonstrated in the NetBeans Platform Quick Start, your own object can also provide a Lookup. So, therefore, let's provide a Lookup for Customer objects. 
    import org.openide.util.Lookup;
    import org.openide.util.lookup.AbstractLookup;
    import org.openide.util.lookup.InstanceContent;
    
    public class CustomerLookupProvider implements Lookup.Provider {
    
        private Lookup lookup;
        private InstanceContent instanceContent;
    
        public CustomerLookupProvider() {
            // Create an InstanceContent to hold capabilities...
            instanceContent = new InstanceContent();
            // Create an AbstractLookup to expose the InstanceContent...
            lookup = new AbstractLookup(instanceContent);
            // Add a "Read" capability to the Lookup of the provider:
            //...to come...
            // Add a "Update" capability to the Lookup of the provider:
            //...to come...
            // Add a "Create" capability to the Lookup of the provider:
            //...to come...
            // Add a "Delete" capability to the Lookup of the provider:
            //...to come...
        }
    
        @Override
        public Lookup getLookup() {
            return lookup;
        }
        
    }
  3. Point out the fact that, in the same way as we can publish an object into the Lookup of a TopComponent, we can now also publish an object into the Lookup of our CustomerLookupProvider. Instead of publishing a String, as in the NetBeans Platform Quick Start, we'll publish an instance of our own type. And here is the type:
    public interface ReadCapability {
        public void read() throws Exception;
    }

    And here is an implementation of our type added to our Lookup:

    public class CustomerLookupProvider implements Lookup.Provider {
    
        private Set<Customer> customerSet;
        private Lookup lookup;
        private InstanceContent instanceContent;
    
        public CustomerLookupProvider() {
            customerSet = new HashSet<Customer>();
            // Create an InstanceContent to hold capabilities...
            instanceContent = new InstanceContent();
            // Create an AbstractLookup to expose the InstanceContent...
            lookup = new AbstractLookup(instanceContent);
            // Add a "Read" capability to the Lookup of the provider:
            instanceContent.add(new ReadCapability() {
                @Override
                public void read() throws Exception {
                    ProgressHandle handle = ProgressHandleFactory.createHandle("Loading...");
                    handle.start();
                    customerSet.addAll(new CustomerDataAccess().getAllCustomers());
                    handle.finish();
                }
            });
            // Add a "Update" capability to the Lookup of the provider:
            //...to come...
            // Add a "Create" capability to the Lookup of the provider:
            //...to come...
            // Add a "Delete" capability to the Lookup of the provider:
            //...to come...
        }
    
        @Override
        public Lookup getLookup() {
            return lookup;
        }
    
        public Set<Customer> getCustomers() {
            return customerSet;
        }
        
    }
  4. Point out that we can now create a new instance of our Lookup (in some other module, so long as it has a dependency on the module providing the CustomerLookupProvider and the ReadCapability), retrieve the ReadCapability, and then do something with the customers that are returned, here in the rewritten constructor of the TopComponent, without needing to know anything about how the database access is actually achieved since that is hidden in the implementation of our type, above:
    public CustomerViewerTopComponent() {
    
        initComponents();
        setName(Bundle.CTL_CustomerViewerTopComponent());
        setToolTipText(Bundle.HINT_CustomerViewerTopComponent());
    
    //        EntityManager entityManager = Persistence.createEntityManagerFactory("CustomerLibraryPU").createEntityManager();
    //        Query query = entityManager.createNamedQuery("Customer.findAll");
    //        List<Customer> resultList = query.getResultList();
    //        for (Customer c : resultList) {
    //            jTextArea1.append(c.getName() + " (" + c.getCity() + ")" + "\n");
    //        }
    
        CustomerLookupProvider lookup = new CustomerLookupProvider();
        ReadCapability rc = lookup.getLookup().lookup(ReadCapability.class);
        try {
            rc.read();
            for (Customer c : lookup.getCustomers()) {
                jTextArea1.append(c.getName() + " (" + c.getCity() + ")" + "\n");
            }
        } catch (Exception ex) {
            Exceptions.printStackTrace(ex);
        }
    
    }

Does the above make as much sense to others as it does to me, including the naming of the classes? Feedback would be appreciated! Then I'll integrate into the tutorial and do the same for the other sections, i.e., "Create", "Update", and "Delete". (By the way, of course, the tutorial ends up showing that, rather than using a JTextArea to display data, you can use Nodes and explorer views to do so.)

Comments:

I commented that this was an abuse of Lookup. Specifically, if CustomerLookupProvider can refer to ReadCapability, then there is no purpose in using Lookup at all. Geertjan asked:

> So when would it make sense to ever implement Lookup.Provider in my own code?

Generally it would be used wherever you want to define an object in a base module, yet permit SPIs from modules you cannot “see” to be attached to this object for possible use by anyone holding a reference to this object which can in fact see those SPIs. Typically this interface is extended by other domain-specific interfaces which wish to provide adaptability: Project, DataObject, and so on.

More deeply, I would say that checked casts and instanceof are low-level language features associated with reflection which should never be used in application code, only plumbing. Wherever you might be tempted to do

BaseObject b = ...;
if (b instanceof SpecializedStuff) {
((SpecializedStuff) b).doSomething();
}

you should make BaseObject extend Lookup.Provider and write

BaseObject b = ...;
SpecializedStuff s = b.getLookup().lookup(SpecializedStuff.class);
if (s != null) {
s.doSomething();
}

Note that these are usually 0…1 mappings, i.e. a BaseObject may be associated with a SpecializedStuff instance or not. 0…∞ are possible when multiple modules are contributing the same SPI to the same object; in this case it is wise to define a matching API which encapsulates the knowledge of how to pick among these instances, or merge them, or resolve conflicts, or whatever your desired semantics are. (Such an API is useful even for 0…1 mappings, to encapsulate null handling and also provide for independent compatible evolution of the API and SPI.)

But if you find yourself with multiple instances in a Lookup.Result of the same concrete type, or if instances are appearing or disappearing for mundane reasons (i.e. not module enablement or the like), you are probably trying to stretch Lookup to perform a task for which it is not well suited. There are plenty of ways to write listenable collections which are far more type-safe, efficient, and usable than Lookup, if you have a particular element type in mind.

Another rule of thumb is that if the declared type of the Lookup.Provider implementation can refer to the type you are looking up (as in this example), it is unnecessary to use Lookup at all; just write a plain old getter, or preferably some more polished Java API. (In the case of Project, there are in fact some SPIs defined in the org.netbeans.modules.projectapi which are intended to be placed in its Lookup, but this is for consistency with the great majority of its capabilities which cannot be defined here.)

Posted by Jesse Glick on September 06, 2012 at 06:10 AM PDT #

Hi, thanks for the tutorial. I am trying to complete the Mavenized version of NB Platform CRUD Application tutorials and stuck towards the end where Savables are used for undo/redo functionality. The problem is that Maven is refusing to build the editor module complaining about the following:

NBM Plugin generates manifest
Private classes referenced in module: [javax.persistence.Persistence, javax.persistence.Query, javax.persistence.EntityManager, javax.persistence.EntityTransaction, javax.persistence.EntityManagerFactory]
Project depends on packages not accessible at runtime in module com.mycompany:MavenPlatformCRUDApp-dbaccess:jar:1.0-SNAPSHOT

This is coming from MySavable:handleMessage() where entity manager is created and used to create a transaction.

I would appreciate if you could point me to where the problem lies. I have googled it but couldn't find any useful info.

Thanks
H.

Posted by Halis on October 22, 2012 at 03:14 PM PDT #

Hi all !!!
I'd like to extend this excillent example to a new level.
What about implementing a Netbeans Search Provider whith lookup, so when user search specific Customer, when user hit enter, specific top component which deal with customers opens, and explorer managers lists for example, all purchases made by that specific client ?
I'm stuck, and don't know how to do this, actually, I tried to equip search provider with lookup, but global lookup doesn't see it.
It would be very nice that someone create this tutorial, because, I feel it could be almost standard NB tutorial !

Posted by Dobrivoje on January 16, 2014 at 12:37 AM PST #

Hello everybody !

I finally managed to solve quicksearch with lookup capability,
so someone, might find it useful.

It's on stackoverflow :
stackoverflow dot com*questions*20290523*netbeans-quicksearch-result-to-lookup*21508560#21508560
(change * with /)

Posted by Dobri on February 02, 2014 at 12:44 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today