2007-12-18

Central Lookup: Creating a central repository and lookup for an application context in a NetBeans RCP application

I have been working on a NetBeans RCP project where I needed a central place to register different interfaces and allow different views to operate on these regular POJOs one would use in a regular Swing application and to be able to listen to changes to them from the standpoint of some kind of an Application Context where different instances will come and go. A good example is a desktop application requiring a login. You will have user information and want to be able to track a global login or validation token.

I create a simple class and API in a NetBeans module and called it CentralLookup. This is all the code for such a simple thing (please ignore the formatting of the code...maybe I can update it later, but it seems blogger isn't wanting to format it correctly when posted):


package org.netbeans.modules.centrallookup.api;

import java.util.Collection;

import org.openide.util.Lookup.Result;

import org.openide.util.lookup.AbstractLookup;

import org.openide.util.lookup.InstanceContent;

/**
* Class used to house anything one might want to store

* in a central lookup which can affect anything within

* the application. It can be thought of as a central context

* where any application data may be stored and watched.

*

* A singleton instance is created using @see getDefault().

* This class is as thread safe as Lookup. Lookup appears to be safe.

* @author Wade Chandler

* @version 1.0

*/

public class CentralLookup extends AbstractLookup {

private InstanceContent content = null;

private static CentralLookup def = new CentralLookup();

public CentralLookup(InstanceContent content) {
super(content);
this.content = content;
}

public CentralLookup() {

this(new InstanceContent());
}

public void add(Object instance) {

content.add(instance);
}

public void remove(Object instance) {

content.remove(instance);
}

public static CentralLookup getDefault(){
return def;
}
}


The nice thing about this code is that it is real simple. The strange thing for me is that there hasn't been something already implemented in the base RCP. It seems pretty common a thing. For instance, the normal global lookups deal with Actions, Services, and Nodes (which are part of the NetBeans data model), and that is fine until one needs to work with generic interfaces and classes from a global perspective.


Regardless, I can now write code to listen to changes in this global context or Lookup in this case. The code doesn't have to know about any implementation except for the code which does the injection, and the code in other places can just setup a result and a listener. The following illustrates this a bit, and of course it is just a sub-set of the code:

final class CentralLookupTest1TopComponent extends TopComponent {

private static CentralLookupTest1TopComponent instance;
private Lookup.Result userInfoResult = null;
private CentralLookupTest1TopComponent() {

Lookup.Template template = new Lookup.Template(UserInformation.class);

CentralLookup cl = CentralLookup.getDefault();
userInfoResult = cl.lookup(template);
userInfoResult.addLookupListener(new UserInformationListener());
}

private javax.swing.JTextField name;
private javax.swing.JTextField pwd;
private javax.swing.JTextField token;
private javax.swing.JTextField uid;

private class SetterRunnable implements Runnable {
UserInformation ui = null;

public SetterRunnable(UserInformation ui) {
this.ui = ui;
}

public void run() {
name.setText(ui.getName());
pwd.setText(ui.getPassword());
uid.setText(ui.getUserID());
token.setText(ui.getToken());
}
}

private class UserInformationListener implements LookupListener {

public void resultChanged(LookupEvent evt) {
Object o = evt.getSource();
if (o != null) {
Lookup.Result r = (Lookup.Result) o;
Collection infos = r.allInstances();
if (infos.isEmpty()) {
EventQueue.invokeLater(new SetterRunnable(new DefaultUserInformation()));
} else {
Iterator it = infos.iterator();
while (it.hasNext()) {
UserInformation info = it.next();
EventQueue.invokeLater(new SetterRunnable(info));
}
}
}
}
}
}


Notice we have a result. The result has attached to it a listener. The listener knows about DefaultUserInformation and the UserInformation interface, and that is it. It then knows it needs to listen for instances of UserInformation and act accordingly. It doesn't know how it got there, but knows what it must do with it. We then have some other code in another class which will inject the instances into the CentralLookup based on some user input removing any previous:

private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
//ok we need to remove any user information from the central lookup
//and then we need to add this new one

CentralLookup cl = CentralLookup.getDefault();
Collection infos = cl.lookupAll(UserInformation.class);

if(!infos.isEmpty()){
Iterator it = infos.iterator();
while(it.hasNext()){
UserInformation info = it.next();
cl.remove(info);
}
}

DefaultUserInformation info = new DefaultUserInformation();
info.setName(name.getText());
info.setPassword(pwd.getText());
info.setUserID(uid.getText());
info.setToken(token.getText());
cl.add(info);

}


You can get a good idea from here the possibilities. This is a rather simple example, but it certainly shows it working. This can be used for any other services which need to use regular POJOs and not much else. Why add more overhead of class wrappers etc when they are not needed for everything? Sometimes one just needs a dynamic application context which can breakup the application into a more modular framework of simple classes and interfaces without major restrictions.

I have an AVI video formatted video of this in action. It is a very simple example, but it shows it working. The one piece of code doesn't know anything other than the fact that user information will appear magically in the CentralLookup in the form of the UserInformation interface.


2007-08-09

Have to say it: What Sun is doing with the JCK (Java Development Kit and runtime TCK/compatibility kit) license seems pretty dirty

The part which seems dirty is the way the license is obviously being extended to OpenJDK based projects to allow OpenJDK project contributors to be able to use the TCK without any encumbrances on their builds fields of use while keeping other, from scratch, open-source efforts from being able to do the same. The obvious part: if developers were not able to use the TCK, it would hurt the OpenJDK effort as contributors would not be able to distribute any builds and call them Java as they would not have passed the TCK. Passing the TCK is part of the rules for being able to implement the Java specifications, claim adherence, and distribute the end result.

If the goal is to support open source Java and to be fair, then any open-source Java implementation regardless of the license would be able to use the TCK in the same manner. So, as a pretty open Sun supporter, I do not like what they are doing with the TCK at all, and it damages my view of them as a corporation as it specifically relates to honesty. I think if they are going to go about it this way they should at least have the honor and guts to say exactly what they mean and the reason for doing it instead of dancing around the issue by talking about license terms, not open-sourcing the TCK because they want to protected what it means to be "compatible" (which I totally understand), and not being able to please everyone. These reasons are pretty weak as a reason for the terms of the license only being given to OpenJDK based projects.

The fact is they could just as easily let anyone use the TCK for free to certify any open-source implementation. The TCK does not have to be open sourced for this to happen. I think if the reason, which to me it can only be, is to protect corporation investments and only support those efforts which directly support their interests then they should just say it. Personally I don't think this is outside the bounds of what corporations try to do; I mean lets face it, they have to make money, but the JCP (http://www.jcp.org) member agreement and processes clearly state what can and can not exist in limiting anyones right to fully implement JCP specifications, and the scholarship TCK license itself does encumber ones ability to create clean and independent implementations of the Java specifications, and this is the only free license organizations or individuals creating open-source Java implementations can obtain unless they are based on Suns OpenJDK and also use the GPL license. This is against the agreement of the JCP as I have read it as it imposes restrictions beyond the ones stated in the agreement, and the agreement specifically states that no more restrictions beyond those listed within the agreement can exist which hinder anyones ability to create independent implementations of any JCP specification.

So, Sun employees, don't take this personal. I happen to work with a few of you on different open-source projects, and you know who you are :-D, and I know you don't have anything to do with these decisions. The law department and which ever managers approved this however, this seems pretty darn dirty, at least what I have seen so far. Maybe someone has a good explanation, but for now I'm calling it what it looks like.