Archives for December 2008
Griffon Weather App
James Clarke recently updated his JSON Weather Service application to fête the release of JavaFX 1.0. I happened upon it on DZone and decided to port it to Griffon. The original source is 227 lines of code whereas the Griffon version is 151.
Here's the code breakdown:
| Name | Files | LOC |
| Controllers | 1 | 38 |
| Models | 1 | 13 |
| Views | 1 | 28 |
| Lifecycle | 5 | 68 |
| Integration Tests | 1 | 4 |
| Total | 9 | 151 |
The first major difference is that instead of an instance class Weather, all the variables live in the GriffonWeatherServiceModel.groovy.
import groovy.beans.Bindable
class GriffonWeatherServiceModel {
@Bindable stationName = ''
@Bindable clouds = ''
@Bindable windDirection = 0
@Bindable windSpeed = 0
@Bindable temperature = 0
@Bindable dewPoint = 0
@Bindable humidity = 0
@Bindable seaLevelPressure = 0
@Bindable observation = ''
@Bindable code = ''
}
One place where I was able to condense code was in GriffonWeatherServiceController.groovy.
import net.sf.json.JSONArray
import javax.swing.JOptionPane
import net.sf.json.groovy.JsonSlurper
class GriffonWeatherServiceController {
// these will be injected by Griffon
def model
def view
void mvcGroupInit(Map args) {
// this method is called after model and view are injected
}
def parseJSON = { evt = null, text ->
JsonSlurper slurper = new JsonSlurper()
def json = slurper.parseText(text)?.get('weatherObservation')
if (json != null) {
for (prop in model.getProperties()) {
if (json.get(prop.key) != null) {
model.{prop.key} = json.getString(prop.key)
}
}
} else {
JOptionPane.showMessageDialog(null, "Either the code is invalid or there was a problem.", "error", JOptionPane.ERROR_MESSAGE)
}
}
def getJSON = { url ->
doLater {
app.appFrames[0].getGlassPane().setSize(app.appFrames[0].getSize())
app.appFrames[0].getGlassPane().setVisible(true)
app.appFrames[0].getGlassPane().setBusy(true)
}
doOutside{
def result = new java.net.URL(url).getText()
parseJSON(null, result)
app.appFrames[0].pack()
app.appFrames[0].getGlassPane().setBusy(false)
app.appFrames[0].getGlassPane().setVisible(false)
}
}
}
Instead of walking the tree for each attribute, I inspect the properties of the model and see if they exist in the JSONObject. If so, they are assigned. If an incorrect code is entered (or there is no Internet), an error dialog appears. Retrieving and parsing the JSON is done outside the EDT.
Another area of major savings was in the view(GriffonWeatherServiceView.groovy). I was able to declare the functionality of a label/value pair and repeatedly invoke the defined closures.
import net.miginfocom.swing.MigLayout
application(title:'Griffon Weather', pack:true, size:[700,400], pack:true, locationByPlatform:true, layout:new MigLayout()) {
label("Airport Code:")
textField(id:'tf',columns:5, constraints:'spanx',
actionPerformed:{controller.getJSON('http://ws.geonames.org/weatherIcaoJSON?ICAO=K'+tf.text?.toUpperCase())
})
def createHorizontalBox = { fieldName, boundParam ->
label(fieldName, constraints:"newline")
label(id:fieldName.replace(' ','_')+'Label', foreground: java.awt.Color.BLUE,
text:bind(source:model, sourceProperty:boundParam), constraints:'wrap'
)
}
def createHorizontalBoxWithSuffix = { fieldName, boundParam, suffix ->
label(fieldName, constraints:'newline')
label(id:fieldName.replace(' ','_')+'Label', foreground: java.awt.Color.BLUE,
text:bind(source:model, sourceProperty:boundParam, converter:{it + suffix}), constraints:"wrap, spanx"
)
}
createHorizontalBox('Station: ', "stationName")
createHorizontalBox('Clouds: ', "clouds")
createHorizontalBoxWithSuffix('Wind Direction: ', "windDirection", " degrees")
createHorizontalBoxWithSuffix('Wind Speed: ', "windSpeed", " knots")
createHorizontalBoxWithSuffix('Temperature: ', "temperature", " C degrees")
createHorizontalBoxWithSuffix('Dew Point: ', "dewPoint", " C degrees")
createHorizontalBoxWithSuffix("Humidity: ", "humidity", "%")
createHorizontalBoxWithSuffix("Sea Level Pressure: ", "seaLevelPressure", "mb")
// createHorizontalBox("METAR Observation: ", "observation")
}
On startup, I declared a JXBusyLabel to live on our GlassPane and indicate when the system is busy. From Startup.groovy:
import java.awt.Dimension import java.awt.geom.* import org.jdesktop.swingx.JXBusyLabel import org.jdesktop.swingx.painter.BusyPainter def busyLabel = new JXBusyLabel(app.appFrames[0].getSize()) busyLabel.setOpaque(false) busyLabel.setBusy(false) busyLabel.getBusyPainter().setHighlightColor(java.awt.Color.DARK_GRAY) busyLabel.getBusyPainter().setPointShape(new Ellipse2D.Float(1,1,10,10)) app.appFrames[0].setGlassPane(busyLabel)

Griffon BOF at Devoxx 2008
Generally, I prefer giving BOFs to regular sessions because there are more informal, intimate, and people feel less intimidated to stop you and ask questions. The downside is that most conferences seem to schedule them after 7 PM when most people are first getting a chance to relax so attendance can suffer a bit. Maybe a good place for them would be partway into lunch hour. Anyways...
Being the last BOF of the day but possibly bolstered by the carry-over from the Grails BOF before it, the room was pretty full with about 30-40 people. There was lots of interest and questions about bind/@Bindable and how Griffon stacks up to JavaFX. Several noted how SwingBuilder looks similar to JavaFXScript to which I pointed out that SwingBuilder was around first. Some were especially surprised at how much Java you could use with Griffon, eventually evolving to groovier code rather than having to make a break with all Java code and learn a totally new language. I could see the lightbulbs lighting up when they realized that not only does Groovy extend their ROI on learning Java but Griffon does the same for Grails. It's a giant sybiotic chain.
Thanks to Griffon co-despot Andres Almiray, I was able to demo SwingPad, a Griffon app to prototype Swing UIs without having to declare and initialize a SwingBuilder...very similar to GfxPad. 24 hours before our 0.0 release, we had basic Netbeans support (thanks to Geertjan Wielaga) and this was certainly a big surprise for this release. I only had a day to play around with it and change around my slides.
The cold I had been recovering from made an appearence and somehow I segued for a couple minutes on European Fanta vs. America Fanta.
Here are my slides:
Devoxx 2008
Devoxx 2008 is over so expect to see a deluge of wrap-up blog post (like this one ;-) ). Even if you are on the West coast, I think Devoxx is worth the trip. Unlike JavaOne, you can actually find hotels for less than $90 per night, decent food is reasonably priced and a full access pass is about 1/3 of a CommunityOne + JavaOne pass. As expected, there was a major JavaFX push. IMHO, It bordered on insanity. Developers are born cynics. If you have 5 sessions and the presenters are repeat the same slogan phases, it comes off a little forced.
As always, the JavaPosse was a blast, though technical difficultes forced @joeracer to be Tor's interpreter for most of the session. Much thanks to Atlassian for the beer.
There were lots of sessions of interest to Groovyists. Guillaume gave a 3hr talk on Groovy and Grails on the first of the University days. Andy Glover led a Tools in Action session on using REST with Grails. John F. Smart talked about easyB. There was also a BOF on Grails by Fabrice Robini and of course my Griffon BOF.
I'll preface the next line by saying that I know that probably no one else does it yet. It would really be nice if the BOFs were recorded. Maybe not with the painstaking effort of the main sessions but there were a lot of cool BOFs I went to that the people at home won't get to enjoy.