Making Swing Groovy, Part I

I’ve been spending a lot of time lately trying to understand how much Groovy improves Swing user interfaces. Like so many Java developers, I rarely write client-side programs. Almost all of my time and effort with Java over at least the last five years has been on the server side, mostly with frameworks like Struts, and then open source projects like Spring and Hibernate. During all that time, the client-side interface was restricted to what HTML and CSS can handle.

Of course, the growth of Ajax changed a lot of that. I like several of the major Ajax libraries, like prototype, scriptaculous, jQuery, YUI, and so on. One of the interesting trends I’ve been watching in the industry is whether browser-based GUIs in the future will be written in a Java component library that renders in HTML (like JSF), or written in Java libraries that translate to Ajax/JavaScript (like GWT), or are written using JavaScript components directly (like in YUI or dojo).

Still, I used to write user interfaces in Swing. Several years ago I wrote a Jeopardy-like game that I still use in training classes, where the game board itself is a Swing UI and the clients all have “buzzers” that connect to the game board via RMI. One of my most interesting, and certainly most fragile, projects was about ten years ago when I had to put a Swing GUI on top of a Fortran program (shudder). In case you’re wondering, you can’t get there from here. You have to go Java to C via JNI, and then C to Fortran (even though they handle arrays differently). It’s basically like putting an notch in a beam and telling it to break right here.

But those are stories for another day. Suffice it to say that I feel quite comfortable writing Swing GUIs in raw code (as opposed to graphical drag-and-drop interfaces), complete with inner classes to implement ActionListeners or the like for each button, and so on. I still struggle a bit with the intricacies of GridBagLayout, but I can get pretty far with lots of JPanels and combinations of BorderLayout and FlowLayout.

So when it came time to see what Groovy brought to the table, I thought I was quite prepared. Imagine my surprise to find that, believe it or not, in the last several years the field actually moved on, despite the fact that I almost never get requests for training classes in Swing or related technologies. A lot of people (Andres Almiray, Danno Ferrin, Geertjan, and many more) have spent considerable time and effort applying Groovy to user interfaces and making all kinds of progress.

The culmination of all of this effort is the new, seriously cool, Griffon framework. Griffon promises to bring Grails-like productivity improvements to client-side Java applications. The more I learn about it, the more impressed I am. It’s been a bit disheartening, however, to find out how much I still have to learn before I really understand it.

No matter. I like to say that I’m often wrong, but I rarely stay wrong. My next few posts here will be dedicated to principles that I’ve been learning about user interfaces in Groovy. Hopefully, in addition to providing a nice record for me of what I’m learning, maybe this will help someone else avoid my mistakes.

In this first post, let’s get the really well-known stuff out of the way. That’s the wonderful elegance of Groovy’s groovy.swing.SwingBuilder. SwingBuilder is a great Groovy class, well-described in GinA, that let’s you create Swing interfaces almost declaratively.

Here’s a trivial Swing application in Java. All it does is take text typed in a text field and echoes it in the text of a label. It’s about a simple as Swing applications come.

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

public class EchoGUI extends JFrame {
	private JTextField input = new JTextField(20);
	private JLabel prompt = new JLabel("Input text: ");
	private JLabel echo = new JLabel("Echo: ");
	private JLabel output = new JLabel();
	
	private Container cp = this.getContentPane();
	
	public EchoGUI() {
		super("Echo GUI");
		cp.setLayout(new GridLayout(0,2));  // 2 cols, as many rows as necessary
		
		cp.add(prompt);
		cp.add(input);
		cp.add(echo);
		cp.add(output);
		
		input.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				output.setText(input.getText());
			}
		});
		
		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		setSize(300,100);
		setVisible(true);
	}
	
	public static void main(String[] args) {
		new EchoGUI();
	}
}

First of all, this shows me to be an “old school” Swing developer, because I made my application extend JFrame rather than contain a JFrame instance. The trend these days is always to favor composition over inheritance, partly to reduce the coupling and partly to make testing easier (I’ll have more to say about testing Swing UIs in a future post). Still, this is the way I learned to do it, and I still fall into old habits, especially for trivial examples.

Second, I’m also arguably violating best practices because my main method instantiates the GUI directly, rather than calling SwingWorker.invokeLater to make sure all user interface updates are done in the Event Dispatch Thread (EDT). You can generally get away with this here, though, because until the GUI is actually rendered it isn’t much of a problem. Understanding the details of running on the EDT or not, however, is a big issue for later, too.

Also, I’m still in the habit of adding components to the content pane. I remember vividly when I had to rewrite all my existing Swing apps to add to content pane instead of directly to the frame itself, and I still do that, even though I don’t think it’s strictly necessary any more.

Finally, I did something here I generally don’t do, which is to use an anonymous inner class to handle the action listener for the button. Normally I create an actual (named) inner class for each component that generates events, and add an instance of each to the relevant component. Here, for example, I would normally write an inner class called something like EchoListener, and then add an instance of it to the button. Still, with only one button and an almost trivial inner class, it seemed easier to add it directly, even though arguably this is the ugliest syntax in all of Java, with the possible exception of generics.

Groovy’s SwingBuilder cuts this example down to practically nothing.

import groovy.swing.SwingBuilder
import javax.swing.WindowConstants as WC

SwingBuilder.build() {
	frame(title:'Echo GUI', size:[300,100],  
		visible:true, defaultCloseOperation:WC.EXIT_ON_CLOSE) {
		gridLayout(rows: 0, cols: 2)
		label 'Input text: '
		input = textField(columns:10, 
			actionPerformed: { output.text = input.text })
		label 'Echo: '
		output = label()
	}
}

The SwingBuilder means that I can just write words like frame, label, and textField, and, through the magic of metaprogramming, they are interpreted by the builder as Swing components. The other great part is that Groovy doesn’t force you to create an inner class just to hold a method. Closures are perfectly valid objects in their own right, so I can just assign one to the actionPerformed property of the text field, and I’m all set.

That’s an amazing savings, and it’s just the beginning. The combination of builder methods and closures cuts the amount of code required in a typical Swing GUI by a huge amount.

(I don’t know exactly how much, but I’m going to say 80%, because, as you may have heard, 80% of all statistics are made up on the spot.)

For more details, see GinA (Groovy in Action for the uninitiated, and if you are uninitiated and find Groovy cool, get thee hence to a bookstore and buy thee a copy) and the wiki pages at groovy.codehaus.org.

Still, this much I knew when I started my recent adventure. I had no idea about Groovy’s cool bind method, which dramatically simplifies the use of PropertyChangeListeners and is a great step along the way to a pure MVC architecture. Then there’s the @Bindable annotation that’s part of Groovy 1.6 beta 2, which makes it even easier. Then there’s the elegant way that Griffon lets you handle scheduling tasks inside or outside the EDT. I also realized that Swing hasn’t been idle, too, with the creation of the whole SwingX project and its associated set of components.

It’s been a wild ride for me recently, and I don’t feel I’m anywhere near finished. My feeling, though, is that Griffon is eventually going to be a very big deal. I know it’s making a big difference in how I think about Swing, and I’m looking forward to playing with it more and more.

Minor bug in Grails selenium plugin

There is a minor bug in the selenium plugin for Grails. It has been discussed on the mailing list, but I thought I would also document it here. I’m using Grails 1.0.3 with version 0.4 of the selenium plugin, which wraps selenium core version 0.8.3.

If you install the plugin and try to run the new Grails tasks (create-selenium-test, create-selenium-domain-test, or run-selenium), the system fails with the error

Could not execute method appContext
No such property: appContext for class: ...

It turns out that the way the appContext variable is handled in Grails has changed, and the plugin hasn’t yet updated to accommodate it.

The recommended fix is to go into the “plugins/selenium-0.4/scripts” directory in your Grails project, and replace ${appContext} with ${grailsAppName} in all the Groovy scripts.

If you use the Selenium IDE in Firefox to write your tests, then the fix doesn’t matter until you try to use the run-selenium target. Still, might as well fix it everywhere. This came up on the mailing list back in July, so I assume an updated plugin will be available as soon as the author gets around to it.

I also find that adding the path to firefox.exe to my system path (default is “c:\Program Files\Mozilla Firefox” — don’t you hate Windows paths with spaces??) makes it easier to run the test suite.

Incidentally, unlike Canoo Web Test, you have to make sure your web app is running before executing the test suite. Selenium fires up the browser and puts it through the proper sequence, so the app has to be running first.

I’ve decided that rather than choosing between Selenium and WebTest, I actually like using both. The Selenium IDE in Firefox is really slick and easy to use, while the output reports in WebTest are gorgeous.

Star rating in the Grails RichUI plugin

I’ve been working on a Grails application for rating the popularity of Grails plugins.  Rather than just use a simple form with radio buttons or a drop-down list, I thought I’d use the star rating component of the RichUI plugin.

The initial set-up is easy enough.  First, install the RichUI plugin.

grails install-plugin richui

then go into any GSP page where you want to use star rating and add

<resource:rating />

and after that it’s just a question of using the <g:render /> tag appropriately.

Mine looks like this:


<g:each in="${pluginList}" var="plugin">
   ...
   <g:render template="rate"
      model='[plugin: plugin, rating: "${plugin.rating}"]' />

along with some other stuff.

The documentation says to add a RatingController which computes the new rate.  I considered just adding the rating method to my PluginController, but eventually decided to keep it separate.  My RatingController looks like


class RatingController {
    def rate = {
        def rating = params.rating
        def plugin = Plugin.get( params.id )
        def average = (rating.toDouble() + 
            plugin.rating*plugin.totalVotes)/
                (plugin.totalVotes + 1)
        plugin.rating = average
        plugin.totalVotes += 1
        plugin.save()
        session.voted[plugin.name] = true
        render(template: "/plugin/rate", 
            model: [plugin: plugin, rating: average])
    }
}

That’s all pretty much taken from the sample, except for the session.voted[...] business.  I’ll come back to that in a moment.

The template I’m using is called _rate.gsp in the plugin view folder.  It consists of


<div id="plugin${plugin.id}">
    <% def dynamic = !session.voted[plugin.name] %>
    <richui:rating dynamic="${dynamic.toString()}" id="${plugin.id}" units="5"
        rating="${rating}" updateId="plugin${plugin.id}" controller="rating" action="rate"  />
    <p class="static">
        Rating ${java.text.NumberFormat.instance.format(plugin.rating)}
        based on ${plugin.totalVotes} vote<g:if test="${plugin.totalVotes != 1}">s</g:if>
    </p>
    <g:if test="${!dynamic}">
        <div style="color: green;" id="vote${plugin.id}">Thanks for voting!</div>
    </g:if>
</div>

And here we see some good stuff.  First, when you click on the star rating as it comes “out of the box”, it only updates the average value, which it optionally displays.  I wanted to show the number of total votes, too.  As it turns out, there’s an excellent blog post by Jan Sokol that deals with exactly this problem.  See the blog post for details, but essentially it involves changing update ID to that for a div wrapper, which allows you to update a whole section instead.

The <richui:rating> tag has an attribute called dynamic which determines whether you can vote or not.  If dynamic is false, you get a (naturally enough) static view.  If dynamic is true, you can mouseover the stars, highlighting them as you go, and then click to vote.

In most applications I’ve seen that use the star rating, you have to register and login in order to be able to rate anything.  I now believe that’s so they can render the rating tag as dynamic when you enter and then change it to static when you click.  The state is then kept in a user table, which remembers whether you’ve already voted or not.

My problem, though, is that I wanted to let anyone vote without having to register (a decision I’m currently reviewing).  So the question is, how do I keep a person from just voting over and over again?

I tried a couple of ideas, like a toggle or putting something in the page, but if someone browsed to a different page and came back they could vote again.  In the end, I found that I can use the session, even though nobody has logged in!

(That may be obvious to you, but even after teaching server-side Java courses for years now, I guess it never really hit me that a session exists even if you’re not logged in.  Whenever I visited a site with a shopping cart, I always had to log in.  It turns out that was only to buy the products, not to have a session at all. I guess in retrospect it seems obvious, but I never really thought about it until now.)

Anyway, that left me with the question of how to use the session for 90-some different plugins.  I decided to use a boolean array, where the index was the plugin name and the value was true if the person had voted and false otherwise.  As you can see from the RatingController code above, whenever anyone votes, I simply go

session.voted[plugin.name] = true

and I’m all set, because the rating template has

<% def dynamic = !session.voted[plugin.name] %>

and

<richui:rating dynamic="${dynamic.toString()}" ... />

in it.

The only remaining question was how to initialize that array for the session.  I decided that was a classic application of an interceptor, so in my controller for the plugins themselves, I have


def beforeInterceptor = {
    if (!session || !session.voted) {
        def voted = [:]
        def names = Plugin.list().collect { it.name }
        names.each { name ->
            voted[name] = false
        }
        session.voted = voted
   }
}

and there you have it.  Of course, it’s better if you test, and while I still struggle with that, I was able to come up with something effective in this case.  Here’s my PluginControllerTests class, which is, of course, an integration test.  (There’s no doubt a way to make it a unit test instead, but hey, at least it’s tested.)


class PluginControllerTests extends GroovyTestCase {
    def pc

    void setUp() {
        pc = new PluginController()
    }

    void testNoVotedArrayBeforeIntercepting() {
        assertNull pc.session?.voted
    }
   
    void testVotedArrayExistsAfterIntercepting() {
        pc.beforeInterceptor()
        assertNotNull pc.session
        assertNotNull pc.session.voted
    }

    void testVotedArrayHasAcegi() {
        pc.beforeInterceptor()
        assertFalse pc.session.voted['acegi']
   }

   void tearDown() {
        pc = null
    }
}

So it all works, at least so far.  Soon I’ll be able to deploy it, but now I’m still fighting with CSS styles.  Whoever thought doing layout with CSS was a good idea has some explaining to do.

Incidentally, anyone who has used WordPress for blogging knows the frustrations of trying to format code in it. I think there are plugins available to make it easier, but I’m not running WordPress myself — I’m letting them host my blog. The bottom line, therefore, is that sometimes it’s really hard to read code that is posted here. I think, when my app is finished, I’ll follow the recent trend and upload it to github. If I do that, I’ll be sure to mention it here.

%d bloggers like this: