Over a million developers have joined DZone.

Flying with Griffon

· Java Zone

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

New frameworks come and go. They tend to stand and fall based on whether people start experimenting with them. The new Griffon framework is unlikely to fall any time soon, since the large and vibrant Groovy/Grails community has a vested interest in it.

However, to do my bit, I'm going to start playing with it myself. The Griffon creators were smart enough to include some samples with their intial distribution and so the first application I've made is a small subset of one of these samples. Though it is small, many of the basic Griffon concepts are touched on and the end result is no different to any other Swing application. It is a JXTree inside a JScrollPane within a JPanel, with a JToolBar containing two buttons that expand/collapse tree nodes containing the text of the nodes in the NetBeans Javadoc:

However, I also have an applet (and a JNLP application), created via "griffon run-app", i.e., the same Griffon command that created the above Swing application. The applet behaves identically to the Swing application, so that the 'Collapse all' and 'Expand all' buttons do what you would expect them to do, with the general appearance of the applet being identical to the Swing application:

Note: For purposes of this simple example, I didn't implement a TreeSelectionListener, so nothing happens when you click any of the leaf nodes above.

Let's first look at all the code, before putting everything together into an application. Since Griffon encourages an MVC structure, the code is going to be split into those three parts.

The View

We begin with a simple view that does nothing:

application( title: "Griffon Demo", size: [250,300], locationByPlatform: true ) {
panel( ) {
scrollPane( constraints: CENTER ) {
jxtree( id: "topics" )
toolBar( constraints: SOUTH ) {
button( )
button( )

The above is the complete content (i.e., there's nothing more than that) in a Groovy file called "GriffonDemoView". Why do we assign an id to the JXTree? So that we can refer to it from the controller! That's where we'll get the content of the JXTree and then pass it into the view. The Groovy code is such that one should be able to read the above with the naked eye and then immediately visualize what the user interface will consist of. However, we want our two buttons to be able to do something. Therefore, we will have a separate Groovy file, in this case called "GriffonDemoActions", where all our actions will be found. Then we'll hook them into the view. Here's the complete content of a separate Groovy file, where all our actions will be defined:

actions {

action( id: 'collapseAllAction',
name: "Collapse all",
closure: controller.collapseAll,
accelerator: shortcut('C'),
mnemonic: 'C',
shortDescription: "Collapse all categories",
smallIcon: imageIcon("org/tango-project/tango-icon-theme/16x16/actions/go-first.png")

action( id: 'expandAllAction',
name: "Expand all",
closure: controller.expandAll,
accelerator: shortcut('E'),
mnemonic: 'E',
shortDescription: "Expand all categories",
smallIcon: imageIcon("org/tango-project/tango-icon-theme/16x16/actions/go-last.png")


Everything above is how you'd expect it to be, except for each action's "closure" property. There you see a reference to a closure defined in the controller. That's where the real work is done by each action. We'll look at that part when we deal with the controller. For now, we'll hook the actions into the buttons:


application( title: "Griffon Demo", size: [250,300], locationByPlatform: true ) {
panel( ) {
scrollPane( constraints: CENTER ) {
jxtree( id: "topics" )
toolBar( constraints: SOUTH ) {
button( action: collapseAllAction )
button( action: expandAllAction )

Take note of line 1 above, which includes the Groovy file "GriffonDemoActions", so that our two buttons can refer to the actions we defined in that file. Currently the Actions file needs to be wired explicitly, but possibly in the future "GroovyDemoActions" would automatically be included, Andres tells me.

The Controller

Next, the controller.

import javax.swing.tree.*

class GriffonDemoController {

def model
def view

def expandAll = { evt ->
ViewUtils.expandTree( view.topics )

def collapseAll = { evt ->
ViewUtils.collapseTree( view.topics )

def loadPages() {
doOutside {
def contents = new DefaultMutableTreeNode("NetBeans Javadoc")
def leafNodes = new URL(model.menuUrl).text
def lastCategory = null
(leafNodes =~ /href="(([a-zA-Z-]+)\/(.+?)\.html)"/).each { match ->
def category = new DefaultMutableTreeNode(match[2])
def pageNode = new PageNode( title: match[3] )
if( lastCategory?.toString() == category.toString() ){
lastCategory.add( new DefaultMutableTreeNode(pageNode) )
lastCategory = category
category.add( new DefaultMutableTreeNode(pageNode) )
contents.add( category )
doLater {
view.topics.model = new DefaultTreeModel(contents)


Let's take a look at what's going on here. Lines 9 and 13 refer to a utility class that I copied, together with the PageNode file (which is a simple POGO) from the GrailsSnoop sample (which is part of the Griffon distribution). I'm hoping the whole ViewUtils class will be part of the API and I've written to Andres about this. Notice how it is wired into the view, via the "topics" id of the JXTree. Similarly, line 34 fills the JXTree's model with the content that is built from line 18 to 32. (Look at line 20 to see a reference to the model, where the URL to the location that will be parsed is set.)

And when is the "loadPages()" method called? From one of the lifecycle files that is automatically created when you run "griffon create-app"; this particular one is called "Startup.groovy":
def rootController = app.controllers.root

Hence, the entry point to your application is in the controller, via the lifecycle files that the "griffon create-app" creates for you. But, how does Griffon know that "app.controllers.root" is "GriffonDemoController"? For that purpose, several configuration files are found in "griffon-app/conf", populated during the running of "griffon create-app". One of these, called "Application.groovy", marks out our generated files as follows:

mvcGroups {
root {
model = 'GriffonDemoModel'
view = 'GriffonDemoView'
controller = 'GriffonDemoController'

In other words, not only does Griffon explicitly handle your lifecycle, but it also generates skeletons for the requisite classes for you.

The Model

Finally, the model. In this case, it's really simple:

class GriffonDemoModel {
String baseUrl = "http://bits.netbeans.org/dev/javadoc/"
String menuUrl = baseUrl + "allclasses-frame.html"

And that's it! The application is complete. Here's the structure once everything above is put together:

Of the files listed above, the ONLY one you had to create manually was "GriffonDemoActions", though even that could be included in the "create-app" script in the future. Now run "griffon run-app" and you'll have a Swing application, an applet, and a JNLP application. As shown at the start of this article, your application will list the content of the NetBeans Javadoc. Pretty cool, I reckon.

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!


Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}