The Source for Java Technology Collaboration
User: Password:



Evan Summers's Blog

May 2006 Archives


Swing and Roundabouts 1: Event DTs

Posted by evanx on May 30, 2006 at 07:30 AM | Permalink | Comments (14)

swings4.jpg Introduction
"People always said there were no monsters, no real ones... but there are."

In his blog "A Simple Framework for Desktop Applications" John O'Conner summarised a JavaOne presentation by Hans Muller and Scott Violet vis-a-vis JSR 296.

John reports that this framework would give some guidance to Swing developers, to avoid common bad practices. Such as Swing apps that only support English, and Swing apps that are "a tangle of actionPerformed methods that block the event dispatch thread (EDT)."

I commented there that the issue of EDT-blocking event handlers is a big one for me. In my last project, most event handlers kicked off a string of "long tasks" (namely, communicating to a server over a GSM network), and I got into such a tangle with SwingWorkers upon SwingWorkers, that the code became increasingly difficult to follow.

This blog presents how I eventually untangled that application.

alien6.jpg
Application
"All we know is that there is still is no contact with the colony, and that a xenomorph may be involved."

Imagine a client-server application, where the client application communicates to the server application via the network.

Actually, this system is deployed on a remote planet managed by aliens. Those same class of aliens as we met in the Aliens movies, if you can believe it. The server room is installed in a secure complex on one of the moons. (I can't be more specific because of the NDA the Company makes everyone sign, so...)

Let's consider the login dialog. The alien enters its username and password, and then hits the "Login" button. This invokes our event handler to communicate to the server as follows.

   Gui gui = new Gui(); // our GUI helper 
   Messenger messenger = new Messenger(); // our server comms helper

   public void actionPerformed(ActionEvent event) {
      if (event.getSource() == loginButton) {
         loginActionPerformed();
      }
   }
   
   protected void loginActionPerformed() {      
      try {
         LoginResponse loginResponse = messenger.sendLogin(getLoginBean());
         if (loginResponse.getErrorMessage() != null) {
            gui.showMessageDialog(loginResponse.getErrorMessage());
            if (loginResponse.isInvalidPassword()) passwordField.requestFocusInWindow();
            else usernameField.requestFocusInWindow();
            return;
         }
         if (loginResponse.isPasswordExpiring()) {
            String password = gui.showPasswordInputDialog("Enter new password");
            if (password != null) {
               ChangePasswordResponse response = messenger.sendChangePassword(loginResponse, password);
               if (response.getErrorMessage() != null) {
                  gui.showMessageDialog(response.getErrorMessage());
               } else {
                  gui.showMessageDialog("Password changed");
               }
            }
         }
         login(loginResponse);            
         gui.showMessageDialog("Welcome, " + loginResponse.getAlienName());
      } catch (CommsException ce) {
         gui.showMessageDialog(ce, "The frelling connection is down again.");
      } catch (Exception e) {
         gui.showMessageDialog(e, "System failure. Try a hard reboot.");
      }
   }

As you can see, we communicate the login information to the server (ie. username and password), which might return with an error such as "Unknown Alien", "Access Denied" or "Invalid password." Or we might get a comms exception, eg. caused by the high gamma ray activity on this particular alien planet.

ktalkd.png Finally, if the login is valid, but the password is expiring soon, then we ask the alien to enter a new password.

Now the problem is that we are blocking the EDT in this method! In particular when we communicate to the server. Because it might take 10 or 20 seconds to get a response from the server on the moon.


EDT Blocking Problem
"In case you haven't been paying attention to current events, we just got our asses kicked, pal!"

To be 100% safe, we should not "manipulate" Swing components outside the EDT. Because, as the Java Tutorial says,

"Swing event-handling and painting code executes in a single thread, called the event-dispatching thread. This ensures that each event handler finishes executing before the next one executes and that painting isn't interrupted by events. To avoid the possibility of deadlock, you must take extreme care that Swing components and models are created, modified, and queried only from the event-dispatching thread."

That's fine because we typically manipulate our components (causing visual changes on the screen) in our EDT event handlers. For example, when a button is pressed, an actionPerformed() method is invoked by, and within, the EDT. And that's where our application actually does stuff, ie. in response an alien pressing or clicking something. Because GUI applications are event-driven.

alienFight3.jpg The problem is that we should not block the EDT for any length of time, eg. while waiting for a response from the server. Because then our Swing app is "hung" eg. if the alien starts moving windows around, they don't get repainted, and if it presses a window's "close" button, or a "cancel" button, or clicks on a different tab, nothing frikkin happens. Quite frustrating. Now understand that these aliens get very irritated, very quickly, and can go flying off the handle, and we really don't want that.


Waking the SwingWorker
"This a lead works isn't it? All we gotta do is lure the beast into the mould!"

So the solution is to use a SwingWorker thread, so that our long task can run as a separate thread outside the EDT. Then we can start a SwingWorker thread, and let the EDT exit the actionPerformed() method immediately, so that it can service other events if necessary (eg. in the response to the user clicking on something).

We might restructure our earlier loginActionPerformed() method to use a SwingWorker as follows.

   public void loginActionPerformed() {
      SwingWorker loginWorker = new SwingWorker() {
         LoginResponse loginResponse = null;
         Exception exception = null;
         public Object construct() { 
            // this is outside of the EDT, so no GUI stuff, just for our long task
            try {
               loginResponse = messenger.sendLogin(getLoginBean());
            } catch (Exception e) {
               exception = e;
            }
            return null;
         }
         public void finished() { // this is inside the EDT, for GUI stuff 
            if (exception != null) {
               String message = null;
               if (loginResponse != null) message = loginResponse.getErrorMessage();
               if (message == null) message = "A frak up has occurred";
               gui.showMessageDialog(exception, message);
            } else if (loginResponse == null) {
               gui.showMessageDialog(exception, "No response from server");
            } else if (loginResponse.getErrorMessage() != null) {
               gui.showMessageDialog(exception, loginResponse.getErrorMessage());
            } else {
               if (loginResponse.isPasswordExpiring()) {
                  String password = gui.showPasswordInputDialog("Enter new password");
                  if (password != null) {
                     ChangePasswordResponse response = messenger.sendChangePassword(loginResponse, password);
                     ... // oh frell, a long task, and we are in the EDT here...       
                     ... // so this is so not gonna work
                  }   
               }          
               gui.showMessageDialog("Welcome, " + loginResponse.getAlienName());               
            }
         }       
      };
      loginWorker.start(); // start the worker thread and free the        EDT
   }            

As you can see, we had to cajole our event handler quite significantly to make it jump through a SwingWorker hoop. "Uh oh, this is crashing my code-assist neural implants... I don't like it."


SwingWorker hive
"Seventeen days? Look man, I don't wanna rain on your parade, but we aint gonna last seventeen hours!"

testbed_protocol.png The problem is that our "long task" is actually a chain of long tasks. First off, we communicate the login info to the server. Clearly, this is a long task, which should run outside of the EDT. When we get a response, we might popup a dialog to enter a new password. Uh oh, that should run in the EDT. Then we communicate to the server again, which is another long task. Uh oh, that should run outside the EDT.

So we have to switch maybe a few times in and out of the EDT. SwingWorker is only good for one switch out of the EDT (and then back again). "Frell it! You just can't win with these alien requirements!"

Nested SwingWorkers? Let's not even go there!


Reversing into a solution
"I say we take off and nuke the entire site from orbit. That's the only way to be sure."

Really, we wanna leave the code as it was before ie. as in the original loginActionPerformed() implemention further above, without having to worry about blocking the EDT and SwingWorkers and all that.

But we know for sure that in order to make our event handler non-blocking, we will need to run it via a SwingWorker thread. That's a given. OK, so let's try the following.

   public void actionPerformed(ActionEvent event) {
      if (event.getSource() == loginButton) {       
         gui.startWorker(new Runnable() {
            public void run() {
               loginActionPerformed();
            }
         });
      }
   }      

We have introduced a startWorker(runnable) helper method, which we implement as follows.

    public void startWorker(final Runnable runnable) {
        SwingWorker swingWorker = new SwingWorker() {
            public Object construct() {
                try { 
                   setWaitCursor(true);
                   runnable.run();
                } catch (Exception e) {
                   showExceptionDialog(e);
                } finally {
                   setWaitCursor(false);
                }
                return null;
            }
        };
        swingWorker.start();
    }

Note that we have no finished() method for GUI code, and our runnable event handler is run from the construct() method ie. outside the EDT, so it must be "EDT-safe." Let's put that aside for a second...

The above method takes care of showing the hour glass, as follows.

    public void setWaitCursor(boolean waitCursor) {
        if (!waitCursor) applicationFrame.setCursor(null);
        else applicationFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        glassPane.setVisible(waitCursor);
    }

Notice that we might also activate a glass pane to disable a particular panel while our task is in progress, eg. to force the user to wait for the task to complete.


Lose on the Swing, gain on the roundabout
"Nobody never gave me nothing. So I say, let's fight this beast ourselves! This is as good a place as any to take our first steps to heaven. Do you wanna die on your feet, or on your frikkin knees?"

ksysguard.png The trick is that our runnable GUI code must be made "EDT-agnostic." In particular, since we use a GUI helper class, it's methods must be made EDT-agnostic.

Firstly, let's implement an EDT-agnostic invokeAndWait() method as follows.

   public void invokeAndWait(Runnable runnable) {
      if (!SwingUtilities.isEventDispatchThread()) {
         SwingUtilities.invokeAndWait(runnable);
      } else {
         runnable.run();
      }
   }

The above implementation of invokeAndWait() can be invoked safely inside the EDT, or outside it, eg. in a SwingWorker's construct() method. As such, it is "EDT-safe."

Now we can "EDT-harden" methods invoked on Swing components in the loginActionPerformed() further above, such as showConfirmDialog() in our Gui helper class, as follows.

    int dialogOptionValue;

    public int showConfirmDialog(final Object message, final String title, final int option) {
        invokeAndWait(new Runnable() {
            public void run() {
                dialogOptionValue = JOptionPane.showConfirmDialog((Component) applicationFrame, message, title, option);
            }
        });
        return dialogOptionValue;
    }

Similarly, we EDT-harden other methods in our GUI helper class, where these are commonly in our event handlers, eg. requestFocusInWindows() as below.

    
    public void requestFocusInWindow(final Component component) {
        invokeAndWait(new Runnable() {
            public void run() {
                component.requestFocusInWindow();
            }
        });
    }  

While this seems verbose and tedious, it is relatively quick to EDT-harden a method in our GUI helper class, by cutting and pasting the invokeAndWait(Runnable) boiler-plate code from another EDT-hardened method.

yast_profile-manager.png At first, methods in our helper class are not necessarily EDT-agnostic, that is to say, they do not use invokeAndWait(). When we encounter a need for that method to be used in the construct() method of a SwingWorker, eg. via our startWorker(runnable) helper, then we make that method EDT-agnostic in our helper class via invokeAndWait(Runnable).

Similarly when we encounter a need for a method on a Swing component, we should delegate that to our helper class, where we can make it EDT-safe.


Conclusion
"I dunno if I'm happy about this... I mean running around here in the dark with that frikkin beast after us."

alienBox2.jpg We should not block the EDT for any length of time, eg. while waiting from a response from the server. Otherwise our Swing app appears to be "hung" to the user.

So we should use a SwingWorker thread, for our long tasks. However, the SwingWorker is only good for one switch out of the EDT, and then back again.

This is a problem if our "long task" is actually a chain of long tasks, requiring GUI invocations in between, eg. popping up a confirmation dialog before proceeding to the next long task.

If our event handler performs a long task, then we put it into a SwingWorker thread. But the trick is that we take care that any GUI manipulation is performed via an EDT-agnostic helper class.

In our helper class, we use an invokeAndWait() method which will work whether we are in the Swing EDT or not, by checking SwingUtilities.isEventDispatchThread().

Our helper class may seem verbose and tedious, owing to the invokeAndWait(Runnable) boiler-plate code everywhere. However, our application code is simpler and clearer. So it's a very good bargain.

We isolate the tedium in a single helper class, to free up the whole application for developers. It's a win-win, for us and the aliens.

Which means the aliens won't be blowing any developers out of the hatch, and visa versa, for now.

See the articles "Bean Curd" and "Swing trashes Ajax" on my blog for further Swing reading :)

Refactoring Translations

Posted by evanx on May 26, 2006 at 05:20 AM | Permalink | Comments (3)


Introduction.
"There is no problem that cannot be solved by the use of high explosives."

locale.png Recently I was tasked with making an app translatable. It was a relatively small Swing app, eg. 200 classes.

That means moving strings, like exception messages, into a resource bundle. I had some fun with a phased approach, which I present here.


Moving the strings
"The best armor is staying out of gun-shot."

The first phase was moving the string literals in the code into a "message class" as below.

public class IMessage {
    public static String systemErrorLogin = "~ logging in";
    public static String systemBusyCommunicatingWithServer = "Communicating with the server...";
    public static String systemErrorOccurred = "An error has occurred";
    public static String systemErrorOccuredFormatTilde = "An error has occurred while %s";
    public static String systemSendOpLogoffReq = "~ sending logoff message";
    public static String systemUpdateError = "~ updating application";
    public static char systemLoginMnemonic = 'L';
    public static String[] periodOptions = {"Today", "Yesterday", ...};
    ...
}

Note that we allow different types in the message class, ie. char for mnemonics, and string arrays for combo boxes.

(Incidently, in the above example, we use a notation where a tilde at the beginning of exception messages is substituted with "An error has occurred while..." It's lazy, and that's what I really dig about it, man!

The application code becomes "stringless" as follows.

   public void run() {
      try {
         login();
      } catch (Exception e) {
         e.printStackTrace();
         gui.showExceptionDialog(e, IMessage.systemErrorLogin);
      }
   }

jabber_protocol.png An advantage of this approach, is that the "keys" we are choosing are refactorable field names, eg. systemErrorLogin, rather than string references. Once all strings have been refactored out into this message class, we can review the keys for naming consistency, spelling, etcetera. Renaming them is safe and easy, eg. using Netbean's refactorings.

By the way, this is in keeping with my "Bean Curd" blog, where I argue that "string references" (eg. resource bundle keys in this case) hinder refactoring, and so we should aim for applications with "no string references attached."


Generating the resource bundle
"The best tank terrain is that without anti-tank weapons."

The second phase is to generate the resource bundle from this message class. We use the field name as the key, and use reflection to generate the resource bundle as follows.

   public void generateResourceBundleContent() throws Exception {
      IMessage messages = new IMessage();
      for (Field field : IMessage.class.getFields()) {
         // iterate through all the fields in the message class 
         String key = field.getName();
         Object value = field.get(messages);
         if (field.getType() == String.class) {
            // output a regular string message  
            logger.println(key + " = " + value);
         } else if (field.getType() == String[].class) {
            // output a string array, eg. combo box items 
            String[] array = (String[]) value;
            int index = 0;
            for (String string : array) {
               logger.println(key + index + " = " + array[index]);
               index++;
            }
         } else if (field.getType() == char.class) {
            // output a char, eg. a mnemonic 
            logger.println(key + " = " + value);
         }
      }
   }   

In the above method, we generate the content to be cut and pasted into our resource bundle file eg. myapp_en.properties. Note that we handle string arrays by appending an index digit to the key.


Loading the resource bundle
"Samuel Morse must have lost his mind if he believes in this Dots and Dashes idea himself!" A Government Official (1842).

kdmconfig.png Now we can translate the resource bundle file into other languages, eg. myapp_de.properties for German. When our application starts up, we need to load the messages for the current locale's resource bundle. Firstly, to simplify the processing, we find it convenient to read the resource bundle into a Map, as follows.

    public static final Map<String, String> resourceMap = new HashMap();

    public void loadMessages() throws Exception {
       for (Enumeration<String> it = resourceBundle.getKeys(); it.hasMoreElements();) {
          String key = it.nextElement();
          String value = resourceBundle.getString(key);
          resourceMap.put(key, value);
        }
        logger.exiting(resourceMap.size());
    }

So now we gonna load the resource bundle messages into our messages class (which is otherwise still initialised to the original English strings). We use reflection, as follows.

   public void configureMessages() throws Exception {
      IMessage messages = new IMessage();
      for (Field field : IMessage.class.getFields()) {
         // iterate through all the fields in the message class          
         String key = field.getName();
         Object defaultValue = field.get(messages);
         Object resourceValue = null;
         if (field.getType() == String.class) {
            // we are looking for a regular string message
            resourceValue = resourceMap.get(key);
            if (resourceValue == null) { 
               throw new IRuntimeException(field);
            }
            field.set(messages, resourceValue);
         } else if (field.getType() == String[].class) {
            // we are looking for an array of strings in this case
            String[] defaultArray = (String[]) defaultValue; 
            List<String> stringList = new ArrayList();
            for (int index = 0;; index++) {
               String string = resourceMap.get(key + index);
               if (string == null) break;
               stringList.add(string);
            }
            if (stringList.size() != defaultArray.length) {
              throw new IRuntimeException(field); 
            }
            resourceValue = stringList.toArray(new String[stringList.size()]);
            field.set(messages, resourceValue);
         } else if (field.getType() == char.class) {
            // we are looking for a char resource
            String resourceString = resourceMap.get(key);
            if (resourceString == null || resourceString.length() != 1) {
               throw new IRuntimeException(field);
            }  
            resourceValue = resourceString.charAt(0);
            field.set(messages, resourceValue);
        }
    }

Note that in the above code, if an entry is found in the resource bundle that is inconsistent with the messages class, eg. an unrecognised key, or different length string array, then an exception is thrown. This should be performed as a unit test. Anyway we will know as soon as we run the application if our resource bundle is not as it should be (via an exception).


Testing
"Airplanes suffer from so many technical faults that it is only a matter of time before any reasonable man realizes that they are useless." - Scientific American (1910)

fonts.png Resource bundles, with string literals as keys in the code, eg. getString("loginError"), are fragile. For example, a misspelt key for some obscure exception message, might only be picked up (as a "dangling" string reference) when that exception occurs. That might only happen down the line, in production.

An advantage of the message class approach, is that it enables unit testing of our resource bundles. For example, we can easily test that every one of our messages (as declared in the message class) is translated in our resource bundles, as follows.

   public void test() throws Exception {
      IMessage messages = new IMessage();
      for (Field field : IMessage.class.getFields()) {
         String key = field.getName();
         if (resourceMap.get(key) == null) {
            throw new IRuntimeException(key); 
         }
      }
   }

The above code sample is over-simplified, but hopefully illustrates the point.


Merging messages
"No flying machine will ever fly from New York to Paris." - Orville Wright.

appearance.png What may be useful, is to generate the content of a message class from an existing resource bundle (eg. one produced using Netbeans GUI designer, for labels and such), as below. Then we can cut and paste that content into our message class. In this case, we can identify name clashes, and also happily generate the resource bundle file in its entirety later (from the message class, as shown above).

   public void emitMessagesCode() throws Exception {
      for (String key : resourceMap.keySet()) {
         String value = resourceMap.get(key);
         logger.println("public static String " + key + " = \"" + value + "\";");
      }
   }


To Bundle, or not to bundle?
"We are not retreating - we are advancing in another direction." - Gen. Douglas MacArthur

yast_babelfish.png Another option is to translate messages in code as below. The advantage of this approach is that the keys remain refactorable. And then translators can use Netbeans, and commit directly to the source code CVS, yay!

   public void installGerman() {
      IMessage.systemError = "Eine Störung trat auf";
      ...
   }


Conclusion
"I have a catapult. You will agree to my terms, or I will fling an enormous rock at your city." - Latin literature.

mozillacrystal.png We introduce a phased approach for translating an application. First, we move strings into a message class. This is achieved by cutting and pasting strings out of application classes into the message class. (Additionally, an existing resource bundle, as produced by the Netbeans GUI designer for example, might be merged into the messages class, with the assistance of some trivial code generation.)

This first phase enables us to ensure neat and consistent naming of the keys we use to reference messages. For example, we can readily rename the message keys using IDE refactorings, to correct spelling mistakes and inconsistent naming conventions.

The next phase is to generate the master resource bundle from the message class. We use reflection on the message class to generate the key/value pairs, which we cut and paste into the master resource bundle file. After this stage, the resource bundle can be translated into multiple languages.

At startup, the application loads the resource bundle for the current locale, and uses reflection to configure the message class from the resource bundle. This offers a mechanism to ensure that the resource bundle is consistent, ie. there are no strings that remain untranslated.

In general, I argue that source code should contain no string literals whatsoever! The reason for this is that string literals are typically fragile references, which are not refactorable. This applies to strings that refer to field or method names as discussed in my earlier blog "Explicit Reflection", and string references to properties, as discussed in "Bean Curd (Chapter 1)". (Strings used in OR queries will be discussed in an up-coming blog, "Bean Curd 2: Native Query Beans.")

Clearly strings that are text messages are also undesirable, because they should be externalised for translation (in resource bundles).

And finally string references to externalised messages in resource bundles, are fragile and unable to be unit tested, and consequently dangerous, eg. getString("loginError").

So I think that covers all the evil strings that we might find lurking in our code? Let's root them out and banish them forever!

My Postscript Punt for today is... "Bin Bash Java (Chapter 1)." And not "Swing trounces Ajax" as usual ;)

Bean Curd 1: Swing Turntables

Posted by evanx on May 25, 2006 at 05:07 AM | Permalink | Comments (0)


Introduction

Scott Violet's blog "Ease of Swing Development - Beans Binding" introduces JSR 295, which aims to provide a standardised API for binding GUI components to beans, yay!

In an earlier blog, I wished for "Explicit Reflection" of fields and methods, for "stringless referencing." I should amend that to include properties, in addition to fields and methods, for the purposes of bean binding.

We glean from Graham Hamilton's JavaOne 2006 slides, that "lightweight references" of methods (for GUI event handlers), and hopefully also of properties (for "bean binding"), are in the works for Dolphin, which is expected out in 2008.

handcuffs.jpg In the meantime, I've been using an approach to reference and bind properties, which I present here. I have applied this to object-relational mapping, forms (Swing and HTML), and tables (Swing, HTML and PDF). It works nicely for me. Probably because of my personal "no string references attached" rule. "Woohoo, look me!" ;)

I'll present a Swing JTable example here. In a follow-up blog, I'll show the same approach used for ORM with "native queries" - now that is cool, so look out for that in a week or two. I've been wanting to write about that six months, and now the time has come, I'm so excited!


Problem

Consider that we are building a CRUD application for a database of users. You know, CURD, as in... create, um, update, delete, you know, CUD. What is the R for? Google knows.

So anyway, these users are cows that need to login to our new MMORP, "World of Animal Farm Revisited: Cult of the Raging Bulls."

So we gonna have a CowBean class, with properties realName, username, password, et cetera.

public class CowBean {
   protected String realName;
   protected String username;
   protected String password;
   protected int earId;

   public void setRealName(String realName) {
      this.realName = realName;
   }

   ... // other getters and setters
}

Say we wanna build a GUI form for editing a cow. For when customers phone our call center saying, "Update my real name on your system, cow!" "Who you calling a cow? I aint nobody's cow!"

attach.png So we would want to bind text fields to properties, eg. CowForm.realNameField would be bound to CowBean.realName. Then when the text field is edited on the screen, we invoke setRealName() with the value from the text field, ie. realNameField.getText(). We might code it explicitly as follows.

   public void actionPerformed(ActionEvent event) {
      if (event.getSource() == realNameField) {
         cowBean.setRealName(realNameField.getText());
      }
   }

In order to do this binding in a framework, we would refer to the realName property using a string ie. "realName", maybe as follows.

      GBeanBinding cowBeanBinding = new GBeanBinding(CowBean.class); 
      cowBeanBinding.bind(realNameField, "text", "realName");

Now we get to the rub...


String references

There are a couple of problems with using string literals as references to properties (and fields and methods).

The most serious problem is that they make refactoring fragile. For example, consider that we wish to rename the realName property to just name. The problem is that our IDE doesn't know to change the string reference "realName" as well.

Cows3.jpg So we can, and probably will, overlook renaming the string reference, since there is no prompting by the IDE or compiler to tell us to do so. This will definitely result in a runtime error. "That's gonna hurt in the morning!" Ok, this problem should be solved using unit tests to make sure that fragile string references are valid.

Hey, I'm lazy. So i don't like string references, because they do not take advantage of the IDE's prompting, auto-completion, and error highlighting. In this sense, they are not readily "toolable."

Like when we rename the realName property to name, "realName" string references are not underlined with a swigly red line. "Where are you, you silly error!? Don't you make me come down there to find you!"

And worse, we can't type "use" and press Control-Space to have that completed to "username" for us by the IDE. We have to actually type the whole thing out. "Thaaat's mentil! I'm a developer, not a typewriter!"

So the two related problems with string references are that they are "fragile" and not readily "toolable."


Explicit bean info

A possible future solution to this problem is to introduce a convention into the language for the explicit referencing of properties.

For example, imagine a notation where CowBean:realName is a reference to the "realName" PropertyDescriptor. This might be compiled into bytecode as Introspector.getBeanInfo(CowBean.class).getPropertyDescriptor("realName") for all I care.

The important thing is that it is recognised by the IDE for refactoring, prompting, completion and error-detection.

But back to the real world of today...

In the absence of such a language feature, we could define references to our properties in explicit bean info classes as follows (where our GBeanInfo superclass implements BeanInfo).

public class CowBeanInfo extends GBeanInfo<CowBean> {
   public final GPropertyDescriptor realName = createPropertyDescriptor();
   public final GPropertyDescriptor username = createPropertyDescriptor();
   public final GPropertyDescriptor password = createPropertyDescriptor();
   ... // references to other properties
}

In the GBeanInfo superclass, we would configure and verify each GPropertyDescriptor property descriptor, ie. find its corresponding java.beans.PropertyDescriptor using its field name as the property name.

Hey, I guess one could easily write an Ant task to generate our explicit bean info classes. So maybe I'll try that one of these days, and write a follow-up article on it.

But anyway if CowBean is used by only one component, we find it convenient to absorb the above into that component, as we will now demonstrate. "Please stand back, cows and bulls!"


The table model

Consider that we wish to build a JTable, which will display CowBean instances. So let's build the table model for starters.

7days.png Let's say that we are using a Swing application framework that extends Swing components, eg. the framework's GTableColumn extends Swing's TableColumn. Excuse this obfuscation, but this is how I do it in aptframework, and yes, this is an insidious attempt to assimilate you. That is, assuming you are not a cow already. Do you like grass? That's always a give-away.

So our CowTableModel might be coded as follows.

public class CowTableModel extends GTableModel<CowBean> {
   
   @TableColumnProperties(width = 150, label = "Mofo's real name")
   GTableColumn realNameColumn = createTableColumn();

   @TableColumnProperties(width = 100, label = "L33t cow handle")
   GTableColumn usernameColumn = createTableColumn();

   ... // other column declarations 

   public CowTableModel() {
      super();
      super.configure();
   }
}

As you can see, we use an annotation to configure some default GUI properties in the code. Just to be different.

Incidently, notice that the above table model implementation exposes the column components explicitly, in keeping with the "no string references attached" rule, for later when we want to play nicely with them in our IDE. That is, we can manipulate columns by referencing them explicitly, eg. passwordColumn.setRenderer(). "Carry on, soldier!"

Now there are a few things we need to accomplish. Firstly we must bind realNameColumn to the realName property of CowBean.

button_cancel.png For the reasons given earlier, we want to avoid using a string literal reference, eg. realNameColumn.setBindingPropertyName("realName").

As you might guess, that configure() method lurking in the constructor comes into play...


The table model superclass

So let's look at the implementation of the framework's GTableModel superclass, in particular this configure() method.

public class GTableModel<Bean> extends AbstractTableModel {
   protected GBeanInfo beanInfo;
   protected Class beanClass; // eg. CowBean.class
   protected List<Bean> beanList = new ArrayList();
   protected List<GTableColumn> columnList = new ArrayList();

   ...

   public void configure() {
      for (Field field : getClass().getFields()) {
         if (field.getType() == GTableColumn.class) {
            field.setAccessible(true);
            GTableColumn column = (GTableColumn) field.get(this);
            column.configure(field);
         }
      }
   }   

   ... // some methods required by AbstractTableModel as below 

   public int getColumnCount() {
      return columnList.size();
   }

   public String getColumnName(int columnIndex) {
      return columnList.get(columnIndex).getLabel();
   }   
}

search.png Please excuse liberties taken in my code samples for the sake of brevity, eg. omitting reflection exception handling in the above example. I'm actually having to type this stuff by hand you know!

So the configure() method uses reflection to invoke column.configure(field) on the columns declared in the table model.


The table column

So let's check out the GTableColumn implementation, in particular its configure(field) method.

public class GTableColumn<Value> extends TableColumn {
   protected GPropertyDescriptor propertyDescriptor;
   ...
   public void configure(Field field) {
      String propertyName = field.getName(); // eg. "realNameColumn"
      propertyName = removeEndsWith(propertyName, "Column"); // eg. to "realName"
      setBindingPropertyName(propertyName);
      TableColumnProperties properties = field.getAnnotation(TableColumnProperties.class);
      if (properties != null) configure(properties);
      ... // configure from resource bundle, using propertyName
      ... // configure from preferences, using propertyName
   }
   ...
}

So the configure(field) method sets the default "binding property name" for binding this component to a property in the bean, eg. CowBean.realName. It uses the component's field name as a clue, eg. from "realNameColumn" we cunningly deduce that the default binding property name might be "realName". "Dr Watson, do you concur?"

cowToy1.jpg Also, the column can configure itself using annotations. In our table model code sample above, remember we specified a default width and label for the column using a TableColumnProperties annotation. Hopefully we are on the same page here. Because this article has only one page, so...

At this stage, we would also want to configure our column via resource bundles and user preferences. For example, our default column label might be translated in a resource bundle.

We might want to set the width of the column to the user's preference from last time the user ran the application. That is, when they use the mouse to change the column width, we might wanna remember that for next time using the Preferences API.

So we configure the column from externalised properties and preferences, using the field name as the key. In the case of resource bundles, this is fragile, since if you rename the column field, then you need to remember to rename the key in the resource bundles as well. Darn, we just can't win!


Unit testing the fragile bits

This brings us to unit tests we should implement. "I'll give you two to start with, and if you need more, then lemme know. Globalisation, Streamlining..." (From the "The Office" heh heh)

First we check that our binding is valid. For example, if we have a realNameColumn component bound to CowBean.class with a binding property name of "realName" then, upon reflection, we should find a CowBean.realName property.

The thing is that when we rename a property in the bean, we have to remember to rename components that will be bound to that property eg. realNameColumn. And we will forget, so we need unit tests to remind us, because the IDE and the compiler aint gonna.

whatsnext.png Also we need to remember to rename keys in our resource bundles. So we should have unit tests that confirm that all our labels are translated in all our resource bundles. "With all those things out there? Well you can count me out!" (Bill Paxton's character in the movie "Aliens" heh heh)

For example, since we have a realNameColumn component, our resource bundles should contain a key like "CowTableModel.realNameColumn.label".

This is fragile because as soon as we rename components, all our resource bundles break. "Dammit to hell, Leeroy, why'd you rush in there like that and rename things?!"


Conclusion

We present an approach for binding GUI components to data beans, without using string references.

cowSuit.jpg This is achieved using reflection on the component names, and assuming that by default, their names match the binding property names in the backing bean, eg. realNameColumn and realNameField are bound to realName.

However, when we rename components, or properties, we need to take care to rename them in multiple places, including in the resource bundles.

That is the price we pay, if we wish not to use string literal references, as a rule. Those are similarly fragile, and not readily toolable.

We hope that Dolphin will make things better via property references. In the meantime, if you can think of a better way, or some tweaks, please comment.

I look forward to continuing this series, in particular, applying a similar approach to object-relational mapping, to enable native queries. So look out for that article which is coming up.

Postscript. I miss writing tongue-in-cheek articles. You know, trying to get all riled up, emotions flying off the handle, and old gripes bursting out of my chest like a scifi movie. Goddamn, this technical article is so dry and boring! Sorry about that. Hey I'll spice it up before I publish it. And the next one too. Hey that'll be easy, it'll be about persistence so I can slag off Hibernate and slaughter some other sacred cows while I'm at it, so check it out, cow! :)

In the meantime if you are looking for some heavy reading, then "Swing trashes Ajax" was made for you!

Bin Bash Java (Chapter 1)

Posted by evanx on May 24, 2006 at 01:37 AM | Permalink | Comments (4)

penknife3.jpg Introduction

In the preceding blogs "Java is all you'll ever need" and "A Fool's Errand" I alluded to using Java for "small tasks" eg. file/system tasks, rather than shell scripts. I promised to present some examples along these lines.

This is Chapter 1 of many, and presents a basic design. We'll thrash it out in subsequent chapters.


Motivation

My motivation for trying to move away from shell scripting (or using some other scripting language like python or groovy), in favour of writing "tasklets" in Java, are as follows.

  • My personal familiarity and productivity with my favourite Java and Netbeans environment.
  • The unlimited power of available Java libraries and tools. "Yeah baby, bring it on!"
  • Using Netbeans' convenient CVS/Subversion to manage and version tasklets.
  • Using Netbeans to "keep it clean" eg. revising, commenting (javadocs) and refactoring tasklets.
  • Moving towards a portable, cross-platform solution for file and system tasks (compared to shell scripts).


Problem

A typical task is backing up our computer, so let's consider this one of our first goals. OK, this could take a while. Hopefully we'll achieve this goal in subsequent chapters. Doing a good design here would be a great start.

linux-av.png We want our tasklets to be cross-platform. In particular, we want to use our "backup tasklet" to backup our Linux PC, Windows laptop, and MacOSX media center. So let's keep that in mind.

Of course our "framework" should be reusable for other tasks, besides the backup one. And very convenient to use. So that some day soon, we can write all our file and system tasks in Java, and never look back, woohoo!


Design Overview

Let's create some helper classes, eg. TFileHelper for handling files, and TProcessHelper for handling processes. And we will create a TTaskContext which exposes our "library" to our tasklets, and/or can be used as a superclass by our tasklet classes.

Note that I've choosen the letter T to prefix our "tasking" project classes. This avoids any potential namespace conflicts, and makes it clear which classes are our new ones.

To give us some flexibility, let's create our own classes to represent files and processes, namely TFile and TProcess. We will extend those eg. TDir and TZipFile from TFile.

folder1_man.png
Files

First let's design our file helper class. (Note that I'm using abstract declarations to highlight the interface at this stage, rather than the implementation.)

public class TFileHelper {
   public boolean exists(TFile file);
   public boolean isDirectory(TFile file);
   public void remove(TFile file);
   public void removeIfExists(TFile file);
   public void removeDirectory(TDir directory);
   public void moveToTrash(TFile file);
   public void moveToTrashDirectory(TDir directory);
   public TDir createDirectory(TDir directory);
   public void copy(TFile file, TFile destination);
   public void copyOverwrite(TFile file, TFile destination);
   public List<TFile> listDirectory(TDir directory);
   public List<TFile> listDirectoryRecursively(TDir directory);
   public List<TFile> findRecursively(TDir directory, String pattern);

   public TZipFile zipRecursively(TFile ... paths);
   public TZipFile openZipFile(TZipFile zipFile);

   public String getDigestString(TFile file);
   public boolean equalsDigest(TFile file, TFile otherFile);
}

That's just for starters. We can expect this class to grow a lot!

yast_zip.png The methods related to zip files might be refactored out later eg. into TArchiveFileHelper, and the digest methods to TDigestHelper, but let's not overdesign this thing quite yet. Later when we wanna support different archives, eg. tar et al, and different digests, eg. MD5 vs CRC32, then we might create generic interfaces for archives and digests, for different implementations. But not today. Rome wasn't built in a day, innit.

WindowsTasks.png We might have these methods throw wrapped runtime exceptions, eg. TIOException, which is a RuntimeException wrapping IOException. So that's why the methods above are not throwing checked exceptions, in case you were wondering.


Processes

Next let's design our process helper class.

public class TProcessHelper {
   public void kill(TProcess ... processes);
   public boolean killAll(TProcess process);
   public TFile pipe(TProcess ... processes);
   public TFile exec(TProcess process, Object ... args);
   public List<TProcess> getProcessList(TProcess ... processes);

   public TProcess fileWriter(TFile outputFile);
   public TProcess fileReader(TFile inputFile);
   public TProcess gzipProcess(TFile gzipFile);
   public TProcess tarProcess(TFile tarFile);
   public TProcess xargProcess(TProcess execProcess);
   public TProcess findProcess(TProcess execProcess);
}

The file reader and writer processes above would be used for redirection, eg. the file reader as the first argument in the pipe(), and/or the file writer as the last, as follows.

   processHelper.pipe(findProcess, grepProcess,
      xargProcess, gzipProcess, fileWriter);  

As you can see, we are loving Java5 varargs :)


Context

For convenience, let's introduce a superclass for our tasklets.

public class TTaskContext {
   public TFileHelper fileHelper = new TFileHelper(); 
   public TProcessHelper processHelper = new TProcessHelper(); 
   public TCalendarHelper calendarHelper = new TCalendarHelper(); 

   public TDir toDir(String dirName);
   public TFile toFile(String fileName);
   public TProcess toProcess(String processName);
}

Let's slide this baby into gear and start putting rubber to road.


Example Implementation

So let's imagine what our first backup tasklet implementation might look like, eg. for zipping up our home directory on Linux.

public class BackupTask extends TTaskContext {

   @TArgument TDir targetDir = toDir("/home/evan");
   @TArgument TDir archiveDir = toDir("/backups/evan"); 
   @TArgument String zipBaseFileName = "evan"; 

   public void run() {
      String timestamp = calendarHelper.getNumericTimestamp();
      TZipFile zipFile = fileHelper.zipRecursively(targetDir);
      zipFile.setOutputDirectory(archiveDir);
      zipFile.saveAs(zipBaseFileName + timestamp);
   }
}

Ok, so if we wanna write tasklets like the above, we still gotta bit of work to do. It's gonna rock tho, innit.

So you spotted those annotations. The idea is that our framework could prompt for those eg. if not specified on the command-line...


Future plans

So we gonna use annotations to tell the framework what it needs to know to provide a "tasklet container" with a management console in future maybe. Now we're talking!

I'm getting way ahead of myself here, but let's dream on...

This "management console" could have a Swing interface, and/or a web interface.

yast_remote.png A Swing interface could support a file chooser, which would be very handy, and would be quick to throw together using Netbeans/Mattise. Hey, it could even let us invoke remote tasklets, on lists of multiple machines... Hang on, this could grow up to be a network management system! "Heel, boy!"

On the other hand, a web interface would be quick and easy for administering remote boxen, eg. backing up our media center in the lounge using our laptop. Even on a local machine, having a web-based console for tasks would be handy.

You know what, I just decided we gonna embed a web server into our tasklet framework. So that our tasklets are always remoteable, and deployable via a single self-contained jar... OK, I better stop, I'm getting too excited here!


Conclusion

TProcessHelper2.png This concludes "Chapter 1." We made a start which is the most important thing. We came up with a design. Hopefully it'll hold up. We'll find out soon enough.

Like when you are reversing your car towards a wall, you find out very quickly when you've gone too far. Rather that than driving in the wrong direction for hours on end, and not knowing it.

Let's hope for accidents of the former "loud and clear" type, rather than misdirections of the latter, later type.

In the next chapter, we might refine this design before we kick off, so please post comments to that effect.

Then we can start rolling up our sleeves to implement some of the file IO functionality. Uh oh, sounds like work. So we'll google and cut and paste. We are developers after all, with a limited amount of time, so the less code we actually write the better, innit ;)

Update. Check out NailGun which aims to obviate the start time of the JVM, eg. for Java command-line tasks. They say, "Java has an extensive and robust core API and huge number of available open source libraries. It's a great big hammer, making almost any programming project look like a nail." :)

Postscript. Here's punting my blog "Swing thumps Ajax" again. Check it out, dog.

A Fool's Errand

Posted by evanx on May 19, 2006 at 02:44 AM | Permalink | Comments (5)

A reader commented to my blog "Java is all you'll ever need" that "anyone thinking he needs only a single tool to do any job is a fool." That would be me. So lemme introduce you to this fool's errand...

penknife.jpg Of course we need more than one tool. But it depends what you define as a "tool." Every library is arguably a different tool. So then a programming language is a tool to write tools (libraries) to help with some specific tasks. If you prefer one programming language (eg. Java) and development environment (eg. Netbeans) over all others (eg. bash, python, perl, vi) then that is your sharpest tool for writing tools.

I have my favourite Java Leatherman with me, and so I gonna leave my Python Army Knife at home.

Bash is written in C. Clearly it could be rewritten in Java. But let's not do that, let's write a better bash. And rather than a scripting language, let's just write a framework and library.

Even if not as concise as a purpose-built scripting language, our approach will have the advantage of being able to leverage tools like Netbeans and Eclipse right off the bat to write our "scripts." And that is no small thing in my book.

My main argument against bash, python, perl, C et al, is very simple and personal. Just that they are not my favourite language, programmable using my favourite IDE. That is, they are not my personal favourite programming tool.

When I was at school in the 1980s, my first big pet project was writing a 4GL in Basic. A scripting language of sorts. I spent years on that thing. I was emulating my father because he wrote 4GL language tools.

When i discovered C, everything changed. Now rather than write a new scripting language, you would just write a new framework and library, because C let you do that. And Java has made that truer than it ever was with C.

nail-hammer.jpg It's true that shell scripts (and scripting languages in general) are gonna be much more concise right off the bat for scripting system tasks. Bash is specially designed for that purpose.

But Java (like any language) let's us write a library to support writing tasks, so that we can approach the brevity of shell scripts, without the downsides of shell programming. And with a big upside if Java/Netbeans is your favourite hammer and so you want everything to start looking like a nail.

So watch out for my blog in the next few days titled "Bin Bash Java (Chapter 1)." As you can surmise, it's the first in a series. It's the beginning of a journey, to create a library so that I can say goodbye to my bash scripts and do some serious "scripting" on Windows too. I hope to meet you along the way!

Update. This blog continues over at "Bin Bash Java (Chapter 1)". Also Check out NailGun which aims to obviate the start time of the JVM, eg. for Java command-line tasks. They say, "Java has an extensive and robust core API and huge number of available open source libraries. It's a great big hammer, making almost any programming project look like a nail."

CDDL'ing up with Sun

Posted by evanx on May 18, 2006 at 07:52 AM | Permalink | Comments (6)

Now that Sun's big boys are trying to decide "how" to opensource Java (with the "why" worked out by Jonathan and Rich a long time ago I'm sure), it's time for the little boys like me to get excited about opensource licenses and what they mean.

By the way, I suggested in an earlier blog "Swing trounces Ajax" that Sun will opensource Java to counter competitors (Microsoft, IBM and Red Hat) and their competitive offerings (C#, Harmony and GCJ) because "Sun wants developers to be field of Sunflowers following the Sun." I liked that quote, so I'm repeating it, and I liked that blog so that's why I keep punting it. What I do change with each punt is the title, eg. Swing trumps, thumps, trounces... and who knows what'll be next? ;)

In my previous blog "Duke in a Tux" I mused about Java being opensourced as GPL, as opposed to Apache. This was a silly suggestion because then all Java programs would have to be GPL'ed too! (I suppose the JVM could be GPL'ed and the class libraries LGPL'ed but lemme put that bone aside.)

Jim Driscoll commented that OpenSolaris and GlassFish are both under CDDL. Since I didn't know what CDDL is (besides being an approved opensource license) I decided to do some reading... (Isn't Google great?)

Scales1.jpg So Google took me straight to Jim's "What is this CDDL thing, anyway?" (June 2005) which is perfectly terse with important links namely Sun's executive summary on "Why the CDDL", Simon Phipps' "Failed as in succeeded wildly" (April 2005) and Claire Giordano's "CDDL - Is it so bad, then, to be misunderstood?" (April 2005) which was also a great read on the CDDL.

So the story is that Sun liked the Mozilla Public License (MPL), but it needed some bugfixes, and so that's what the CDDL is - a refreshed MPL after a day at the health spa.

CDDL aims to reduce the proliferation of opensource licenses by fixing up the MPL license for everyone. I get the impression that this was an honest and noble effort on Sun's part. Hey, it now has the full support of this little fish in the sea of developers - hold the presses!

OpenSolaris prompted the creation of the CDDL. Sun has subsequently reused the CDDL for GlassFish. Maybe Mustang will be next? So what is this CDDL, and how does it differ from the GPL and ASL (the Apache Software License)?

Claire explains that the GPL and the BSD-family of licenses (which includes Apache's ASL) where considered for OpenSolaris. GPL was too viral, in that derived works also have to be GPL'ed. So it's not company-friendly in that respect. We know that the GPL is developer-friendly license. Hey I need one of those RMS collars, that processes your speech, and everytime you say "Linux" instead of "GNU/Linux" it gives you an electric shock ;)

The BSD-family of licenses (of which the ASL is the state-of-the-art, Simon Phipps says) is great for companies, since it says "Use this software as you wish, baby, just don't sue me." Not in those exact words of course. I guess they got some lawyers in, and they worked it out.

After the GPL and BSD-family, the third distinct class of license is the MPL (of which the CDDL is the state-of-the-art, Simon says).

The MPL neatly defines "modifications" and "derived works" in terms of files, eg. C source files. Changes to existing files are modifications, and new files that you add to the source tree, are "derived works." I like this because it's clear what's what, innit.

Bazaar3.jpg Now to grasp the difference between GPL, BSD and MPL in terms I can understand, let's use Simon's great definition that "an open source software project is a software source-code commons."

In this case, (re)licensing your software means you are placing it into a "public commons" where everyone can play nicely with it. According to the license of that particular commons of course. And here's the difference...

In the BSD commons, you don't have to contribute your modifications or your derived works back into the commons.

In the MPL commons, you have to contribute only your modifications back into the commons.

In the GPL commons, you have to contribute both your modifications and your derived works back into the commons.

So it's clear that the MPL is some happy medium between the ASL and GPL licenses. I can see why it's important to have a state-of-the-art bug-free MPL. Enter the CDDL. "I'll take one of those happy meals with the free Mozilla CDDLy toy - hold the BSD fries and GNU cola please!"

Here is Simon Phipps' definition of opensource (April 2005).

An open source software project is a software source-code commons maintained by a creative community, which uses the content of the commons to create richness and innovation, with and around the commons. In the process of that creativity, the community enriches the commons for the benefit of all and may be compensated by the recipients of the creative act.

He goes on to say of the CDDL that

the commons is continually enriched... That's GPL-style (copyleft) for your work on the commons and BSD-style for your own creations

So the CDDL is copy-left for modifications (like the GPL), and "free use" for derived works (like the BSD/ASL). GPL is the most restrictive license, and BSD the least, with the CDDL sitting inbetween. The CDDL is less restrictive than GPL (it allows "free use" of derived works) but is more restrictive than BSD/ASL licenses (it is copyleft with respect to modifications).

blueScales.jpg Someone wrote in a comment to Jim's blog on how Sun manages their CDDL projects. Now this is nothing to do with the CDDL - just how Sun happens to manage these projects, which happen to be under the CDDL. He says that contributors should grant a joint copyright to Sun. Then Sun can relicense those contributions as they wish. So those modifications stay in the common, but Sun (as well as the contributor of course) have the right to use those modifications outside of the commons. For example they could relicense them into a different commons, or use them in a closed product. Without detracting from the rights of contributors, it should be said.

As Jim points out, many groups require this joint-copyrighting, including Apache. And including the FSF, just joint-copylefting in their case of course ;) Apache have their Contributor Agreements, after which Sun modelled their SCA (Sun Contributor Agreement). SCO taught the world the need for these, innit.

The GPL is great for developers that don't want some other company building a closed commercial product using their work. But what if later you wanna relicense your GPL project, eg. under ASL or CDDL, and/or build a commercial system using your project? Maybe for your new best client that is gonna help you pay off your house, by letting you work on your project all week instead of all weekend.

The problem is that if you've accepted contributions, you can't. Because you don't hold copyright over all those little patches - here, there and everywhere - submitted by a whole bunch of handy helpers.

Justice3.jpg So I think joint-copyright, via a Contributor's Agreement, is desirable. Actually what I had in mind for my little GPL'ed project is to require that any contributions are dual-licensed under GPL and Apache. That means I can switch the whole project to an Apache license in future, and also I'm always free to build a closed commercial product on top of my GPL project, incorporating any contributions by outside parties. Not because I want to take advantage of outside contributions per se, but so that I can accept outside contributions without limiting my future options. The "problem" is that no one else is allowed to build a closed commercial project, because it is GPL'ed. That is a bug and a feature of the GPL.

This does make the point that the license a project is released under, is not the whole part of the story. The terms and conditions of acceptance of contributions is another chapter. I guess in the extreme case, no contributions are accepted, ie. such a project would have to be forked in order to make any "contribution" to the project.

Another case is the requirement that contributions are multiply-licensed, ie. contributed under a different license in addition to the original license. In my example, a project is released under the GPL (the most restrictive license), but contributions to the master source tree are only accepted if made under the ASL (the least restrictive license).

Finally, a common case seems to be the requirement of a Contributor's Agreement which grants joint-copyright. This is an attractive solution for the project originators, since they retain the freedom to relicense their software into another commons, and/or use the software (which is predominantly their own) in a closed product. That is, keep their options open for the future. Also, as the FSF says, it it makes it easier to defend against infringements in court, if you can argue as the single copyright holder (and not one of hundreds).

Traditionally one would assume that contributions would be accepted under the same license as the source is released under, without any further conditions. The point is that there is no guarantee of this, or that contributions would be accepted at all - clearly patches can be, and are, rejected, eg. in the case of the Linux kernel.

Opensource promotes common development, where the idea is to engender a community of contributors. But maybe in some cases it's more about distribution. And in others, it's a marketing gimmick. And in most cases, it's some combination of all of the above!



Duke in a Tux

Posted by evanx on May 17, 2006 at 03:07 AM | Permalink | Comments (13)

As we know, Sun is relicensing JDK for inclusion in Linux distros. This is a fantastic development for Java. In this blog, I muse about GPL'ing Java as "GlassFishBowl" and rigorously protect the Java trademark, so that Java still means "Java" as in the JCP, TCK, JEE, et cetera. I posted a comment to this effect on Calvin's article "Java on Linux - the real story".

By the way, I rattled on a bit about opensourcing Java in my earlier blog "Swing thumps Ajax" together with too many other issues.

Netbeans.png Hopefully we'll see more and more opensource Java apps shipping with Linux distros once they include Sun's JRE.

This makes it a real option for opensource developers to consider Java for their desktop apps, knowing that they can get shipped in Linux distros, alongside Sun's Mustang - which is such a great JRE for Java and Swing on Linux.

I would like to see Netbeans shipping in Linux distros too! Since Netbeans provides a great out-the-box experience, for both GUI and web apps (and mobile apps), this would encourage young developers to give Java/Swing/Netbeans a twirl, on their freshly squeezed Ubuntu boxen.

As Calvin quoted on his blog, Sun's lawyers and Debian Ubuntu developers spent months thrashing out a new "binary license for the Java platform."

The distribution of the Java platform, in particular the JRE, is clearly very important for Sun to get right, and I'm sure they have done so. Probably this is just a first step, on the Duke's opensource march. As Rich Green said at JavaOne about opensourcing Java, "It's not a question of whether, it's a question of how."

So now I'm gonna muse about a possible next step...

GlassFishBowl.jpg What if Sun GPL'ed mustang.dev.java.net as "GlassFishBowl" but rigorously protected their "Java" trademark, to ensure compatibility of a single Java platform, controlled and evolved by Sun and the JCP. So "Java" becomes a standard (and not an implementation), with "GlassFishBowl" as its opensource reference implementation. Sound familiar?

Hey Linux and OpenSolaris are GPL. KDE and GNOME are GPL. Because GPL is the most developer-friendly license. And in the case of Java, the developer is Sun. Apache is BSD-style, and is the most company-friendly, and hence all the support they get from the "biggest" company of them all, IBM.

Sun don't want to be too friendly to their competition, like IBM and Red Hat. So they don't want an Apache license. So I think the GPL would be best. Or a simple variant of it, eg. the GPL plus a clause or two agreeing to protect the "Java" trademark with our lives.

Let's try to imagine how this would unfold. The competitors that Sun worries about the most are IBM/Apache, and Red Hat. How would IBM and Red Hat react to the above scenario? Would Apache fork GlassFishBowl into TomcatFishingBowl? Well all Apache products are Apache-licensed, so forking a GPL project into another GPL project doesn't make sense for Apache.

Let's say IBM or Red Hat wanted to take Java off into their own direction. (That would be crazy, but let's pretend they got crazy.) They could fork it, and call it SeaShark. But it wouldn't be Java. It would have to compete with C# and Java. Hey, it might introduce some great features that the JCP community includes into a JSR for Java, and that might make it into Java and the GlassFishBowl reference implementation. So Java wins there.

Maybe researchers and students would want to fork GlassFishBowl into PlexiFishBowl to try out some wierd and wonderful tangential things. But surely Red Hat et al, would rather choose to distribute GlassFishBowl, so that they can license the official Java branding (with its promise of compatibility) for their distribution? And the licensing royalties for "Java" would still come flowing in for Sun, wouldn't they?

Update. If Java was gonna be GPL'ed, I guess the class libraries would have to be LGPL'ed, to allow non-GPL'ed Java programs to link to them? Anyway, see my follow-on blog "CDDLing up with Sun" which considers the CDDL as a better bet for Java than the GPL/LGPL.

kubuntu3.png As a postscript, I'm thrilled that Ubuntu's Mark Shuttleworth (a fellow South African countryman) has played a role in relicensing Java for Linux. Hopefully next on his list is Adobe, for Flash and Acrobat. After that, we just need a blanket license for including freely-distributable MP3, DVD and MPEG players in Linux distros, and the desktop war is won :) Oh, and those nVidia and ATi drivers, I forgot about those... And that can't happen because those drivers are specifically engineered to beat those darn spec tests, innit.

Java is all you'll ever need

Posted by evanx on May 15, 2006 at 02:16 AM | Permalink | Comments (9)

In his blog, Damien Katz wrote on "Signs you are a crappy programmer, and don't know it." At the top of the list is "Java is all you'll ever need." Since i'm guilty of that, here is my response - why i choose not to add other languages to my toolbox.

So you have some system task to do. This is where you jump into vi and hack together a bash, python or perl script to accomplish this task in quick (and dirty) way, right?

This first problem for me is that this means leaving my Netbeans environment. These days I'd rather go to the dentist for root canal than do that.

Like most people, I guess I don't like going to the dentist. It does have one advantage though, which is if you aren't enjoying your job, you can schedule the dentist in the late afternoon and leave work early. Which i did regularly one year, when i wasn't enjoying my job, and had to go to the dentist a few times for a nagging pain. For my perly whites. Yes, I was using Perl at the time, and vi. So I'd be in that dentist's chair, with the drill screeching in my ears, and thinking, "Ah, what a pleasure not to be at work right now!"

GShell.png So faced with some small task for which the sharpest drill is python, we should use that because in Java it takes "more lines" of code? No, sorry, I'm gonna take the 5th Netbeans amendment. When all you use is Netbeans, everything starts looking like being nailed.

So I choose Netbeans, to build up a library of support classes for various archetypes of tasks, eg. system tasks. This sharpens my skills in Java in different elements of the standard libraries, eg. System and Runtime. And it keeps me "thinking in Java."

I'm obsessive compulsive about code. Happily Netbeans lets me rename things, to keep my code "clean." Writing in python and C, I get an immediate dentist reaction to lazy and legacy programming conventions, like abbreviations and underscores. I've had that sore tooth removed so that pain would never come back.

Incidently, C# is shaping up well for system tasks, with the C# .Net command-line in Vista. Hopefully the Java community will get a similar thing via Groovy at some point.

In a large company, every engineer and programmer might choose their own favourite language, to automate various tasks. Very soon you end up with an unmanaged and unmanageable environment which is held together with prestuck and sticky-taped scripts, written by ex-employees, in every language you've heard of, and some you haven't.

NetbeansPrompt.png If I was a CIO, i would have everything done in one language (Java of course), everything managed and versioned in Subversion, and everything documented, ie. javadoc'ed.

And a great way to encourage employees to write overview documentation (in addition to javadocs) is to have a technical company newsletter or blog - and incentivise and reward submissions, with gift vouchers, public praise, and "get out of the dentist's chair free" cards - you know, the important things in life!

But LAMP hackers are real men and use vi innit. They don't want or need an IDE, or strict conventions. The problem is that in large teams, some programmers are veteran gurus of the system, while others are one-month newbies (which might be future veteran gurus, or otherwise working for your competition). Now those newbies want IDEs, to navigate the (javadoc'ed) code with ease, to be prompted with method names, etcetera. And you, the CIO, want to help them help you - show them the Netbeans!

Having those fantastic hackers that Paul Graham waxes lyrically about, is great, until they leave the company to go and work for Google. And leave fantastically terse, undocumented code, behind. To make their newbie replacement's life a spaghetti hell for a few months. Time for the dentist!

Another thing Damien mentions, is cutting and pasting. I think that API's, and superclasses and such, should be blindingly simple. Because it's difficult to predict how you might want to (re)use them in future. So if you build in functionality you need for your current problem, that might render that superclass unsuitable later, for a different unforeseen case. And in trying to change that superclass, or API, you tend to break older code that has been working fine.

So cutting and pasting can serve to isolate code, to make it safe from the rug being pulled out from under it later. An (over-engineered) superclass or API, that is great today, might be a sore tooth tomorrow...

Update. Check my blog "A Fool's Errand" which follows on from this, and introduces an upcoming series "Bin Bash Java" inspired by this blog entry.

My Desktop OS: Windows XP

Posted by evanx on May 11, 2006 at 10:06 AM | Permalink | Comments (4)

Linux.com has a great series "My Desktop OS..." which is usually a Linux desktop. I thought i should do a tongue-in-cheek one "My Desktop OS: Windows XP" - to highlight some of my frustrations with the Linux desktop of late - multimedia to be specific. Which boils down to me being too lazy to bother about my own freedom. I'll follow this up with another article, "My Desktop OS: FreeDOS" ;)

So after getting my hands dirty with Linux desktops over the past 8 years, I started using XP again. ("'Shock horror! Linux advocate caught using XP!' it says here in the paper.")

ICL.png Let me rewind a little bit. When I was at school, in the 1980's, I had access to computers at my father's computer company. My first desktop was an ICL 1500 mini-computer with huge removable 5Mb disks.

Actually I had lots of "exotic" computers to play with because my father's company had to port their 4GL software to all these different "micro-computers" and mini-computers, mostly from ICL (which was British for "IBM" in those days). This was in the days before the IBM PC came along and changed everything.

Before the PC arrived, my favourite "micro" computer was an CP/M multi-terminal machine. It was powered by a Zilog CPU, just like my friends' Sinclair Spectrums. I didn't have a Spectrum - I had a great museum playground of ICL computers of the twentieth century! Naturally I spent every hour I could out of school at that computer playground.

So this favourite CP/M box of mine was the size of the original PC, but it had four terminals hanging off it - fantastic! Especially when you're boss' kid playing at the office after school. There was always a terminal free, so I never cycled all the way from school to the office, only to have to wait for hours for someone to finish running their job.

Then an "IBM" arrived. An "IBM PC." Now understand, we were predominantly an "ICL" house. But we had heard of this American "startup" computer company called "IBM." Just that I personally hadn't seen a computer made by IBM (or DEC) before. South Africa was sanctioned by the US, and I think that IBM PC had to be sneaked in via the UK. (Maggie Thatcher, you did the wrong thing for the wrong reasons - you turned me into a computer geek!)

IBMPC.png So this IBM PC ran PC-DOS, and had a 360k floppy, and a 10Mb hard drive - now we're talking! They called it a "C Drive." The real computer geeks referred to it just "C colon." The office personnel - secretaries and operators and such - were strictly instructed, "Under no circumstances, type 'format C:', ever!" This was an honest mistake, when you were constantly having to type "format A:" and "format B:" to use those new 360k 5.25" floppy disks.

I would have preferred this PC to run my favourite multi-user CP/M, but that didn't curb my enthusiasm one iota - I was on a path of discovery! (Pity it was computers and not girls, but still it was something.)

My IBM PC desktop then was the GW-Basic fullscreen editor - fantastic! You could move the cursor around the whole screen - and that really was something special in those days, I promise you!

In the mid-eighties, I progressed to Norton's Editor, and TurboC - woohoo! Little did i know that for the next decade and a half, except for the addition of a mail application (pine), and a browser application (Netscape), my desktop metaphor would remain essentially the same. Instead of programming in Norton's Editor, using the DOS command-line to compile and run, I would be programming in vi, and using the bash command-line to compile and run. I made the not-so-huge leap from run.bat to run.sh. "A small step for man, a giant leap for mankind." RMS will back me up on that.

Doom.png I pretty much skipped the Windows 3.1 and Windows 95 desktops, and went straight from DOS to Linux in the late 1990s. Why use Windows when Norton's Editor and Norton Commander were the bomb? I used Norton, Borland Turbo C, and LaTeX. And Doom. Windows was a waste of time, literally - it just made my 386 with 2Mb RAM run like a dog. And anyway what applications did it have? Just slow buggy graphical versions of otherwise solid DOS programs. I used Lotus 1-2-3 and MS-Word on DOS - they did the job very nicely, thank you!

You were right Bill, you really did only need 640k in those days, and those PCs flew! I don't remember any hour-glasses, or waiting for web pages to load... Oh, because there was no web, i forgot about that.

But Linux, with its X Windows and Netscape, rocked so hard! The best thing about Linux for me, was that it opened up the wonderful world of networking, and the Internet, and the World Wide Web, oh wow, i was blown away! Once you got your XF86 config file working, you were on Al's superhighway. Actually I never got my XF86 config working - so a friend got it working for me, yeah baby!

I xterminated X windows with bash, pine, vi, gcc and later java. To be honest, I only used two X applications: Netscape and xterm.

Then a few years later I got a Java development contract through that very same friend that had converted me to Linux six years earlier (and got my XF86 config file working for me). He upgraded to a new laptop and gave me his old laptop (exactly what i would have done). I was all fully set up with IntelliJ, JDO, MySQL, on... XP! He explained that our clients all use Windows XP, so we are deploying our application to XP, and so we develop on XP.

Actually for the month before that, I had been using my sister's XP machine to check my webmail and surf occassionally, because I was between contracts and minus a computer to call my own. So I was au fait with Windows XP, or should I say with Firefox running on Windows XP.

At first I found XP very frustrating. Mostly Windows Explorer. Point and click and wait. Repeat. I was used to bash, which with its file name completion, was quick and easy to navigate the file system, and run applications - even better than the 1980's with CP/M and PC-DOS - because bash has got file name completion - it's the bomb, baby!

OpenOffice.pngI put my frustrations with Windows Explorer aside, installed my favourite apps, namely Firefox, Thunderbird and OpenOffice, and some GNU utilities I need like rsync and zsh for backup scripting, and ncftp to upload web pages and stuff. And wget so I can download stuff even tho the connection always gets reset after 4 hours downloading, with 10 minutes still to go. Usually when my niece picks up the other phone. Modems don't like that.

So I got used to Windows XP. It is slick, and runs all my favourite opensource apps, so it's pretty much like a Linux desktop. I spend 99% of my time in my IDE (which was IntelliJ), Firefox and Thunderbird, just like on Linux. I was a happy puppy, I won't lie to you. For one thing, IntelliJ was definitely a step up from that vi rock i had been living under for so many years.

My next contract, the same thing happened. "Here is your XP machine. I am your network admin. Let me know if your Outlook doesn't work, or you can't print something." I somehow knew I couldn't ask him if he had a set of Fedora CDs laying around for me for reinstall my machine. My job was a Java developer, paid by the hour expressly not to be self-installing company property with unsupported operating systems and software! So I was a good user. Except for occassionally like when the printer wasn't working. I emailed the network admin that I've pressed Print about twenty times and still nothing is coming out of the network printer, but i'll keep trying! Best part is he never knew if I was joking or not. After all I was just a lame Windows user and not a l33t Linux hax0r.

v16-rmi-eclipse.png So with this next contract, the only thing that changed is that instead of using IntelliJ, I was using Eclipse, and instead of Thunderbird, I was using Outlook, d'oh! But otherwise I was still a happy XP user. I installed Firefox, and spent my days lavishing in Eclipse and Firefox, and making the best of Outlook. Then something strange happened...

When that contract was over, I tried to go back to Linux on my new home PC. Because really, I couldn't be using this "Windows Phone Home." I am a real programmer and IT professional, goddamnit! And a longtime Linux advocate to boot. Heck, I've co-founded at least one Linux assocation that i can remember, and a LUG too. My frikkin car license plate is even "LINUX." It's a known Linux bug in Cape Town. Yes, a VW Beetle.

So i booted into Knoppix for starters. This is when the strange thing happened. I hated it! I wanted my XP back! The fonts looked terrible! The icons looked unfamiliar! The resolution was terrible! No accelerated video drivers. The mouse went flying across the screen at the million miles an hour. My bluetooth USB key didn't work. How am I gonna get pictures from my phone to email my family!? No iTunes for my iPod! No MP3, no Flash, no Quicktime, no podcasts, no YouTube. Darn, my life as I knew it was taken away from me in the blink of a lilo!

I phoned a friend, a Linux friend, in a panic, and said, I'm doomed. I can't play that new Doom3 demo, and that's just the start of it! He said, no problem, you just gotta set up your system properly. Check this "Tips and Tricks" site, download those RPMs, these dependencies, insert those suppositories, and slap-get this other thing.

I said, "Listen, buddy, I'm on frikkin holiday. It's my frikkin day off, so I'm not going to spend the whole weekend being a computer geek. I've been a computer geek my whole life, and it's enough! I just want my computer to work. Yes I'm concerned about freedom - my freedom to go outside and have a beer in the sun, rather than spending the day slapt-getting my frikkin hard drive! I've been working for the whole week on a frikkin computer, and my evenings and weekends must be free as in beer! Configuring drivers and plugins sounds very much like work to me. So get your slapt arse over here, linux boy, and configure my frikkin computer for me, bitch!"

Thunderbird.png OK, so that didn't work. Which brought me back to... my desktop, Windows XP! It's got the best apps, for free - Firefox, Thunderbird, OpenOffice, Netbeans, RealPlayer. In addition to that, every application that runs on Windows, runs on Windows, and there's a lot of them! Installing software is a breeze. Everything comes with an installer. There is no "dependency hell" - it's like being single again! Move over Linspire, this is "Click and Run" like you've never seen it before!

Everything just works. My sound and video works like a dream. My weekends are my own, or should I say, MPlayer's. My bluetooth camera phone works, so I can send pictures to my Mom of me blowing up nitrous ballons at parties again. (That's my story and I'm sticking to it.) My iPod works. I got my tunes. Can life be better? (I heard that, RMS. Just let me enjoy the moment please!) Ah. I can hear the birds chirping outside... Oh it's my GoogleTalk!



Swing trumps Ajax and Web 2.0

Posted by evanx on May 10, 2006 at 05:03 AM | Permalink | Comments (25)

Swing.png Health warning: This is a long posting about "Swing versus everything else" and more. I'll split it up in coming weeks into Swing vs .Net, Swing vs SWT, Swing vs GNOME and KDE, Swing vs the Web 2.0, et al, so wait for those if you can, rather than read this bloated rant. If you do read this, note that italicised text is all off topic...

Joshua Marinacci asked what's stopping people from shipping Swing applications: was it the large JRE to download, or difficulty in Swing programming, or what? That was a really great question that enjoyed a lot of comments. Including this one, which goes right off topic.

Most contracts/companies i have been involved with are all deploying Swing apps, woohoo! Stands to reason since i'm a Swing developer, innit.

Developers complain that Swing components are buggy "on the edges." Now i'm talking about bright colleagues (not me) that tried to make Swing components deliver some corner-case functionality. These colleagues also complained bitterly about old bugs not getting fixed, even after years. Presumably because of Sun's focus on web/JEE in the dot-com/server-side era which is understandable, or maybe because fixing them could break some customers' applications, also understandable.

OpenOffice.png Lemme digress, about Sun and opensource and Java, and say that, i love Sun, for Java, Netbeans and OpenOffice. Sun adopted BSD in the early days, and have opensourced Solaris (albeit to counter Red Hat, but still). They have contributed their enterprise Java stack to Glassfish (probably to counter JBoss, um... Red Hat). In addition to Netbeans, they have opensourced all their developer tools, like Creator, and the C/C++ one too (partly to counter Eclipse, from IBM, their other nemesis). Inevitably Sun will opensource Java. They will do it to counter C# (their Redmond nemesis), Harmony (supported by IBM), and GCJ (Red Hat). Sun wants developers to be a field of Sunflowers, following the Sun. Sun's next step is for Linux distributions to follow FreeBSD's suite, and include Java and Netbeans, woohoo!

So if i appear to "criticise" Sun at any point, that's just a joke. And as the Russians say (loosely translated) "Every joke is partly a joke". Another russian joke is: "You know why you can't have sex in public? Because you'll get too many suggestions!" - as Sun well knows.

Mono.png I love Swing. First it frustrates you, then it becomes the "devil you know," and then it really starts growing on you. Lemme digress about Mono! Why did the Mono people embrace .Net rather than Java, which is Linux-friendly? They could have written an opensource Java with superb GNOME bindings rather than .Net Mono. According to the US government and the European Commission, Microsoft abuse their competitors something awful. I don't think that they will abuse Mono (thanks to Novell's XML patents), but still, Java would have been a safer bet.

Since Mono gives GNOME's "unixversal" platforms a foot-up to run Windows .NET applications, i assume Mono's choice of C# over Java is, at its root, a Linux-advocacy decision (to see flat the desktop mono-poly), rather than a pure technology decision. (Excuse me when i mistakingly say "Linux" when i actually mean "GNU, GNOME, KDE/Qt, GNU/Linux, *BSD, Darwin/MacOSX and OpenSolaris.")

The Mono guys will argue that C# is a better language and has easier native library integration, which is clearly very important for GNOME. Still i wish the opensource community would unite behind Java. When Sun opensources Java, we'll have a complete opensource Java "stack" including tools (eg. Netbeans, Eclipse) and enterprise platform (eg. Glassfish, JBoss).

The problem with Mono, Python et al, is that they do not have awesome opensource IDEs like Netbeans and Eclipse. In my opinion, this makes them less suitable than Java for many project archetypes. (By the way, having this "opensource" Java development stack doesn't help the Mono guys because their philosophy requires "free software," and so "opensource" just won't do.)

KDE2.png Lemme digress even further off topic about Mono vis-a-vis GNOME vs KDE... C# is an attractive option for the next generation of opensource desktop developers. So i expect that the GNOME developer community will grow the fastest, in which case GNOME might overtake KDE, notwithstanding the RADness of Qt/kdelibs.

At some point, MacOSX might want to move away from Objective-C towards a "modern C." I predict it will be C#/Mono, because that would enable MacOSX to win over Windows developers, applications and users. So "desktop Java" is sidelined in a possible future where most Windows, Linux, and MacOSX developers might favour .Net/Mono for new desktop applications. And desktop tinkerers grow up to be enterprise programmers, so...

I look forward to a practical Java desktop emerging from Looking Glass, one that encourages young opensource developers to tinker with Java/Swing, rather than C#. Otherwise... "Houston, we got a problem." Java is perfectly poised right now, with Netbeans 5, JDNC, Glassfish, and millions of students being taught Java... Hopefully the Java Community will capitalise on this, and achieve world domination, woohoo! I want a Jubuntu desktop with a Looking Glass desktop manager, and "NetFinder" - a super-browser built on Netbeans RCP, with modules like Gecko renderer, ODF viewers, GMailer, GDriver, GoogleData editor, WebStart... Oh man!

Back to the main thread, which is Joshua's question why people might not be shipping Swing applications?

Let me say right out: Swing looks beautiful and is plenty fast enough - just look at applications like IntelliJ and Netbeans. And Swing just gets better, in every department, with every release. I'm sure the Netbeans team, and the Java Desktop community, have been giving great feedback to Sun's Swing developers, and it shows.

v16-rmi-eclipse.png Lemme digress! Why did IBM Eclipse develop SWT, when they should have predicted Hotspot and Swing optimisations, and did the math with Moore's Law and realised that the best not-short-term solution is Swing and not SWT!? Now they have to test and debug everywhere, ouch! Fix SWT on MacOSX and, darn, you break the Windows bindings under certain conditions. They must be chasing their tails supporting native bindings on multiple platforms. Heck, the Mono guys gave up on a native binding of WinForms to GTK, and are gonna settle on Swing's approach of "lightweight" components and events, programmed in C#, atop a minimal native drawing layer. And those guys are incredibly clever - MdI is probably the best desktop programmer in the world, innit.

Incidently, a by-product of Swing's lightweight approach, is that it makes it feasible for projects such as GNU Classpath to deliver an opensource Java implementation, which together with IBM donating their JVM to Harmony, might spur Sun to opensource their Java implementation.

Some people argue that blazing performance is all that users want. I argue that better usability plus new features, is what users want sooner. (With near-blazing performance if possible please!) So it's gotta be blindingly quick and easy for teams of developers to deliver features that users want. That's why we gotta use modern languages like Java, with its safe syntax, memory-management and extensive libraries, and great tools like Netbeans and Eclipse. Thankfully, Moore's Law doubles our application's performance every year, for free, and JDK engineers boost performance with every release. And we can use the Netbeans profiler to join the party.

But back to Joshy's question...

SwingLabs.png My impatient wish is that JDNC/SwingX was more extensive and mature already. We need this rich and extensive set of high-level components for throwing together archetypical apps right off the bat, eg. business apps that any old analyst programmer can handle - no computer science PhDs or opensource propellerheads required!

I suspect we lack this simplicity (through a standard framework and components as promised by JDNC/SwingX) after 10 years because AWT/Swing was envisaged as a web/consumer/desktop application platform rather than an enterprise application presentation platform. And because Java's focus quickly shifted to the web and server-side, and Swing lost some attention in all the dot-com excitement.

It appears that much work has been done on JEE/EJB, and relatively little on Swing as an presentation platform for JEE applications, eg. JDNC. "That's mentil!" (That's from the BBC Series "The Office", when everyone tells David, the manager, that they preferred the other boss Neil's jokes. David says "Thaaat's mentil!" and storms out. Heh heh.)

So this cuban friend of mine used to start every sentence with "Attention!" pointing his finger, and ended every sentence with "Unnerstan!?". As in one of his memorable quotes: "Attention! In Cuba you do what the gover'ment want you to do, not what you wanna do... Unnerstan!?"

jw-0706-webstart3.jpg Attention! WebStart is not realising its huge potential yet. Maybe it should be pimped with some more eye-candy and feature-candy to promote Swing as a rich internet application platform. Maybe rework it as a module suite built on Netbeans RCP. Unnerstan?

So back to Joshy's question: who is not developing and shipping Swing? This brings me to the main thrust of this blog and if you believe that you'll believe anything.

There is one company i worked for recently that ships a JBoss application when it really really should have been Swing. It's a big intranet application run in hundreds of sites. They went web-based rather than Swing/WebStart, and their developers and clients and themselves are paying a very big price for that decision, because web applications are so convoluted and kludgy! Maintaining the application and adding functionality is very slow and difficult. Granted this company's architecture has other problems that exacerbate their situation, but lemme press on before i forget what i was going to say... Darn, i have actually forgotten! Oh yes...

So why did this company go the web route? Because everyone's infatuated with the web as a platform. Lets rather leverage the web as a Swing application delivery platform, not an application platform in itself.

But we need JDNC, with lots of high-level components, blueprints and tutorials, to illustrate that Swing apps are easier to develop and maintain, compared to the mix-up of Java/JSP/Javascript/HTML/CSS that is web applications. That's a hodge-podge of languages and technologies mixed and mashed up something terrible! ;) Besides being easier to develop, Swing apps offer a better user experience to boot. They make Ajax apps look relatively pedesterian, innit.

Ajax might make Web 2.0 applications a better experience for the user, but Ajax doesn't make developers' jobs fundamentally easier. In the crystal ball of my dreams, Web 3.0 is powered by Swing, JDNC, and Webstart, and not Ajax. And not .Net with Avalon or WinForms or whatever.

GoogleCalendar.png Now for the obligatory discussion about Google, because everyone talks and writes about Google all the time, and so why should i be contrary?

I argue that Google Calendar and Google Mail would be considerably easier to develop in Swing rather than Ajax. Soon some Web 3.0 startup is bound to build a better Google Calendar, GMail et al, in Swing, using Webstart, and they will rubbish all Web 2.0 Ajax applications faster than you can google. Swing webapps will have more richness it their little inbox than GMail and Zimbra put together, and their simplicity and agility will ensure that enhancements come fast and furious.

I also wonder, compared to developing GMail (which is a technological feat), how relatively difficult would it be for Google to zhouzh-up Thunderbird to support their GMail back-end transparently, including persisting contacts, settings, et al, to your GDrive - ie. make Thunderbird a stateless rich client internet application. I can't accept that people are not willing to download a 5Mb Google "GoZilla" client, which loads its configuration from your GDrive, rather than your local filesystem. I wish that GDrive-powered versions of Thunderbird and OpenOffice are coming to a Google Pack near me soon, woohoo!

ThinkFreeCalc.png People are banging on about web-based office software. Look at Writely, and then look at ThinkFree. Clearly, the web browser is a pitiful application platform compared to Swing for desktop applications. And worse news for us developers, is that it's so much trickier to develop (and debug and refactor) Web 2.0 "rich internet" applications compared to traditional apps in Swing (or .Net/WinForms or Mono/GTK#). With a framework like JDNC, and a tool like Netbeans, Swing is a webstart dream waiting to happen!

As the designer of the NASA Apollo computer pointed out, the most reliable components are those you leave out. We not gonna leave Java out, so lets try to leave out everything else that we possibly can, like all that convoluted web presentation stuff. Keep it simple, safe, easily deliverable.

To wrap up, my dream for the future of computing, is stateless rich client applications using web data services, where we cache in on rich applications via the web (using some kind of mashup of Google Toolbar, Google Pack and WebStart), and where your documents and settings follow you around on your GDrive.

So if your hard-drive crashes (or you walk into an internet cafe, or sit down at a colleague's computer), just pop in a live/install USB key (or not), login in to your Google/Yahoo/Live account, and you're good to go - your documents, settings, and program shortcuts, are all at your finger tips, exactly where they were last time you logged in. So I love what Red Hat is doing with "Stateless Linux." And I hope Google and Sun are cooking up a "OpenOffice with GDrive stateless dressing" free lunch in your Google Pack.

I suspect that OpenOffice is gonna light the way to Web 3.0, with purposeful integration to your searchable GDrive repository. Ok it's not in your browser, it's from your browser, so what?. Have a cup of coffee while OpenOffice2Go and Thunderbird2Go downloads/updates, and thereafter forever have a richer application to use everytime and anywhere - nicer for the users, and easier on us developers.

Similarly with enterprise applications. I hope they are going to use Swing and not Ajax. Then everyone's a winner - end users get a richer application, and features coming sooner, and developers enjoy rapid painless development and happy users.

Disclaimer: I'm a Swing developer, writing a Swing framework, namely aptframework.dev.java.net - so take everything i say as self-serving conjecture, which is exactly what it is, with the main purpose of having this opportunity to shamelessly plug myself and my project, so...

Netbeans Day (South Africa) Part 3

Posted by evanx on May 07, 2006 at 11:08 AM | Permalink | Comments (3)

This is Part 3 of my notes and impressions from Netbeans Day (South Africa), held in Johannesburg this week, and covers Sang Shin's talk "What makes Netbeans the best IDE for Java EE Development?" (See Part 1 about Netbeans IDE 5.0 features, and Part 2 on Netbeans RCP).

Health warning: the italic text is just me rambling and so should probably be skipped.

Blueprints.png

Sang Shin is a Java evangelist from Sun. See his site, javapassion.com. This has a very cool master tutorial index - check it out.

Sang Shin (and Geertjan) emphasised that Netbeans offers an unrivalled "out the box" experience, eg. it comes bundled with servers, namely Tomcat and the Sun App Server. Just one download, and you're good to go. A Netbeans 5.5 preview is available with bundled Glassfish (JEE 1.5 with EJB 3.0), and of course you can develop/deploy to JBoss and WebLogic. Support for Websphere is coming soon. (One of the new features in 5.5, is the generation of entity classes and CRUD JSF pages from a database table, as Geertjan writes.) I read that Derby (which i think Sun is calling "Java DB") is gonna be bundled in future Netbeans releases.

I must say, i love that Netbeans works out-the-box - for one thing, this makes it easy to upgrade to a newer release without hassle - whether you are a desktop, mobile or web developer. (By the way, for this reason, i think it's worthwhile switching from HSQL to Derby for Netbeans-ready demos and such - and to Glassfish too.)

Excuse me for gushing, but the thought of Netbeans 6.0 with integrated Derby and Glassfish gets me so excited, i gotta jump up from my computer and walk around the garden to calm down! It is so 100% "good to go" - just add coffee! What a pleasure for the next generation of developers - we never had it so good before. The golden age is dawning, with fantastic languages, libraries, tools, databases, servers, frameworks, components - all freely available, and cross-platform - with tutorials to rule them all - and with Netbeans, it's all in one litt