layer.xml

a netbeans rcp blog

Posts Tagged ‘Lookup’

Great Lookup video

Posted by Nicklas Löf on November 3, 2010

Just found this great video about Lookup on blip.tv by NetBeans API architect Jaroslav Tulach.

http://blip.tv/file/2631958/

Advertisements

Posted in Netbeans | Tagged: | Leave a Comment »

That other Lookup

Posted by Nicklas Löf on November 2, 2010

In my previous posts I have shown Lookup as a way to communicate between different components in a Netbeans RCP application, even between different modules by using Lookup as a communication device where one component can send out a message without knowing who is listening and what they do with the data.

When I started using Lookup at first I was a bit confused because I did mix up the two Lookups that exists in Netbeans. The first one is the one above which is an more advanced way of implemeting listners. The other Lookup is a way to get an instance of a class. I did play a bit with it but I always found 0 instances. Now I have learned more and have started to using this method in my application.

Lets code!

In my the Basic of nodes I was using a CustomerService class that had a bunch of static methods for retreving and adding customers. And the customers was stored in static arraylist. And then I did use this by calling CustomerService.getCustomers(). Now we gonna do this trough the Lookup instead. First of all we need to create an Interface. Netbeans actually contains refactoring tools for this. The best way to convert a class to an interface is doing this:

* Replace all static methods with normal ones
* Make the methods public

In my case my class was called CustomerService which I want my Interface to be called so I’m just renaming it to CustomerServiceImpl. To create an interface just go to the refactor menu and choose Extract interface. Select all the methods, name it CustomerService and we are done. CustomerServiceimpl will now automatically implement CustomerService.

Adding an implementation as a ServiceProvider.

We are not completly done yet. We need to tell Netbeans that out Impl class is a serviceprovider, just implementing an interface isn’t good enough. So we need to add an annotation just before the class implementation line.

@ServiceProvider(service=CustomerService.class)

Doing this is that easy in Netbeans 6.9. In earlier versions you had to create files into the META-INF folder specifying this but with help of annotations it’s done automatically now.

Replace our static call in the application

Now we need to replace our CustomerService.getCustomer() call with the Lookup version instead. It’s done with this modification to our createKeys() method in our ChildFactory:

    @Override
    protected boolean createKeys(List<Customer> list) {
        CustomerService cs = Lookup.getDefault().lookup(CustomerService.class);
        list.addAll(cs.getCustomers());
        return true;
    }

See how we call Lookup and asking for the default lookup. Then we tells it to get a CustomerService implementation. And as you can see we aren’t asking for CustomerServiceImpl but for the Interface that we did extract and implemented. This is very powerful as I will show later.

Also you probably noticed that our CustomerServiceImpl got it’s instance automatically created for us, we didn’t have to invoke new on it. So how can this be useful? Well.. you don’t have to know the name of the class that implements the feature you want, all you need to know is the service. So you can rename the implementation class without having to change the code.

Multiple instances

Here is the thing that can be very powerful. This isn’t an example one would use in a real application but as an example. lets say that our register of customers can be retrieved from more than one location. One is from a database and another one is from a web service. How can we fetch them in one call? It’s probably tempting to add code into the getCustomer method that will fetch data from both locations and join them in an arraylist. It’s working but since we have something more powerful here to use lets use Lookup instead. The way to do it is by creating two classes that implements the CustomerService interface. So we can have CustomerDatabase implements CustomerService and CustomerWebservice implements CustomerService.

Now we have two classes that grabs the data in different ways. Now we have to do some small modifications to our Node childfactory. Instead of using lookup(Interface) we are gonna use lookupAll(Interface) and guess what that will return? Yes! A collection!

    protected boolean createKeys(List<Customer> list) {
        Collection<? extends CustomerService> customerServices = Lookup.getDefault().lookupAll(CustomerService.class);

        for (CustomerService cs : customerServices) {
            list.addAll(cs.getCustomers());
        }
        return true;
    }

So we are getting all our classes that implements CustomerService (and has the @ServiceProvider annotation). THen we just loop over them and ask for getting the customers for each of them. And as you can see our factory doesnt have a clue of what instances there is and how many there is. It could be 0, it could be 25. And the different classes doesnt have to be in the same module.

Lookup without Netbeans

Yes it’s possible. You can use all of this outside a netbeans application. You just have to add the openide-lookup jar to your application and start using it and you will get both Lookup methods.

Posted in Netbeans | Tagged: , | 2 Comments »

Refresh Node children automatically

Posted by Nicklas Löf on October 14, 2010

Hello followers!

Todays problem to solve is how to refresh your Node children when there is a change in the data model. For example if you have a list of Customers and a delete button. When it’s pressed you have added code that are removing the selected customer from the list of customers but the node will still be visible in your list view. How can this be resolved?

In my basic of nodes post I did use a ChildFactory to create my nodes. Childfactories has a createKeys() method. In createKeys you populate a list with all the data objects you want to create nodes from and createNodeForKey() is called automatically for each object. Childfactories also have a nice method called refresh(). What refresh does is that it calls the createKeys() method again but it only calls createNodeForKey() for objects that doens’t already have a node. This is great. The old nodes (with current cookiesets and other states) are still there, only the new ones are added (or removed in case there is data that has been removed).

So lets call this refresh method then? well.. you can’t.. it’s protected and can only be called from the class that are extends Childfactory which is our nodechildfactory. At first I created a public method called update() which then called the refresh() method but then I started to realize that I need to keep an instance of my childfactory public in my TopComponent and call it when my data was updated. This works but isn’t a good way to do it. You know.. decoupling and all that. The lesser dependencies the better.

What can we do instead? Once again the power of lookups saves us! This solution is very simliar to the one I used in my post about using Lookup to change views in your application but lets look at some code again and lets start with creating a total empty class.

public class NodeRefreshEvent {
}

Phew.. that was a complicated one. Now lets look at our class that provides us with the model data objects. It’s the same class that I used in Basic for nodes. It’s a class with a static method that returns a list of Customer. Lets say that we have a deleteCustomer() method now and when a customer is deleted I want my node view to reflect this change.

public class CustomerService implements Lookup.Provider{
    private InstanceContent content = new InstanceContent();
    private Lookup lookup = new AbstractLookup(content);

    @Override
    public Lookup getLookup()
    {
        return lookup;
    }
}

So what have been added? I’m implemeting Lookup.Provider that contains the method getLookup(). And I need a Lookup to return so I create a lookup using the InstanceContent class which is a List where I can put instances of any object. So what do you say.. should I be crazy and put my small and silly NodeRefreshEvent class into this list when my data has been updated? Let’s try that!

public void deleteCustomer(int id){
    customers.delete(id);  // Lets pretend customers is a map with id as key
    content.add(new NodeRefreshEvent());
}

(See the Important section at the end of this post for some important information about this method)

Was that everything? So easy!.. No.. you need to do one more thing. Remember the childfactory?

class CustomerNodeFactory extends ChildFactory<Customer> implements LookupListener{
    private final Result<NodeRefreshEvent> lookupResult;
    public CustomerNodeFactory() {
        lookupResult = CustomerService.getLookup().lookupResult(NodeRefreshEvent.class);
        lookupResult.addLookupListener(this);
    }
}

So.. in my ChildFactory I’m now implementing a LookupListener. It does excatly what it says. It listens to changes on a Lookup. Which Lookup? Thats what I define in the constructor. I say that I want to listen for changes in the Lookup of CustomerService but only ones that are of the type NodeRefreshEvent.

Now we need to react when a change is done. How can that be done? Trough the resultChanged method that the LookupListener have added for me off course. This is where we will do our magic.

    @Override
    public void resultChanged(LookupEvent le)
    {
        Lookup.Result r = (Lookup.Result) le.getSource();
        Collection c = r.allInstances();

        for (Object object : c)
        {
            if (object instanceof NodeRefreshEvent){
                refresh(true);
            }
        }
    }

So here we check if the event we got in the Lookup is an instance of NodeRefreshEvent. If it is then do refresh() and boom.. the magic happens.. The createKeys() method is called and together with createNodeForKey() the nodes list is updated with new entries or removal of deleted entries!

And once again we have done some decoupling. Our CustomerService doesn’t have any idea who is interested in the addition or removal of customers. And remember we aren’t limited to just one listener. We could have 20 of them that listen to a NodeRefreshEvent and do things.

If you need to send a message together with the NodeRefreshEvent it can easily be done. Just create a field and a getter. When you creates your new instance of NodeRefreshEvent add the message to the constructor and pick it up in the Listener like this:

for (Object object : c)
{
    if (object instanceof NodeRefreshEvent){
        String message = ((NodeRefreshEvent)object).getMessage();
        refresh(true);
        doSomethingWithMessage;
    }
}

Message is offcourse not limited to a String. It can be anything you want. An enum, another object….

One last important thing

As Darrin pointed out in the comments section I forgot to include one important thing. The InstanceContent list is never emptied. We just add new things into the Lookup but we never removes them. And since we loop over all content in the Lookup we gonna update the nodes multiple time. So this is something we must prevent and that is to remove our previous NodeRefreshEvent instance before adding a new one.

Unfortuneatly there is no removeAll or clear method that can be called. The only way to remove an object from the InstanceContent is by using the instance itself. So we would need to save our created instance of NodeRefreshEvent and then call content.remove(ourInstance); This is how I did in a previous blog post but a few days ago I found this post that showed me another way to do it. BeanDev

So lets use a modified version of this example in our removeCustomer method

public void deleteCustomer(int id){
    customers.delete(id);  // Lets pretend customers is a map with id as key
    content.set(Arrays.asList(new NodeRefreshEvent()), null);
}

So instead of adding one object we use set which wants a list that we simply can create with Arrays.asList. And now the whole content of the InstanceContent is replaced.

Posted in Netbeans | Tagged: , , | 12 Comments »

The one with the cookies and some action and a little bit of Lookup

Posted by Nicklas Löf on October 13, 2010

So.. this is a blog post about cookies. Don’t we all love cookies? Sure we do but this isn’t about those small sugar bombs, neither is it about cookies in web browsers. Since this is a blog about Netbeans you might have already guessed that there is something in Netbeans RCP called Cookies.

I recently needed a way to tell my application that my data was updated and I knew that those cookies could help me. What I wanted to achieve was that by pressing a button in my toolbar the nodes that was updated had to be processed. What cookies does is giving your nodes a temporary state so you can track them.

Lets start by see if we can feed our nodes with some cookies…

First we need to create a cookie. In this example I’m creating an interface that extends a Node cookie. The interface contains one method called update().

public interface UpdateCookie extends Node.Cookie{
    public String update();
}

Ok.. now I have a cookie! What should I do with it? Should I have my node to implement this interface? No.. remember what I said earlier.. I want to give my node a temporary state. But add and remove interfaces can’t be done at runtime can they? No.. so we need to something else in our node so lets look at that.

public class CustomerNode extends BeanNode<Customer> {
    public CustomerNode(Customer bean) throws IntrospectionException {
        super(bean);
        setDisplayName(bean.getName());
    }
}

So this is a basic BeanNode. It has something called a CookieSet which is a Java Collection with Cookies. By default it’s empty but let’s add our cookie to it. And remember it’s an interface so it will ask us to implement all our abstract methods.

getCookieSet().add(new UpdateCookie() {
    @Override
    public String update() {
        return "Hello from" + getDisplayName() +" I'm updated!";
    }
});

Cookie added! And our update method will just return a String in this example but this is where you should call your application logic and do something with your data for example update a database or call a webservice..

This is a bit of bad example because now we have added the cookie to all our nodes. Normally you would have a method in your node that you call when the data is updated for example.. and that method should add the cookie to the cookieset. And to improve this example let’s add a small if statement that only adds this cookie to one of the nodes.. like this:

if (bean.getName().equals("Apple")) {
    getCookieSet().add(new UpdateCookie() {.........  like above....}
}

As I said, this is only to add the cookie for just one node in this example, normally it’s done somewhere else in your application.

Ok.. so now one of our Nodes has a UpdateCookie assigned. What can we do now? Let’s create an action so we can do something with our nodes. What actions are is out of scope of this post but the basic is that an action is something that are called when you click an icon in the toolbar or select something in the menu. So let’s create a menu item.

Add a new action by right click at your project and select New->Netbeans Module->Action. Leave the action to be Always enabled. Put it in the file category and call it UpdateCustomerAction with display name Update customers. Netbeans will now do everything for you and open up our newly created action. When an action is choosen the actionPerformed() method is called so this is where we will add our code.

What do we want to do? We want to grab all our Nodes that have the UpdateCookie assigned. First of all we need to get hold of our Nodes which are located in an Explorermanager in a Topcomponent. It’s VERY important that we add this command to our Topcomponent first:

associateLookup(ExplorerUtils.createLookup(myExplorerManager, this.getActionMap()));

This tells the Topcomponent that it should place the current selected nodes into the Lookup of the Topcomponent. So each time one or serveral nodes are selected those nodes are put into the Lookup of our Topcomponent so we can grab them externally.

So lets go back to our action. Now we can get our nodes.. at first this code might be tempting to write:

public final class UpdateCustomerAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        Node[] nodes = TopComponent.getRegistry().getCurrentNodes();

        for (int i = 0; i < nodes.length; i++) {
            Node node = nodes[i];
            UpdateCookie cookie = node.getCookie(UpdateCookie.class);
            if (cookie != null) {
                System.out.println(cookie.update());
            }
        }
    }
}

If we run our application now and select Update customer in the File menu with all our nodes selected in the Topcomponent it would correctly say in the Output window “Hello from Apple I’m updated!” but before explaining what happend I want to make the code above a bit cleaner. Instead of looping over all nodes and assign the cookie to a local variable and check for null there is a much more elegant way to do this. How? By using Lookup off course!

public void actionPerformed(ActionEvent e) {
        Collection<? extends UpdateCookie> lookupAll = TopComponent.getRegistry().getActivated().getLookup().lookupAll(UpdateCookie.class);
        for (UpdateCookie updateCookie : lookupAll) {
            System.out.println(updateCookie.update());
        }
    }
}

So what am I doing here? I’m asking for the current activated TopComponent. From this TopComponent I’m asking for the Lookup and tells it that I want to get all Nodes that are associated with an UpdateCookie. I will get a Java Collection with the result and since I just asked for UpdateCookie I only got the Cooke instance and not the whole Node and I can also be sure that it’s safe to call the update method.

Decoupling
There is actually a third way to grab our cookies. It’s a bit like above but using the global lookup instead. This is a proxy of all TopComponent lookups and from here you can get the selected node without having to reference to a specific TopComponet. This is good for decoupling and reusage. If we do it that way we can actually reuse our UpdateCustomerAction in other components. So the final code would look like this:

    public void actionPerformed(ActionEvent e) {
        for (UpdateCookie updateCookie : Utilities.actionsGlobalContext().lookupAll(UpdateCookie.class)) {
            System.out.println(updateCookie.update());
        }
    }

So to summarize what have been done.

  • I added an UpdateCookie to my Apple node. UpdateCookie is an interface with one method. This method will be called and from it I can do whatever I need to do with my node. In this case I just returned a string with the display name of the Node.
  • Then I added an action that grabbed all my UpdateCookie nodes. I looped over them and called the update() method that returned me the string with the name of the Node.

After we have done our updates we want our Cookie to be removed from our node since it’s only a temporary state. This can be done with this line added to the update() method:

getCookieSet().remove(getCookieSet().getCookie(UpdateCookie.class));

The next time you select Update customer nothing will happen because the cookie was removed.

Ending and exercise

This is just one example of what can be done with cookies. For fun and exercise try add SaveCookie to one of your nodes cookieset. Then watch the default Save button in the toolbar when you select different nodes. I will explain what happens in a later episode of Layer.xml 🙂

Posted in Netbeans | Tagged: , , , , | 1 Comment »

Power of Lookup

Posted by Nicklas Löf on October 7, 2010

Just as Nodes are one very important building block of Netbeans RCP applications Lookup are almost even more important. At first the concept can be confusing and you might not know what usage you have of them. I only have a basic knowledge of Lookups and had one of those A-ha! moments a few days ago. As I’m still learning Netbeans I probably missunderstand things and everything written in this post might not be the correct way to do things.

Scenario

Lets look at a scenario where Lookup can help us a lot to create a much better design of our code. Lets say that you are building an application that only consists of one module in a suite. You have a menu (maybe a JXTaskpane shown earlier in my blog) and when you select one item the content in a Topcomponent that contains an Outlineview showing nodes are changes to a different set of nodes. How would the communication between the menu and the Outlineview be done?

The easiest way would seem to be a call to a public method like this: WindowManager.getDefault.findTopComponent(“YourTopComponent”).switchViewToB(), WindowManager.getDefault.findTopComponent(“YourTopComponent”).switchViewToC(), WindowManager.getDefault.findTopComponent(“YourTopComponent”).switchViewToD(). And this method would take care of updating the nodes. This is a working solution and would probably work fine for a one menu with one topcomponent application. But what if you want to bring up another Topcomponent when you switch to D? Where should you do this? In the menu action calling the other topcomponent? Or in the switchViewToD() method?

Adding this to the SwitchViewToD() method also works but now you need to make sure that you are showing and hiding a bunch of topcomponents from the method. When you add one more TopComponent you need to go trough all others and make sure that they are opened and closed correctly. Starts to feel like a small nightmare doesn’t it?

Now lets say that your boss wants to earn more money. He wants to sell an enterprise edition of your application that only should be distributed to customers paying extra. This enterprise application contains a few more panels that should be opened up on certain views. The best way to implement this part is by creating an Enterprise module to your suite. But now things starts to get complicated..  How will you bring up the extra panels? You can’t have calls to TopComponents that only exists in this module since this would cause Nullpointers for customers without the Enterprise module. You could add if statements that checks if the enterprise module is installed and only call the TopComponents then..   or maybe…

No!  stop thinking now! This has gone the completely wrong path from the beginning. There is a much much better ways than creating dependency hell between the TopComponents. If we do this correctly TopComponentA would not know that TopComponentB even exists. This is something called decoupling in the software development world.

The solution is by using Lookup

So how could this Lookup thing help us? As always.. lets dive into some source code.

First of all I want to create some kind of object that tells my TopComponents what view I want to switch to. This object will then be sent out into the Lookup. So let’s create a very basic class.

public class TaskSwitch {
    private final GUITask task;
    public TaskSwitch(GUITask task) {
        this.task = task;
    }
    public GUITask getTask() {
        return task;
    }
}

What is GUITask? It’s an enum and it’s created like this:

public enum GUITask {
    myFirstView,
    mySecondView,
    myThirdView
}

Ok.. so now we a class and an enum. How can we use them? First by adding this fields to our menu (I’m using JXTaskPane in a separate TopComponent for switching views).

private InstanceContent taskContent = new InstanceContent();
private Lookup lookup = new AbstractLookup(taskContent);
private TaskSwitch taskSwitch;

This will create a Lookup based on InstanceContent which is a builtin class in Netbeans that provides the Lookup with content where we can add instances of our classes.

InstanceContent works as a list and instances can be added and removed but there exists now removeAll() method so we need to keep track of our instance added to it. For this I created a small method that will remove the previous TaskSwitch instance from the content and add the new one.

private void switchTask(TaskSwitch taskSwitch) {
    try {
        taskContent.remove(this.taskSwitch);
    } catch (Exception e) {
    }
    this.taskSwitch = taskSwitch;
    taskContent.add(this.taskSwitch);
}

Now I can use this method in my menu actions to add a new TaskSwitch instance to my Lookup.

switchTask(new TaskSwitch(GUITask.myFirstView));

We also need to make our Menu to implement Lookup.Provider and a getLookup method that returns our lookup field.

public final class MyMenu extends TopComponent implements Lookup.Provider
public Lookup getLookup() {
    return lookup;
}

What have we done? We have created an InstanceContent instance which is a list of instances of any type. We have created a Lookup that monitors this List and acts when something has been added to it. Each time a menu item is selected I’m adding a new Instance of my TaskSwitch class that contains an enum value of the view I want to have.

Now lets switch to my TopComponent where I have my OutlineView.

public final class MyOutlineTopComponent implements LookupListener
private final Result taskLookupResult;

Here Im making my TopComponent to be a LookupListener. Sounds like a good idea. I want to listen to changes in the Lookup.
In my constructor I have to tell what I want to listen to.

taskLookupResult = WindowManager.getDefault().findTopComponent("MyMenuTopComponent").getLookup().lookupResult(TaskSwitch.class);
taskLookupResult.addLookupListener(this);

Ok.. now I have told what I want to listen to all changes where instances of TaskSwitch is involved into.

Now we need to do something when we get notified about a change. This is done in a method called resultChanged that contains a collection of all the changes. It can ben one or it can be many.

@Override
    public void resultChanged(LookupEvent le) {
        Lookup.Result r = (Lookup.Result) le.getSource();
        Collection c = r.allInstances();

        if (!c.isEmpty()) {
            for (Object object : c) {
                if (object instanceof TaskSwitch) {
                    changeView((TaskSwitch) object);
                }
           }
      }
}

So each time somethings happens that involves an instance of TaskSwitch.class the resultChanged method will be called. We will get a collection of all the instances that has something to do with TaskSwitch.class. In our case there should only be one. I prefer to do an extra type check by checking if Object is an instance of TaskSwitch and if it is I’m calling a method called changeView();

And this is how my changeView() method looks like:

private void changeView(TaskSwitch taskSwitch) {
    switch (taskSwitch.getTask()) {
        case myFirstView:{
            switchToFirstView();
            break;
        }
        case mySecondView:{
             switchToSecondView();
             break;
        }
    }
}

And we are done. Now I have switched the content in my TopComponent without the menu knowing where the changes occur. I can now do the same thing in every other TopComponent in my suite and it doesn’t even have to be in the same Module.

This is just an example of what can be done. Other usages could be if you have a background thread that checks for new data from a web service that needs to be added to your node views. Each time the background thread finds new data it could send the data over the lookup and the classes that are interested if there is new data can pick it up and display it, for example refresh the nodes list without knowing anything about the background thread and how the new data was added into the Lookup. It just knows the data is there and what to do with it.

And if you want to show or hide certain TopComponents depending on which view that is selected just call this.open() and this.close() in the changeView()/resultChanged method. Just make sure that you have registered your lookup in the constructor and not in componentOpened() since it would never open the first time. Also don’t deregister the lookup in componentClosed().

So this is one of the many possibilities of using Lookups in your Netbeans RCP application.

Posted in Netbeans | Tagged: , | 4 Comments »