Over a million developers have joined DZone.

Porting to Griffon

DZone's Guide to

Porting to Griffon

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

In Flying with Griffon, I created a simple scenario in Griffon, to show how this new Swing MVC framework functions and what some of its benefits are. A different approach, one that is potentially even more illustrative, is to take an existing Java desktop application and port it to Griffon. That is what I propose to do in this article.

First, a puzzler. What's the difference between these two screenshots?

Answer: The 1st is one of the standard Java desktop samples delivered with every distribution of NetBeans IDE. It is a small GUI application, with a JFrame designed in the Matisse GUI Builder, with an interface and an implementation for evaluating the user's attempt at figuring out anagrams. And what's the 2nd screenshot? It's the same application, this time written on the Griffon framework! Clearly the result is identical, at least at face value. You're going to have to trust me that the rest works the same too or simply follow the instructions below and see for yourself.

Before looking at the advantages and disadvantages of porting an application to Griffon, let's look at what it mean to "port an application to Griffon". Below you see a screenshot of both applications. The first is the original Java application, i.e., the same as you'll find in NetBeans IDE, while the second is the Griffon equivalent:

Look at the first application and then look at the second. Some of the advantages of the second should be obvious already. But before exploring these, let's take the MVC approach to examining each of the parts you see above and what it took to port them:

By the way, thanks a lot to Andres Almiray, who looked at the code in this sample and tweaked quite a lot of it. He's not responsible for any remaining inaccuracies, however!

The View

As can be seen in the previous screenshot, the original view consisted of two JFrames. The first provides the user interface for the Anagram game; the second provides the About box. Both were created in the Matisse GUI Builder, resulting in an application that is locked into NetBeans IDE.

The original view was very tightly connected to the code that evaluates the user's input and to the code used for dealing with the user's actions. Porting to Griffon means spending a lot of time unwiring your code and carefully considering which bit does what. The aim for the view is for it to be as "viewy" as possible, with no processing code of any kind cluttering it up.

A Griffon view can itself be split into three parts. First, there's the main view that the user sees. Supplemental views, such as, in this case, the About box, are defined in a separate file, ending in "Dialogs" (although that is currently not a rule, but just a convention that makes sense in the context of everything else). Similarly, all the actions find themselves defined in a separate file, with a name ending in "Actions". Why is all this necessary? So that you (and, especially, all the maintainers of your code) can know immediately where to find things, which is a central concern of "convention over configuration", but this time brought to the world of the Java desktop.

So, here's my view file, called "AnagramGameGriffonView.groovy", which is automatically created (and registered in the appropriate places) by means of the "griffon create-app" command: 

I rewrote the Anagram game user interface as follows, using the same LayoutManager as the original, i.e., GridBagLayout:


application(title:'Anagrams', minimumSize:[297, 200], location:[50,50],
    pack:true, locationByPlatform:true) {
    menuBar( id: 'menuBar') {
        menu(text: 'File', mnemonic: 'F') {
    panel(border:emptyBorder(12)) {
        label(text: 'Scrambled Word:',
            constraints: gridBagConstraints(
                gridwidth: 1, gridheight: 1,
                fill: HORIZONTAL, anchor: WEST,
                weightx: 0.0, weighty: 0.0,
                insets: [0,0,12,6]))
        textField(id: 'scrambledWord', text: bind {model.scrambledWord},
            columns: 20, editable: false,
            constraints: gridBagConstraints(
                gridwidth: REMAINDER, gridheight: 1,
                fill: HORIZONTAL, anchor: CENTER,
                weightx: 1.0, weighty: 0.0,
                insets: [0,0,12,0]))
        label(text: 'Your Guess:',
            constraints: gridBagConstraints(
                gridwidth: 1, gridheight: 1,
                fill: HORIZONTAL, anchor: WEST,
                weightx: 0.0, weighty: 0.0,
                insets: [0,0,20,6]))
        textField(id: 'guessedWord',
            columns: 20,
            constraints: gridBagConstraints(
                gridwidth: REMAINDER, gridheight: 1,
                fill: HORIZONTAL, anchor: CENTER,
                weightx: 1.0, weighty: 0.0,
                insets: [0,0,20,0]))
        label(id:'feedbackLabel', text: bind {model.feedback},
            constraints: gridBagConstraints(
                gridx: 1, gridy: RELATIVE,
                gridwidth: REMAINDER, gridheight: 1,
                fill: HORIZONTAL, anchor: CENTER,
                weightx: 1.0, weighty: 0.0,
                insets: [0,0,20,0]))
        button(action: guessWordAction, constraints: gridBagConstraints(
                gridx: 1, gridy: RELATIVE,
                gridwidth: 1, gridheight: REMAINDER,
                fill: NONE, anchor: SOUTHEAST,
                weightx: 1.0, weighty: 1.0,
                insets: [0,0,0,6]))
        button(action: nextWordAction, constraints: gridBagConstraints(
                gridwidth: REMAINDER, gridheight: REMAINDER,
                fill: NONE, anchor: SOUTHEAST,
                weightx: 0.0, weighty: 1.0,
                insets: [0,0,0,0]))
        bean( model, guessedWord: bind {guessedWord.text} )

All that GridBagLayout stuff is pretty cumbersome and I'd be happy with a different LayoutManager, but since I'm trying to replicate the original as closely as possible, I simply went along with this complexity. At some point I'd like to see how GroupLayout would work in this context, as well as MiGLayout.

Next, look at lines 19, 39, and 57. What's all that "binding" about? The properties "text: bind {model.scrambledWord}" and "text: bind {model.feedback}" bind the text of the textfield (in the first case) and the text of the feedback label (in the second case) to a property defined in the model. When the model changes, via something that happens in the controller, the texts of these two components will automatically be updated.

Now look again at the definitions of the two buttons above, as well as the line with which the above lines begin. As explained in "Flying with Griffon", the combination of that initial statement with the two "action:" declarations in the buttons, lets the buttons be filled with the descriptions specified in a separate file, which as indicated above is called "AnagramGameGriffonActions.groovy":

actions {

action( id: 'guessWordAction',
name: "Guess",
closure: controller.guessWord,
accelerator: shortcut('G'),
mnemonic: 'G',
shortDescription: "Guess the given scrambled word"

action( id: 'nextWordAction',
name: "New Word",
closure: controller.nextWord,
accelerator: shortcut('N'),
mnemonic: 'N',
shortDescription: "Move to the next word"

action( id: 'exitAction',
name: "Exit",
closure: controller.exit,
mnemonic: 'E',

action( id: 'aboutAction',
name: "About",
closure: controller.about,
mnemonic: 'A',


Above, you see the action descriptions for 4 actions, two ("guessWordAction" and "nextWordAction") are used in the main view of the Anagram game, while the other two are used for the menu items under the "File" menu (lines 5 to 10). Since we are still in the view, we are not concerned in any way with the action events. As you can see above, the "closure" property points to the controller (which is automatically defined within the configuration files as being "AnagramGameGriffonController"), which is where we'll later see what the actions in question actually do.

Finally, in the same way as the actions are defined in a list of closures, the same is true for dialogs. Here's the "AnagramGameGriffonDialogs" file, which in this case defines only one dialog:

title: "About Anagram Game", minimumSize: [123,141],
preferredSize: [298,216], id: "aboutDialog",
modal: true, pack:true, locationByPlatform:true) {

columns: 25,
editable: false,
lineWrap: true,
rows: 8,
text:"Anagrams\n\nCopyright (c) 2003 Irritable Enterprises, Inc.",
wrapStyleWord: true,
border: null,
focusable: false)
button(text: "Close", actionPerformed: {event ->
constraints: gridBagConstraints(
gridx: 0, gridy: 1,
gridwidth: 1, gridheight: 1,
fill: NONE, anchor: SOUTHEAST,
weightx: 0.0, weighty: 0.0,
insets: [0,0,0,0]))


Notice that the button's "actionPerformed" is here defined within the Dialogs file, instead of in the controller. That's kind of cheating, but since what needs to happen is so trivial, it can be found here instead of in the controller. (At least, that's my interpretation, based on one of the samples that came with the Griffon distribution.) Here I again used the GridBagLayout because that's what the original used too.

The Controller

The controller is where ALL the processing takes place. All of it takes place within a single file called "AnagramGrameGriffonController.groovy". First of all, we need to put the original processing code there. This is the original code that figures out whether the user's entered code correctly unscrambled the given word:

private static final String[] WORD_LIST = {

private static final String[] SCRAMBLED_WORD_LIST = {

public String getWord(int idx) {
return WORD_LIST[idx];

public String getScrambledWord(int idx) {

public int getSize() {
return WORD_LIST.length;

public boolean isCorrect(int idx, String userGuess) {
return userGuess.equals(getWord(idx));

Note: The actual application has many more words (i.e., more than the 3 you see above), but I removed the majority of them to save space.

And here is the Groovified version of the above:

def WORD_LIST = [


def getWord(int idx) {

def getScrambledWord(int idx) {

def getSize() {

def isCorrect(int idx, String userGuess) {

A second area of processing that the controller is concerned with is the action events. Remember how the buttons and menu items in the View file referred to descriptions in the Actions file, which in turn referred to the controller? Well, here are the action events, all within the controller. Take special note of the references to the model (such as "model.feedback"), because here is where the model is changed which, because of the "bind" attributes pointed out in the previous section, will then result in the view automatically being updated, thus creating a loosely coupled view/controller relationship:

def guessWord = { evt ->
if (isCorrect(wordIdx, model.guessedWord)){
model.feedback = AnagramGameGriffonModel.FEEDBACK_CORRECT
} else {
model.feedback = AnagramGameGriffonModel.FEEDBACK_INCORRECT
view.guessedWord.text = ""

def nextWord = { evt ->
wordIdx = (wordIdx + 1) % getSize()
model.scrambledWord = getScrambledWord(wordIdx)
model.feedback = AnagramGameGriffonModel.FEEDBACK_NONE

def exit = { evt ->

def about = { evt ->

private void showDialog( dialogName ) {
def dialog = view."$dialogName"
if( dialog.visible ) return
int x = app.appFrames[0].x + (app.appFrames[0].width - dialog.width) / 2
int y = app.appFrames[0].y + (app.appFrames[0].height - dialog.height) / 2
dialog.setLocation(x, y)

Isn't it cool that the action events are in the same file as the related processing code? In the original Java application, this is what the "guessedWordActionPerformed" looked like:

private void guessedWordActionPerformed(java.awt.event.ActionEvent evt)
if (wordLibrary.isCorrect(wordIdx, guessedWord.getText())){
feedbackLabel.setText("Correct! Try a new word!");
} else {
feedbackLabel.setText("Incorrect! Try again!");

Notice how "isCorrect" is called on "wordLibrary" above. No longer is this necessary and no longer is the view and the controller as intertwined as it is above, in retrospect.

Another thing to notice is how the "showDialog" method above (lines 24 to 32, two snippets ago) works. Look at it. It receives a dialog name and then opens the related dialog in the view. The related dialog name is the id of the dialog, defined in this case as "aboutDialog". So, the same "showDialog" method could be used for every dialog in the application, i.e., you would use it wherever applicable, simply passing in the id of the dialog you'd like the "showDialog" method to show for you. But how does the "showDialog" method find the About box? Or any other dialog? In fact, how is the whole application initialized? That's the final element of the controller that hasn't been touched on yet. Similar to the approach taken in "Flying with Griffon", the controller exposes an entry point (and wiring to the other parts of the MVC triad) like this:

// these will be injected by Griffon
def model
def view

def wordIdx = 0

def loadPages() {
// called inside EDT
model.scrambledWord = getScrambledWord(wordIdx)

The "loadPages()" could be called anything at all. However, the "Startup.groovy" file, which is one of the application's lifecycle files, is as follows, i.e., is used to initialize the whole application, as well as to add the "AnagramGameGriffonDialogs" file to the mix:

def rootBuilder = app.builders.root
def rootController = app.controllers.root
def rootModel = app.models.root


And that's all that happens in the controller. In fact, everything happens in the controller, which is exactly how it should be.

The Model

Finally, nice and cleanly in a separate file, called "AnagramGameGriffonModel.groovy", all our static variables are found, so now we know exactly where to look for them:

import groovy.beans.Bindable

class AnagramGameGriffonModel {
static final String FEEDBACK_NONE = ""
static final String FEEDBACK_CORRECT = "Correct! Try a new word!"
static final String FEEDBACK_INCORRECT = "Incorrect! Try again!"

@Bindable String scrambledWord = ""
@Bindable String feedback = FEEDBACK_NONE
@Bindable String guessedWord = ""

In addition, note that we have three bindables here. What is a @Bindable? As discussed here, "When a property has this annotation the AST Transformation will generate (if it doesn't exist) a PropertyChangeSupport object, appropriate listener addition methods, and then a setter that uses the property change support. The end result is a bound JavaBeans property, with a whole lot less boilerplate code."


So, finally, what are some basic truths that can be gathered from all of the above? Well, aside from the fact that I now also have an applet and a JNLP application (though not working completely correctly yet, for reasons I don't understand), I was able to use Groovy instead of Java to do my coding, while producing exactly the same result as before. The advantages of Groovy over Java is that one can code much more quickly because many of Java's stodgy requirements (commas, return statements, etc) are simply not required. (Though, if you like them anyway, you can continue using them in Groovy.) Another nice thing about Groovy over Java is that a lot of file handling is done in far fewer lines of code (look at "Flying with Griffon" to see how easily HTML or XML can be parsed).

Then, despite the fact that you're using the far less structured coding rules that Groovy provides, Griffon enables you to put all your files in very structured places within your application. That's structure that helps rather than hinders. In some ways, the difference between using Griffon and not using Griffon is the same difference as having a cupboard to put your clothes into and not having one. Your life will still be okay without a cupboard, except that all your clothes will be on the floor. 

In the porting process described above, I quickly discovered that the Matisse GUI Builder has made me a very lazy programmer indeed. I couldn't even remember how to set the size of a textfield. First I tried "size", then "width", then "length", and eventually figured out that I should have been using "columns". Hand coding user interfaces has always been hard and GUI Builders like Matisse have come to the rescue and have done so very successfully. However, in the process, aren't we producing a generation of programmers who don't know the first thing about layout managers? "Ah," you might say, "but you don't need to know about layout managers anymore." That may be so. I don't need to know how to fix my sink either, because that's what plumbers are for. But in an emergency, I don't have the first clue where to start. Not knowing about "columns" is equivalent to not knowing how to shut off the mains when a pipe bursts. However, in this context, some layout managers are better than others when hand coding, and my guess is GridBagLayout is not the best for hand coders but, as I said, I was trying to be consistent with the original application. An exploration of MiGLayout and GroupLayout in this context is next on my agenda.

Finally, I'm not necessarily advocating porting all Swing applications everywhere to Griffon. I'd say it makes more sense to port to a framework that offers at least a docking system, because that will then provide the opportunity for the application to grow. If Griffon were to offer those kind of services (i.e., services for Swing applications like those provided by Spring RCP and NetBeans Platform), it would suddenly find itself in a very different ballpark, one that it probably is less interested in. On the other hand, for small/small-ish applications (i.e., that don't need a windowing system) that make use of Swing, i.e., applications that are small and will stay small, even the pure intellectual exercise (on top of the wonderful opportunity of using Groovy in a structured context) of moving it to Griffon is something worth considering. Like picking up your clothes and putting them away is also worth considering. Speaking of which, I should probably go and do just that...

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}