A Groovy Chuck Norris Script

This week I’m at UberConf (http://uberconf.com), a truly alpha-geek experience with sessions morning, afternoon, and evening. On Tuesday I did a day-long tutorial on Groovy for Java developers, which was really fun. I love teaching Groovy to existing Java developers. Sometimes they get tears in their eyes at how much easier life can be. 🙂 Wednesday I had a Spock workshop followed by a Grails workshop. Yesterday I did my Bayes’ Rule talk (“Bayes’ Rule Says You’ll Attend This Talk”), and today I’m doing my “Managing Your Manager” talk and my “Making Java Groovy” talk again.

Making Java Groovy has now moved into the production phase. So far this means discussing art work with the illustrators and trying to explain to them what I actually meant with my limited attempts at using a drawing tool.

I’ve been working on this book for nearly five years, and that means some of the earliest chapters needed some serious updating recently. When I started I think Groovy had just released version 1.6, so it’s changed versions at least four times during the writing process. Groovy 2.2 may even come out when the book shows up in print, at which point I’ll have to go back to the GitHub repo and update all the Gradle build files again.

One of the earliest chapters I wrote was the “Groovy by Example” chapter. In it I used the Google Chart API (which is now deprecated but still available) and an early version of my Groovy Baseball application. I really wanted to update all that to newer examples, but there simply wasn’t time, especially given that they all still worked correctly.

Still, I did take the time to write another example, even if it turned out to be too late to add it to the book. I’d like to show it here, because it’s simple but still pretty interesting, which is a good combination.

In many of my presentations (including one I’ll be doing later today), I like to access the ICNDB web site as an example of a RESTful web service that serves up JSON data. That’s ICNDB, as in the Internet Chuck Norris Database. ICNDB is a RESTful service in name only; it doesn’t do content negotiation (it doesn’t even set the “Content-Type” header correctly, a fact I discovered when I made an Android front end for it — see the ICNDB app in the Google Play store) and it only supports GET requests.

(Mandatory jokes: wouldn’t a RESTful service that only supports GET requests be called a GETful service? And if it’s stateless, wouldn’t that make it a FORGETful service? Insert rimshot here.)

The Groovy JDK makes it trivially easy to access a URL with a GET request. Groovy adds a toURL method to the String class, which converts a String into an instance of java.net.URL, and a getText method to the URL class, which returns the response as a String. Calling it is as simple as:

import groovy.json.JsonSlurper

String base = 'http://api.icndb.com/jokes/random?limitTo=[nerdy]'
def json = new JsonSlurper().parseText(base.toURL().text)
println json?.value?.joke

Following the normal Groovy idiom, I invoke getText by accessing the text property of the URL. Then I use a JsonSlurper to parse the result, which essentially converts it into a nested map-based data structure. The JSON object has a value property that holds another JSON object, which stores the actual joke in a property called joke. I don’t really need the safe de-reference operator here, but it doesn’t hurt.

The results are similar to:

Chuck Norris can unit test entire applications with a single assert.

The interesting question then becomes, how do I turn this into a client-side application with a graphical interface?

The Groovy API includes a so-called “builder” class called SwingBuilder, which makes Swing development almost (but not quite) cool. It even has easy ways to handle threading issues, so that I can access the service off of the event dispatch thread, but update the GUI back on it. The key is in the doOutside and doLater methods. The former allows you to do work without locking up the GUI, while the latter is where you update the interface.

(The best discussion of SwingBuilder I’ve ever seen is contained in Griffon in Action, by Andres Almiray, Danno Ferrin, and Jim Shingler.)

Here’s the whole script, which I’ll explain afterwards. In the book source code (just because I couldn’t put the app in the book doesn’t mean I couldn’t add it to the repo), this script is called icndb_with_label_and_button.groovy.

import groovy.json.JsonSlurper
import groovy.swing.SwingBuilder

import java.awt.BorderLayout as BL
import java.awt.Color
import java.awt.Font

import javax.swing.WindowConstants as WC

String startHTML = "<html><body style='width: 100%'>"
String endHTML = '</body></html>'

String base = 'http://api.icndb.com/jokes/random?limitTo=[nerdy]'
def slurper = new JsonSlurper()

new SwingBuilder().edt {
    frame(title:'ICNDB', visible: true, pack: true,
        defaultCloseOperation:WC.EXIT_ON_CLOSE) {
        panel(layout:new BL(), preferredSize:[300, 250], background: Color.WHITE) {
            label('Welcome to ICNDB', 
                constraints:BL.NORTH,
                font: new Font('Serif', Font.PLAIN, 24), 
                id: 'label')
            button('Get Joke', constraints:BL.SOUTH,
                actionPerformed: {
                    doOutside {
                        def json = slurper.parseText(base.toURL().text)
                        doLater { 
                            label.text = "${startHTML}${json?.value?.joke}${endHTML}" 
                        }
                    }
                }
            )
        }
    }
}

A couple of the import statements use the as operator to create aliases. That is, I can use WC in the script to refer to javax.swing.WindowConstants, and BL to represent java.awt.BorderLayout. That’s an interesting but underused feature of the as operator.

I want to add the joke to a JLabel, which supports HTML. Therefore, I defined strings to surround the HTML when I put the joke in the label.

The SwingBuilder class has an edt method which lets me operate on the event dispatch thread when building the GUI. Then terms (actually, method calls) like frame, panel, label, and button create instances of JFrame, JPanel, JLabel, and JButton from the Swing API. The attributes, like visible:true and title:'ICNDB' invoke the setVisible and setTitle methods, respectively.

Arguably the best part, though, is the actionPerformed property. If you’ve ever done Swing development, you know that clicking a JButton generates an ActionEvent. In order to capture the event you need an ActionListener, which has a single method called actionPerformed. In regular Swing development, the result is usually an inner class (or even an anonymous inner class) that implements the interface and updates the GUI. You use an inner class because the inner class is able to access the private attributes of the outer class, and when I used to do Swing development I normally made the labels and buttons attributes of my GUI class.

Here, though, I don’t need an inner class at all. Instead I assign the actionPerformed property to a closure, and I’m finished. One of the nicest uses of closures is to eliminate anonymous inner classes this way.

Inside the closure I use the doOutside method to access the service. Then I get back onto the EDT using doLater and update the GUI. By giving the label an id attribute, I was able to access it inside the doLater closure using the value of the id.

The result is something like:
icndb_label_with_button

That’s all there is to it. I’m not thrilled with the HTML parts, though, so I also implemented the script using a text area instead. The resulting script is in the file icndb_with_textarea_and_button.groovy:

import groovy.json.JsonSlurper
import groovy.swing.SwingBuilder

import java.awt.BorderLayout as BL
import java.awt.Color
import java.awt.Font

import javax.swing.WindowConstants as WC

String base = 'http://api.icndb.com/jokes/random?limitTo=[nerdy]'
def slurper = new JsonSlurper()

new SwingBuilder().edt {
    frame(title:'ICNDB', visible: true, pack: true,
        defaultCloseOperation:WC.EXIT_ON_CLOSE) {
        panel(layout:new BL(), preferredSize:[300, 250], background: Color.WHITE) {
            scrollPane {
                textArea('Welcome to ICNDB', 
                    constraints:BL.NORTH,
                    font: new Font('Serif', Font.PLAIN, 24),
                    lineWrap: true, wrapStyleWord: true, editable: false,
                    id: 'textArea')
            }
            button('Get Joke', constraints:BL.SOUTH,
                actionPerformed: {
                    doOutside {
                        def json = slurper.parseText(base.toURL().text)
                        doLater {
                            textArea.text = json?.value?.joke
                        }
                    }
                }
            )
        }
    }
}

I wrap the text area in a scroll pane, but other than that it’s essentially the same code. The result now looks like:
icndb_with_textarea_and_button

So that’s it: a small, but hopefully non-trivial application that demonstrates Groovy capabilities including builders, thread handling, RESTful web services, and even the as operator. Have fun with it.

In the meantime, I’m ignoring the take down notice I received from Chuck Norris’s lawyers about my ICNDB app in the Play store. Sigh.

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

2 Responses to A Groovy Chuck Norris Script

  1. cmtopinka says:

    May need

    Properties systemProperties = System.getProperties();
    systemProperties.setProperty(“http.proxyHost”, “host”);
    systemProperties.setProperty(“http.proxyPort”, “proxy”);

    But not if you’re CN.

  2. Ken Kousen says:

    Easier is adding -DproxyHost=… -DproxyPort=… when you run the groovy command. If you want to do it programmatically, like you did, you can probably do:

    Properties props = System.properties
    props << [proxyHost:'host', proxyPort:'port']

    Left-shift will append map entries, so you should be fine.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: