Posted on Dec 28, 2010 by

Painless Java Desktop Application Development with Griffon, MigLayout, and IntelliJ

As Java consultants and developers, a very large percentage our projects are web applications.

We have become accustomed to depending on MVC web frameworks, and almost take for granted that at some level, we will be handling HTTPServletRequests, providing HTTPServletResponses, and deploying our applications to either a servlet container or a full J2EE server.

Every once in a while, however, the need arises to write an old-fashioned, desktop, thick-ui, non-web-centric application. If you have ever written a Java Swing application, you may have painful memories of dealing with things like BorderLayouts, GridBags, Event Listeners and Event Dispatching Threads, JFrames, and the like.

As a web developer, when you think about building your desktop application, you might start to think about how you will structure your application, and how you will build and package it. Will you have to re-package your code and all of your dependencies in one giant uber-jar?

Will you make it a web-start application? Fortunately, some folks have been thinking about, and working on these issues.

Griffon is a Groovy-based MVC framework for writing desktop applications. It is written by the Groovy Swing team, and is heavily influenced by Grails. If you have worked with Grails, then Griffon will be a very familiar environment. Griffon answers all of the above questions, and more.

Using the convention-over-configuration and command-line-driven scaffolding paradigms made popular by Ruby on Rails, Django, and Groovy on Grails, among others, Griffon generates the project structure, the model, view, and controller objects, and all of the configuration for you. You then customize these to produce your application.

Working in Groovy, of course, makes this a much more pleasurable experience, dispensing with the ceremony of Java development, and letting you write high-level, fairly terse, yet expressive code, and enabling you to accomplish quite a bit very quickly. The prime example of this in the Griffon framework is the SwingBuilder object - a Groovy Builder that provides a DSL for building Swing user interfaces. Views in Griffon are SwingBuilder scripts, and greatly simplify the task of creating Swing UIs.

Griffon uses the Gant build system (as does Grails), and by default, packages your application with all its dependencies as an uber-jar (with the correct manifest entries to make it 'executable'), as an applet, and as a java-webstart application.

When working with Swing, even with the SwingBuilder, one potentially still has to deal with the frustration of laying out the user interface by using a LayoutManager. GridBagLayout is flexible (too flexible?), but about as complicated as it can get. BorderLayout is fairly simple, but the simplicity comes at a cost - you typically wind up nesting lots of BorderLayouts in other BorderLayouts to get the desired behavior. Over the years, alternate layout managers have appeared, and some are quite useful, most notably the JGoodies Form Layout and RiverLayout. I tried MigLayout with my Griffon project, and found it to be very flexible and easy to use.

Here is a screenshot of the main UI of my application:

ServiceTester

As mentioned previously, the View is a Groovy SwingBuilder script. The first part of the script sets up an 'application' - the application's "MainFrame", which Griffon interprets according to the runtime environment - for a stand-alone application, it will build a JFrame, and for an applet it will build a JApplet:

package servicetester
import java.awt.FlowLayout
import net.miginfocom.swing.MigLayout
application(title: 'Service Tester', minimumSize: [297, 200], maximumSize: [297,200], location: [50, 50], pack: true, locationByPlatform: true) {

Note that minimumSize, maximumSize, location, etc. are set as properties here. In straight Java Swing code, these would have been at least one line of code each, and several would have involved building Dimension objects.

Next is setup of the actions and the menu bar that uses the actions, in this case, a simple 'File' menu with a 'Quit' menu item to exit the application.
Note the use of Groovy closures for associating the Action object with implementation of the action.

actions {

    action(id: 'exitAction',
            name: 'Quit',
            closure: { System.exit(1) },
            mnemonic: 'Q',
            accelerator: shortcut('Q'),
            shortDescription: 'Quit'
    )
  }
  menuBar(id: 'menuBar') {
    menu(text: 'File', mnemonic: 'F') {
      menuItem(exitAction)
    }
  }

I chose to use the MigLayout for the main panel of the view. There are lots of ways to initialize a MigLayout, I used the "fill" parameter to the constructor, which tells it to grow to occupy all available space, which makes sense here because it is the top-level component in the frame.

panel(border: emptyBorder(12), layout: new MigLayout('fill')) {

As you can see in the screenshot, there are three groupings of controls - the group of fields above the separator (the input fields), the group of fields below the separator (the output fields), and the "button bar" at the bottom (only one button). To achieve these groupings, I added two panels, each with MigLayout initialized with "fill", as with the main frame, and a third panel with a FlowLayout for the button bar.

These MigLayouts are given constraints of "growy" and "wrap":

panel(layout: new MigLayout('fill'),
          constraints: "growy, wrap") {

"growy" means "grow vertically", and "wrap" means that the next component should be placed below, instead of to the right. With this configuration, the MigLayout in the main frame gives the button bar it's preferred size, and resizes the other two panels as the main window is resized, giving equal weight to them.

The two MigLayout panels are very similar. Each has sets of label / input field pairs, which look like:

label(text: 'AMQ Broker URL:')
      textField(
              id: 'brokerUrl',
              columns: 40,
              text: bind(target: model, targetProperty: 'brokerUrl', value: model.brokerUrl),
              enabled: bind {model.enabled},
              constraints: 'wrap'
      )

The "text:bind" property binds the value entered in the textField to a property in the model - the value entered by the user is placed into the model.

One big "gotcha" here is that bi-directional binding is not supported. It is possible to bind in each direction, but not both directions at the same time. As one might expect, lots of people are asking for this capability, so I wouldn't be surprised to see it show up soon. I ran into this when trying to set the initial value of the field from the value in the model. The answer for this case is to use the "value" property as shown above - it gives the field it's initial value, and does not participate in the binding mechanism, so it doesn't present the same problem as bi-directional binding. If you do truly need bi-directional binding, at the moment, you'll have to roll your own.

Here is the full source code for this View.

Due to its highly-developed sense of structure and general lack of need for configuration, working on a Griffon application using the command line and a text editor is very straightforward. However, IDE support is always nice. Eclipse does not directly support Griffon, but does support Groovy. Both NetBeans and IntelliJ have direct support for Griffon projects. IntelliJ 9 and 10 have direct support for creating, importing, testing, running, and debugging Griffon applications, and I found these features to work quite well.

I found the combination of Groovy, Griffon, MigLayout, and IntelliJ to be an extremely productive environment for creating Swing applications, providing much of the application infrastructure, allowing me to dispense with ceremony and concentrate on writing the parts of the application that make it unique. Before embarking on a large or mission-critical Griffon project, however, you do need to be aware that Griffon is still in Beta (0.9.2-beta-3 was just released on Dec. 21st, 2010), and so is still in a bit of a state of flux.

If you are interested in reading about Griffon, there's documenatation at at the Griffon home page, and the Griffon In Action book is available from Manning.

Tags: , , , ,