That other Lookup
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;
}
[sourcecode language="java"] /sourcecode]
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.