Suppose you have a complicated Ant build which builds your latest code from SVN. It is very complicated and running it requires a set of parameters to be passed into it. This involves creating and maintaining a set of properties files and probably also batch files which start the build as you don't want to manually type in all props every time. Also, source files need to be updated before running the build. Does it not sound like a tough manual and repeatable work?
How about a simple GUI where you could type in (or even choose from drop downs, if possible) required properties and run the build by clicking one button? Yes, why not, but we don't want to waste two weeks or more writing such a tool, in Swing for example. Ok, how about Groovy and its brilliant features, like SwingBuilder, AntBuilder and closures? Hmm, that sounds much better!
Recently I have developed such a GUI build tool using Groovy. It took me 2 days to code it. Now guess what features it has:
- full checkout from SVN to a local directory
- project update before each build
- running the old Ant build, written in XML
- intercepting build output for error messages
- reading tool properties from an external XML config
All in about 350 lines of groovy code! Most of the code are
SwingBuilder closures where I am building the GUI and
AntBuilder doing the build job. The most interesting parts however are:
Defining external svn ant taskTo get that to work some additional jars are needed. Assuming they are under local lib directory, the svn ant task can be defined like that:
def PATH = "task.path"
ant.path(id:PATH) {
ant.pathelement(location:"lib/ganymed.jar")
ant.pathelement(location:"lib/svnkit.jar")
ant.pathelement(location:"lib/svnant.jar")
ant.pathelement(location:"lib/svnClientAdapter.jar")
ant.pathelement(location:"lib/svnkit-javahl.jar")
}
ant.taskdef(
resource:"org/tigris/subversion/svnant/svnantlib.xml",
classpathref:PATH)
Accessing SVNNow, having the svn ant task defined, accessing svn (checkout, update etc.) is as easy as:
def svnCall = { task ->
ant.svn(username:USER, password:PASS) {
task()
}
}
where the task can be svn checkout:
svnCall {
ant.checkout(url:url, destpath:dest)
}
or svn update:
svnCall {
ant.update(dir:dest)
}
As you can see there is no code duplication. The ant.svn call is common for all svn tasks. I am passing a closure as a parameter into it, which then can call ant.checkout or ant.update. Closures - it's brilliant, isn't it?
Running an external Ant build and reading output info.The XML ant build needs to be started from command line (no, we are not going to rewrite it using Gant, not yet ;-) ). How do we do this in Grooovy?
def build = "cmd /c ant -f build.xml
-propertyfile build.properties target".execute()
The build variable is actually a Process instance, so we can now intercept the in and err streams:
build.in.eachLine { line ->
//consume each line
}
build.err.eachLine { line ->
//consume each line
}
Why intercepting the err stream? Well, if the build fails Ant writes BUILD FAILED + the error to that stream. Then I can show an info message using JOptionPane as MsgBOX:
import javax.swing.JOptionPane as MsgBOX
...
MsgBOX.showMessageDialog(gui, message, title,
MsgBOX.INFORMATION_MESSAGE)
The build toolTo give you an idea what the build tool looks like, here is a screenshot (I have removed real labels/data) :
Title borders, drop downs, text fields, labels and box layout - all in 2 days? Yep, plus event handling for all the combo boxes, which make up some sort of hierarchy, actually. This makes the tool a bit complicated, but not the code! Event handling is as easy as clicking a button (actionPerformed as a closure):
def comboBox = swing.comboBox(items:items,
actionPerformed:action)
and the hierarchy is read from an external XML file, using Groovy's XML parser:
def XML = new File(xmlFile).getText()
root = new XmlParser().parseText(XML)
Nice and simple, isn't it?
SummaryIt was such a pleasure writing the tool, in no time actually. I have to admit that it was my first bigger piece of Groovy code, which now really works fine in my company! What else can I say - thank you, Groovy! You saved me at least a week of swing coding in Java!