« April 2004 | Main | June 2004 »

May 2004 Archives

May 17, 2004

Groovy, Verbosity, and Swing

While I was on vacation in Hawaii Jonathan Simon lamented about how people still aren't getting it.. I think that the single biggest problem creating the misconception that swing is slow is that people don't understand or use threading on their GUIs. Two main problems exist. First any event you handle is inside the event dispatch thread which is also responsible for painting the GUI. Second, practically any manipulation of a JComponent must be done from within the event dispatch thread. Practically a paradox, but it can be dealt with.

Consider an action event that updates a label named jLabel with a string value from a long running method called takesTenSeconds. You may be tempted to do something like

public void actionPerformed(ActionEvent e) {
    jLabel.setText(takesTenSeconds());
}

but then you violate the first rule: no long running calls in the event thread. With anonymous methods it can be handled however...

public void actionPerformed(ActionEvent e) {
    (new Thread(new Runnable() {
        public void run() {
            jLabel.setText(takesTenSeconds());
        }
    }).start();
}

That's some progress. But we now violate the second rule: Don't use thread unsafe methods on JComponents or mess with models they use. In this case it will not be a problem, but when you start with JTrees, JTables, JLists, JComboBoxes, and the like you can create these lovely stack traces to system.out involving concurrent modification, null pointer, index out of bounds, and other stuff that looks bad to QA and clients. How do we fix this?

public void actionPerformed(ActionEvent e) {
    (new Thread(new Runnable() {
        public void run() {
            final String newText = takesTenSeconds();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    jLabel.setText(newText);
                }
            });
        }
    }).start();
}

A code snippet that a random VB developer would assume is one line morphs to 3 times it size in characters and (depending on your codeing style) 10 lines in the code. Even with another utility method for the background call we have to make two runnable classes, whether anonymous, inner, or stand alone that's a lot of code bloat. What would be nice is a more concise syntax for a runnnable. Enter Groovy and closures. This can be made much smaller with the creation of a few simple utility closures...

invokeAndWait = { SwingUtilities.isEventDispatchThread() ? it() : SwingUtilitites.invokeAndWait(it); }
invokeLater = { SwingUtilities.invokeLater(it) }
invokeOutside = { SwingUtilities.isEventDispatchThread() ? Thread.start(it) : it() }

From this, the proper way becomes much shorter...

invokeOutside {
    text = takesTenSecondes()
    invokeLater { jLabel.text = text}
}

With some tweaking on the sample closures it could probably even be reduced to...

invokeLater { jLabel.text = invokeOutside { takesTenSeconds() } }

I think that the greatest problem with the swing APIs are not that they are too complex or too poorly explained. The biggest problem I see is that to do simple things the correct way, it can get quite verbose. Kind of like reliable file I/O, but you get far fewer urgent-1 bug in the GUI than in core I/O routines. Perhaps the best thing isn't to change the Swing APIs, but to add more syntactic sugar.

May 20, 2004

Movable Type 3 Upgrade

I've just upgraded to Movable Type version 3.0D free edition. Recently I've been getting spammed by someone in eastern europe advertising their laptop site in my comments. So the whole typekey authenticated commends and approval for anonymous comments really got me to move over. You can still post random comments, but if you don't put your moderated login with it you may need to wait to see it. I'll need to upgrade a template or two but that's better than deleting 8 or more comment spams in a 10 minute span.

<span class="political rant">And I'm doing this all despite the whining that some of the Open Source Evangelics have been doing WRT SixApart charging for some non-commercial uses. I really don't have a problem with it. For my use it's just as free1 as it ever was. And if you are really running a truely personal site it still is free1. It never was free2 so people shouldn't really be astonsished when they limit the scalability of the free1 version. And when you consider just how much has gone in, the fact that the Menas are charging for heavy users it isn't astonishing, you can't run a software development shop for free1. And if they don't release a free1 version of 4.0 (whenever they do and whatever it is) then it will either be good enough for me to upgrade, or I just won't upgrade. That's the way a market3 works. If you really scale that much the cost of the license is surely some fraction of annual hosting fees.</span>

As for moving to a Java based weblogging tool like Roller, SnipSnap, or any of the other options are a no go since my incredably inexpensive host only has Tomcat 3 or 4 on here (the more recent one that isn't supported by roller). Besides, I do enough J2EE at work.


1 Free as in [root] Beer
2 Free as in Liberty
3 As opposed to a Bazzar

May 25, 2004

Extending Swing via Listeners

With a framework as nimble as Swing there are many ways to skin a cat. This is possibly one of the sources of many complains about Swing. For example Sam Dalton wrote a class that implements a dialog that closes when the escape key is pressed. Subclassing JDialog is certainly one way t do it, but I probably would have simply wrote a utility method...

static void makeDisposeOnEscapeKey(final RootPaneContainer rootPane) { Action action = new AbstractAction() { public void actionPerformed(ActionEvent arg0) { ((java.awt.Window)rootPane).dispose(); } }; KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); rootPane.getRootPane() .getActionMap() .put(action,action); rootPane.getRootPane() .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(stroke, action); }

A couple of changes reflect my swing codeing preferences. First, I call dispose() instead of hide(). My reasons are twofold: first until a window is disposed it takes up native graphics resources, and can essentially be a big memory leak until the object becomes inaccessible and is finalized. The second reason has to do with a clean JVM exit. While any AWT resources are realized (i.e. have native OS resources allocated) the AWT event thread will keep running and the JVM will not exit. If all of the windows are disposed() then the AWT event thread will enter a state that will allow the JVM to terminate. No more calls to System.exit(0).

The second thing I changed was which action map I put the escape key in. Sam used WHEN_FOCUSED which is fine if there are no focusable components in the dialog. But throw in a button or text field and the action may never get fired. There are actually two options here, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and WHEN_IN_FOCUSED_WINDOW. I chose the latter because this is also how the JMenuItems register their accelerator keys and the desired operation is essentially the same. Since we are adding the action to the JRootPane there are no differences between the two.

By using a utility method we can also add this behavior to any free standing component. One of the issues Swing has to deal with is that higher up in the AWT hierarchy dialogs and frames were separated. To deal with this they created an interface javax.swing.RootPaneContainer so that they can treat all root level components the same. Hence this behavior can be added to any JFrame, JDialog, JWindow, JApplet, or JInternalFrame.

And please, if you have obnoxious splash screens either have a user preference to turn it off or add this to the JWindow so the user can click it and press escape. Wouldn't that be nice.

May 27, 2004

Swing and Groovy: Low-Carb Coding, not Syntactic Sugar

Use Groovy and shed KLOCs over night. Sounds like some advert for the latest weight pill, but here is some empirical evidence. I was able to re-write the ButtonDemo.java section of the SwingSet2 demo that comes in your JDK distribution with about one third of the source code lines and one half of the total characters.

There are two principal contributors to this amazing weight loss* (YMMV of course). The first and IMHO nicest features is the use of closures. For those of you who either didn't major in Computer Science or suppressed the trauma of the sophomore year functional programming classes you may wonder what closures are. Closures are a very particular name for a very particular type of "first class function," or way to confuse the code segment and the data segment. They are very tight and concise, and most importantly can be assigned to specific event listeners in less than one line of code. And most importantly, Groovy closures look like simple java blocks.

Another feature allowing for the compression in size is the way the SwingBuilder handles JavaBeans properties. It looks like a constructor taking some sort of a python type dictionary as the arguments, but really the code that is generated is to call the default constructor and set the specific bean properties after the bean is constructed. It allows for readable compression of setting multiple properties in one line.

The demo is currently waiting to be added to Groovy itself as a sample, but here's a quick comparison of Groovy vs. Plain Java setting up the "Enabled" CheckBox on the right side of the demo to give you a taste.

checkBox(text:"Enabled", mnemonic:'E', selected:true,
  toolTipText:"Click here to enable or disable the buttons.",
  actionPerformed: { event |
    btns.each { it.enabled = event->source->selected } } )

Groovy is 3 or 4 lines depending on what your end column is set to

JCheckBox enabled = new JCheckBox("Enabled");
enabled.setToolTipText("Click here to enable or disable the buttons.");
enabled.setSelected(true);
enabled.addItemListener(buttonDisplayListener);
enabled.setMnemonic('e');
// nearly 45 lines later in a different method and
// an anonymous nested class
public void itemStateChanged(ItemEvent e) {
  JCheckBox cb = (JCheckBox) e.getSource();
  if(cb.getText().equals("Enabled")) {
  for(int i = 0; i < currentControls.size(); i++) {
    c = (Component) currentControls.elementAt(i);
    c.setEnabled(cb.isSelected());
    c.invalidate();
  }
  // code for other buttons

Plain Java is 10+lines, not counting comments. Plus the code doing the checkbox action isn't interrupted by 40+ lines of other code. I am really starting to like Groovy alto and I am sorely tempted to actually use it in shipping code, but I plan on at least waiting until it is declared 1.0 soup and not beta.

About May 2004

This page contains all entries posted to ... And They Shall Know Me By My Speling Errors in May 2004. They are listed from oldest to newest.

April 2004 is the previous archive.

June 2004 is the next archive.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33