Desktop Development - The Fun Way

Tutorial – Griffon: Building Desktop Applications with Groovy

Andres Almiray
griffon.11

Creator of Griffon, Andres Almiray takes us on a tour of the Grails-inspired application framework that is bringing fun back to desktop development

Building desktop applications can be an enjoyable experience if you’re willing to throw in a bit of Groovy into the mix. Griffon is an application framework that follows the spirit of Grails to bring back the fun to desktop development.

Desktop application development, a term that isn’t heard much these days as web development nor concurrency and parallelism are. However that doesn’t mean it’s dead as some proclaim. There are indeed some industry sectors where a desktop application is the top alternative for solving a particular problem; in some other environments it’s the only choice for security reasons. Think of financial institutes, banks, the health industry, biological research, chemical laboratories, satellite operations and the military; just to name a few. All of them impose a particular set of restrictions where desktop applications excel over web applications, such as security, access to local resources, devices and port communications. The other thing that they have in common is Griffon. Yes, the Griffon framework has helped teams in all those industries and spaces to get the job done.

You may have heard of Griffon before but still wonder what it is. In short, it’s a desktop application platform for the JVM. It has strong roots in the Groovy community as the project is the brain child of the Groovy Swing team: Danno Ferrin, James Williams and myself. That being said, you may excuse me if I get a bit excited explaining some of Griffon’s features, as I’m very fond of the project. A key driving force behind the framework’s design is that it should be easy to pick up by Java developers. It should also enable quick coding cycles while keeping the source nice and tidy. Finally, the productivity gains and the fun factor must be immediately perceived.

For these reasons the team decided to follow in the steps of the Grails framework and its community. There are a lot of similarities between both frameworks. For instance, both have a command line interface that helps you get around with the usual tasks of creating, building, packaging and deploying applications. Both frameworks leverage the Groovy language as a glue for their respective software stacks. The tool integration is also quite good, as major IDEs and popular text editors offer good support for dealing with this kind of projects.

But enough of the theory, let’s get some practice! The rest of this article will be devoted to building a simple address book application. We will definitely not build a full-fledged application in the few pages that we have to spare but it’s my hope that all the things to be discussed will give you enough pointers to get you going with the framework.

Setup and Configuration

The first step is to download and configure Griffon on your computer; there are several choices for doing so. If you pick the universal installer from the download page it will unpack the binaries and configure the path environments for you, particular on Windows platforms. Or if you’re on Linux machines you can give it a try to either the RPM or Debian-based packages. ZIP or TGZ packages may work as your last resort. Simply download the package, uncompress it on a directory of your choosing – preferably one without spaces. Next configure an environment variable GRIFFON_HOME pointing to the directory where the Griffon binary distribution was unpacked. Lastly make sure that the PATH environment variable contains a reference to GRIFFON_HOME/bin. If all goes well, invoking griffon command with the –version flag turned on should display a similar output as the following one

$ griffon –version

———————————————

Griffon 0.9.5

———————————————

Build: 15-Mar-2012 12:56 PM

Groovy: 1.8.6

Ant: 1.8.2

Slf4j: 1.6.4

Spring: 3.1.0.RELEASE

JVM: 1.6.0_29 (Apple Inc. 20.4-b02-402)

OS: Mac OS X 10.6.8 x86_64

 

Alright. Time to get down to business…

Initial Steps 

First things first, how do we create an application? Typically you may choose the Maven-based approach and select an appropriate archetype to bootstrap a project. Or you just can simply create a new directory, fetch some Ant scripts and be done with it. Or let your trusty IDE decide. Choices, choices, choices. The griffon command line tool is here to help. Every Griffon application starts in the same way, by invoking the following command

$ griffon create-app addressbook

$ cd addressbook

You’ll notice a flurry of lines in the output. Go ahead and inspect the contents of the newly created application if you want. The create-app command initializes the application by creating several directories and some files. One of these directories is of particular importance, its name is griffon-app. Within this directory you’ll find another set of directories that help keeping the source code organized. Figure 1 shows the expanded content of the griffon-app directory as created moments ago.

Figure 1:  Contents of the address book application

As you can appreciate, Griffon makes use of the MVC pattern to arrange the elements that comprise an application. When an application is created, you’ll also get an initial MVC group whose name matches the name of the application. Inside each MVC member there’s enough code to make the application run. Yes, believe it or not, the application is ready to be launched. Go back to the console and execute the following command

$ griffon run-app

This should compile the application sources, package resources, roundup dependencies and launch the application. In a matter of seconds you should see a window popping up, like the one shown in Figure 2.

Figure 2: Addressbook application running for the first time

Granted, it doesn’t loom like much, but we haven’t written a single line of code yet! Listing 1 shows what you’ll find inside the file griffon-app/views/addressbook/AddressbookView.groovy when you open it.

Listing 1 – AddressbookView

 

package addressbook
application(title: 'addressbook',
  preferredSize: [320, 240],
  pack: true,
  //location: [50,50], 
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]) {
    // add content here
    label('Content Goes Here') // delete me
}

 

What we see here is a Swing-based Domain Specific Language (or DSL for short) based on a popular Groovy feature: builders. In our particular case we’re dealing with SwingBuilder. Builders are but a collection of nodes and rules that know how to build hierarchical structures. It so happens that a Swing UI is comprised of a tree of components. In the View we can observe a top level node named “application” and some properties being applied to it. Next we see a child node named “label” with a single text entry. You may recognize the code structure with what’s shown by Figure 2. That’s the power of the Swing DSL. The code and the UI resemble each other a lot, it’s quite easy to follow how components are structured when reading the DSL.

                      

Building the UI

Now that we have seen a bit of code in the Views, let’s continue with this MVC member, we’ll cover the other two in a moment. In the spirit of keeping things simple we’ll update the UI so that it looks like Figure 3.

Figure 3: New looks for the address book application

Let’s decompose each section. On the left we see a white space with a title ‘Contacts’. This will be a list that holds all the contacts in our address book. In the middle we see a form that we may use to edit the details of a particular contact found in the list. Next on the right we discover a series of buttons that will perform operations on a contact. You may also appreciate a menu named ‘Contacts’ which contains menu items with the same names as the buttons. Listing 2 describes the updated AddressbookView.

Listing 2 – AddressbookView Updated

 

package addressbook
application(title: 'Addressbook',
  pack: true,
  resizable: false,
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]) {
    menuBar {
        menu('Contacts') {
            controller.griffonClass.actionNames.each { name ->
                menuItem(getVariable(name))
            }            
        }
    }
    migLayout(layoutConstraints: 'fill')
    list(model: eventListModel(source: model.contacts), 
         constraints: 'west, w 180! ',
         border: titledBorder(title: 'Contacts'),
         selectionMode: ListSelectionModel.SINGLE_SELECTION,
         keyReleased: { e ->  // enter/return key
             if (e.keyCode != KeyEvent.VK_ENTER) return
             int index = e.source.selectedIndex
             if (index > -1) model.selectedIndex = index
         },
         mouseClicked: { e -> // double click
             if (e.clickCount != 2) return
             int index = e.source.locationToIndex(e.point)
             if (index > -1) model.selectedIndex = index
         })
    panel(constraints: 'center', border: titledBorder(title: 'Contact')) {
        migLayout(layoutConstraints: 'fill')
        for(propName in Contact.PROPERTIES) {
            label(text: GriffonNameUtils.getNaturalName(propName) + ': ',
                        constraints: 'right')
            textField(columns: 30, constraints: 'grow, wrap',
                text: bind(propName, source: model.currentContact,
                           mutual: true))
        }
    }
    panel(constraints: 'east', border: titledBorder(title: 'Actions')) {
        migLayout()
        controller.griffonClass.actionNames.each { name ->
            button(getVariable(name), constraints: 'growx, wrap')
        }
    }
}

We can see the menuBar node we mentioned earlier. We can also see three main components: a list that will be placed on the west side; a panel in the middle that somehow creates a pair or label and textField for each property found in a list of properties. Then we see the third component, a panel that holds buttons. The code may look a bit magical at first glance but really what we’re doing is taking advantage of the conventions laid out by Griffon. A View MVC member has access to the other two members, that is, the Model and the Controller. The job of the View is to lay out the UI components. The job of the Controller is to hold the logic that reacts to user input. The job of the Model is to serve as a communication bridge between View and Controller. We can see references to a model and a controller variables in the View; these variables point to their respective MVC members.

Next we’ll update the Model found in griffon-app/models/addressbook/AddressbookModel.groovy. Here we’ll make sure the Model keeps a list of contacts in memory; it will also hold a reference to the contact currently being edited. We’ll make use of GlazedLists (a popular choice among Swing developers) to organize the list of contacts. Listing 3 shows all the code that’s required to build the list and keep a reference to the currently edited contact. Now, the contacts list has a special binding defined in relation to its elements. Whenever an element is edited it will publish a change event that the list will intercept; the list in turn will update whoever is interested in lists changes. Looking back to Listing 2 you can see that the list definition makes use of a listEventModel node. This is exactly the component that will notify the UI whenever an update is available, this repainting the affected region. And we only had to connect a pair of components for this to happen! The AddressbookModel works with two domain specific classes: Contact and ContactPresentationModel. The first can be seen as a plain domain class, as it defines what a contact should be in terms of simple properties. The latter is an observable wrapper around the Contact class. Presentation models are usually decorators that can support binding operations. We’ll see these two classes in just a moment.

Listing 3

 

package addressbook
import groovy.beans.Bindable
import ca.odell.glazedlists.*
import griffon.transform.PropertyListener
import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener

class AddressbookModel {
    final EventList<ContactPresentationModel> contacts = 
             new ObservableElementList<ContactPresentationModel>(
        GlazedLists.threadSafeList(
        new BasicEventList<ContactPresentationModel>()),
        GlazedLists.beanConnector(ContactPresentationModel)
    )
    
    final ContactPresentationModel currentContact = new ContactPresentationModel()
    
    @PropertyListener(selectionUpdater)
    @Bindable int selectedIndex = -1
    
    private selectionUpdater = { e ->
        currentContact.contact = contacts[selectedIndex].contact
    }
    
    AddressbookModel() {
        currentContact.addPropertyChangeListener(new ModelUpdater())
    }
    
    private class ModelUpdater implements PropertyChangeListener {
        void propertyChange(PropertyChangeEvent e) {
            if(e.propertyName == ‘contact’ || selectedIndex < 0) return
            contacts[selectedIndex][e.propertyName] = e.newValue
        }
    }

    void removeContact(Contact contact) {
        currentContact.contact = null
        ContactPresentationModel toDelete = contacts.find { 
            it.contact == contact 
        }
        if(toDelete != null) contacts.remove(toDelete)
    }
}

 

But before we show the domain let’s cover the final MVC member: the Controller. It’s the job of the controller to react to user input and orchestrate the flow of information. For now we only need to fill in the blanks to make the application work again, for example by pasting the code shown in Listing 4 into griffon-app/controllers/addresbook/AddressbookController.groovy

Listing 4

 

package addressbook
class AddressbookController {
    def model
    void newAction(evt) { }
    void saveAction(evt) { }  
    void deleteAction(evt) { }

 

Do you remember that Models mediate data between Controllers and Views? That’s precisely why there’s a model property on the controller class. Griffon sports a basic dependency injection mechanism that will assure each MVC member can talk to the other two as long as certain properties are defined in their respective classes.

                      The Domain

If you’re a Grails developer you may have noticed we did not start by modelling the domain, which is the usual case when working with Grails applications. There are two reasons for making this choice. First, to show you the basics of the MVC members and how they interact with each other. The second is that Griffon does not support domain classes out of the box, at least not as Grails understands it, that is, there’s no GORM API for Griffon, yet. But we can manage ourselves by writing simple domain classes. Listing 5 for example shows what the Contact domain class may look like.

Listing 5

 

package addressbook
@groovy.transform.EqualsAndHashCode
class Contact {
    long id
    String name
    String lastname
    String address
    String company
    String email
    
    String toString() { "$name $lastname : $email" }
    
    static final List<String> PROPERTIES = ['name', 'lastname', 
         'address', 'company', 'email']

 

This class can be defined in the file src/main/addressbook/Contact.groovy. Next to it we’ll define the companion presentation model, in src/main/addressbook/ContactPresentationModel.groovy with the code seen in Listing 6 in it.

Listing 6

 

package addressbook
import groovy.beans.Bindable
@griffon.transform.PropertyListener(propertyUpdater)
class ContactPresentationModel {
    // attributes
    @Bindable String name
    @Bindable String lastname
    @Bindable String address
    @Bindable String company
    @Bindable String email
    
    // model reference
    @Bindable Contact contact = new Contact()
    
    private propertyUpdater = { e ->
        if(e.propertyName == 'contact') {
            for(property in Contact.PROPERTIES) {
                def bean = e.newValue
                delegate[property] = bean != null ? bean[property] : null
            }
        }
    }

    String toString() { "$name $lastname" }

    void updateContact() {
        if(contact) {
            for(property in Contact.PROPERTIES) {
                contact[property] = this[property]
            }
        }
    }
}

 

As we mentioned before, the domain class is simple in its design; it only needs to be concerned with the data we want to keep. The presentation model on the other hand mirrors the domain class by having the same properties but with a slight modification: each one of them is observable. This means that whenever the value of any of those properties changes, an event will be fired. These events are the ones that enable binding. Griffon makes use of the @Bindable annotation to instruct the Groovy compiler to inject a set of instructions into the byte code that makes this class an observable one. @Bindable belongs to a set of special interfaces found in the Groovy language that open the door for byte code manipulation. This set is known as AST transformations. There’s another AST transformation found in this code, it’s @PropertyListener. This transformation is a fancy way to define and attach a PropertyChangeListener on a particular class and property. In our case, we’re attaching a PropertyChangeListener that reacts on all property changes. The next effect of all the code we have in Listing 6 is that when a Contact instance is attached to an instance of ContactPresentationModels, all of the property values will be copied from the contact to the model. The reverse operation will take effect when the updateContact() method is called.

Good. We’re almost ready to run the application again. But before we do, we must install a group of plug-ins that will make our life easier. We said we’ll make use of GlazedLists. This library is provided by a plug-in, so we’ll install that. In the view we make use of MigLayout, so we’ll install a plug-in for it too. Finally we’ll install another plug-in that makes creating UI actions based on controller methods a breeze. Go to your console prompt and type the following commands

$ griffon install-plugin glazedlists

$ griffon install-plugin miglayout

$ griffon install-plugin actions

After a few lines of output for each plug-in install we have all that’s needed to run the application again.

    

Making Contacts Persistent

Time to finish up the application. We have a few tasks ahead of us:

  • Fill the code required by each controller action

  • Save the contact list to a database

  • Make sure to load the contacts from the database when the application starts up

 Filling up the actions is a straightforward operation, as the bulk of the data manipulation is already taken care of by the bindings we have put in place. Listing 7 shows the final code for AddressbookController.

Listing 7 – AddressbookController

package addressbook
class AddressbookController {
    def model
    def storageService

    void newAction(evt) {
        model.selectedIndex = -1
        model.currentContact.contact = new Contact()
    }
    
    void saveAction(evt) {
        // push changes to domain object
        model.currentContact.updateContact()
        boolean isNew = model.currentContact.contact.id < 1
        // save to db
        storageService.store(model.currentContact.contact)
        // if is a new contact, add it to the list
        if(isNew) {
            def cpm = new ContactPresentationModel()
            cpm.contact = model.currentContact.contact
            model.contacts << cpm
        }
    }
    
    void deleteAction(evt) {
        if(model.currentContact.contact && model.currentContact.contact.id) {
            // delete from db
            storageService.remove(model.currentContact.contact)
            // remove from contact list
            execInsideUIAsync {
                model.removeContact(model.currentContact.contact)
                model.selectedIndex = -1
            }
        }
    }
    
    void dumpAction(evt) {
        storageService.dump()
    }

    void mvcGroupInit(Map args) {
        execFuture {
            List<ContactPresentationModel> list = storageService.load().collect([]) {
                new ContactPresentationModel(contact: it)
            }
            execInsideUIAsync {
                model.contacts.addAll(list)
            }
        }
    }
}

 

The first action, newAction, is concerned by resetting the current selection (if any) and creating an empty Contact. The saveAction() should push the changes from the presentation model back to the domain object, store the data in the database and in the case that this is a new contact, add it to the list of contacts. Notice that when dealing with database concerns we’ll delegate to another component called storageService which we’ll see right after we finish describing the Controller. The third action deletes a contact first by removing it from the database, then removing it from the contacts list. We added a fourth action that will be used to dump the database contents into the console. The last piece of information found in the code relates to loading the data from the database and filling up the contacts list. The method name is special as it’s a hook into the MVC lifecycle. This particular method will be called after all MVC members have been instantiated, think of it as a member initializer. We’re ready to have a look at the storageService component.

Services in Griffon are nothing more than regular classes but they receive special treatment from the framework. For example, they are treated as singletons and will be automatically injected into MVC members as long as the member defines a property whose name matches the service name. Armed with this knowledge we’ll create a StorageService class, like this:

$ griffon create-service storage

This will create a file in griffon-app/services/addressbook/StorageService.groovy with default content. Listing 8 shows the code that must be put inside that file for the application to work.

Listing 8

package addressbook
class StorageService {
    List<Contact> load() {
        withSql { dsName, sql ->
            List tmpList = []
            sql.eachRow('SELECT * FROM contacts') { rs ->
                tmpList << new Contact(
                    id:       rs.id,
                    name:     rs.name,
                    lastname: rs.lastname,
                    address:  rs.address,
                    company:  rs.company,
                    email:    rs.email
                )
            }
            tmpList
        }
    }

    void store(Contact contact) {
        if(contact.id < 1) {
            // save
            withSql { dsName, sql ->
                String query = 'select max(id) max from contacts'
                contact.id = (sql.firstRow(query).max as long) + 1
                List params = [contact.id]
                for(property in Contact.PROPERTIES) {
                    params << contact[property]
                }
                String size = Contact.PROPERTIES.size()
                String columnNames = 'id, ' + Contact.PROPERTIES.join(', ')
                String placeHolders = (['?'] * size + 1)).join(',')
                sql.execute("""insert into contacts ($columnNames)
                   values ($placeHolders""", params)
            }
        } else {
            // update
            withSql { dsName, sql ->
                List params = []
                for(property in Contact.PROPERTIES) {
                    params << contact[property]
                }
                params << contact.id
                String clauses = Contact.PROPERTIES.collect([]) { prop ->
                    "$prop = ?"
                }.join(', ')
                sql.execute("""update contacts
                    set $clauses where id = ?""", params)
            }
        }
    }
    
    void remove(Contact contact) {
        withSql { dsName, sql ->
            sql.execute('delete from contacts where id = ?', [contact.id])
        }
    }
    
    void dump() {
        withSql { dsName, sql ->
            sql.eachRow('SELECT * FROM contacts') { rs ->
                println rs
            } 
        }
    }
}

 

Each one of the service methods makes use of a method named withSql. This method becomes available if we install another plug-in. Let’s do that now:

$ griffon install-plugin gsql

Perfect. We now have enabled Groovy SQL support in our little application. Groovy SQL is another DSL on top of regular SQL. With it, you can make SQL calls using a programmatic API that closely resembles working with objects and object graphs. As a matter of fact, you can even apply Groovy closures, Groovy strings, and other Groovy tricks, like those shown in the implementation of the StorageService class. There are two more items we must take care of before launching the application again. We must tell the GSQL plug-in that the withSql method must be applied to services; secondly, we must define the database schema. As we said earlier there’s no GORM API yet, database schemas must be defined by hand.

The first task is accomplished by editing the file griffon-app/conf/Config.groovy and appending the following line

griffon.datasource.injectInto = ['service']

The second task gets done by creating a file in griffon-app/resources/schema.ddl with the following content:

 

DROP TABLE IF EXISTS contacts;

CREATE TABLE contacts(

id INTEGER NOT NULL PRIMARY KEY,

name VARCHAR(30) NOT NULL,

lastname VARCHAR(30) NOT NULL,

address VARCHAR(100) NOT NULL,

company VARCHAR(50) NOT NULL,

email VARCHAR(100) NOT NULL

);

Good, we’re done. What about seeding the database with some initial data? In Grails this is done by editing a file named BootStrap.groovy, in Griffon it’s done by editing griffon-app/conf/BootstrapGsql.groovy. Let’s add an entry to the contacts table, as Listing 9 shows.


import groovy.sql.Sql
class BootstrapGsql {
    def init = { String dataSourceName = 'default', Sql sql ->
        def contacts = sql.dataSet('contacts')
        contacts.add(
            id: 1,
            name: 'Andres',
            lastname: 'Almiray',
            address: 'Kirschgartenstrasse 5 CH-4051 Switzerland',
            company: 'Canno Engineering AG',
            email: 'andres.almiray@canoo.com'
        )
    }

    def destroy = { String dataSourceName = ‘default’, Sql sql ->
    }
} 

Phew. Now we’re really done. Launch the application once more. You should see one entry in the contacts list as Figure 4 shows. Select it with the mouse and either press enter or double-click on it. This will make the contact the active one and place all of its values in the middle form. Edit some of its properties, then click on the save button. Create a new contact and save it too. Now click on the dump button. You should see as many rows in the output as entries can be found in the contacts list.

Figure 4: Address book application with one entry selected

The Aftermath

We have had quite the whirlwind tour on the Griffon basics. Granted, there are more things that meet the eye in just a few pages and code listings. I hope this has been enough to wet your appetite for more, or at least consider giving Griffon a try. All of the behaviour we wrote so far fits in a mere 291 lines of code. Don’t believe me? Run the following command:

 

$ griffon stats

 

A table listing all sources by type will appear, similar to this one:

 

+———————-+——-+——-+

| Name | Files | LOC |

+———————-+——-+——-+

| Models | 1 | 32 |

| Views | 1 | 45 |

| Controllers | 1 | 39 |

| Services | 1 | 57 |

| Lifecycle | 5 | 3 |

| Groovy/Java Sources | 2 | 40 |

| Unit Tests | 1 | 13 |

| Integration Tests | 1 | 15 |

| Configuration | 2 | 47 |

+———————-+——-+——-+

| Totals | 15 | 291 |

+———————-+——-+——-+

Nice. The full source code for this application can be found at GitHub. There are more things that we could add to this application. For example, there’s no error handling whatsoever. What if SQL is not your cup of tea? No problem, you can pick any of the supported NoSQL options. Or maybe Swing is not for you. No problem either, Griffon supports SWT and JavaFX as well. Changing UI toolkits will mean changing the View mostly while keeping the other components almost intact. There are definitely many choices ahead of you.

Author Bio: Andres Almiray

Andres Almiray is a Java/Groovy developer with more than 12 years of experience in software design and development. He has been involved in web and desktop application development since the early days of Java. His current interests include Groovy and desktop apps. He is a true believer of open source and has participated in popular projects like Groovy, Griffon, JMatter and DbUnit, as well as starting his own projects (Json-lib, EZMorph, GraphicsBuilder, JideBuilder). Andres is a founding member and current project lead of the Griffon framework. He likes to spend time with his beloved wife, Ixchel, when not hacking around. 

Additional Resources

http://www.glazedlists.com

http://www.miglayout.com

 

This article originally appeared in Java Tech Journal – Groovy. Read more of that issue here.

Author
Andres Almiray
Andres Almiray is a Java/Groovy developer with more than 12 years of experience in software design and development. He has been involved in web and desktop application development since the early days of Java. His current interests include Groovy and desktop apps. He is a true believer of open source and has participated in popular projects like Groovy, Griffon, JMatter and DbUnit, as well as starting his own projects (Json-lib, EZMorph, GraphicsBuilder, JideBuilder). Andres is a founding member and current project lead of the Griffon framework. He likes to spend time with his beloved wife, Ixchel, when not hacking around.
Comments
comments powered by Disqus