
- Building Rich Swing Applications with Groovy - Part I
- Building Rich Swing Applications with Groovy - Part II
- Building Rich Swing Applications with Groovy - Part III
Building Rich Swing Applications with Groovy - Part IV
Andres Almiray
On previous articles of this series you have learned how SwingBuilder can mitigate Swing's pain points, like verbose setup, threading issues and binding. But building a full fledged Swing application requires more work, this is where Griffon comes in, a brand new Swing application development framework based on the same principles as Grails: make your job fun and easy.
What is Griffon
Griffon is, in a nutshell, a Swing application development framework that follows closely the lead of Grails and its community. In other words, it brings to the Java desktop arena the lessons learned in Grails, adding a few tricks of its own in the process. There are several advantages in following Grails' model for example
- Convention over configuration: every artifact has an specific meaning and metadata just by following the naming convention or being placed at the correct location. This speeds up development as it reduces the amount of configuration.
- Groovy as glue language: the JVM is a great ecosystem for building applications but Java (the language) is not as expressive as Groovy. Another advantage is the usage of SwingBuilder as a DSL for declarative UI programming.
- Java friendly: the Griffon team loves Java and we know you do too, so don't throw away your Java knowledge and/or favorite libraries, they are more than welcome! Synergy is much better than reinventing the wheel all over again.
- Plugin based architecture: if the functionality you're looking for is not provided by the core framework it is most likely provided by a plugin, but if that is not the case then you could easily create a plugin of your own.
- Testing is first class citizen: we all recognize the importance of testing, it becomes more relevant with a dynamic language as Groovy, Griffon takes that into account by helping you keeping the bar green and the code clean.
Griffon also provides a set of command line tools similar to Grails, meaning that seasoned Grails developers may be able to pick up the pace quickly, it also means that Griffon developers may be able to jump into web development with Grails in a matter of hours; this is another major win, the time of adjustment when switching from web to desktop development is reduced, the frameworks will figure out most of the plumbing for you, leaving the task of getting acquainted with a particular technology (like Swing, HTML, CSS) to you.
If you are as excited as I am about Griffon with this preamble then let's not delay any longer, let's build an application! For this exercise we will revisit the FileViewer application described on the first part of this series, we will add more polish to it resulting in something similar to what is shown in the next figure
Figure 1
A quick survey on Figure 1 reveals that the application has a menu bar and a two content sections (note the dropShadow border on each section). Let's get the tools and start coding, shall we?
Griffon: Dowload and Install
The Griffon project is distributed in the same way as Grails is, this means you can download a zip package from http://griffon.codehaus.org/Download unpack it on the directory of your choice; set an environment variable GRIFFON_HOME pointing to the directory where you unpacked the binary distribution; make sure you have a JAVA_HOME environment variable that points to a Jdk5 installation; make sure GROOVY_HOME/bin and JAVA_HOME/bin are configured on your PATH environment variable. Or you could simply download the cross-platform installer (made with the wonderful IzPack project) that does the heavy lifting for you. Assuming you have configured Griffon correctly you should see a similar output when issuing the 'griffon' command
[aalmiray@localhost work]$ griffon Welcome to Griffon 0.1-SNAPSHOT - http://griffon.codehaus.org/ Licensed under Apache Standard License 2.0 Griffon home is set to: /usr/local/griffon No script name specified. Use 'griffon help' for more info or 'griffon interactive' to enter interactive mode
Building your first Griffon Application
We're ready to start building the FileViewer application, type 'griffon create-app FileViewer', you should see an output similar to this one
aalmiray@localhost work]$ griffon create-app FileViewer Welcome to Griffon 0.1-SNAPSHOT - http://griffon.codehaus.org/ Licensed under Apache Standard License 2.0 Griffon home is set to: /usr/local/griffon Base Directory: /tmp/work Running script /usr/local/griffon/scripts/CreateApp_.groovy Environment set to development [mkdir] Created dir: /tmp/work/FileViewer/griffon-app [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/conf [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/conf/keys [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/conf/webstart [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/controllers [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/i18n [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/lifecycle [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/models [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/resources [mkdir] Created dir: /tmp/work/FileViewer/griffon-app/views // SNIP ... the actual output is much longer than this Created Model for FileViewer Created View for FileViewer Created Controller for FileViewer Created Tests for FileViewer [propertyfile] Updating property file: /tmp/work/FileViewer/application.properties Created Griffon Application at /tmp/work/FileViewer
Right now you have a full working Swing application! the create-app command follows pretty much the same conventions as its Grails counterpart, this means that it created a bunch of directories that adhere to the Griffon conventions (Figure 2), it also created an MVC Group named FileViewer. Though both Grails and Griffon follow the MVC pattern Griffon treats its controllers in a different manner as the interaction in Swing applications is not driven by the request-response cycle that web applications have.
Figure 2
Griffon controllers are responsible for providing the business logic of your application, they should be the only components that interact directly with the view, to aid in that regard Models serve as a communication hub, they are perfect targets for binding data from and to the View. Views as you would have suspected are constructed using code understandable by SwingBuilder and its siblings. Let's peek inside of each member of the MVC group to see what our minimal application does right now. First the View:
A very simple view, really. It includes an application node that contains properties similar to those supported by frame which might make you think that this nodes resolves to a JFrame (which is partly true but let's wait until we get to the running stage to figure out the rest). On to the Model:
There is nothing much here, just an empty model and a suggestion that its properties can be observable, thanks to the @Bindable annotation. Lastly here are the contents of the basic Controller:
It definitely looks similar to a Grails controller but with more skeleton code. By default the Griffon runtime will inject a model and view properties into each matching controller if those properties are defined in the controller, that being said, remove the model property and it won't be set. Griffon will also inject a controller and model properties into each View script, that way you can link actions to the appropriate controller and configure binding on the matching model. Controller actions can be defined as either methods or closures (same as in Grails), I personally recommend the latter as changing the value of a property closure for testing purposes is much easier than overriding a method. Whichever way you choose make sure that the parameter always has a default value, it usually is set to the event that triggered the action (such as an ActionEvent or a MouseEvent), setting a default parameter makes it easier to call the action from an event-less method or in testing scenarios.
Griffon also gives you an opportunity to tweak the MVC triad upon instantiation, that is what the mvcGroupInit method is for, you could for instance read a resource file or spawn a background thread that performs a sanity check.
Running the Application
Griffon provides another command that runs the application, type the following on your console
[aalmiray@localhost FileViewer]$ griffon run-app Welcome to Griffon 0.1-SNAPSHOT - http://griffon.codehaus.org/ Licensed under Apache Standard License 2.0 Griffon home is set to: /usr/local/griffon Base Directory: /tmp/work/FileViewer Running script /usr/local/griffon/scripts/RunApp.groovy Environment set to development [mkdir] Created dir: /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/classes [groovyc] Compiling 11 source files to /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/classes [mkdir] Created dir: /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/resources/griffon-app/i18n [mkdir] Created dir: /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/resources/griffon-app/resources [copy] Copying 1 file to /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/resources/griffon-app/i18n [copy] Copying 1 file to /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/classes [copy] Copying 4 files to /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/resources [copy] Copied 1 empty directory to 1 empty directory under /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/resources [copy] Copying 1 file to /tmp/work/FileViewer/staging [copy] Copying 1 file to /tmp/work/FileViewer/staging [jar] Building jar: /tmp/work/FileViewer/staging/FileViewer.jar [copy] Copying 1 file to /tmp/work/FileViewer/staging [copy] Copying 4 files to /tmp/work/FileViewer/staging
That should popup a small Swing frame with a 'Content Goes Here' label (Figure 3), and that's pretty much it. But the fun doesn't end here. Swing applications can be deployed as webstart enabled application or even as applets, which is why Griffon supports those two option as well, without a single extra line of configuration from your side, yes you read that right: Griffon supports 3-way application deployment with zero configuration. If you'd like package and run the application in webstart mode then simply type 'griffon run-webstart', if applets are your thing then type 'griffon run-applet', which by the way will automatically be draggable if you run it with Java 6 update 10 (or above) and FireFox 3. Now that's being productive with Swing.
Figure 3
Updating the View
We're ready to add more content into the application, let's start with the view (as opposed to the Model) as the first thing you see is the view. It is also a good idea to start with the View because you can create a prototype application with no model and logic whatsoever.
As described in issue #2 SwingBuilder has a couple of official extensions (two more were added recently, look for the news in GroovyMag #3) one of them being SwingXBuilder, which we will use to build both header sections ("File Location" and "Contents"). You could locate a binary SwingXBuilder distribution and drop all the required jars into FileViewer/lib but there is a better way, we can install the builder using the power of plugins, a list of them at the time of this writing consists of the following ones
[aalmiray@localhost FileViewer]$ griffon list-plugins Welcome to Griffon 0.1-SNAPSHOT - http://griffon.codehaus.org Licensed under Apache Standard License 2.0 Griffon home is set to: /usr/local/griffon Base Directory: /tmp/work/FileViewer Running script /usr/local/griffon/scripts/ListPlugins_.groovy set to development Reading remote plug-in list ... Plug-ins available in the Griffon.codehaus.org repository are listed below: ------------------------------------------------------------- code-coverage <0.1> -- Generates Code Coverage reports easyb <no releases> -- No description available fest <0.1> -- Enables application testing with FEST flamingo-builder <0.1> -- FlamingoBuilder Plugin installer <0.1> -- Allows creating installers for your Griffon application jdepend <0.1> -- Runs JDepend metrics on your Griffon application jide-builder <0.1> -- Jide Builder Plugin macwidgets-builder <0.1> -- MacWidgetsBuilder Plugin swingx-builder <0.1-SNAPSHOT> -- SwingX Builder Plugin tray-builder <0.1> -- TrayBuilder Plugin
Swingx-builder is the one we need, you can install it by issuing the following command
[aalmiray@localhost FileViewer]$ griffon install-plugin swingx-builder Welcome to Griffon 0.1-SNAPSHOT - http://griffon.codehaus.org/ Licensed under Apache Standard License 2.0 Griffon home is set to: /usr/local/griffon
Base Directory: /tmp/work/FileViewer Running script /usr/local/griffon/scripts/InstallPlugin.groovy Environment set to development Reading remote plug-in list ... [get] Getting: http://svn.codehaus.org/griffon/plugins/griffon-swingx-builder/tags/RELEASE_0_1-SNAPSHOT/griffon-swingx-builder-0.1-SNAPSHOT.zip [get] To: /home/aalmiray/.griffon/0.1-SNAPSHOT/plugins/griffon-swingx-builder-0.1-SNAPSHOT.zip // SNIP ... Griffon will download the plugin here, marking the process with dots [copy] Copying 1 file to /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/plugins Installing plug-in swingx-builder-0.1-SNAPSHOT [mkdir] Created dir: /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/plugins/swingx-builder-0.1-SNAPSHOT [unzip] Expanding: /home/aalmiray/.griffon/0.1-SNAPSHOT/plugins/griffon-swingx-builder-0.1-SNAPSHOT.zip into /home/aalmiray/.griffon/0.1-SNAPSHOT/projects/FileViewer/plugins/swingx-builder-0.1-SNAPSHOT Executing swingx-builder-0.1-SNAPSHOT plugin post-install script ... Adding SwingXBuilder to Builders.groovy Plugin swingx-builder-0.1-SNAPSHOT installed Found events script in plugin swingx-builder
Griffon downloaded the latest version of the plugin, installed it and configured the builder for its usage, you can take a look at griffon-app/conf/Builder.groovy to see how it was add to your application. The full contents of griffon-app/conf/FileViewerView.groovy are more than what are actually shown next, for the full source code please refer to the attached sources or go to FileViewer's git repository. Here we can see the usage of 'jxtitledPanel' to create each section, know that all nodes contributed by SwingXBuilder will have a 'jx' prefix.
There are two model properties referenced by the view (fileUrl and fileContents), those properties are used in conjunction with binding to communicate with the controller and any other component that registers itself as a PropertyChangeListener on the model; every time the textField's value changes the fileUrl model property will be updated, whenever the fileContents model property is updated the editorPane's text property will mirror the change (for more information on binding refer to GroovyMag #3). There are three actions that reference the controller, this is how you will normally wire up behavior in the view.
Adding Behavior
Let's take a look now at the Model and the Controller
A minimal controller implementation as it doesn't validate the existence of the file, nor if it is a directory or if it has the correct read permissions, nevertheless this implementation runs perfectly well as shown in Figure 4
Figure 4
It is left as an exercise to the reader to finish up the missing bits: add a menubar, add a dropShaodw border to both sections, display an 'About' Dialog, verify the existence of the file to read and its permission; or you could simple peruse the attached sources or visit FileViewer's git repository. In the end you should have a fancy file viewer Griffon application, before we finish this article let me show you some statistics
[aalmiray@localhost FileViewer]$ griffon stats Welcome to Griffon 0.1-SNAPSHOT - http://griffon.codehaus.org/ Licensed under Apache Standard License 2.0 Griffon home is set to: /usr/local/griffon Base Directory: /tmp/work/FileViewer Running script /usr/local/griffon/scripts/Stats.groovy Environment set to development +----------------------+-------+-------+ | Name | Files | LOC | +----------------------+-------+-------+ | Controllers | 1 | 44 | | Models | 1 | 5 | | Views | 1 | 35 | | Lifecycle | 5 | 76 | | Integration Tests | 1 | 4 | +----------------------+-------+-------+ | Totals | 9 | 164 | +----------------------+-------+-------+
You could argue, what is the advantage between FileViewer's single script version (25 LoC) vs the Griffon version (9 files and 164 LoC), try adding a menubar and more actions to the first version and you will notice that it gets harder and harder when the features start to pile up, that doesn't happen with the Griffon version as every feature you add has its own place.
Conclusion
As the proverbial saying goes, this is just the tip of the iceberg there is more to Griffon that what is described here, for example we didn't touch the subject of the application life cycle nor build events, but judging by the commands we used on this article and the application's structure you can be pretty sure they follow closely on the Grails conventions.
The Griffon project is still in the early stages but it has the potential to take the Swing world by storm, you are more than welcome to join the Griffon community and help us shape the future of Swing application development.
Update #1: the latest stable release of Griffon is 0.2. FileViewer's source code has been updated to run with 0.2.
Keep on Groovying!