<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Kaptain on ... stuff</title>
	<atom:link href="http://www.kellyrob99.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kellyrob99.com/blog</link>
	<description>Tales of development, life and the folly that goes along with both</description>
	<lastBuildDate>Thu, 29 Mar 2012 03:53:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>JFreeChart with Groovy and Apache POI</title>
		<link>http://www.kellyrob99.com/blog/2012/03/18/jfreechart-with-groovy-and-apache-poi/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=jfreechart-with-groovy-and-apache-poi</link>
		<comments>http://www.kellyrob99.com/blog/2012/03/18/jfreechart-with-groovy-and-apache-poi/#comments</comments>
		<pubDate>Mon, 19 Mar 2012 01:50:09 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[builders]]></category>
		<category><![CDATA[ChartBuilder]]></category>
		<category><![CDATA[DSL]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[ExcelBuilder]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[JFreeChart]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[MarkupBuilder]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[theKaptain]]></category>
		<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1845</guid>
		<description><![CDATA[The point of this article is to show you how to parse data from an Excel spreadsheet that looks like this: and turn it into a series of graphs that look like this: Recently I was looking for an opportunity to get some practice with JFreeChart and ended up looking at a dataset released by [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/10/24/groovy-reverse-map-sort-done-easy/' rel='bookmark' title='Groovy reverse map sort done easy'>Groovy reverse map sort done easy</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>The point of this article is to show you how to parse data from an Excel spreadsheet that looks like this:</p>

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/excelheaders.png" title="" class="shutterset_singlepic78" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/78_watermark_700x400_excelheaders.png" alt="excelheaders" title="excelheaders" />
</a>

<p>and turn it into a series of graphs that look like this:</p>

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/naturalresourcescanadanewseedlings-total-ca.png" title="" class="shutterset_singlepic79" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/79_watermark_700x400_naturalresourcescanadanewseedlings-total-ca.png" alt="naturalresourcescanadanewseedlings-total-ca" title="naturalresourcescanadanewseedlings-total-ca" />
</a>

<p>Recently I was looking for an opportunity to get some practice with JFreeChart and ended up looking at a dataset released by the Canadian government as part of their <a href="http://www.data.gc.ca">&#8216;Open Data&#8217; initiative</a>.</p>
<p>The particular set of data is entitled <a href="http://www.data.gc.ca/default.asp?lang=En&#038;n=5175A6F0-1&#038;xsl=datacataloguerecord&#038;xml=5175A6F0-61E1-49FC-8E5D-0BBCDAF5969D&#038;formid=F2E3F796-9AF1-4237-8C5B-10456B12ACF3&#038;showfromadmin=1&#038;readonly=true">&#8216;Number of Seedlings Planted by Ownership, Species&#8217;</a> and is delivered as an Excel spreadsheet, hence the need for the <a href="http://poi.apache.org/">Apache POI</a> library in order to read the data in. As is fairly usual, at least in my experience, the Excel spreadsheet is designed primarily for human consumption which adds a degree of complexity to the parsing. Fortunately the spreadsheet does follow a repetitive pattern that can be accounted for fairly easily, so this is not insurmountable. Still, we want to get the data out of Excel to make it more approachable for machine consumption so the first step is to convert it to a JSON representation. Once it is in this much more transportable form we can readily convert the data into graph visualizations using JFreeChart.</p>
<h2>The spreadsheet format</h2>
<p>Excel as a workplace tool is very well established, can increase individual productivity and is definitely a boon to your average office worker. The problem is that once the data is there it&#8217;s often trapped there. Data tends to be laid out based on human aesthetics and not on parsability, meaning that unless you want to use Excel itself to do further analysis, there&#8217;s not a lot of options. Exports to more neutral formats like csv suffer from the same problems- namely that there&#8217;s no way to read in the data coherently without designing a custom parser. In this particular case, parsing the spreadsheet has to take into account the following:</p>
<ul>
<li>Merged cells where one column is meant to represent a fixed value for a number of sequential rows.</li>
<li>Column headers that do not represent all of the actual columns. Here we have a &#8216;notes&#8217; column for each province that immediately follows its&#8217; data column. As the header cells are merged across both of these columns, they cannot be used directly to parse the data.</li>
<li>Data is broken down into several domains that lead to repetitions in the format.</li>
<li>The data contains a mix of numbers where results are available and text where they are not. The meanings of the text entries are described in a table at the end of the spreadsheet.</li>
<li>Section titles and headers are repeated throughout the document, apparently trying to match some print layout, or perhaps just trying to provide some assistance to those scrolling through the long document.</li>
</ul>
<p>Data in the spreadsheet is first divided into reporting by Provincial crown land, private land, Federal land, and finally a total for all of them.</p>
<p>Within each of these sections, data is reported for each tree species on a yearly basis across all Provinces and Territories along with aggregate totals of these figures across Canada.</p>
<p>Each of these species data-tables has an identical row/column structure which allows us to create a single parsing structure sufficient for reading in data from each of them separately.</p>
<h2>Converting the spreadsheet to JSON</h2>
<p>For parsing the Excel document, I&#8217;m using the Apache POI library and a Groovy wrapper class to assist in processing. The wrapper class is very simple but allows us to abstract most of the mechanics of dealing with the Excel document away. The full source is available on <a href="http://www.technipelago.se/content/technipelago/blog/44">this blog post from author Goran Ehrsson</a>. The key benefit is the ability to specify a window of the file to process based on &#8216;offset&#8217; and &#8216;max&#8217; parameters provided in a simple map. Here&#8217;s an example for reading data for the text symbols table at the end of the spreadsheet.</p>
<p>We define a Map which states which sheet to read from, which line to start on(offset) and how many lines to process. The ExcelBuilder class(which isn&#8217;t really a builder at all) takes in the path to a File object and under the hood reads that into a <a href="http://poi.apache.org/apidocs/org/apache/poi/hssf/usermodel/HSSFWorkbook.html">POI HSSFWorkbook</a> which is then referenced by the call to the <em>eachLine</em> method.</p>
<pre class="brush: groovy; title: ; notranslate">
public static final Map SYMBOLS = [sheet: SHEET1, offset: 910, max: 8]
...
    final ExcelBuilder excelReader = new ExcelBuilder(data.absolutePath)
    Map&lt;String, String&gt; symbolTable = [:]
    excelReader.eachLine(SYMBOLS) { HSSFRow row -&gt;
        symbolTable[row.getCell(0).stringCellValue] = row.getCell(1).stringCellValue
    }
</pre>
<p>Eventually when we turn this into JSON, it will look like this:</p>
<pre class="brush: plain; title: ; notranslate">
    &quot;Symbols&quot;: {
        &quot;...&quot;: &quot;Figures not appropriate or not applicable&quot;,
        &quot;..&quot;: &quot;Figures not available&quot;,
        &quot;--&quot;: &quot;Amount too small to be expressed&quot;,
        &quot;-&quot;: &quot;Nil or zero&quot;,
        &quot;p&quot;: &quot;Preliminary figures&quot;,
        &quot;r&quot;: &quot;Revised figures&quot;,
        &quot;e&quot;: &quot;Estimated by provincial or territorial forestry agency&quot;,
        &quot;E&quot;: &quot;Estimated by the Canadian Forest Service or by Statistics Canada&quot;
    }
</pre>
<p>Now processing the other data blocks gets a little bit trickier. The first column consists of 2 merged cells, and all but one of the other headers actually represents two columns of information: a count and an optional notation. The merged column is handled by a simple <em>EMPTY</em> placeholder and the extra columns by processing the list of headers;.</p>
<pre class="brush: groovy; title: ; notranslate">
public static final List&lt;String&gt; HEADERS = ['Species', 'EMPTY', 'Year', 'NL', 'PE', 'NS', 'NB', 'QC', 'ON', 'MB', 'SK', 'AB',
    'BC', 'YT', 'NT *a', 'NU', 'CA']
/**
* For each header add a second following header for a 'notes' column
* @param strings
* @return expanded list of headers
*/
private List&lt;String&gt; expandHeaders(List&lt;String&gt; strings)
{
    strings.collect {[it, &quot;${it}_notes&quot;]}.flatten()
}
</pre>
<p>Each data block corresponds to a particular species of tree, broken down by year and Province or Territory. Each species is represented by a map which defines where in the document that information is contained so we can iterate over a collection of these maps and aggregate data quite easily. This set of constants and code is sufficient for parsing all of the data in the document.</p>
<pre class="brush: groovy; title: ; notranslate">
        public static final int HEADER_OFFSET = 3
        public static final int YEARS = 21
        public static final Map PINE = [sheet: SHEET1, offset: 6, max: YEARS, species: 'Pine']
        public static final Map SPRUCE = [sheet: SHEET1, offset: 29, max: YEARS, species: 'Spruce']
        public static final Map FIR = [sheet: SHEET1, offset: 61, max: YEARS, species: 'Fir']
        public static final Map DOUGLAS_FIR = [sheet: SHEET1, offset: 84, max: YEARS, species: 'Douglas-fir']
        public static final Map MISCELLANEOUS_SOFTWOODS = [sheet: SHEET1, offset: 116, max: YEARS, species: 'Miscellaneous softwoods']
        public static final Map MISCELLANEOUS_HARDWOODS = [sheet: SHEET1, offset: 139, max: YEARS, species: 'Miscellaneous hardwoods']
        public static final Map UNSPECIFIED = [sheet: SHEET1, offset: 171, max: YEARS, species: 'Unspecified']
        public static final Map TOTAL_PLANTING = [sheet: SHEET1, offset: 194, max: YEARS, species: 'Total planting']
        public static final List&lt;Map&gt; PROVINCIAL = [PINE, SPRUCE, FIR, DOUGLAS_FIR, MISCELLANEOUS_SOFTWOODS, MISCELLANEOUS_HARDWOODS, UNSPECIFIED, TOTAL_PLANTING]
        public static final List&lt;String&gt; AREAS = HEADERS[HEADER_OFFSET..-1]

        ...

        final Closure collector = { Map species -&gt;
            Map speciesMap = [name: species.species]
            excelReader.eachLine(species) {HSSFRow row -&gt;
                //ensure that we are reading from the correct place in the file
                if (row.rowNum == species.offset)
                {
                    assert row.getCell(0).stringCellValue == species.species
                }
                //process rows
                if (row.rowNum &gt; species.offset)
                {
                    final int year = row.getCell(HEADERS.indexOf('Year')).stringCellValue as int
                    Map yearMap = [:]
                    expandHeaders(AREAS).eachWithIndex {String header, int index -&gt;
                        final HSSFCell cell = row.getCell(index + HEADER_OFFSET)
                        yearMap[header] = cell.cellType == HSSFCell.CELL_TYPE_STRING ? cell.stringCellValue : cell.numericCellValue
                    }
                    speciesMap[year] = yearMap.asImmutable()
                }
            }
            speciesMap.asImmutable()
        }
</pre>
<p>The defined <em>collector</em> Closure returns a map of all species data for one of the four groupings(Provincial, private land, Federal and totals). The only thing that differentiates these groups is their offset in the file so we can define maps for the structure of each simply by updating the offsets of the first.</p>
<pre class="brush: groovy; title: ; notranslate">
    public static final List&lt;Map&gt; PROVINCIAL = [PINE, SPRUCE, FIR, DOUGLAS_FIR, MISCELLANEOUS_SOFTWOODS, MISCELLANEOUS_HARDWOODS, UNSPECIFIED, TOTAL_PLANTING]
    public static final List&lt;Map&gt; PRIVATE_LAND = offset(PROVINCIAL, 220)
    public static final List&lt;Map&gt; FEDERAL = offset(PROVINCIAL, 441)
    public static final List&lt;Map&gt; TOTAL = offset(PROVINCIAL, 662)

    private static List&lt;Map&gt; offset(List&lt;Map&gt; maps, int offset)
    {
        maps.collect { Map map -&gt;
            Map offsetMap = new LinkedHashMap(map)
            offsetMap.offset = offsetMap.offset + offset
            offsetMap
        }
    }
</pre>
<p>Finally, we can iterate over these simple map structures applying the <em>collector</em> Closure and we end up with a single map representing all of the data.</p>
<pre class="brush: groovy; title: ; notranslate">
        def parsedSpreadsheet = [PROVINCIAL, PRIVATE_LAND, FEDERAL, TOTAL].collect {
            it.collect(collector)
        }
        Map resultsMap = [:]
        GROUPINGS.eachWithIndex {String groupName, int index -&gt;
            resultsMap[groupName] = parsedSpreadsheet[index]
        }
        resultsMap['Symbols'] = symbolTable
</pre>
<p>And the JsonBuilder class provides an easy way to convert any map to a JSON document ready to write out the results.</p>
<pre class="brush: groovy; title: ; notranslate">
        Map map = new NaturalResourcesCanadaExcelParser().convertToMap(data)
        new File('src/test/resources/NaturalResourcesCanadaNewSeedlings.json').withWriter {Writer writer -&gt;
            writer &lt;&lt; new JsonBuilder(map).toPrettyString()
        }
</pre>
<h2>Parsing JSON into JFreeChart line charts</h2>
<p>All right, so now that we&#8217;ve turned the data into a slightly more consumable format, it&#8217;s time to visualize it. For this case I&#8217;m using a combination of the JFreeChart library and the <a href="http://java.net/projects/groovychart">GroovyChart project</a> which provides a nice DSL syntax for working with the JFreeChart API. It doesn&#8217;t look to be under development presently, but aside from the fact that the jar isn&#8217;t published to an available repository it was totally up to this task.</p>
<p>We&#8217;re going to create four charts for each of the fourteen areas represented for a total of 56 graphs overall. All of these graphs contain plotlines for each of the eight tree species tracked. This means that overall we need to create 448 distinct time series. I didn&#8217;t do any formal timings of how long this takes, but in general it came in somewhere under ten seconds to generate all of these. Just for fun, I added GPars to the mix to parallelize creation of the charts, but since writing the images to disk is going to be the most expensive part of this process, I don&#8217;t imagine it&#8217;s speeding things up terribly much.</p>
<p>First, reading in the JSON data from a file is simple with JsonSlurper.</p>
<pre class="brush: groovy; title: ; notranslate">
        def data
        new File(jsonFilename).withReader {Reader reader -&gt;
            data = new JsonSlurper().parse(reader)
        }
        assert data
</pre>
<p>Here&#8217;s a sample of what the JSON data looks like for one species over a single year, broken down first by one of the four major groups, then by tree species, then by year and finally by Province or Territory.</p>
<pre class="brush: plain; title: ; notranslate">
{
    &quot;Provincial&quot;: [
        {
            &quot;name&quot;: &quot;Pine&quot;,
            &quot;1990&quot;: {
                &quot;NL&quot;: 583.0,
                &quot;NL_notes&quot;: &quot;&quot;,
                &quot;PE&quot;: 52.0,
                &quot;PE_notes&quot;: &quot;&quot;,
                &quot;NS&quot;: 4.0,
                &quot;NS_notes&quot;: &quot;&quot;,
                &quot;NB&quot;: 4715.0,
                &quot;NB_notes&quot;: &quot;&quot;,
                &quot;QC&quot;: 33422.0,
                &quot;QC_notes&quot;: &quot;&quot;,
                &quot;ON&quot;: 51062.0,
                &quot;ON_notes&quot;: &quot;&quot;,
                &quot;MB&quot;: 2985.0,
                &quot;MB_notes&quot;: &quot;&quot;,
                &quot;SK&quot;: 4671.0,
                &quot;SK_notes&quot;: &quot;&quot;,
                &quot;AB&quot;: 8130.0,
                &quot;AB_notes&quot;: &quot;&quot;,
                &quot;BC&quot;: 89167.0,
                &quot;BC_notes&quot;: &quot;e&quot;,
                &quot;YT&quot;: &quot;-&quot;,
                &quot;YT_notes&quot;: &quot;&quot;,
                &quot;NT *a&quot;: 15.0,
                &quot;NT *a_notes&quot;: &quot;&quot;,
                &quot;NU&quot;: &quot;..&quot;,
                &quot;NU_notes&quot;: &quot;&quot;,
                &quot;CA&quot;: 194806.0,
                &quot;CA_notes&quot;: &quot;e&quot;
            },
    ...
</pre>
<p>Building the charts is a simple matter of iterating over the resulting map of parsed data. In this case we're ignoring the 'notes' data but have included it in the dataset in case we want to use it later. We're also just ignoring any non-numeric values.</p>
<pre class="brush: groovy; title: ; notranslate">
GROUPINGS.each { group -&gt;
            withPool {
                AREAS.eachParallel { area -&gt;
                    ChartBuilder builder = new ChartBuilder();
                    String title = sanitizeName(&quot;$group-$area&quot;)
                    TimeseriesChart chart = builder.timeserieschart(title: group,
                            timeAxisLabel: 'Year',
                            valueAxisLabel: 'Number of Seedlings(1000s)',
                            legend: true,
                            tooltips: false,
                            urls: false
                    ) {
                        timeSeriesCollection {
                            data.&quot;$group&quot;.each { species -&gt;
                                Set years = (species.keySet() - 'name').collect {it as int}
                                timeSeries(name: species.name, timePeriodClass: 'org.jfree.data.time.Year') {
                                    years.sort().each { year -&gt;
                                        final value = species.&quot;$year&quot;.&quot;$area&quot;
                                        //check that it's a numeric value
                                        if (!(value instanceof String))
                                        {
                                            add(period: new Year(year), value: value)
                                        }
                                    }
                                }
                            }
                        }
                    }
...
}
</pre>
<p>Then we apply some additional formatting to the JFreeChart to enhance the output styling, insert an image into the background, and fix the plot color schemes.</p>
<pre class="brush: groovy; title: ; notranslate">
                    JFreeChart innerChart = chart.chart
                    String longName = PROVINCE_SHORT_FORM_MAPPINGS.find {it.value == area}.key
                    innerChart.addSubtitle(new TextTitle(longName))
                    innerChart.setBackgroundPaint(Color.white)
                    innerChart.plot.setBackgroundPaint(Color.lightGray.brighter())
                    innerChart.plot.setBackgroundImageAlignment(Align.TOP_RIGHT)
                    innerChart.plot.setBackgroundImage(logo)
                    [Color.BLUE, Color.GREEN, Color.ORANGE, Color.CYAN, Color.MAGENTA, Color.BLACK, Color.PINK, Color.RED].eachWithIndex { color, int index -&gt;
                        innerChart.XYPlot.renderer.setSeriesPaint(index, color)
                    }
</pre>
<p>And we write out each of the charts to a formulaically named png file.</p>
<pre class="brush: groovy; title: ; notranslate">
                    def fileTitle = &quot;$FILE_PREFIX-${title}.png&quot;
                    File outputDir = new File(outputDirectory)
                    if (!outputDir.exists())
                    {
                        outputDir.mkdirs()
                    }
                    File file = new File(outputDir, fileTitle)
                    if (file.exists())
                    {
                        file.delete()
                    }
                    ChartUtilities.saveChartAsPNG(file, innerChart, 550, 300)
</pre>
<p>To tie it all together, an html page is created using <a href="http://groovy.codehaus.org/api/groovy/xml/MarkupBuilder.html">MarkupBuilder</a> to showcase all of the results, organized by Province or Territory.</p>
<pre class="brush: groovy; title: ; notranslate">
    def buildHtml(inputDirectory)
    {
        File inputDir = new File(inputDirectory)
        assert inputDir.exists()
        Writer writer = new StringWriter()
        MarkupBuilder builder = new MarkupBuilder(writer)
        builder.html {
            head {
                title('Number of Seedlings Planted by Ownership, Species')
                style(type: &quot;text/css&quot;) {
                    mkp.yield(CSS)
                }
            }
            body {
                ul {
                    AREAS.each { area -&gt;
                        String areaName = sanitizeName(area)
                        div(class: 'area rounded-corners', id: areaName) {
                            h2(PROVINCE_SHORT_FORM_MAPPINGS.find {it.value == area}.key)
                            inputDir.eachFileMatch(~/.*$areaName\.png/) {
                                img(src: it.name)
                            }
                        }
                    }
                }
                script(type: 'text/javascript', src: 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', '')
                script(type: 'text/javascript') {
                    mkp.yield(JQUERY_FUNCTION)
                }
            }
        }
        writer.toString()
    }
</pre>
<p>The generated html page assumes that all images are co-located in the same folder, presents four images per Province/Territory and, just for fun, uses JQuery to attach a click handler to each of the headers. Click on a header and the images in that div will animate into the background. I&#8217;m sure the actual JQuery being used could be improved upon, but it serves its purpose. Here&#8217;s a sample of the html output:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;ul&gt;
      &lt;div class='area rounded-corners' id='NL'&gt;
        &lt;h2&gt;Newfoundland and Labrador&lt;/h2&gt;
        &lt;img src='naturalResourcesCanadaNewSeedlings-Federal-NL.png' /&gt;
        &lt;img src='naturalResourcesCanadaNewSeedlings-PrivateLand-NL.png' /&gt;
        &lt;img src='naturalResourcesCanadaNewSeedlings-Provincial-NL.png' /&gt;
        &lt;img src='naturalResourcesCanadaNewSeedlings-Total-NL.png' /&gt;
      &lt;/div&gt;
    ...
</pre>
<p>The resulting page looks like this in Firefox.</p>

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/htmlpagescreenshot.png" title="" class="shutterset_singlepic80" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/80_watermark_700x400_htmlpagescreenshot.png" alt="htmlpagescreenshot" title="htmlpagescreenshot" />
</a>

<h2>Source code and Links</h2>
<p>The source code is <a href="https://github.com/kellyrob99/JFreeChart-POI-Groovy">available on GitHub</a>. So is the <a href="http://kellyrob99.github.com/JFreeChart-POI-Groovy/site/naturalResourcesCanadaSeedlingsCharts.html">final resulting html page</a>. The entire source required to go from Excel to charts embedded in an html page comes in at slightly under 300 lines of code and I don&#8217;t think the results are too bad for the couple of hours effort involved. Finally, the JSON results are also <a href="http://kellyrob99.github.com/JFreeChart-POI-Groovy/site/NaturalResourcesCanadaNewSeedlings.json">hosted on the GitHub pages</a> for the project for anyone else who might want to delve into the data.</p>
<p>Some reading related to this topic:</p>
<ul>
<li><a href="http://www.technipelago.se/content/technipelago/blog/44">Groovy loves POI and POI loves Groovy</a></li>
<li><a href="http://fbflex.wordpress.com/2010/06/11/writing-batch-import-scripts-with-grails-gsql-and-gpars/">Writing batch import scripts with Grails, GSQL and GPars</a></li>
<li><a href="http://blog.andresteingress.com/2012/01/13/gsheets-a-groovy-builder-based-on-apache-poi/">GSheets – A Groovy Builder based on Apache POI</a></li>
<li><a href="http://groovy.codehaus.org/Groovy+for+the+Office">Groovy for the Office</a></li>
</ul>

<div class="ngg-galleryoverview" id="ngg-gallery-15-1845">

	<!-- Slideshow link -->
	<div class="slideshowlink">
		<a class="slideshowlink" href="http://www.kellyrob99.com/blog/2012/03/18/jfreechart-with-groovy-and-apache-poi/?show=slide">
			[Show as slideshow]		</a>
	</div>

	<!-- Piclense link -->
	<div class="piclenselink">
		<a class="piclenselink" href="javascript:PicLensLite.start({feedUrl:'http://www.kellyrob99.com/blog/wp-content/plugins/nextgen-gallery/xml/media-rss.php?gid=15&amp;mode=gallery'});">
			[View with PicLens]		</a>
	</div>
	
	<!-- Thumbnails -->
		
	<div id="ngg-image-79" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/naturalresourcescanadanewseedlings-total-ca.png" title=" " class="shutterset_set_15" >
								<img title="naturalresourcescanadanewseedlings-total-ca" alt="naturalresourcescanadanewseedlings-total-ca" src="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/thumbs/thumbs_naturalresourcescanadanewseedlings-total-ca.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-78" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/excelheaders.png" title=" " class="shutterset_set_15" >
								<img title="excelheaders" alt="excelheaders" src="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/thumbs/thumbs_excelheaders.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-80" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/htmlpagescreenshot.png" title=" " class="shutterset_set_15" >
								<img title="htmlpagescreenshot" alt="htmlpagescreenshot" src="http://www.kellyrob99.com/blog/wp-content/gallery/apache-poi-with-groovy-and-jfreechart/thumbs/thumbs_htmlpagescreenshot.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 	 	
	<!-- Pagination -->
 	<div class='ngg-clear'></div>
 	
</div>


<!-- AdSense Now! V1.95 -->
<!-- Post[count: 2] -->
<div class="adsense adsense-leadout" style="float:right;margin: 12px;"><script type="text/javascript"><!--
google_ad_client = "pub-6955914197200080";
/* 728x90, created 8/3/09 */
google_ad_slot = "4051815125";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div><p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/10/24/groovy-reverse-map-sort-done-easy/' rel='bookmark' title='Groovy reverse map sort done easy'>Groovy reverse map sort done easy</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2012/03/18/jfreechart-with-groovy-and-apache-poi/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Hooking into the Jenkins(Hudson) API, Part 2</title>
		<link>http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=hooking-into-the-jenkinshudson-api-part-2</link>
		<comments>http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/#comments</comments>
		<pubDate>Sun, 26 Feb 2012 23:42:57 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Hudson]]></category>
		<category><![CDATA[jenkins]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[kellyrob99.com]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1793</guid>
		<description><![CDATA[It&#8217;s been almost a year, but I finally had some time to revisit some code I wrote for interacting with the Jenkins api. I&#8217;ve used parts of this work to help manage a number of Jenkins build servers, mostly in terms of keeping plugins in sync and moving jobs from one machine to another. For [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API'>Hooking into the Jenkins(Hudson) API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/' rel='bookmark' title='A Grails App Demoing the StackExchange API'>A Grails App Demoing the StackExchange API</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been almost a year, but I finally had some time to revisit some code I wrote for <a href="http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/" title="Hooking into the Jenkins(Hudson) API">interacting with the Jenkins api</a>. I&#8217;ve used parts of this work to help manage a number of <a href="http://jenkins-ci.org/" target="_blank">Jenkins</a> build servers, mostly in terms of keeping plugins in sync and moving jobs from one machine to another. For this article I&#8217;m going to be primarily focusing on the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI" target="_blank">CLI jar functionality</a> and some of the things you can do with it. This has mostly been developed against Jenkins but I did some light testing with <a href="http://hudson-ci.org/" target="_blank">Hudson</a> and it worked there for everything I tried, so the code remains mostly agnostic as to your choice of build server.</p>
<p></p>
<h1>The project structure</h1>
<p></p>
<p>The <a href="https://github.com/kellyrob99/Jenkins-api-tour" target="_blank">code is hosted on Github</a>, and provides a <a href="http://gradle.org/" target="_blank">Gradle</a> build which downloads and launches a Jenkins(or Hudson) server locally to execute tests. The server is set to use the Gradle build directory as its working directory, so it can be deleted simply by executing <strong><em>gradle clean</em></strong>. I tried it using both the Jenkins and the Hudson versions of the required libraries and, aside from some quirks between the two CLI implementations, they continue to function very much the same. If you want to try it with Hudson instead of Jenkins, pass in the command flag <strong><em>-Pswitch</em></strong> and the appropriate war and libraries will be used. The project is meant to be run with Gradle 1.0-milestone-8, and comes with a <a href="http://gradle.org/docs/current/userguide/gradle_wrapper.html">Gradle wrapper</a> for that version. Most of the code remains the same since the original article, but there are some enhancements and changes to deal with the newer versions of Jenkins and Hudson.<br />
The library produced by this project is published as a Maven artifact, and later on I&#8217;ll describe exactly how to get at it. There are also some samples included that demonstrate using that library in Gradle or Maven projects, and in Groovy scripts with Grapes. We&#8217;re using Groovy 1.8.6, Gradle 1.0-milestone-8 and Maven 3.0.3 to build everything.</p>
<p></p>
<h1>Getting more out of the CLI</h1>
<p>
As an alternative to the api, the CLI jar is a very capable way of interacting with the build server. In addition to a variety of built-in commands, Groovy scripts can be executed remotely, and with a little effort we can easily serialize responses in order to work with data extracted on the server. As an execution environment, the server provides a <a href="http://groovy.codehaus.org/Groovy+Shell" target="_blank">Groovysh</a> shell and stocks it with imports for the <a href="http://javadoc.jenkins-ci.org/hudson/model/package-summary.html" target="_blank">hudson.model package</a>. Also passed into the <a href="http://groovy.codehaus.org/api/groovy/lang/Binding.html" target="_blank">Binding</a> is the instance of the <a href="http://javadoc.jenkins-ci.org/hudson/model/Hudson.html" target="_blank">Jenkins/Hudson singleton object</a> in that package. In these examples I&#8217;m using the backwards-compatible Hudson version, since the code is intended to be runnable on either flavor of the server.</p>
<p></p>
<h2>The available commands</h2>
<p></p>
<p>There&#8217;s a rich variety of built-in commands, all of which are implemented in the <a href="http://javadoc.jenkins-ci.org/hudson/cli/package-summary.html">hudson.cli</a> package. Here are the ones that are listed on the CLI page of the running application:</p>
<ul>
<li>build: Builds a job, and optionally waits until its completion.</li>
<li>cancel-quiet-down: Cancel the effect of the &#8220;quiet-down&#8221; command.</li>
<li>clear-queue: Clears the build queue</li>
<li>connect-node: Reconnect to a node</li>
<li>copy-job: Copies a job.</li>
<li>create-job: Creates a new job by reading stdin as a configuration XML file.</li>
<li>delete-builds: Deletes build record(s).</li>
<li>delete-job: Deletes a job</li>
<li>delete-node: Deletes a node</li>
<li>disable-job: Disables a job</li>
<li>disconnect-node: Disconnects from a node</li>
<li>enable-job: Enables a job</li>
<li>get-job: Dumps the job definition XML to stdout</li>
<li>groovy: Executes the specified Groovy script.</li>
<li>groovysh: Runs an interactive groovy shell.</li>
<li>help: Lists all the available commands.</li>
<li>install-plugin: Installs a plugin either from a file, an URL, or from update center.</li>
<li>install-tool: Performs automatic tool installation, and print its location to stdout. Can be only called from<br />
        inside a build.
    </li>
<li>keep-build: Mark the build to keep the build forever.</li>
<li>list-changes: Dumps the changelog for the specified build(s).</li>
<li>login: Saves the current credential to allow future commands to run without explicit credential information.
    </li>
<li>logout: Deletes the credential stored with the login command.</li>
<li>mail: Reads stdin and sends that out as an e-mail.</li>
<li>offline-node: Stop using a node for performing builds temporarily, until the next &#8220;online-node&#8221; command.</li>
<li>online-node: Resume using a node for performing builds, to cancel out the earlier &#8220;offline-node&#8221; command.</li>
<li>quiet-down: Quiet down Jenkins, in preparation for a restart. Don&#8217;t start any builds.</li>
<li>reload-configuration: Discard all the loaded data in memory and reload everything from file system. Useful when<br />
        you modified config files directly on disk.
    </li>
<li>restart: Restart Jenkins</li>
<li>safe-restart: Safely restart Jenkins</li>
<li>safe-shutdown: Puts Jenkins into the quiet mode, wait for existing builds to be completed, and then shut down<br />
        Jenkins.
    </li>
<li>set-build-description: Sets the description of a build.</li>
<li>set-build-display-name: Sets the displayName of a build</li>
<li>set-build-result: Sets the result of the current build. Works only if invoked from within a build.</li>
<li>shutdown: Immediately shuts down Jenkins server</li>
<li>update-job: Updates the job definition XML from stdin. The opposite of the get-job command</li>
<li>version: Outputs the current version.</li>
<li>wait-node-offline: Wait for a node to become offline</li>
<li>wait-node-online: Wait for a node to become online</li>
<li>who-am-i: Reports your credential and permissions</li>
</ul>
<p>It&#8217;s not immediately apparent what arguments are required for each, but they almost universally follow a CLI pattern of printing usage details when called with no arguments. For instance, when you call the <em>build</em> command with no arguments, here&#8217;s what you get back in the error stream:</p>
<blockquote><p>
    Argument &#8220;JOB&#8221; is required<br />
    java -jar jenkins-cli.jar build args&#8230;<br />
    Starts a build, and optionally waits for a completion.<br />
    Aside from general scripting use, this command can be<br />
    used to invoke another job from within a build of one job.<br />
    With the -s option, this command changes the exit code based on<br />
    the outcome of the build (exit code 0 indicates a success.)<br />
    With the -c option, a build will only run if there has been<br />
    an SCM change<br />
    JOB : Name of the job to build<br />
    -c  : Check for SCM changes before starting the build, and if there&#8217;s no<br />
    change, exit without doing a build<br />
    -p  : Specify the build parameters in the key=value format.<br />
    -s  : Wait until the completion/abortion of the command
</p></blockquote>
<p></p>
<h2>Getting data out of the system</h2>
<p>
All of the interaction with the remote system is handled by streams and it&#8217;s pretty easy to craft scripts that will return data in an easily parseable String format using built-in Groovy facilities. In theory, you should be able to marshal more complex objects as well, but let&#8217;s keep it simple for now. Here&#8217;s a Groovy script that just extracts all of the job names into a List, calling the Groovy inspect method to quote all values.<br />
<div id="gist-1910293" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;glassfish&#39;</span><span class="o">,</span> <span class="n">root</span> <span class="o">=</span> <span class="s1">&#39;http://maven.glassfish.org/content/groups/public/&#39;</span><span class="o">)</span></div><div class='line' id='LC2'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;github&quot;</span><span class="o">,</span> <span class="n">root</span> <span class="o">=</span> <span class="s2">&quot;http://kellyrob99.github.com/Jenkins-api-tour/repository&quot;</span><span class="o">)</span></div><div class='line' id='LC3'><span class="nd">@Grab</span><span class="o">(</span><span class="s1">&#39;org.kar:hudson-api:0.2-SNAPSHOT&#39;</span><span class="o">)</span></div><div class='line' id='LC4'><span class="nd">@GrabExclude</span><span class="o">(</span><span class="s1">&#39;org.codehaus.groovy:groovy&#39;</span><span class="o">)</span></div><div class='line' id='LC5'><span class="kn">import</span> <span class="nn">org.kar.hudson.api.cli.HudsonCliApi</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="n">String</span> <span class="n">rootUrl</span> <span class="o">=</span> <span class="s1">&#39;http://localhost:8080&#39;</span></div><div class='line' id='LC8'><span class="n">HudsonCliApi</span> <span class="n">cliApi</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HudsonCliApi</span><span class="o">()</span></div><div class='line' id='LC9'><span class="n">OutputStream</span> <span class="n">out</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ByteArrayOutputStream</span><span class="o">()</span></div><div class='line' id='LC10'><span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="o">[</span><span class="s1">&#39;groovysh&#39;</span><span class="o">,</span> <span class="s1">&#39;hudson.jobNames.inspect()&#39;</span><span class="o">],</span> <span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">,</span> <span class="n">out</span><span class="o">,</span> <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">)</span></div><div class='line' id='LC11'><span class="n">List</span> <span class="n">allJobs</span> <span class="o">=</span> <span class="n">Eval</span><span class="o">.</span><span class="na">me</span><span class="o">(</span><span class="n">cliApi</span><span class="o">.</span><span class="na">parseResponse</span><span class="o">(</span><span class="n">out</span><span class="o">.</span><span class="na">toString</span><span class="o">()))</span></div><div class='line' id='LC12'><span class="n">println</span> <span class="n">allJobs</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1910293/f99afdca7f00fe298900fca3b9cbff3ef6863556/nameAllJobs.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1910293#file_name_all_jobs.groovy" style="float:right;margin-right:10px;color:#666">nameAllJobs.groovy</a>
            <a href="https://gist.github.com/1910293">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
<br />
Once we get the response back, we do a little housekeeping to remove some extraneous characters at the beginning of the String, and use Eval.me to transform the String into a List. Groovy provides a variety of ways of turning text into code, so if your usage scenario gets more complicated than this simple case you can use a GroovyShell with a Binding or other alternative to parse the results into something useful. This easy technique extends to Maps and other types as well, making it simple to work with data sent back from the server.</p>
<p></p>
<h1>Some useful examples</h1>
<p></p>
<h2>Finding plugins with updates and and updating all of them</h2>
<p>Here&#8217;s an example of using a Groovy script to find all of the plugins that have updates available, returning that result to the caller, and then calling the CLI &#8216;install-plugin&#8217; command on all of them. Conveniently, this command will either install a plugin if it&#8217;s not already there or update it to the latest version if already installed.<br />
<div id="gist-1907114" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="kt">def</span> <span class="n">findPluginsWithUpdates</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;</span></div><div class='line' id='LC2'><span class="s1">Hudson.instance.pluginManager.plugins.inject([]) { List toUpdate, plugin -&gt;</span></div><div class='line' id='LC3'><span class="s1">    if(plugin.hasUpdate())</span></div><div class='line' id='LC4'><span class="s1">    {</span></div><div class='line' id='LC5'><span class="s1">        toUpdate &lt;&lt; plugin.shortName</span></div><div class='line' id='LC6'><span class="s1">    }</span></div><div class='line' id='LC7'><span class="s1">    toUpdate</span></div><div class='line' id='LC8'><span class="s1">}.inspect()</span></div><div class='line' id='LC9'><span class="s1">&#39;&#39;&#39;</span></div><div class='line' id='LC10'><span class="n">OutputStream</span> <span class="n">updateablePlugins</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ByteArrayOutputStream</span><span class="o">()</span></div><div class='line' id='LC11'><span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="o">[</span><span class="s1">&#39;groovysh&#39;</span><span class="o">,</span> <span class="n">findPluginsWithUpdates</span><span class="o">],</span> <span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">,</span> <span class="n">updateablePlugins</span><span class="o">,</span> <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">)</span></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'><span class="kt">def</span> <span class="n">listOfPlugins</span> <span class="o">=</span> <span class="n">Eval</span><span class="o">.</span><span class="na">me</span><span class="o">(</span><span class="n">parseOutput</span><span class="o">(</span><span class="n">updateablePlugins</span><span class="o">.</span><span class="na">toString</span><span class="o">()))</span></div><div class='line' id='LC14'><span class="n">listOfPlugins</span><span class="o">.</span><span class="na">each</span><span class="o">{</span> <span class="n">plugin</span> <span class="o">-&gt;</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="o">[</span><span class="s1">&#39;install-plugin&#39;</span><span class="o">,</span> <span class="n">plugin</span><span class="o">])</span></div><div class='line' id='LC16'><span class="o">}</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1907114/72ab308fa00c3e6f9107d35012138cb595155d47/updateAllPlugins.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1907114#file_update_all_plugins.groovy" style="float:right;margin-right:10px;color:#666">updateAllPlugins.groovy</a>
            <a href="https://gist.github.com/1907114">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p></p>
<h2>Install or upgrade a suite of Plugins all at once</h2>
<p>This definitely beats using the &#8216;Manage Plugins&#8217; UI and is idempotent so running it more than once can only result in possibly upgrading already installed Plugins. This set of plugins might be overkill, but these are some plugins I recently surveyed for possible use.<br />
<div id="gist-1907283" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s1">&#39;glassfish&#39;</span><span class="o">,</span> <span class="n">root</span><span class="o">=</span><span class="s1">&#39;http://maven.glassfish.org/content/groups/public/&#39;</span><span class="o">)</span></div><div class='line' id='LC2'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;github&quot;</span><span class="o">,</span> <span class="n">root</span><span class="o">=</span><span class="s2">&quot;http://kellyrob99.github.com/Jenkins-api-tour/repository&quot;</span><span class="o">)</span></div><div class='line' id='LC3'><span class="nd">@Grab</span><span class="o">(</span><span class="s1">&#39;org.kar:hudson-api:0.2-SNAPSHOT&#39;</span><span class="o">)</span></div><div class='line' id='LC4'><span class="nd">@GrabExclude</span><span class="o">(</span><span class="s1">&#39;org.codehaus.groovy:groovy&#39;</span><span class="o">)</span></div><div class='line' id='LC5'><span class="kn">import</span> <span class="nn">static</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">HttpURLConnection</span><span class="o">.*</span></div><div class='line' id='LC6'><span class="kn">import</span> <span class="nn">org.kar.hudson.api.*</span></div><div class='line' id='LC7'><span class="kn">import</span> <span class="nn">org.kar.hudson.api.cli.HudsonCliApi</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'><span class="n">String</span> <span class="n">rootUrl</span> <span class="o">=</span> <span class="s1">&#39;http://localhost:8080&#39;</span></div><div class='line' id='LC10'><span class="n">HudsonCliApi</span> <span class="n">cliApi</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HudsonCliApi</span><span class="o">()</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'><span class="o">[</span><span class="s1">&#39;groovy&#39;</span><span class="o">,</span> <span class="s1">&#39;gradle&#39;</span><span class="o">,</span> <span class="s1">&#39;chucknorris&#39;</span><span class="o">,</span> <span class="s1">&#39;greenballs&#39;</span><span class="o">,</span> <span class="s1">&#39;github&#39;</span><span class="o">,</span> <span class="s1">&#39;analysis-core&#39;</span><span class="o">,</span> <span class="s1">&#39;analysis-collector&#39;</span><span class="o">,</span> <span class="s1">&#39;cobertura&#39;</span><span class="o">,</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;project-stats-plugin&#39;</span><span class="o">,</span><span class="s1">&#39;audit-trail&#39;</span><span class="o">,</span> <span class="s1">&#39;view-job-filters&#39;</span><span class="o">,</span> <span class="s1">&#39;disk-usage&#39;</span><span class="o">,</span> <span class="s1">&#39;global-build-stats&#39;</span><span class="o">,</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;radiatorviewplugin&#39;</span><span class="o">,</span> <span class="s1">&#39;violations&#39;</span><span class="o">,</span> <span class="s1">&#39;build-pipeline-plugin&#39;</span><span class="o">,</span> <span class="s1">&#39;monitoring&#39;</span><span class="o">,</span> <span class="s1">&#39;dashboard-view&#39;</span><span class="o">,</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;iphoneview&#39;</span><span class="o">,</span> <span class="s1">&#39;jenkinswalldisplay&#39;</span><span class="o">].</span><span class="na">each</span><span class="o">{</span> <span class="n">plugin</span> <span class="o">-&gt;</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="o">[</span><span class="s1">&#39;install-plugin&#39;</span><span class="o">,</span> <span class="n">plugin</span><span class="o">])</span></div><div class='line' id='LC17'><span class="o">}</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'><span class="c1">//  Restart a node, required for newly installed plugins to be made available.</span></div><div class='line' id='LC20'><span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="s1">&#39;safe-restart&#39;</span><span class="o">)</span></div><div class='line' id='LC21'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1907283/001c560d865b79c0d3be489357fe854257e7eeb6/setupNewServer.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1907283#file_setup_new_server.groovy" style="float:right;margin-right:10px;color:#666">setupNewServer.groovy</a>
            <a href="https://gist.github.com/1907283">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p></p>
<h2>Finding all failed builds and triggering them</h2>
<p>It&#8217;s not all that uncommon that a network problem or infrastructure event can cause a host of builds to fail all at once. Once the problem is solved this script can be useful for verifying that the builds are all in working order.<br />
<div id="gist-1911932" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;glassfish&#39;</span><span class="o">,</span> <span class="n">root</span> <span class="o">=</span> <span class="s1">&#39;http://maven.glassfish.org/content/groups/public/&#39;</span><span class="o">)</span></div><div class='line' id='LC2'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;github&quot;</span><span class="o">,</span> <span class="n">root</span> <span class="o">=</span> <span class="s2">&quot;http://kellyrob99.github.com/Jenkins-api-tour/repository&quot;</span><span class="o">)</span></div><div class='line' id='LC3'><span class="nd">@Grab</span><span class="o">(</span><span class="s1">&#39;org.kar:hudson-api:0.2-SNAPSHOT&#39;</span><span class="o">)</span></div><div class='line' id='LC4'><span class="nd">@GrabExclude</span><span class="o">(</span><span class="s1">&#39;org.codehaus.groovy:groovy&#39;</span><span class="o">)</span></div><div class='line' id='LC5'><span class="kn">import</span> <span class="nn">org.kar.hudson.api.cli.HudsonCliApi</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="n">String</span> <span class="n">rootUrl</span> <span class="o">=</span> <span class="s1">&#39;http://localhost:8080&#39;</span></div><div class='line' id='LC8'><span class="n">HudsonCliApi</span> <span class="n">cliApi</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HudsonCliApi</span><span class="o">()</span></div><div class='line' id='LC9'><span class="n">OutputStream</span> <span class="n">out</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ByteArrayOutputStream</span><span class="o">()</span></div><div class='line' id='LC10'><span class="kt">def</span> <span class="n">script</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;hudson.items.findAll{ job -&gt;</span></div><div class='line' id='LC11'><span class="s1">    job.isBuildable() &amp;&amp; job.lastBuild &amp;&amp; job.lastBuild.result == Result.FAILURE</span></div><div class='line' id='LC12'><span class="s1">}.collect{it.name}.inspect()</span></div><div class='line' id='LC13'><span class="s1">&#39;&#39;&#39;</span></div><div class='line' id='LC14'><span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="o">[</span><span class="s1">&#39;groovysh&#39;</span><span class="o">,</span> <span class="n">script</span><span class="o">],</span> <span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">,</span> <span class="n">out</span><span class="o">,</span> <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">)</span></div><div class='line' id='LC15'><span class="n">List</span> <span class="n">failedJobs</span> <span class="o">=</span> <span class="n">Eval</span><span class="o">.</span><span class="na">me</span><span class="o">(</span><span class="n">cliApi</span><span class="o">.</span><span class="na">parseResponse</span><span class="o">(</span><span class="n">out</span><span class="o">.</span><span class="na">toString</span><span class="o">()))</span></div><div class='line' id='LC16'><span class="n">failedJobs</span><span class="o">.</span><span class="na">each</span><span class="o">{</span> <span class="n">job</span> <span class="o">-&gt;</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="o">[</span><span class="s1">&#39;build&#39;</span><span class="o">,</span> <span class="n">job</span><span class="o">])</span></div><div class='line' id='LC18'><span class="o">}</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1911932/38f4f6defe3d1ca227692253b0b2750d91e37c2b/findAndTriggerFailedBuilds.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1911932#file_find_and_trigger_failed_builds.groovy" style="float:right;margin-right:10px;color:#666">findAndTriggerFailedBuilds.groovy</a>
            <a href="https://gist.github.com/1911932">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p></p>
<h2>Open an interactive Groovy shell</h2>
<p>If you really want to poke at the server you can launch an interactive shell to inspect state and execute commands. The System.in stream is bound and responses from the server are immediately echoed back.<br />
<div id="gist-1912014" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;glassfish&#39;</span><span class="o">,</span> <span class="n">root</span> <span class="o">=</span> <span class="s1">&#39;http://maven.glassfish.org/content/groups/public/&#39;</span><span class="o">)</span></div><div class='line' id='LC2'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;github&quot;</span><span class="o">,</span> <span class="n">root</span> <span class="o">=</span> <span class="s2">&quot;http://kellyrob99.github.com/Jenkins-api-tour/repository&quot;</span><span class="o">)</span></div><div class='line' id='LC3'><span class="nd">@Grab</span><span class="o">(</span><span class="s1">&#39;org.kar:hudson-api:0.2-SNAPSHOT&#39;</span><span class="o">)</span></div><div class='line' id='LC4'><span class="nd">@GrabExclude</span><span class="o">(</span><span class="s1">&#39;org.codehaus.groovy:groovy&#39;</span><span class="o">)</span></div><div class='line' id='LC5'><span class="kn">import</span> <span class="nn">org.kar.hudson.api.cli.HudsonCliApi</span></div><div class='line' id='LC6'><span class="cm">/**</span></div><div class='line' id='LC7'><span class="cm"> * Open an interactive Groovy shell that imports the hudson.model.* classes and exposes</span></div><div class='line' id='LC8'><span class="cm"> * a &#39;hudson&#39; and/or &#39;jenkins&#39; object in the Binding which is an instance of hudson.model.Hudson</span></div><div class='line' id='LC9'><span class="cm"> */</span></div><div class='line' id='LC10'><span class="n">HudsonCliApi</span> <span class="n">cliApi</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HudsonCliApi</span><span class="o">()</span></div><div class='line' id='LC11'><span class="n">String</span> <span class="n">rootUrl</span> <span class="o">=</span> <span class="n">args</span> <span class="o">?</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">:</span><span class="s1">&#39;http://localhost:8080&#39;</span></div><div class='line' id='LC12'><span class="n">cliApi</span><span class="o">.</span><span class="na">runCliCommand</span><span class="o">(</span><span class="n">rootUrl</span><span class="o">,</span> <span class="s1">&#39;groovysh&#39;</span><span class="o">)</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1912014/18037db0106665e1b91de00b44c7fbeb3e709655/jenkinsGroovyShell.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1912014#file_jenkins_groovy_shell.groovy" style="float:right;margin-right:10px;color:#666">jenkinsGroovyShell.groovy</a>
            <a href="https://gist.github.com/1912014">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p></p>
<h1>Updates to the project</h1>
<p>A lot has happened in the last year and all of the project dependencies needed an update. In particular, there have been some very nice improvements to Groovy, Gradle and Spock. Most notably, Gradle has come a VERY long way since version 0.9.2. The JSON support added in Groovy 1.8 comes in handy as well. Spock required a small tweak for rendering dynamic content in test reports when using <a href="http://java.dzone.com/articles/unrolling-spock-advanced" target="_blank">@Unroll</a>, but that&#8217;s a small price to pay for features like <a href="http://blog.freeside.co/blog/2012/02/15/spock-killer-features-the-old-method/">the &#8216;old&#8217; method</a> and <a href="http://meetspock.appspot.com/script/45001">Chained Stubbing</a>. Essentially, in response to changes in Groovy 1.8+, a Spock @Unroll annotation needs to change from:</p>
<pre class="brush: groovy; title: ; notranslate">
@Unroll(&quot;querying of #rootUrl should match #xmlResponse&quot;)
</pre>
<p>to a Closure encapsulated GString expression:</p>
<pre class="brush: groovy; title: ; notranslate">
@Unroll({&quot;querying of $rootUrl should match $xmlResponse&quot;})
</pre>
<p>It sounds like the syntax is still in flux and I&#8217;m glad I found this <a href="http://spock-framework.3207229.n2.nabble.com/Problem-with-Unroll-with-groovy-1-8-td6317496.html" target="_blank">discussion of the problem online</a>.</p>
<p></p>
<h1>Hosting a Maven repository on Github</h1>
<p>Perhaps you noticed from the previous script examples, we&#8217;re referencing a published library to get at the HudsonCliApi class. I read an <a href="http://chkal.blogspot.com/2010/09/maven-repositories-on-github.html" target="_blank">interesting article last week</a> which describes how to use the built-in <a href="http://pages.github.com/">Github Pages</a> for publishing a Maven repository. While this isn&#8217;t nearly as capable as a repository like Nexus or Artifactory, it&#8217;s totally sufficient for making some binaries available to most common build tools in a standard fashion. Simply publish the binaries along with associated poms in the standard Maven repo layout and you&#8217;re off to the races! Each dependency management system has its quirks(I&#8217;m looking at you Ivy!) but they&#8217;re pretty easy to work around, so here&#8217;s examples for Gradle, Maven and Groovy Grapes to use the library produced by this project code. Note that some of the required dependencies for Jenkins/Hudson aren&#8217;t available in the Maven central repository, so we&#8217;re getting them from the Glassfish repo.</p>
<p></p>
<h2>Gradle</h2>
<p>Pretty straight forward, this works with the latest version of Gradle and assumes that you are using the Groovy plugin.</p>
<pre class="brush: groovy; title: ; notranslate">
repositories {
    mavenCentral()
    maven {
        url 'http://maven.glassfish.org/content/groups/public/'
    }
    maven {
        url 'http://kellyrob99.github.com/Jenkins-api-tour/repository'
    }
}
dependencies {
    groovy &quot;org.codehaus.groovy:groovy-all:${versions.groovy}&quot;
    compile 'org.kar:hudson-api:0.2-SNAPSHOT'
}
</pre>
<p></p>
<h2>Maven</h2>
<p>Essentially the same content in xml and in this case it&#8217;s assumed that you&#8217;re using the GMaven plugin</p>
<pre class="brush: xml; title: ; notranslate">
&lt;repositories&gt;
    &lt;repository&gt;
        &lt;id&gt;glassfish&lt;/id&gt;
        &lt;name&gt;glassfish&lt;/name&gt;
        &lt;url&gt;http://maven.glassfish.org/content/groups/public/&lt;/url&gt;
    &lt;/repository&gt;
    &lt;repository&gt;
        &lt;id&gt;github&lt;/id&gt;
        &lt;name&gt;Jenkins-api-tour maven repo on github&lt;/name&gt;
        &lt;url&gt;http://kellyrob99.github.com/Jenkins-api-tour/repository&lt;/url&gt;
    &lt;/repository&gt;
&lt;/repositories&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.codehaus.groovy&lt;/groupId&gt;
        &lt;artifactId&gt;groovy-all&lt;/artifactId&gt;
        &lt;version&gt;${groovy.version}&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.kar&lt;/groupId&gt;
        &lt;artifactId&gt;hudson-api&lt;/artifactId&gt;
        &lt;version&gt;0.2-SNAPSHOT&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;
</pre>
<p></p>
<h2>Grapes</h2>
<p>In this case there seems to be a problem resolving some transitive dependency for an older version of Groovy which is why there&#8217;s an explicit exclude for it.</p>
<pre class="brush: groovy; title: ; notranslate">
@GrabResolver(name='glassfish', root='http://maven.glassfish.org/content/groups/public/')
@GrabResolver(name=&quot;github&quot;, root=&quot;http://kellyrob99.github.com/Jenkins-api-tour/repository&quot;)
@Grab('org.kar:hudson-api:0.2-SNAPSHOT')
@GrabExclude('org.codehaus.groovy:groovy')
</pre>
<p></p>
<h1>Links</h1>
<p><a href="https://github.com/kellyrob99/Jenkins-api-tour/">The Github Jenkins-api-tour project page</a><br />
<a href="http://chkal.blogspot.com/2010/09/maven-repositories-on-github.html" title="Maven repositories on Github" target="_blank">Maven repositories on Github</a><br />
<a href="http://scriptlerweb.appspot.com" title="Scriptler" target="_blank">Scriptler example Groovy scripts</a><br />
<a href="https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI">Jenkins CLI documentation</a></p>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API'>Hooking into the Jenkins(Hudson) API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/' rel='bookmark' title='A Grails App Demoing the StackExchange API'>A Grails App Demoing the StackExchange API</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Five Cool Things You Can Do With Groovy Scripts</title>
		<link>http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=five-cool-things-you-can-do-with-groovy-scripts</link>
		<comments>http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/#comments</comments>
		<pubDate>Sun, 04 Dec 2011 21:51:34 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[gist]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Hudson]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[jenkins]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[JsonSlurper]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1761</guid>
		<description><![CDATA[1. Ensure all of your Jenkins builds are building the correct branch from source control I manage a large number of builds at work, spread across several build servers. When we release a new version all of the builds need to be updated to point to new working branches. This script takes advantage of the [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/' rel='bookmark' title='Using Gradle to Bootstrap your Legacy Ant Builds'>Using Gradle to Bootstrap your Legacy Ant Builds</a></li>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API'>Hooking into the Jenkins(Hudson) API</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h2>1. Ensure all of your Jenkins builds are building the correct branch from source control</h2>
<p>I manage a large number of builds at work, spread across several build servers. When we release a new version all of the builds need to be updated to point to new working branches. This script takes advantage of the fact that our branches all end in the version number to quickly check that all of the last builds were on the expected version.<br />
Using the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API">Jenkins API</a> is very easy and the new capabilities of Groovy introduced by <a href="http://groovy.codehaus.org/gapi/groovy/json/JsonSlurper.html">JSONSlurper</a> make it easier than ever to consume.<br />
<div id="gist-1430845" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="kn">import</span> <span class="nn">groovy.json.JsonSlurper</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="k">assert</span> <span class="n">args</span> <span class="o">&amp;&amp;</span> <span class="n">args</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">==</span> <span class="mi">2</span></div><div class='line' id='LC4'><span class="kt">def</span> <span class="n">urls</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">split</span><span class="o">(</span><span class="s1">&#39;,&#39;</span><span class="o">)</span></div><div class='line' id='LC5'><span class="kt">def</span> <span class="n">expectedVersion</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span></div><div class='line' id='LC6'><span class="n">urls</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">url</span> <span class="o">-&gt;</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">println</span> <span class="s2">&quot;examining url $url&quot;</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="kt">def</span> <span class="n">json</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonSlurper</span><span class="o">().</span><span class="na">parseText</span><span class="o">(</span><span class="n">url</span><span class="o">.</span><span class="na">toURL</span><span class="o">().</span><span class="na">text</span><span class="o">)</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">json</span><span class="o">.</span><span class="na">jobs</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">try</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">{</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kt">def</span> <span class="n">job</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonSlurper</span><span class="o">().</span><span class="na">parseText</span><span class="o">(</span><span class="s2">&quot;${it.url}/api/json?depth=1&quot;</span><span class="o">.</span><span class="na">toURL</span><span class="o">().</span><span class="na">text</span><span class="o">)</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="o">(!</span><span class="n">job</span><span class="o">.</span><span class="na">builds</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">changeSet</span><span class="o">.</span><span class="na">revisions</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">module</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="n">expectedVersion</span><span class="o">))</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">{</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">println</span> <span class="s2">&quot;$it.url fails!&quot;</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">{</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">println</span> <span class="s2">&quot;$it.url passes!&quot;</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span></div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">catch</span> <span class="o">(</span><span class="n">e</span><span class="o">)</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">{</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">println</span> <span class="s2">&quot;Exception thrown processing $it.url : $e&quot;</span></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span></div><div class='line' id='LC28'><span class="o">}</span></div><div class='line' id='LC29'><br/></div><div class='line' id='LC30'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1430845/bbc1beba1dfd59a6fe9925af14d301460202f0d3/jenkinsSVNVersionChecker.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1430845#file_jenkins_svn_version_checker.groovy" style="float:right;margin-right:10px;color:#666">jenkinsSVNVersionChecker.groovy</a>
            <a href="https://gist.github.com/1430845">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<h2>2. Look up the artifacts from the last successful Jenkins build</h2>
<p>This is very handy if you want to automate deployment of software from your build system and can replace a lot of error prone manual updating of scripts. In this particular example you can link up quickly with the latest, greatest build of JRuby from <a href="http://ci.jruby.org/">their public CI server</a>.<br />
<div id="gist-1430856" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="kn">import</span> <span class="nn">groovy.json.*</span></div><div class='line' id='LC2'><span class="kt">def</span> <span class="n">HOSTNAME</span> <span class="o">=</span> <span class="s1">&#39;http://ci.jruby.org&#39;</span></div><div class='line' id='LC3'><span class="kt">def</span> <span class="n">JOBNAME</span> <span class="o">=</span> <span class="s1">&#39;jruby-dist&#39;</span></div><div class='line' id='LC4'><span class="kt">def</span> <span class="n">JOB_URL</span> <span class="o">=</span> <span class="s2">&quot;$HOSTNAME/job/$JOBNAME/lastSuccessfulBuild&quot;</span></div><div class='line' id='LC5'><span class="kt">def</span> <span class="n">text</span> <span class="o">=</span> <span class="s2">&quot;$JOB_URL/api/json&quot;</span><span class="o">.</span><span class="na">toURL</span><span class="o">().</span><span class="na">text</span></div><div class='line' id='LC6'><span class="n">println</span> <span class="n">JsonOutput</span><span class="o">.</span><span class="na">prettyPrint</span><span class="o">(</span><span class="n">text</span><span class="o">)</span></div><div class='line' id='LC7'><span class="kt">def</span> <span class="n">json</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonSlurper</span><span class="o">().</span><span class="na">parseText</span><span class="o">(</span><span class="n">text</span><span class="o">)</span></div><div class='line' id='LC8'><span class="n">json</span><span class="o">.</span><span class="na">artifacts</span><span class="o">.</span><span class="na">each</span><span class="o">{</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">println</span> <span class="n">it</span></div><div class='line' id='LC10'><span class="o">}</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1430856/cea0186a862217015fb4fc2b63eb0ad3575c06fc/jenkinsBuildArtifacts.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1430856#file_jenkins_build_artifacts.groovy" style="float:right;margin-right:10px;color:#666">jenkinsBuildArtifacts.groovy</a>
            <a href="https://gist.github.com/1430856">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
<br />
Here&#8217;s the sample output at time of writing:</p>
<blockquote><p>[relativePath:dist/jruby-bin-1.7.0.dev.tar.gz, fileName:jruby-bin-1.7.0.dev.tar.gz, displayPath:null]<br />
[relativePath:dist/jruby-bin-1.7.0.dev.tar.gz.md5, fileName:jruby-bin-1.7.0.dev.tar.gz.md5, displayPath:null]<br />
[relativePath:dist/jruby-bin-1.7.0.dev.tar.gz.sha1, fileName:jruby-bin-1.7.0.dev.tar.gz.sha1, displayPath:null]<br />
[relativePath:dist/jruby-bin-1.7.0.dev.zip, fileName:jruby-bin-1.7.0.dev.zip, displayPath:null]<br />
[relativePath:dist/jruby-bin-1.7.0.dev.zip.md5, fileName:jruby-bin-1.7.0.dev.zip.md5, displayPath:null]<br />
[relativePath:dist/jruby-bin-1.7.0.dev.zip.sha1, fileName:jruby-bin-1.7.0.dev.zip.sha1, displayPath:null]<br />
[relativePath:dist/jruby-complete-1.7.0.dev.jar, fileName:jruby-complete-1.7.0.dev.jar, displayPath:null]<br />
[relativePath:dist/jruby-complete-1.7.0.dev.jar.md5, fileName:jruby-complete-1.7.0.dev.jar.md5, displayPath:null]<br />
[relativePath:dist/jruby-complete-1.7.0.dev.jar.sha1, fileName:jruby-complete-1.7.0.dev.jar.sha1, displayPath:null]<br />
[relativePath:dist/jruby-jars-1.7.0.dev.gem, fileName:jruby-jars-1.7.0.dev.gem, displayPath:null]<br />
[relativePath:dist/jruby-jars-1.7.0.dev.gem.md5, fileName:jruby-jars-1.7.0.dev.gem.md5, displayPath:null]<br />
[relativePath:dist/jruby-jars-1.7.0.dev.gem.sha1, fileName:jruby-jars-1.7.0.dev.gem.sha1, displayPath:null]<br />
[relativePath:dist/jruby-src-1.7.0.dev.tar.gz, fileName:jruby-src-1.7.0.dev.tar.gz, displayPath:null]<br />
[relativePath:dist/jruby-src-1.7.0.dev.tar.gz.md5, fileName:jruby-src-1.7.0.dev.tar.gz.md5, displayPath:null]<br />
[relativePath:dist/jruby-src-1.7.0.dev.tar.gz.sha1, fileName:jruby-src-1.7.0.dev.tar.gz.sha1, displayPath:null]<br />
[relativePath:dist/jruby-src-1.7.0.dev.zip, fileName:jruby-src-1.7.0.dev.zip, displayPath:null]<br />
[relativePath:dist/jruby-src-1.7.0.dev.zip.md5, fileName:jruby-src-1.7.0.dev.zip.md5, displayPath:null]<br />
[relativePath:dist/jruby-src-1.7.0.dev.zip.sha1, fileName:jruby-src-1.7.0.dev.zip.sha1, displayPath:null]</p></blockquote>
<h2>3. Read in a CSV file, filter it, and write out the result</h2>
<p>Groovy Grapes and one of my favorite libraries, <a href="http://opencsv.sourceforge.net/">OpenCSV</a>, make it trivial to script &#8216;one off&#8217; solutions for removing work that would normally take place manually in Excel. For me it comes up most often with data import/export from systems I&#8217;m working on and the need to create particular test environments using those import/export mechanisms. For example, using a scripted solution like this you can quickly:</p>
<ul>
<li>Export users from one test system into a CSV file, along with local phone number contact data</li>
<li>Replace the existing phone numbers with a mix of local and international numbers</li>
<li>Import the new data into a different test system</li>
<li>Verify that the system properly deals with international calling concerns</li>
</ul>
<p>This example simply trims out all of the rows where the second column of input contains values less than 100. It&#8217;s also nice to note that OpenCSV is smart enough to let you ignore one or more &#8216;header rows&#8217;, a common attribute of CSV files.<br />
<div id="gist-1430935" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nd">@Grab</span><span class="o">(</span><span class="n">group</span> <span class="o">=</span> <span class="s1">&#39;net.sf.opencsv&#39;</span><span class="o">,</span> <span class="n">module</span> <span class="o">=</span> <span class="s1">&#39;opencsv&#39;</span><span class="o">,</span> <span class="n">version</span> <span class="o">=</span> <span class="s1">&#39;2.3&#39;</span><span class="o">)</span></div><div class='line' id='LC2'><span class="kn">import</span> <span class="nn">au.com.bytecode.opencsv.CSVReader</span></div><div class='line' id='LC3'><span class="kn">import</span> <span class="nn">au.com.bytecode.opencsv.CSVParser</span></div><div class='line' id='LC4'><span class="kn">import</span> <span class="nn">au.com.bytecode.opencsv.CSVWriter</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'><span class="kt">def</span> <span class="n">TEST_FILE_NAME</span> <span class="o">=</span> <span class="s1">&#39;test.csv&#39;</span></div><div class='line' id='LC7'><span class="kt">def</span> <span class="n">TEST_OUTPUT_FILE_NAME</span> <span class="o">=</span> <span class="s1">&#39;testOut.csv&#39;</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">[]&gt;</span> <span class="n">rows</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CSVReader</span><span class="o">(</span><span class="k">new</span> <span class="n">FileReader</span><span class="o">(</span><span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">TEST_FILE_NAME</span><span class="o">)),</span> <span class="n">CSVParser</span><span class="o">.</span><span class="na">DEFAULT_SEPARATOR</span><span class="o">,</span> <span class="n">CSVParser</span><span class="o">.</span><span class="na">DEFAULT_ESCAPE_CHARACTER</span><span class="o">,</span> <span class="n">CSVParser</span><span class="o">.</span><span class="na">DEFAULT_QUOTE_CHARACTER</span><span class="o">,</span> <span class="mi">1</span><span class="o">).</span><span class="na">readAll</span><span class="o">()</span></div><div class='line' id='LC10'><span class="kt">def</span> <span class="n">rowsOver100</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="na">findAll</span> <span class="o">{</span><span class="n">it</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="na">toInteger</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="o">}</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'><span class="n">File</span> <span class="n">output</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">TEST_OUTPUT_FILE_NAME</span><span class="o">)</span></div><div class='line' id='LC13'><span class="k">if</span> <span class="o">(</span><span class="n">output</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span></div><div class='line' id='LC14'><span class="o">{</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">output</span><span class="o">.</span><span class="na">delete</span><span class="o">()</span></div><div class='line' id='LC16'><span class="o">}</span></div><div class='line' id='LC17'><span class="n">output</span><span class="o">.</span><span class="na">createNewFile</span><span class="o">()</span></div><div class='line' id='LC18'><span class="n">output</span><span class="o">.</span><span class="na">withWriter</span> <span class="o">{</span> <span class="n">writer</span> <span class="o">-&gt;</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">new</span> <span class="nf">CSVWriter</span><span class="o">(</span><span class="n">writer</span><span class="o">).</span><span class="na">writeAll</span><span class="o">(</span><span class="n">rowsOver100</span><span class="o">)</span></div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'><span class="o">}</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'><span class="c1">//println rows</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1430935/0c21cfe5a273ed552193c3b94e379d3bb941a5e6/openCsv.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1430935#file_open_csv.groovy" style="float:right;margin-right:10px;color:#666">openCsv.groovy</a>
            <a href="https://gist.github.com/1430935">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<h2>4. Download, install and run a stand-alone webserver</h2>
<p>This example comes courtesy of a <a href="http://www.ibm.com/developerworks/java/library/j-javadev2-20/index.html">recent article by Andrew Glover</a> about <a href="https://github.com/groovypp/gretty/wiki">Gretty</a>, a lightweight http server. I don&#8217;t have a concrete use-case for this right now, but it&#8217;s easy to note that in this case you can code a large part of the server behaviour directly in the script, unlike, for instance, if you were to use a script to stand up a Jetty server. This should let you publish a script once in a central location and stand up as many Gretty servers providing the declared services as you like. This particular example simply echos back &#8216;Hello&#8217; plus whatever path you append to localhost:8080.<br />
<div id="gist-1431012" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="kn">import</span> <span class="nn">org.mbte.gretty.httpserver.*</span> </div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="nd">@GrabResolver</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s1">&#39;gretty&#39;</span><span class="o">,</span> </div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">root</span><span class="o">=</span><span class="s1">&#39;http://groovypp.artifactoryonline.com/groovypp/libs-releases-local&#39;</span><span class="o">)</span></div><div class='line' id='LC5'><span class="nd">@Grab</span><span class="o">(</span><span class="s1">&#39;org.mbte.groovypp:gretty:0.4.279&#39;</span><span class="o">)</span> </div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="n">GrettyServer</span> <span class="n">server</span> <span class="o">=</span> <span class="o">[]</span> </div><div class='line' id='LC8'><span class="n">server</span><span class="o">.</span><span class="na">groovy</span> <span class="o">=</span> <span class="o">[</span> </div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nl">localAddress:</span> <span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="s2">&quot;localhost&quot;</span><span class="o">,</span> <span class="mi">8080</span><span class="o">),</span> </div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nl">defaultHandler:</span> <span class="o">{</span> </div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">response</span><span class="o">.</span><span class="na">redirect</span> <span class="s2">&quot;/&quot;</span> </div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">},</span> </div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;/:name&quot;</span><span class="o">:</span> <span class="o">{</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">get</span> <span class="o">{</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">response</span><span class="o">.</span><span class="na">text</span> <span class="o">=</span> <span class="s2">&quot;Hello ${request.parameters[&#39;name&#39;]}&quot;</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span> </div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">}</span> </div><div class='line' id='LC18'><span class="o">]</span> </div><div class='line' id='LC19'><span class="n">server</span><span class="o">.</span><span class="na">start</span><span class="o">()</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1431012/e0bcf55e0b19d2cf070dab3d3797d2ee0b231655/grettyExample.groovy" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1431012#file_gretty_example.groovy" style="float:right;margin-right:10px;color:#666">grettyExample.groovy</a>
            <a href="https://gist.github.com/1431012">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<h2>5. Remotely execute any of these scripts by url</h2>
<p>As of <a title="Groovy 1.8.3/1.9-beta-4" href="http://glaforge.appspot.com/article/groovy-1-8-3-and-1-9-beta-4-released" target="_blank">Groovy 1.8.3/1.9-beta-4</a> you can refer to a Groovy script hosted at a remote url and execute it locally. If you work on different machines a lot(as I do) this can really help keep your toolbox of scripts handy, regardless of where you are. Since these scripts are publicly hosted on GitHub(thanks guys!), you can simply copy the url from the &#8216;view raw&#8217; link at the bottom and go to your console to execute them like this:</p>
<pre class="brush: groovy; title: ; notranslate">
$ groovy https://gist.github.com/raw/1430856/cea0186a862217015fb4fc2b63eb0ad3575c06fc/jenkinsBuildArtifacts.groovy
{
    &quot;actions&quot;: [
        {
            &quot;causes&quot;: [
                {
                    &quot;shortDescription&quot;: &quot;Started by timer&quot;
                }
            ]
        },
        {
            &quot;buildsByBranchName&quot;: {
                &quot;origin/master&quot;: {
                    &quot;buildNumber&quot;: 684,
                    &quot;buildResult&quot;: null,
                    &quot;revision&quot;: {
                        &quot;SHA1&quot;: &quot;3419fee4bb9436d3222ff3df8dd7fcf2308a6919&quot;,
                        &quot;branch&quot;: [
                            {
                                &quot;SHA1&quot;: &quot;3419fee4bb9436d3222ff3df8dd7fcf2308a6919&quot;,
                                &quot;name&quot;: &quot;origin/master&quot;
                            }
                        ]
                    }
                }
            },
... and so on
</pre>
<h2>Final note</h2>
<p>Being able to share scripts across a variety of machines has definitely made my professional life a lot easier and I&#8217;m very glad to see the ability to execute code from a URL in Groovy. But what really turned me onto the idea was its usage in Gradle, which supports loading remote Groovy/Gradle scripts using the &#8220;apply from: {url or file}&#8221; syntax. I find that more and more I reference particular tasks remotely in a build, especially for static analysis tools that don&#8217;t necessarily need to be run on a regular basis.<br />
A great example of this is the recently released <a href="http://tellurianring.com/wiki/gradle/templates">Gradle Templates Plugin</a>, which is basically the Gradle answer to the <a href="http://maven.apache.org/archetype/maven-archetype-plugin/">Maven Archetype plugin</a>. Create a build.gradle with the following content:</p>
<pre class="brush: groovy; title: ; notranslate">
apply from: 'http://launchpad.net/gradle-templates/trunk/1.2/+download/apply.groovy'
</pre>
<p>And we have the following tasks available to create new Gradle projects and objects with conventional structure:</p>
<pre class="brush: groovy; title: ; notranslate">
Template tasks
--------------
createGradlePlugin - Creates a new Gradle Plugin project in a new directory named after your project.
createGroovyClass - Creates a new Groovy class in the current project.
createGroovyProject - Creates a new Gradle Groovy project in a new directory named after your project.
createJavaClass - Creates a new Java class in the current project.
createJavaProject - Creates a new Gradle Java project in a new directory named after your project.
createScalaClass - Creates a new Scala class in the current project.
createScalaObject - Creates a new Scala object in the current project.
createScalaProject - Creates a new Gradle Scala project in a new directory named after your project.
createWebappProject - Creates a new Gradle Webapp project in a new directory named after your project.
exportAllTemplates - Exports all the default template files into the current directory.
exportGroovyTemplates - Exports the default groovy template files into the current directory.
exportJavaTemplates - Exports the default java template files into the current directory.
exportPluginTemplates - Exports the default plugin template files into the current directory.
exportScalaTemplates - Exports the default scala template files into the current directory.
exportWebappTemplates - Exports the default webapp template files into the current directory.
initGradlePlugin - Initializes a new Gradle Plugin project in the current directory.
initGroovyProject - Initializes a new Gradle Groovy project in the current directory.
initJavaProject - Initializes a new Gradle Java project in the current directory.
initScalaProject - Initializes a new Gradle Scala project in the current directory.
initWebappProject - Initializes a new Gradle Webapp project in the current directory.
</pre>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/' rel='bookmark' title='Using Gradle to Bootstrap your Legacy Ant Builds'>Using Gradle to Bootstrap your Legacy Ant Builds</a></li>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API'>Hooking into the Jenkins(Hudson) API</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using Gradle to Bootstrap your Legacy Ant Builds</title>
		<link>http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=using-gradle-to-bootstrap-your-legacy-ant-builds</link>
		<comments>http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/#comments</comments>
		<pubDate>Sun, 18 Sep 2011 20:44:11 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Ant]]></category>
		<category><![CDATA[AntBuilder]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[GradleBuild]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[Source code]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1422</guid>
		<description><![CDATA[Gradle provides several different ways to leverage your existing investment in Ant, both in terms of accumulated knowledge and the time you&#8217;ve already put into build files. This can greatly facilitate the process of porting Ant built projects over to Gradle, and can give you a path for incrementally doing so. The Gradle documentation does [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/' rel='bookmark' title='Why do I Like Gradle?'>Why do I Like Gradle?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/' rel='bookmark' title='A Groovy/Gradle JSLint Plugin'>A Groovy/Gradle JSLint Plugin</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Gradle provides several different ways to leverage your existing investment in Ant, both in terms of accumulated knowledge and the time you&#8217;ve already put into build files. This can greatly facilitate the process of porting Ant built projects over to Gradle, and can give you a path for incrementally doing so. The <a href="http://gradle.org/current/docs/userguide/userguide_single.html#ant">Gradle documentation</a> does a good job of describing how you can use Ant in your Gradle build script, but here&#8217;s a quick overview and some particulars I&#8217;ve run into myself.</p>
<p></p>
<h2>Gradle AntBuilder</h2>
<p>Every Gradle Project includes an AntBuilder instance, making any and all of the facilities of Ant available within your build files. Gradle provides a simple extension to the existing Groovy AntBuilder which adds a simple yet powerful way to interface with existing Ant build files: the <em>importBuild(Object antBuildFile)</em> method. Internally this method utilizes an Ant ProjectHelper to parse the specified Ant build file and then wraps all of the targets in Gradle tasks making them available in the Gradle build. The following is a simple Ant build file used for illustration which contains some properties and a couple of dependent targets.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;project name=&quot;build&quot; default=&quot;all&quot;&gt;
    &lt;echo&gt;Building ${ant.file}&lt;/echo&gt;

    &lt;property file=&quot;build.properties&quot;/&gt;
    &lt;property name=&quot;root.dir&quot; location=&quot;.&quot;/&gt;

    &lt;target name=&quot;dist&quot; description=&quot;Build the distribution&quot;&gt;
        &lt;property name=&quot;dist.dir&quot; location=&quot;dist&quot;/&gt;
        &lt;echo&gt;dist.dir=${dist.dir}, foo=${foo}&lt;/echo&gt;
    &lt;/target&gt;

    &lt;target name=&quot;all&quot; description=&quot;Build everything&quot; depends=&quot;dist&quot;/&gt;
&lt;/project&gt;
</pre>
<p>
Importing this build file using Gradle is a one-liner.<br />
</p>
<pre class="brush: groovy; title: ; notranslate">
ant.importBuild('src/main/resources/build.xml')
</pre>
<p>
And the output of gradle tasks &#8211;all on the command line shows that the targets have been added to the build tasks.<br />
</p>
<pre class="brush: groovy; title: ; notranslate">
$ gradle tasks --all
...
Other tasks
-----------
all - Build everything
    dist - Build the distribution
...
</pre>
<p>
Properties used in the Ant build file can be specified in the Gradle build or on the command line and, unlike the usual Ant property behaviour, properties set by Ant or on the command line may be overwritten by Gradle. Given a simple build.properties file with <em>foo=bar</em> as the single entry, here&#8217;s a few combinations to demonstrate the override behaviour.<br />

<table id="wp-table-reloaded-id-6-no-1" class="wp-table-reloaded wp-table-reloaded-id-6">
<thead>
	<tr class="row-1 odd">
		<th class="column-1">Command line invocation</th><th class="column-2">Gradle Build Config</th><th class="column-3">Effect</th><th class="column-4">Result</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2 even">
		<td class="column-1">gradle dist</td><td class="column-2">ant.importBuild('src/main/resources/build.xml')</td><td class="column-3">build.properties value loaded from ant build is used</td><td class="column-4">foo=bar</td>
	</tr>
	<tr class="row-3 odd">
		<td class="column-1">gradle dist -Dfoo=NotBar</td><td class="column-2">ant.importBuild('src/main/resources/build.xml')</td><td class="column-3">command line property is used</td><td class="column-4">foo=NotBar</td>
	</tr>
	<tr class="row-4 even">
		<td class="column-1">gradle dist -Dfoo=NotBar</td><td class="column-2">ant.foo='NotBarFromGradle' <br />
ant.importBuild('src/main/resources/build.xml')</td><td class="column-3">Gradle build property is used</td><td class="column-4">foo=NotBarFromGradle</td>
	</tr>
	<tr class="row-5 odd">
		<td class="column-1">gradle dist -Dfoo=NotBar</td><td class="column-2">ant.foo='NotBarFromGradle'<br />
ant.importBuild('src/main/resources/build.xml')<br />
ant.foo='NotBarFromGradleAgain'</td><td class="column-3">Gradle build property override is used</td><td class="column-4">foo=NotBarFromGradleAgain</td>
	</tr>
</tbody>
</table>
</p>
<h2>How to deal with task name clashes</h2>
<p>Since Gradle insists on uniqueness of task names attempting to import an Ant build that contains a target with the same name as an existing Gradle task will fail. The most common clash I&#8217;ve encountered is with the <em>clean</em> task provided by the Gradle BasePlugin. With the help of a little bit of indirection we can still import and use any clashing targets by utilizing the GradleBuild task to bootstrap an Ant build import in an isolated Gradle project. Let&#8217;s add a new task to the mix in the Ant build imported and another dependency on the ant <em>clean</em> target to the <em>all</em> task. </p>
<pre class="brush: xml; title: ; notranslate">
&lt;!-- excerpt from buildWithClean.xml Ant build file --&gt;
    &lt;target name=&quot;clean&quot; description=&quot;clean up&quot;&gt;
        &lt;echo&gt;Called clean task in ant build with foo = ${foo}&lt;/echo&gt;
    &lt;/target&gt;
    &lt;target name=&quot;all&quot; description=&quot;Build everything&quot; depends=&quot;dist,clean&quot;/&gt;
</pre>
<p>And a simple Gradle build file which will handle the import.</p>
<pre class="brush: groovy; title: ; notranslate">
ant.importBuild('src/main/resources/buildWithClean.xml')
</pre>
<p>Finally, in our main gradle build file we add a task to run the targets we want.</p>
<pre class="brush: groovy; title: ; notranslate">
task importTaskWithExistingName(type: GradleBuild) { GradleBuild antBuild -&gt;
    antBuild.buildFile ='buildWithClean.gradle'
    antBuild.tasks = ['all']
}
</pre>
<p>This works, but unfortunately suffers from <a href="http://issues.gradle.org/browse/GRADLE-427">one small problem</a>. When Gradle is importing these tasks it doesn&#8217;t properly respect the declared order of the dependencies. Instead it executes the dependent ant targets in alphabetical order. In this particular case Ant expects to execute the <em>dist</em> target before <em>clean</em> and Gradle executes them in the reverse order. This can be worked around by explicitly stating the task order, definitely not ideal, but workable. This Gradle task will execute the  underlying Ant targets in the way we need.</p>
<pre class="brush: groovy; title: ; notranslate">
task importTasksRunInOrder(type: GradleBuild) { GradleBuild antBuild -&gt;
    antBuild.buildFile ='buildWithClean.gradle'
    antBuild.tasks = ['dist', 'clean']
}
</pre>
<h2>Gradle Rules for the rest</h2>
<p>Finally, you can use a Gradle Rule to allow for calling any arbitrary target in a GradleBuild bootstrapped import.</p>
<pre class="brush: groovy; title: ; notranslate">
tasks.addRule(&quot;Pattern: a-&lt;target&gt; will execute a single &lt;target&gt; in the ant build&quot;) { String taskName -&gt;
    if (taskName.startsWith(&quot;a-&quot;)) {
        task(taskName, type: GradleBuild) {
            buildFile = 'buildWithClean.gradle'
            tasks = [taskName - 'a-']
        }
    }
}
</pre>
<p>In this particular example, this can allow you to string together calls as well, but be warned that they execute in completely segregated environments.</p>
<pre class="brush: groovy; title: ; notranslate">
$ gradle a-dist a-clean
</pre>
<h2>Source code</h2>
<p>All of code referenced in this article is <a href="https://github.com/kellyrob99/gradleAntImport">available on github</a> if you&#8217;d like to take a closer look.</p>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/' rel='bookmark' title='Why do I Like Gradle?'>Why do I Like Gradle?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/' rel='bookmark' title='A Groovy/Gradle JSLint Plugin'>A Groovy/Gradle JSLint Plugin</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Groovy/Gradle JSLint Plugin</title>
		<link>http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=a-groovygradle-jslint-plugin</link>
		<comments>http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/#comments</comments>
		<pubDate>Mon, 04 Apr 2011 01:07:31 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Ant]]></category>
		<category><![CDATA[example]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[GroovyMag]]></category>
		<category><![CDATA[JSLint]]></category>
		<category><![CDATA[kellyrob]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[Source code]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1552</guid>
		<description><![CDATA[This article originally appeared in the January 2011 issue of GroovyMag. Gradle is a build system in which builds are described using a declarative and concise DSL written in the Groovy language. This article describes how you can wrap proven Apache Ant Tasks in a Gradle Plugin to make using them as effortless as possible. [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/' rel='bookmark' title='Using Gradle to Bootstrap your Legacy Ant Builds'>Using Gradle to Bootstrap your Legacy Ant Builds</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/' rel='bookmark' title='Why do I Like Gradle?'>Why do I Like Gradle?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><em>This article originally appeared in the January 2011 issue of <a href="http://www.groovymag.com/">GroovyMag</a>.</em></p>
<blockquote><p>Gradle is a build system in which builds are described using a declarative and concise DSL written in the Groovy language. This article describes how you can wrap proven Apache Ant Tasks in a Gradle Plugin to make using them as effortless as possible. We&#8217;ll also go over some of the tools Gradle provides for building and testing robust Plugin functionality following some easy patterns.</p></blockquote>
<p>Creating new custom Plugins for Gradle is a relatively straightforward and easy process. Within a Plugin it&#8217;s possible to configure a Gradle Project with new properties, dependencies, Tasks &#8211; pretty much anything that you can configure in a build.gradle file can be encapsulated into a Plugin for abstraction, portability and reuse. One of the easier ways to add functionality through a Plugin is to encapsulate an existing Ant Task and enhance it by providing the ease-of-use and configuration that Gradle users have come to expect. Recently, I&#8217;ve been writing a lot more JavaScript and was looking for static analysis tools to help guide me away from &#8216;bad habits&#8217;. The popular choice for static analysis of  JavaScript code seems to be <a href="http://www.jslint.com">JSLint</a>, so here&#8217;s an example of providing that functionality for a Gradle build by wrapping an existing <a href="http://jslint4java.googlecode.com/svn/docs/1.4/ant.html">JSLint task</a> and making it easier to work with.</p>
<p></p>
<h2>Anatomy of a Gradle Plugin</h2>
<p>Gradle plugins can most easily be built using Gradle itself. There is a conveniently available <strong>gradleApi()</strong> method you can call to include the required framework classes, demonstrated in the dependencies section of a build.gradle file shown in Listing 1. For this example we&#8217;re also using the <a href="http://gradle.org/latest/docs/userguide/groovy_plugin.html">Groovy Plugin</a> and <a href="http://www.junit.org/">JUnit</a> for testing, so we will include those dependencies as well.</p>
<pre class="brush: groovy; title: ; notranslate">
dependencies {
    compile gradleApi()
    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.6'
    testCompile group: 'junit', name: 'junit', version: '4.8.2
}
</pre>
<p><em>Listing 1: The dependencies portion of a build.gradle file for building our Plugin </em></p>
<p>Creating a new Gradle <a href="http://www.gradle.org/latest/docs/javadoc/org/gradle/api/Plugin.html">Plugin</a> is a simple matter of implementing the Gradle interface and its single required method, the skeleton of which is shown in Listing 2. Within the apply method, the Plugin can configure the Project to add Tasks or properties as needed.</p>
<pre class="brush: groovy; title: ; notranslate">
class JSLintPlugin implements Plugin&lt;Project&gt;
{
	void apply(Project project)
	{
		//configure the Project here
	}
}
</pre>
<p><em>Listing 2: Skeleton of the Plugin implementation</em></p>
<p></p>
<h2>Integrating with Ant</h2>
<p>I&#8217;ve never been overly fond of <a href="http://ant.apache.org/">Ant</a>, mostly due to the extremely verbose and repetitive nature of the xml declaration. But the fact remains that Ant has been a primary and well-used build tool for years, and the Tasks written for it have been tried and tested by many developers. The Groovy <a href="http://groovy.codehaus.org/api/groovy/util/AntBuilder.html">AntBuilder</a>, in combination with the facilities Gradle provides for dependency resolution and classpath management, makes it very easy to incorporate existing Ant functionality into a build and abstract most of the details away from the end user. For this plugin, we add the library containing the Ant Task to a custom configuration so that we can have it automatically downloaded and easily resolve the classpath.<br />
Listing 3 shows how the configuration for the JSLint4Java Task could appear in an Ant build.xml file. Note that you&#8217;re on your own here to provide the required library for the classpath.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;taskdef name=&quot;jslint&quot;
         classname=&quot;com.googlecode.jslint4java.ant.JSLintTask&quot;
         classpath=&quot;/path/to/jslint4java-1.4.jar&quot;/&gt;
&lt;target name=&quot;jslint&quot;&gt;
    &lt;jslint options=&quot;undef,white&quot; haltOnFailure=&quot;false&quot;&gt;
        &lt;formatter type=&quot;xml&quot; destfile=&quot;${build.dir}/reports&quot;/&gt;
        &lt;fileset dir=&quot;.&quot; includes=&quot;**/*.js&quot; excludes=&quot;**/server/&quot;/&gt;
    &lt;/jslint&gt;
&lt;/target&gt;
</pre>
<p><em>Listing 3: Configuring the Ant target in a build.xml file</em></p>
<p>Gradle makes it easy to separate the configuration and execution phases of the build, allowing for a Plugin to add an Ant Task to the Gradle Project and expose the (optional) configuration in a build script. In addition, Gradle encourages a pattern of providing a &#8216;convention&#8217; object alongside of a Plugin to clearly separate the concerns.<br />
Listing 4 demonstrates some code from the Plugin implementation that adds a &#8216;jslint&#8217; Task to a Gradle Project, setting the specific options based on a convention object. Note how we extract the classpath using the notation  <strong>project.configurations.jslint.asPath</strong>.</p>
<pre class="brush: groovy; title: ; notranslate">
private static final String TASK_NAME = 'jslint'
private Project project
private JSLintPluginConvention jsLintpluginConvention
...
// some of the code in the apply method
this.jsLintpluginConvention = new JSLintPluginConvention(project)
project.convention.plugins.jslint = jsLintpluginConvention
project.task(TASK_NAME) &lt;&lt; {
    project.file(project.reportsDir).mkdirs()
    logger.info(&quot;Running jslint on project ${project.name}&quot;)
    ant.taskdef(name: TASK_NAME, classname: jsLintpluginConvention.taskName,
        classpath: project.configurations.jslint.asPath)
    ant.&quot;$TASK_NAME&quot;(jsLintpluginConvention.mapTaskProperties()) {
        formatter(type: jsLintpluginConvention.decideFormat(),
                destfile:  jsLintpluginConvention.createOutputFileName())
        jsLintpluginConvention.inputDirs.each { dirName -&gt;
            fileset(dir: dirName,
			 includes: jsLintpluginConvention.includes,
			 excludes: jsLintpluginConvention.excludes)
        }
    }
}
</pre>
<p><em>Listing 4: Adding a jslint Task to a Gradle Project </em></p>
<p>If you&#8217;re not already familiar with the Gradle syntax for creating new Tasks inline, the <strong>project.task(String taskName)</strong> method is called to instantiate a new <a href="http://www.gradle.org/latest/docs/javadoc/org/gradle/api/Task.html">Task</a>, and the &#8216;< <' syntax is used to to push the Task activities into the 'doLast' Task lifecycle phase.<br />
Allowing for configuration of the Task in a build script is as simple as exposing a method named the same as the Task that takes in a Closure parameter and applies that Closure to set properties on the convention object, as shown in Listing 5.</p>
<pre class="brush: groovy; title: ; notranslate">
/**
 * Perform custom configuration of the plugin using the provided closure.
 * @param closure
 */
def jslint(Closure closure)
{
    closure.delegate = this
    closure()
}
</pre>
<p>Listing 5: A method to allow for configuration of the jslint Task from a build script </p>
<p></p>
<h2>The simple use-case</h2>
<p>As per usual when working with Gradle, using this plugin in the most basic case requires only these things:</p>
<ul>
<li>Declare a dependency on the Plugin source.  This can be either a released jar to be downloaded from a repository by Gradle or you can download a jar manually from pretty much anywhere and add it to the classpath directly.</li>
<li>Apply the Plugin to a Gradle build.</li>
<li>Call the jslint Task as part of the build.</li>
</ul>
<p>The entire configuration and usage looks something like Listing 6, assuming that the gradle-jslint-plugin jar is found in /usr/home/gradlelibs.</p>
<pre class="brush: groovy; title: ; notranslate">
/* In a build.gradle file */
buildscript {
	dependencies {
		classpath fileTree(dir: '/usr/home/gradlelibs', include: '*.jar')
	}
}
apply plugin: org.kar.jslint.gradle.plugin.JSLintPlugin

/* and on the command line... */
gradle jslint
</pre>
<p><em>Listing 6: Configuring the jslint Plugin in a build script and calling it from the command line</em></p>
<p>By default, this is enough to scan for all .js files under the directory where the build script is located and create a JSLint text report using the basic settings.</p>
<p></p>
<h2>The not-so-simple case</h2>
<p>Of course in the real world the defaults aren't always what we need, so being able to easily configure the Task is essential. Fortunately, Gradle makes extending a custom Plugin to allow for configuration by a simple Closure, so we can exercise the code from Listing 5 in a build script with the Closure  definition in Listing 7.</p>
<pre class="brush: groovy; title: ; notranslate">
jslint {
	haltOnFailure = false
	excludes = '**/metadata/'
	options = 'rhino'
	formatterType = 'html'
}
</pre>
<p><em>Listing 7: Departing from the default jslint Task configuration </em></p>
<p></p>
<h2>Extending Ant Task capabilities</h2>
<p>The Ant Task as is can produce either a plain text document or an xml report, but transforming the results into a more consumable html format is easy to do using an Ant xslt Task. Having Gradle wrap the Task definition allows for simply adding a new formatter type to the configuration and abstracting away the details from the end user. A copy of <a href="http://code.google.com/p/memwords/source/browse/trunk/jslint/jslint.xsl?r=109">an xsl file available online</a> is easy to incorporate with the plugin and can be used to transform the xml output into a nicely formatted web page.  Being able to program around Ant Tasks like this is a great way to enhance their value in your build. An example of simple output from the test cases included in the Plugin is shown in Figure 1.<br />

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/jslint/figure1.png" title="" class="shutterset_singlepic76" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/76__x_figure1.png" alt="figure1" title="figure1" />
</a>
<br />
<em>Figure 1: Example html formatted output from jslint </em></p>
<p></p>
<h2>Testing using ProjectBuilder</h2>
<p>In order to facilitate testing custom Tasks and Plugins, the Gradle framework provides the <a href="http://www.gradle.org/latest/docs/javadoc/org/gradle/testfixtures/ProjectBuilder.html">ProjectBuilder</a> implementation to handle most of the heavy lifting. This gives you a mocked out instance of a Gradle Project that builds into a temporary directory; very handy for testing how Tasks behave under real working conditions.  Having tools like this available directly from the framework removes a lot of potential barriers that might otherwise discourage testing of custom components. The source code that accompanies this article uses the ProjectBuilder to achieve 100% code coverage of the project and is available on github at <a href="https://github.com/kellyrob99/gradle-jslint-plugin">https://github.com/kellyrob99/gradle-jslint-plugin</a> if you'd like to look closer for some ideas on how to test your own Gradle Plugins. An already built version of the jar is also available if you'd like to try without having to build it yourself: <a href="https://github.com/kellyrob99/gradle-jslint-plugin/blob/master/releases/gradle-jslint-plugin-0.1-SNAPSHOT.jar">https://github.com/kellyrob99/gradle-jslint-plugin/blob/master/releases/gradle-jslint-plugin-0.1-SNAPSHOT.jar</a>.</p>
<p></p>
<h2>How could we improve this Plugin?</h2>
<p>This implementation represents the 'brute-force' method of wrapping an Ant Task, and there are several ways to enhance its function, if we wanted to spend some additional time and effort on the Plugin. It would actually be far more flexible if we extended the Gradle <a href="http://gradle.org/latest/docs/groovydoc/org/gradle/api/DefaultTask.html">DefaultTask</a> to provide the actual functionality; this would allow for the possibility of executing the Task separately against different sets of JavaScript in the same project with different configurations of JSLint. In the case of an application with both client and server-side JavaScript, for instance, you might want to apply different rules. In that event you'd probably also want to have the capability to aggregate multiple result sets, which would be a relatively easy feature to add.<br />
Having a separate Task implementation defined would also make it easier to clearly define the inputs and outputs of the process using one of the <a href="http://gradle.org/latest/docs/javadoc/org/gradle/api/tasks/Input.html">@Input</a> or <a href="http://gradle.org/latest/docs/javadoc/org/gradle/api/tasks/Output.html">@Output</a> Gradle annotations, allowing for incremental builds including JSLint execution. The full set of annotations available from the <a href="http://gradle.org/latest/docs/javadoc/org/gradle/api/tasks/package-summary.html">org.gradle.api.tasks package</a> allow for combining File and/or simple property types to make your Tasks smarter regarding whether or not running again would produce a different result than the last execution.<br />
This article was created using the recently released 0.9 version of Gradle and version 1.4.4 of jslint4java.</p>
<p></p>
<h2>Learn more</h2>
<p>If you'd like to find out some background or more about Gradle and how to create your own custom Plugins and Tasks, you can find some good information on these sites:</p>
<ul>
<li>The Gradle documentation at <a href="http://gradle.org/documentation.html">http://gradle.org/documentation.html</a></li>
<li>In particular, the Project API is a great starting point for getting to know Gradle: <a href="http://gradle.org/latest/docs/javadoc/org/gradle/api/Project.html">http://gradle.org/latest/docs/javadoc/org/gradle/api/Project.html</a></li>
<li>The excellent examples of Mr. Haki at <a href="http://mrhaki.blogspot.com/search/label/Gradle%3AGoodness">http://mrhaki.blogspot.com/search/label/Gradle%3AGoodness</a></li>
<li>Published plugins on github can be found with this search: <a href="https://github.com/search?type=Repositories&amp;language=&amp;q=gradle+plugin">https://github.com/search?type=Repositories&amp;language=&amp;q=gradle+plugin</a> </li>
</ul>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/' rel='bookmark' title='Using Gradle to Bootstrap your Legacy Ant Builds'>Using Gradle to Bootstrap your Legacy Ant Builds</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/' rel='bookmark' title='Why do I Like Gradle?'>Why do I Like Gradle?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Hooking into the Jenkins(Hudson) API</title>
		<link>http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=hooking-into-the-jenkinshudson-api</link>
		<comments>http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/#comments</comments>
		<pubDate>Sun, 27 Mar 2011 17:24:09 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[HTTPBuilder]]></category>
		<category><![CDATA[Hudson]]></category>
		<category><![CDATA[jenkins]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[kellyrob99.com]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1587</guid>
		<description><![CDATA[Which one &#8211; Hudson or Jenkins? Both. I started working on this little project a couple of months back using Hudson v1.395 and returned to it after the great divide happened. I took it as an opportunity to see whether there would be any significant problems should I choose to move permanently to Jenkins in [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/' rel='bookmark' title='A Grails App Demoing the StackExchange API'>A Grails App Demoing the StackExchange API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h2>Which one &#8211; Hudson or Jenkins?</h2>
<p>Both. I started working on this little project a couple of months back using Hudson v1.395 and returned to it after the great divide happened. I took it as an opportunity to see whether there would be any significant problems should I choose to move permanently to Jenkins in the future. There were a couple of hiccups- most notably that the new CLI jar didn&#8217;t work right out of the box- but overall v1.401 of Jenkins worked as expected after the switch. The good news is the old version of the CLI jar still works, so this example is actually using a mix of code to get things done. Anyway, the software is great and there&#8217;s more than enough credit to go around.</p>
<p></p>
<h2>The API</h2>
<p>Jenkins/Hudson has a <a href="http://wiki.hudson-ci.org/display/HUDSON/Remote+access+API">handy remote API</a> packed with information about your builds and supports a rich set of functionality to control them, and the server in general, remotely. It is possible to trigger builds, copy jobs, stop the server and even install plugins remotely. You have your choice of XML, JSON or Python when interacting with the APIs of the server.  And, as the build in documentation says, you can find the functionality you need on a relative path from the build server url at:</p>
<blockquote><p>&#8220;/&#8230;/api/ where &#8216;&#8230;&#8217; portion is the object for which you&#8217;d like to access&#8221;.</p></blockquote>
<p>This will show a brief documentation page if you navigate to it in a browser, and will return a result if you add the desired format as the last part of the path. For instance, to load information about the computer running a locally hosted Jenkins server, a get request on this url would return the result in JSON format: http://localhost:8080/computer/api/json.</p>
<pre class="brush: plain; title: ; notranslate">
{
  &quot;busyExecutors&quot;: 0,
  &quot;displayName&quot;: &quot;nodes&quot;,
  &quot;computer&quot;: [
    {
      &quot;idle&quot;: true,
      &quot;executors&quot;: [
        {
        },
        {
        }
      ],
      &quot;actions&quot;: [

      ],
      &quot;temporarilyOffline&quot;: false,
      &quot;loadStatistics&quot;: {
      },
      &quot;displayName&quot;: &quot;master&quot;,
      &quot;oneOffExecutors&quot;: [

      ],
      &quot;manualLaunchAllowed&quot;: true,
      &quot;offline&quot;: false,
      &quot;launchSupported&quot;: true,
      &quot;icon&quot;: &quot;computer.png&quot;,
      &quot;monitorData&quot;: {
        &quot;hudson.node_monitors.ResponseTimeMonitor&quot;: {
          &quot;average&quot;: 111
        },
        &quot;hudson.node_monitors.ClockMonitor&quot;: {
          &quot;diff&quot;: 0
        },
        &quot;hudson.node_monitors.TemporarySpaceMonitor&quot;: {
          &quot;size&quot;: 58392846336
        },
        &quot;hudson.node_monitors.SwapSpaceMonitor&quot;: null,
        &quot;hudson.node_monitors.DiskSpaceMonitor&quot;: {
          &quot;size&quot;: 58392846336
        },
        &quot;hudson.node_monitors.ArchitectureMonitor&quot;: &quot;Mac OS X (x86_64)&quot;
      },
      &quot;offlineCause&quot;: null,
      &quot;numExecutors&quot;: 2,
      &quot;jnlpAgent&quot;: false
    }
  ],
  &quot;totalExecutors&quot;: 2
}
</pre>
<p>Here&#8217;s the same tree rendered using GraphViz.</p>

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/computer.png" title="" class="shutterset_singlepic74" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/74_watermark_700x500_computer.png" alt="computer" title="computer" />
</a>

<p>This functionality extends out in a tree from the root of the server, and you can gate how much of the tree you load from any particular branch by supplying a &#8216;depth&#8217; parameter on your urls. Be careful how high you specify this variable. Testing with a load depth of four against a populous, long-running build server (dozens of builds with thousands of job executions) managed to regularly timeout for me. To give you an idea, here&#8217;s a very rough visualization of the domain at depth three from the root of the api.</p>

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/hudsonapidepth3.png" title="" class="shutterset_singlepic68" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/68_watermark_700x500_hudsonapidepth3.png" alt="hudsonapidepth3" title="hudsonapidepth3" />
</a>

<p>Getting data out of the server is very simple, but the ability to remotely trigger activity on the server is more interesting. In order to trigger a build of a job named &#8216;test&#8217;, a POST on http://localhost:8080/job/test/build does the job. Using the available facilities, it&#8217;s pretty easy to do things like:</p>
<ul>
<li>load a job&#8217;s configuration file, modify it and create a new job by POSTing the new config.xml file</li>
<li>move a job from one build machine to another</li>
<li>build up an overview of scheduled builds</li>
</ul>
<p></p>
<h2>The CLI Jar</h2>
<p>There&#8217;s another way to remotely drive build servers in the CLI jar distributed along with the server. This jar provides simple facilities for executing certain commands remotely on the build server. Of note, this enables installing plugins remotely and executing a remote Groovy shell. I incorporated this functionality with a very thin wrapper around the main class exposed by the CLI jar as shown in the next code sample.</p>
<pre class="brush: groovy; title: ; notranslate">
/**
 * Drive the CLI with multiple arguments to execute.
 * Optionally accepts streams for input, output and err, all of which
 * are set by default to System unless otherwise specified.
 * @param rootUrl
 * @param args
 * @param input
 * @param output
 * @param err
 * @return
 */
def runCliCommand(String rootUrl, List&lt;String&gt; args, InputStream input = System.in,
        OutputStream output = System.out, OutputStream err = System.err)
{
    def CLI cli = new CLI(rootUrl.toURI().toURL())
    cli.execute(args, input, output, err)
    cli.close()
}
</pre>
<p>And here&#8217;s a simple test showing how you can execute a Groovy script to load information about jobs, similar to what you can do from the built-in Groovy script console on the server, which can be found for a locally installed deployment at http://localhost:8080/script.</p>
<pre class="brush: groovy; title: ; notranslate">
def &quot;should be able to query hudson object through a groovy script&quot;()
{
    final ByteArrayOutputStream output = new ByteArrayOutputStream()
    when:
    api.runCliCommand(rootUrl, ['groovysh', 'for(item in hudson.model.Hudson.instance.items) { println(&quot;job $item.name&quot;)}'],
            System.in, output, System.err)

    then:
    println output.toString()
    output.toString().split('\n')[0].startsWith('job')
}
</pre>
<p>Here are some links to articles about the CLI, if you want to learn more :</p>
<ul>
<li><a href="http://wiki.hudson-ci.org/display/HUDSON/Hudson+CLI">Hudson CLI wikidoc</a></li>
<li><a href="http://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI">Jenkins CLI wikidoc</a></li>
<li><a href="http://jenkins-php.org/">A template for PHP jobs on Jenkins</a></li>
<li><a href="http://weblogs.java.net/blog/kohsuke/archive/2009/05/hudson_cli_and.html">An article from Kohsuke Kawaguchi</a></li>
<li><a href="http://meera-subbarao.blogspot.com/2010/08/hudson-and-cli.html">A nice tutorial</a></li>
</ul>
<p></p>
<h2>HTTPBuilder</h2>
<p><a href="http://groovy.codehaus.org/modules/http-builder/">HTTPBuilder</a> is my tool of choice when programming against an HTTP API nowadays. The usage is very straightforward and I was able to get away with only two methods to support reaching the entire API: one for GET and one for POST. Here&#8217;s the GET method, sufficient for executing the request, parsing the JSON response, and complete with (albeit naive) error handling.</p>
<pre class="brush: groovy; title: ; notranslate">
/**
 * Load info from a particular rootUrl+path, optionally specifying a 'depth' query
 * parameter(default depth = 0)
 *
 * @param rootUrl the base url to access
 * @param path  the api path to append to the rootUrl
 * @param depth the depth query parameter to send to the api, defaults to 0
 * @return parsed json(as a map) or xml(as GPathResult)
 */
def get(String rootUrl, String path, int depth = 0)
{
    def status
    HTTPBuilder http = new HTTPBuilder(rootUrl)
    http.handler.failure = { resp -&gt;
        println &quot;Unexpected failure on $rootUrl$path: ${resp.statusLine} ${resp.status}&quot;
        status = resp.status
    }

    def info
    http.get(path: path, query: [depth: depth]) { resp, json -&gt;
        info = json
        status = resp.status
    }
    info ?: status
}
</pre>
<p>Calling this to fetch data is a one liner, as the only real difference is the &#8216;path&#8217; variable used when calling the API.</p>
<pre class="brush: groovy; title: ; notranslate">
private final GetRequestSupport requestSupport = new GetRequestSupport()
    ...
/**
 * Display the job api for a particular Hudson job.
 * @param rootUrl the url for a particular build
 * @return job info in json format
 */
def inspectJob(String rootUrl, int depth = 0)
{
    requestSupport.get(rootUrl, API_JSON, depth)
}
</pre>
<p>Technically, there&#8217;s nothing here that limits this to JSON only. One of the great things about HTTPBuilder is that it will happily just try to do the right thing with the response. If the data returned is in JSON format, as these examples are, it gets parsed into a JSONObject. If on the other hand, the data is XML, it gets parsed into a Groovy GPathResult. Both of these are very easily navigable, although the syntax for navigating their object graphs is different.</p>
<p></p>
<h2>What can you do with it?</h2>
<p>My primary motivation for exploring the API of Hudson/Jenkins was to see how I could make managing multiple servers easier. At present I work daily with four build servers and another handful of slave machines, and support a variety of different version branches. This includes a mix of unit and functional test suites, as well as a continuous deployment job that regularly pushes changes to test machines matching our supported platform matrix, so unfortunately things are not quite as simple as copying a single job when branching. Creating the build infrastructure for new feature branches in an automatic, or at least semi-automatic, fashion is attractive indeed, especially since plans are in the works to expand build automation. For a recent 555 day project, I utilized the API layer to build a Grails app functioning as both a cross-server build radiator and a central facility for server management. This proof of concept is capable of connecting to multiple build servers and visualizing job data as well as specific system configuration, triggering builds, and direct linking to each of the connected servers to allow for drilling down further. Here&#8217;s a couple of mock-ups that pretty much show the picture.<br />

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/grailsapp1.png" title="" class="shutterset_singlepic71" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/71_watermark_700x500_grailsapp1.png" alt="grailsapp1" title="grailsapp1" />
</a>
<br />

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/grailsapp2.png" title="" class="shutterset_singlepic73" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/73_watermark_700x500_grailsapp2.png" alt="grailsapp2" title="grailsapp2" />
</a>
</p>
<p></p>
<h2>Just a pretty cool app for installing Jenkins</h2>
<p>This is only very indirectly related, but I came across this very nice and simple Griffon app, called the <a href="http://code.google.com/p/jenkins-assembler/">Jenkins-Assembler</a> which simplifies preparing your build server. It presents you with a list of plugins, letting you pick and choose, and then downloads and composes them into a single deployable war.<br />

<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/jenkins-assembler.png" title="" class="shutterset_singlepic75" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/75__x_jenkins-assembler.png" alt="jenkins-assembler" title="jenkins-assembler" />
</a>
</p>
<p></p>
<h2>Enough talking &#8211; where&#8217;s the code???</h2>
<p>Source code related to this article is <a href="https://github.com/kellyrob99/Jenkins-api-tour">available on github</a>.  The tests are more of an exploration of the live API than an actual test of the code in this project. They run against a local server launched using the Gradle Jetty plugin. Finally, here&#8217;s some pretty pictures for you.</p>

<div class="ngg-galleryoverview" id="ngg-gallery-13-1587">

	<!-- Slideshow link -->
	<div class="slideshowlink">
		<a class="slideshowlink" href="http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/?show=slide">
			[Show as slideshow]		</a>
	</div>

	<!-- Piclense link -->
	<div class="piclenselink">
		<a class="piclenselink" href="javascript:PicLensLite.start({feedUrl:'http://www.kellyrob99.com/blog/wp-content/plugins/nextgen-gallery/xml/media-rss.php?gid=13&amp;mode=gallery'});">
			[View with PicLens]		</a>
	</div>
	
	<!-- Thumbnails -->
		
	<div id="ngg-image-65" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/computerdepth4.png" title=" " class="shutterset_set_13" >
								<img title="computerdepth4" alt="computerdepth4" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_computerdepth4.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-66" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/gradleconfig.png" title=" " class="shutterset_set_13" >
								<img title="gradleconfig" alt="gradleconfig" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_gradleconfig.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-67" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/hudsonapi.png" title=" " class="shutterset_set_13" >
								<img title="hudsonapi" alt="hudsonapi" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_hudsonapi.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-68" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/hudsonapidepth3.png" title=" " class="shutterset_set_13" >
								<img title="hudsonapidepth3" alt="hudsonapidepth3" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_hudsonapidepth3.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-69" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/lastbuilddepth1.png" title=" " class="shutterset_set_13" >
								<img title="lastbuilddepth1" alt="lastbuilddepth1" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_lastbuilddepth1.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-70" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/overallloaddepth3.png" title=" " class="shutterset_set_13" >
								<img title="overallloaddepth3" alt="overallloaddepth3" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_overallloaddepth3.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-71" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/grailsapp1.png" title=" " class="shutterset_set_13" >
								<img title="grailsapp1" alt="grailsapp1" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_grailsapp1.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-74" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/computer.png" title=" " class="shutterset_set_13" >
								<img title="computer" alt="computer" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_computer.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-73" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/grailsapp2.png" title=" " class="shutterset_set_13" >
								<img title="grailsapp2" alt="grailsapp2" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_grailsapp2.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-75" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/jenkins-assembler.png" title=" " class="shutterset_set_13" >
								<img title="jenkins-assembler" alt="jenkins-assembler" src="http://www.kellyrob99.com/blog/wp-content/gallery/hudson-api/thumbs/thumbs_jenkins-assembler.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 	 	
	<!-- Pagination -->
 	<div class='ngg-clear'></div>
 	
</div>


<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/' rel='bookmark' title='A Grails App Demoing the StackExchange API'>A Grails App Demoing the StackExchange API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Why do I Like Gradle?</title>
		<link>http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=why-do-i-like-gradle</link>
		<comments>http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/#comments</comments>
		<pubDate>Sat, 13 Nov 2010 23:53:47 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Ant]]></category>
		<category><![CDATA[build]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1526</guid>
		<description><![CDATA[Gradle, if you don&#8217;t already know it, is rapidly gaining traction as a strong leader in the next generation of build systems. It builds heavily upon excellent aspects of the Maven and Ant frameworks, yet is pitched as not suffering from the same &#8220;Frameworkitis&#8220;. And I&#8217;ve gotta say &#8211; the results are pretty spectacular. Among [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/' rel='bookmark' title='Using Gradle to Bootstrap your Legacy Ant Builds'>Using Gradle to Bootstrap your Legacy Ant Builds</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/' rel='bookmark' title='A Groovy/Gradle JSLint Plugin'>A Groovy/Gradle JSLint Plugin</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><a href="http://gradle.org/">Gradle</a>, if you don&#8217;t already know it, is rapidly gaining traction as a strong leader in the next generation of build systems. It builds heavily upon excellent aspects of the <a href="http://maven.apache.org/">Maven</a> and <a href="http://ant.apache.org/">Ant</a> frameworks, yet is pitched as not suffering from the same &#8220;<a href="http://www.codinghorror.com/blog/2005/11/conversations-with-erich-gamma.html">Frameworkitis</a>&#8220;. And I&#8217;ve gotta say &#8211; the results are pretty spectacular. Among the major selling features, at least as I see it, are:</p>
<ul>
<li>Groovy syntax and a very terse and descriptive dsl that makes build scripts easily comprehensible</li>
<li>flexibility of layout, configuration, organizing build logic &#8211; pretty much everything</li>
<li>incremental builds, based on an easily implementable pattern</li>
<li>convention over configuration paradigm, thank you very much Maven</li>
<li>clear separation of build configuration from execution</li>
<li>extensibility at every level</li>
</ul>
<h2>The most basic Java build</h2>
<pre class="brush: groovy; title: ; notranslate">
apply plugin: 'java'
</pre>
<p>That&#8217;s it. One line of Groovy in a file called &#8216;build.gradle&#8217; and you can build a Java project with a Maven-standardized project layout.</p>
<pre>
├── build.gradle
└── src
    ├── main
    │   ├── java
    │   └── resources
    └── test
        ├── java
        └── resources
</pre>
<p>Included with the Java plugin are tasks to compile, package, test and javadoc your code. You also get <a href="http://gradle.org/latest/docs/userguide/artifact_dependencies_tutorial.html#sec:artifact_configurations">configuration</a> objects to describe the artifacts that your build depends on and those  that it produces. Of course, with this most basic setup these configurations don&#8217;t yet have anything in them, but in a complex build they&#8217;re very handy for isolating the responsibilities of each task. Because you can both configure the existing configurations and add custom ones yourself, it&#8217;s very easy to accommodate a project that follows a different structure, whether that is just differently named source directories or multiple directories that need specific processing. This is VERY handy for legacy builds.</p>
<h2>Incremental builds</h2>
<p>Gradle provides a very easy way to create tasks that are able to execute only if their declared input and/or output artifacts have changed. This makes it trivial to incorporate your custom build behaviour into an incremental build. As an example I&#8217;d like to expand upon something I read by <a href="http://twitter.com/etiennestuder">Etienne Studer</a> in this month&#8217;s <a href="http://jaxenter.com/jaxmag">JAXmag</a>. It&#8217;s a great example of developing an incremental task. First here&#8217;s the task implementation almost verbatim from the article. I&#8217;ve updated it slightly to make the output more readable using FileUtils, and you can examine the file it spits out from the build/reports/size directory that will be automatically created when it executes.</p>
<pre class="brush: groovy; title: ; notranslate">
class Size extends DefaultTask
{
    @InputFiles FileTree inputDir
    @OutputFile File outputFile

    @TaskAction
    void generate()
    {
        def totalSize = inputDir.files.inject(0) { def size, File file -&gt; size + file.size()
        }
        outputFile.text = FileUtils.byteCountToDisplaySize(totalSize)
    }
}
</pre>
<p>If you simply include this class definition in a build file, it will be automatically compiled and available for use elsewhere in the script. It could just as easily be defined in a separate file(local or remote), in a jar on the classpath or in the buildSrc directory of your Gradle project. This flexibility enables developing and evolving tasks in a very agile fashion, encouraging you to publish the results for reuse instead of re-implementing the logic in other projects.<br />
In order to execute this task as part of a build, we first need to configure it. In this case, I&#8217;m configuring it to work on all declared configurations and all source, both code and associated resources. In order to do so, I&#8217;m using a couple of Gradle internal classes, <a href="http://www.gradle.org/latest/docs/javadoc/org/gradle/api/file/FileTree.html">FileTree</a> and <a href="http://www.gradle.org/latest/docs/javadoc/org/gradle/api/tasks/SourceSet.html">SourceSet</a>, which actually sound pretty self-explanatory to me. The key part is the assignment of the &#8216;inputDir&#8217; and &#8216;outputFile&#8217; properties on the task.</p>
<pre class="brush: groovy; title: ; notranslate">
task size(type:Size){
    def filetree = sourceSets.inject(new UnionFileTree()) { FileTree total, SourceSet sourceSet -&gt;
        total += sourceSet.allSource
        total
    }
    inputDir = filetree
    outputFile = file(&quot;$reportsDir/size/size.txt&quot;)
}
</pre>
<p>Just to prove that it&#8217;s working incrementally, here&#8217;s the output from successive invocations. Note that &#8216;UP-TO-DATE&#8217; in the output that indicates the task was skipped the second time around, because none of the inputs changed and the output file hasn&#8217;t been deleted.</p>
<pre class="brush: groovy; title: ; notranslate">
gradle-intro$ gradle size
:size

BUILD SUCCESSFUL

Total time: 2.963 secs

gradle-intro$ gradle size
:size UP-TO-DATE

BUILD SUCCESSFUL

Total time: 2.912 secs
</pre>
<p>This task does actually have a concrete dependency on the Java plugin, since without that convention applied neither the &#8216;sourceSets&#8217; or &#8216;reportsDir&#8217; objects would be present. Here&#8217;s the complete version of the build file that&#8217;s been built so far, 28 lines including imports and spacing.</p>
<pre class="brush: groovy; title: ; notranslate">
import org.apache.commons.io.FileUtils
import org.gradle.api.internal.file.UnionFileTree
import org.gradle.api.tasks.SourceSet

apply plugin: 'java'           

task size(type:Size){
    def filetree = sourceSets.inject(new UnionFileTree()) { FileTree total, SourceSet sourceSet -&gt;
        total += sourceSet.allSource
        total
    }
    inputDir = filetree
    outputFile = file(&quot;$reportsDir/size/size.txt&quot;)
}

class Size extends DefaultTask
{
    @InputFiles FileTree inputDir
    @OutputFile File outputFile

    @TaskAction
    void generate()
    {
        def totalSize = inputDir.files.inject(0) { def size, File file -&gt; size + file.size()
        }
        outputFile.text = FileUtils.byteCountToDisplaySize(totalSize)
    }
}
</pre>
<h2>Sharing your new Task</h2>
<p>The simplest way to share build logic is to &#8216;apply&#8217; it. This simple shorthand covers everything from plugins to local files to remotely hosted resources. Here&#8217;s how easy it is to incorporate this particular task implementation and configuration into a build using a relative file path.</p>
<pre class="brush: groovy; title: ; notranslate">
apply from : '../gradle-intro/build.gradle'
</pre>
<p>And because only a simple http connection is required to share it to a broader base, here&#8217;s how you can reference a copy on this site. Sorry about the .txt extension, but I didn&#8217;t feel like editing php to allow a new filetype today <img src='http://www.kellyrob99.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<pre class="brush: groovy; title: ; notranslate">
apply from : 'http://www.kellyrob99.com/blog/wp-content/uploads/downloads/2010/11/gradleSizeTask.txt'
</pre>
<h2>Some other reading</h2>
<p>If you&#8217;re still not sold on Gradle, here&#8217;s some articles I&#8217;ve seen recently that at the very least will give you a better perspective.<br />
<a href="http://community.jboss.org/wiki/Gradlewhy">Hibernate &#8211; Gradle why?</a><br />
<a href="http://jaxenter.com/maven-vs-gradle-vs-ant.1-30682.html">Maven VS Gradle VS Ant</a><br />
Maven to Gradle: <a href="http://www.beeworks.be/maven-to-gradle-part-1/">Part 1</a>, <a href="http://www.beeworks.be/maven-to-gradle-part-2/">Part 2</a>,  <a href="http://www.beeworks.be/maven-to-gradle-part-3/">Part 3</a><br />
<a href="http://kaczanowscy.pl/tomek/2010-11/build-script-length-maven3-polyglot-maven-gradle-ant">A comparison of build script length</a><br />
<a href="http://kaczanowscy.pl/tomek/2009-11/ant-gradle-and-maven-comparison-install-script">Ant/Gradle/Maven comparison</a><br />
<a href="http://java.dzone.com/hibernate-users-learn-gradle">DZone article</a><br />
<a href="http://openmrs-mailing-list-archives.1560443.n2.nabble.com/Maven-vs-Gradle-td4511793.html">OpenMRS Mailing list on Maven VS Gradle</a></p>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/09/18/using-gradle-to-bootstrap-your-legacy-ant-builds/' rel='bookmark' title='Using Gradle to Bootstrap your Legacy Ant Builds'>Using Gradle to Bootstrap your Legacy Ant Builds</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/04/03/a-groovygradle-jslint-plugin/' rel='bookmark' title='A Groovy/Gradle JSLint Plugin'>A Groovy/Gradle JSLint Plugin</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/11/13/why-do-i-like-gradle/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Groovy inspect()/Eval for Externalizing Data</title>
		<link>http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=groovy-inspecteval-for-externalizing-data</link>
		<comments>http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/#comments</comments>
		<pubDate>Sun, 26 Sep 2010 20:32:13 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Bamboo]]></category>
		<category><![CDATA[Eval]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Hudson]]></category>
		<category><![CDATA[inspect()]]></category>
		<category><![CDATA[Junit]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1502</guid>
		<description><![CDATA[One of the things I love about Groovy is how easy it makes reading and writing text files. I&#8217;ve written Groovy scripts for everything from parsing log files for extracting timing information to finding (and replacing with selectors) in-line css blocks. Often there&#8217;s a piece of information extracted from a file that I want to [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/' rel='bookmark' title='Groovy and CSV: How to Get Your Data Out?'>Groovy and CSV: How to Get Your Data Out?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>One of the things I love about Groovy is how easy it makes reading and writing text files. I&#8217;ve written Groovy scripts for everything from parsing log files for extracting timing information to finding (and replacing with selectors) in-line css blocks. Often there&#8217;s a piece of information extracted from a file that I want to keep for further examination, or just for future reference. The Groovy <a href="http://groovy.codehaus.org/api/org/codehaus/groovy/runtime/DefaultGroovyMethods.html#inspect%28java.lang.Object%29">inspect()</a> method provides a nice easy way to take simple results that are stored in variables and write them to a file. Then the Groovy <a href="http://groovy.codehaus.org/api/groovy/util/Eval.html">Eval</a> class provides convenience methods to parse that information back from the file in a single line. Here&#8217;s examples of test code that creates Lists and Maps, writes them out to a file and then asserts that evaluating the stored Groovy code results in the same data structures.</p>
<pre class="brush: groovy; title: ; notranslate">
    private static final Closure RANDOM_STRING = RandomStringUtils.&amp;randomAlphanumeric
    private static final String TMP_DIR = System.getProperty('java.io.tmpdir')

    @Test
    public void testSerializeListToFile()
    {
        List&lt;String&gt; accumulator = []
        100.times { int i -&gt;
            accumulator &lt;&lt; RANDOM_STRING(i + 1)
        }
        File file = new File(&quot;$TMP_DIR/inspectListTest.groovy&quot;)
        file.deleteOnExit()
        file &lt;&lt; accumulator.inspect()
        assertThat(accumulator, equalTo(Eval.me(file.text)))
    }

    @Test
    public void testSerializeMapToFile()
    {
        Map&lt;String, String&gt; accumulator = [:]
        100.times { int i -&gt;
            accumulator[RANDOM_STRING(i + 1)] = RANDOM_STRING(i + 1)
        }
        File file = new File(&quot;$TMP_DIR/inspectMapTest.groovy&quot;)
        file.deleteOnExit()
        file &lt;&lt; accumulator.inspect()
        assertThat(accumulator, equalTo(Eval.me(file.text)))
    }
</pre>
<p>Two little tidbits of goodness embedded in this example are the ability to capture a method as a closure, in this case RandomStringUtils.randomAlphanumeric(), and the <a href="http://download-llnw.oracle.com/javase/6/docs/api/java/io/File.html#deleteOnExit%28%29">File.deleteOnExit()</a> method &#8211; which is only cool because I never noticed it in the API before and it turns out to be a great way to clean up after tests.<br />
 <img src='http://www.kellyrob99.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>A particular usage of this technique I&#8217;ve been using lately has been sparked by a shift of tools at work. I&#8217;ve worked with the Atlassian stack of web applications for years now, and have always enjoyed the rich feedback Bamboo gives for build history. But now I&#8217;m using Hudson as the primary continuous integration tool and one of the things I&#8217;ve been sorely missing is the &#8216;Top 10 Longest Running Tests&#8217;.  If you&#8217;re not familiar with this view in Bamboo, stroll on over to <a href="http://bamboo.ci.codehaus.org/browse/GROOVY-DEF17J6EX/test">the Groovy build results page</a> and click on the tab.<br />
One of my present priorities at work is to speed up build times and rooting out slow or inefficient tests is one way to do this. It&#8217;s possible to elicit this information from JUnit test reports using xsl(<a href="http://stackoverflow.com/questions/277923/longest-running-unit-tests">see this link for an example</a>)  but I came up with a nice way to incorporate similar functionality into a Gradle build. Following the <a href="http://gradle.org/">Gradle</a> project-report conventions, this task simply uses the inspect() method to write out a results file in the &#8216;reportsDir&#8217;. I&#8217;ve captured this output(manually) over time to track the progress of speeding up test times, and the format makes it easy to read back in results and do deeper analysis and aggregation &#8211; such as the creation of csv reports and simple graphs for instance.</p>
<pre class="brush: groovy; title: ; notranslate">
tolerance = 2.0
task findLongRunningTests &lt;&lt; {
    description= &quot;find all tests that take over $tolerance seconds to run&quot;
    String testDir = &quot;${project.reportsDir}/tests&quot;.toString()
    file(testDir).mkdirs()
    File file = file(&quot;$testDir/longRunningTests.txt&quot;)
    file.createNewFile()
    BufferedWriter writer = file.newWriter()
    writer &lt;&lt; parseTestResults('**/TESTS-TestSuites.xml', tolerance)
    writer.close()
}

/**
 * Read in xml files in the junit format and extract test names and times
 * @includePattern ant pattern describing junit xml reports to inspect, recursively gathered from the rootDir
 * @tolerance the number of seconds over which tests should be included in the report
 */
private String parseTestResults(includePattern, float tolerance)
{
    def resultMap = [:]
    fileTree(dir: rootDir, include: includePattern).files.each {
        def testResult = new XmlSlurper().parse(it)
        testResult.depthFirst().findAll {it.name() == 'testcase'}.each { testcase -&gt;
            def key = [testcase.@classname.text(), testcase.@name.text()].join(':')
            def value = testcase.@time.text() as float
            resultMap[(key)] = value
        }
    }
    return resultMap.findAll {it.value &gt; tolerance}.sort {-it.value}.inspect()
}
</pre>
<p>Note that this works recursively from the &#8216;rootDir&#8217; of a <a href="http://www.gradle.org/latest/docs/javadoc/org/gradle/api/Project.html">Project</a>, so it is effective for multi-project Gradle builds. Source code for this simple example <a href="http://github.com/kellyrob99/groovy-inspect-eval">is available on github</a> if you want to check it out yourself.</p>
<p>Here&#8217;s a sample result drawn from examining some test output in the Gradle build itself. Only one of the tests executed in WrapperTest takes more than 2 seconds to execute.</p>
<pre class="brush: groovy; title: ; notranslate">
[&quot;org.gradle.api.tasks.wrapper.WrapperTest:testWrapper&quot;:2.37]
</pre>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/' rel='bookmark' title='Groovy and CSV: How to Get Your Data Out?'>Groovy and CSV: How to Get Your Data Out?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Groovy and CSV: How to Get Your Data Out?</title>
		<link>http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=groovy-and-csv-how-to-get-your-data-out</link>
		<comments>http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 21:07:35 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Comma-separated values]]></category>
		<category><![CDATA[csv]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[hsqldb]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[Languages]]></category>
		<category><![CDATA[OpenCSV]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Spreadsheet]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1420</guid>
		<description><![CDATA[I don&#8217;t know exactly how many CSV files I&#8217;ve read/written to date, but I&#8217;m willing to bet it&#8217;s a lot. These kind of files are a simple and common way to exchange data and are interoperable with spreadsheet programs as well, making them more easily accessible to non-programmer types. There is some excellent support out [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/' rel='bookmark' title='Achieving Groovy-like Fluency in Java with Google Collections'>Achieving Groovy-like Fluency in Java with Google Collections</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t know exactly how many <a class="zem_slink" title="Comma-separated values" rel="wikipedia" href="http://en.wikipedia.org/wiki/Comma-separated_values">CSV</a> files I&#8217;ve read/written to date, but I&#8217;m willing to bet it&#8217;s a lot. These kind of files are a simple and common way to exchange data and are interoperable with spreadsheet programs as well, making them more easily accessible to non-programmer types.  There is some excellent support out there for reading and writing CSV files and so here, from simplest to most complex, are a few different ways to load and query the data trapped inside your CSV files. But first off&#8230;</p>
<p></p>
<h2>CSV is Not XML</h2>
<p>Or any other suitably constrained data format for that matter.  There are some pretty clear &#8216;rules&#8217; out there for transporting data in this format, but of course hardly anybody listens to them. I found <a href="http://creativyst.com/Doc/Articles/CSV/CSV01.htm#FileFormat">this</a> and <a href="http://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules">this</a> in the first page of a quick Google search.<br />
However, while looking for available sample material on the web to demonstrate with, I found many great examples of how to screw it up: not quoting empty strings, not escaping embedded line breaks, commas embedded in unquoted text, etc.  All of these are things that prevent automation from properly interacting with the data.  There are also, of course, the CSV &#8216;formats&#8217; that insist on embedding comments and descriptions, trying to present more than one matrix of information or any of the other crazy things you can do when writing to a format with no real metadata layer.</p>
<p>&lt; \rant&gt;</p>
<p>Unfortunately, there&#8217;s nothing as convenient as an xml schema available for describing what information is encoded in the document and, in general, the only potentially useful metadata in an individual file is the header line. The good news is that all of these deficiencies can be overcome if you know they exist. And there&#8217;s some pretty nifty tools you can use to take out much of the guesswork. I&#8217;ll suggest a couple of ideas for how to deal with files that don&#8217;t adhere to the &#8216;rules&#8217;, but the main example I&#8217;ve decided on is a straightforward  and well formed set representing the periodic table of elements, provided generously by <a href="http://akiscode.com/pt/">Akiscode</a>. This file is directly machine readable(has no headers or embedded data), quotes all values and just follows all the &#8216;rules&#8217;. Here&#8217;s the first five lines as an example.<br />

<table id="wp-table-reloaded-id-4-no-1" class="wp-table-reloaded wp-table-reloaded-id-4">
<tbody>
	<tr class="row-1 odd">
		<td class="column-1">Atomic Number</td><td class="column-2">Atomic Mass</td><td class="column-3">Name</td><td class="column-4">Symbol</td>
	</tr>
	<tr class="row-2 even">
		<td class="column-1">1</td><td class="column-2">1.00794</td><td class="column-3">Hydrogen</td><td class="column-4">H</td>
	</tr>
	<tr class="row-3 odd">
		<td class="column-1">2</td><td class="column-2">4.002602</td><td class="column-3">Helium</td><td class="column-4">He</td>
	</tr>
	<tr class="row-4 even">
		<td class="column-1">3</td><td class="column-2">6.941</td><td class="column-3">Lithium</td><td class="column-4">Li</td>
	</tr>
	<tr class="row-5 odd">
		<td class="column-1">4</td><td class="column-2">9.012182</td><td class="column-3">Beryllium</td><td class="column-4">Be</td>
	</tr>
	<tr class="row-6 even">
		<td class="column-1">5</td><td class="column-2">10.811</td><td class="column-3">Boron</td><td class="column-4">B</td>
	</tr>
</tbody>
</table>
<span class="wp-table-reloaded-table-description-id-4 wp-table-reloaded-table-description">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The simple format of the example CSV file.</span>
</p>
<p></p>
<h2>Groovy All by Itself</h2>
<p><a href="http://groovy.codehaus.org/">Groovy</a> makes dealing with String objects pretty painless.  By adding facilities like easy casting and find()/findAll() for quickly turning raw Strings into real data, you can program some fairly complex questions. In this simple example I&#8217;m depending on all values being quoted and I am not protecting against casting problems. I&#8217;m running this through Maven which really helps to keep test data organized; all you have to do is drop your test file in /src/test/resources and it&#8217;s automagically available on the test classpath.<br />
No specific handling is done for different types here; everything is read in as a String and cast to a more specific type as needed. In a &#8216;real&#8217; application you&#8217;re very likely going to encounter mixed alphanumeric data in any given column of data &#8211; &#8216;UNKNOWN&#8217; instead of a number in a column labeled &#8216;Quantity&#8217; for instance &#8211; and be more careful about casting.</p>
<pre class="brush: groovy; title: ; notranslate">
//load and split the file
InputStream inputFile = getClass().classLoader.getResourceAsStream(TEST_FILE_NAME)
String[] lines = inputFile.text.split('\n')
List&lt;String[]&gt; rows = lines.collect {it.split(',')}

/**
 * A little helper method to get rid of the quotes in the input
 * and cast values so they can be compared properly.
 */
private double castToDouble(string)
{
    return string.replaceAll('&quot;', '').toDouble()
}

//OK, it's parsed - let's ask some questions
private static final int ATOMIC_MASS = 1;

def elementsOver200Mass = rows.findAll {castToDouble(it[ATOMIC_MASS]) &gt; 200}
def elementsBetween10And20 = rows.findAll { row -&gt;
            double mass = castToDouble(row[ATOMIC_MASS])
            mass &lt;= 20 &amp;&amp; mass &gt;= 10
}
</pre>
<p>So this approach works fine for well formed input, but falls apart quickly in other cases. For instance, if any columns in the file are missing values the split() function treats them as nulls, leading to different size arrays being stored in the list. Similarly you need to deal with quoted VS non-quoted content, embedded line breaks and other issues individually. Fortunately, some nice guys have done that for us.</p>
<p></p>
<h2>OpenCSV</h2>
<p><a href="http://blogs.bytecode.com.au/glen/">Glen Smith&#8217;s</a> <a href="http://opencsv.sourceforge.net/">OpenCSV</a> library nicely abstracts away a lot of the minutiae of dealing with CSV. It properly deals with escaped characters, multiline input and a host of other issues for you. Give it pretty much anything that satisfies the Reader interface as an input and it&#8217;s good to go. The return type of the CSVReader.readAll() method also satisfies the same contract as the plain Groovy version shown above, so we can interact with the parsed results in exactly the same way as in the previous example.<br />
All values are still typed as Strings, so we need to cast in order to compare numeric values.</p>
<pre class="brush: groovy; title: ; notranslate">
import au.com.bytecode.opencsv.CSVReader

List&lt;String[]&gt; rows = new CSVReader(
        new InputStreamReader(getClass().classLoader.getResourceAsStream(TEST_FILE_NAME)))
            .readAll()

//same finders as in the Groovy version
def elementsOver200Mass = rows.findAll {it[ATOMIC_MASS].toDouble() &gt; 200}
def elementsBetween10And20 = rows.findAll { row -&gt;
    double mass = castToDouble(row[ATOMIC_MASS])
    mass &lt;= 20 &amp;&amp; mass &gt;= 10
}
</pre>
<p></p>
<h2>HsqlDB</h2>
<p>HsqlDB allows for using flat files as <a href="http://hsqldb.org/doc/2.0/guide/texttables-chapt.html">Text Tables</a>, effectively turning a CSV file into a database table. Data values are cast to the types specified in your table definition, so any potential type cast failures happen right up front when loading the data. The benefit is that once the data is read in no further manual processing is necessary. Note: by default the csv file and the database file must be located in the same directory, as a security precaution (can be overridden through configuration).</p>
<p>In addition, leveraging sql makes querying the data extremely easy. Groovy brings in the <a href="http://groovy.codehaus.org/api/groovy/sql/Sql.html">Sql class</a> to abstract away a lot of the normal <a class="zem_slink" title="Java (programming language)" rel="homepage" href="http://java.sun.com">Java</a> boilerplate you encounter when working with a <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/ResultSet.html">ResultSet</a>. I didn&#8217;t do any in-depth testing to prove it out, but this is also the only one of the strategies described here that doesn&#8217;t require holding the entire data in memory in order to do arbitrary queries. That can be mitigated in the previously shown methods by processing files line by line rather than in bulk if memory usage is a concern.</p>
<pre class="brush: groovy; title: ; notranslate">
//create the table definition to insert
String tableName = 'elements'
String tableDefinition = &quot;&quot;&quot;CREATE TEXT TABLE $tableName (
    atomic_number INTEGER PRIMARY KEY,
    atomic_mass NUMERIC,
    name VARCHAR(255),
    symbol VARCHAR(3)
);&quot;&quot;&quot;

//create a new file database and a table corresponding to the csv file
Sql sql = Sql.newInstance(&quot;jdbc:hsqldb:file:${testdbDir.absolutePath}/testdb&quot;, 'sa', ''
    ,'org.hsqldb.jdbcDriver')
sql.execute(tableDefinition)

//set the source to the csv file
sql.execute(&quot;SET TABLE elements SOURCE '${TEST_FILE_NAME};all_quoted=true'&quot;.toString())

//querying the database that's wrapping our CSV file
def elementsOver200Mass = sql.rows(&quot;SELECT * FROM $tableName WHERE atomic_mass &gt; ?&quot;, [200])
def elementsBetween10And20 = sql.rows(
    &quot;SELECT * FROM $tableName WHERE atomic_mass &lt;= ? AND atomic_mass &gt;= ?&quot;, [20, 10])

//simple db aggregates
def count = 0
sql.eachRow(&quot;SELECT count(1) FROM $tableName WHERE atomic_mass &lt;= ?&quot;, [20]){row-&gt;
    count = row[0]
}
def avg = 0
sql.eachRow(&quot;SELECT avg(atomic_mass) FROM $tableName&quot;.toString()){row-&gt;
    avg = row[0]
}
</pre>
<p></p>
<h2>Which One do I Use???</h2>
<p>Pick the one that fits best for your use case is the real answer. I didn&#8217;t know about the HsqlDB option until recently, but OpenCSV has been a personal standby for years. Then again, if you&#8217;re writing something quick and dirty at the script level, the simplicity of just applying a couple of split() operations is pretty appealing. Here&#8217;s how I generally decide.</p>

<table id="wp-table-reloaded-id-5-no-1" class="wp-table-reloaded wp-table-reloaded-id-5">
<thead>
	<tr class="row-1">
		<th class="column-1"></th><th class="column-2">Plain Groovy</th><th class="column-3">OpenCSV</th><th class="column-4">HsqlDB</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2">
		<td class="column-1">Use it</td><td class="column-2">File contains well-formed input and questions to be asked involve a small subset of columns.</td><td class="column-3">File contains well or badly formed input and questions to be asked involve a small subset of columns.</td><td class="column-4">File contains well or badly formed input, questions to be asked involve many columns or span multiple files.</td>
	</tr>
	<tr class="row-3">
		<td class="column-1">Don't use it</td><td class="column-2">File contains badly formed input or questions being asked are complex.</td><td class="column-3">Not able to easily leverage the OpenCSV library.</td><td class="column-4">Not able to easily leverage the HsqlDB database.</td>
	</tr>
	<tr class="row-4">
		<td class="column-1">Where it works</td><td class="column-2">Groovy only</td><td class="column-3">Groovy or Java</td><td class="column-4">Groovy or Java</td>
	</tr>
</tbody>
</table>
<span class="wp-table-reloaded-table-description-id-5 wp-table-reloaded-table-description">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Some common sense guidelines for which method to apply given a problem to solve.</span>

<div class="zemanta-pixie"><a class="zemanta-pixie-a" title="Enhanced by Zemanta" href="http://www.zemanta.com/"><img class="zemanta-pixie-img" src="http://img.zemanta.com/zemified_a.png?x-id=104c23a4-eae4-4092-87ab-7680f9df82e2" alt="Enhanced by Zemanta" /></a><span class="zem-script more-related pretty-attribution"><script src="http://static.zemanta.com/readside/loader.js" type="text/javascript"></script></span></div>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/09/26/groovy-inspecteval-for-externalizing-data/' rel='bookmark' title='Groovy inspect()/Eval for Externalizing Data'>Groovy inspect()/Eval for Externalizing Data</a></li>
<li><a href='http://www.kellyrob99.com/blog/2011/12/04/five-cool-things-you-can-do-with-groovy-scripts/' rel='bookmark' title='Five Cool Things You Can Do With Groovy Scripts'>Five Cool Things You Can Do With Groovy Scripts</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/' rel='bookmark' title='Achieving Groovy-like Fluency in Java with Google Collections'>Achieving Groovy-like Fluency in Java with Google Collections</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>A Grails App Demoing the StackExchange API</title>
		<link>http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=a-grails-app-demoing-the-stackexchange-api</link>
		<comments>http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/#comments</comments>
		<pubDate>Thu, 03 Jun 2010 07:12:17 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Application programming interface]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Google Reader]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[Representational State Transfer]]></category>
		<category><![CDATA[Source code]]></category>
		<category><![CDATA[Stack Overflow]]></category>
		<category><![CDATA[StackApps]]></category>
		<category><![CDATA[StackExchange]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1367</guid>
		<description><![CDATA[So I was making an attempt to catch up on my Google Reader&#8216;ing this weekend and I came across this post on StackOverflow regarding the shiny new StackExchange API. That and a fresh 1.3.1 drop of Grails seemed like as good a reason as any to hack together a little app suitable for seeing what [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API'>Hooking into the Jenkins(Hudson) API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/10/25/grails-ui-datatable-using-xml-for-a-model/' rel='bookmark' title='Grails-UI DataTable using XML for a model'>Grails-UI DataTable using XML for a model</a></li>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>So I was making an attempt to catch up on my <a class="zem_slink" href="http://www.google.com/reader" title="Google Reader" rel="homepage">Google Reader</a>&#8216;ing this weekend and I came across <a href="http://blog.stackoverflow.com/2010/05/stack-exchange-api-contest/">this post on StackOverflow</a> regarding the shiny new <a class="zem_slink" href="http://www.stackexchange.com" title="StackExchange" rel="homepage">StackExchange</a> <a class="zem_slink" href="http://en.wikipedia.org/wiki/Application_programming_interface" title="Application programming interface" rel="wikipedia">API</a>. That and a fresh 1.3.1 drop of <a class="zem_slink" href="http://grails.org" title="Grails (framework)" rel="homepage">Grails</a> seemed like as good a reason as any to hack together a little app suitable for seeing what you can do with said API.<br />
Turns out it&#8217;s not a whole lot, at least not yet. The minor detraction of having  read-only access and limited connects without an API key are more than offset by seeing such a minimal, clean and easy to use interface. According to the grails stats script, it took about this much effort to implement calls to each and every one of the API endpoints for all of the supported domains:</p>
<pre class="brush: xml; title: ; notranslate">
    +----------------------+-------+-------+
    | Name                 | Files |  LOC  |
    +----------------------+-------+-------+
    | Controllers          |     1 |    42 |
    | Groovy Helpers       |     1 |    47 |
    +----------------------+-------+-------+
    | Totals               |     2 |    89 |
    +----------------------+-------+-------+
</pre>
<p>
Please note that this is about as minimal as you could reasonably get away with(and kinda ugly to boot), but nevertheless it does manage to implement a UI and backend for exercising the entire API in a total of 2 gsp files and 1 controller action.  What&#8217;s noticeably missing are some tailored views for each of the different <a class="zem_slink" href="http://json.org/" title="JSON" rel="homepage">JSON</a> responses and the implementation of additional query parameters on the calls.</p>
<p></p>
<h2>StackExchange API</h2>
<p>This is really <a href="http://api.stackoverflow.com/0.8/help">pretty well documented</a> and &#8220;consistent&#8221;. Calls in some cases require an {id} in the url and that&#8217;s about it. Each of the individual calls has its own help page that describes the options, like the <a href="http://api.stackoverflow.com/0.8/help/method?method=badges">badges page</a> for instance. To make things a little more helpful while exploring the API, each of the implemented endpoints in this app are also hyper-linked to the corresponding manual page on api.stackoverflow.com.</p>
<p></p>
<h2><a class="zem_slink" href="http://en.wikipedia.org/wiki/Representational_State_Transfer" title="Representational State Transfer" rel="wikipedia">RESTful</a> Access</h2>
<p>Couldn&#8217;t be a whole lot easier than using <a href="http://groovy.codehaus.org/modules/http-builder/">HttpBuilder</a>, which provides nice and concise ways to execute the GET request, inspect the response and deal with success/failure of the request. Aside from assigning the root domain and method using variables, this is little different from the <a href="http://groovy.codehaus.org/HTTP+Builder">canonical example on codehaus</a>.</p>
<pre class="brush: groovy; title: ; notranslate">
def http = new HTTPBuilder(&quot;http://api.$domain&quot;)
http.request(GET, JSON) {
    uri.path = &quot;/${VERSION}$method&quot;
    headers.'User-Agent' = 'Mozilla/5.0 Ubuntu/8.10 Firefox/3.0.4'
     response.success = { resp, json -&gt;
        answer = json
    }
     response.failure = { resp -&gt;
        answer = &quot;Unexpected error: ${resp.statusLine.statusCode} : ${resp.statusLine.reasonPhrase}&quot;
    }
}
</pre>
<p>The syntax here could probably get even Groovier using the <a href="http://www.grails.org/plugin/rest">Grails REST client plugin</a> wrappers, and indeed incorporating that plugin is how the HttpBuilder dependency is being provided in this app, but I hit on a working implementation first time out so we&#8217;ll leave that for another day, shall we?</p>
<p></p>
<h2>What&#8217;s it Look Like?</h2>
<p><img src='http://www.kellyrob99.com/blog/wp-content/gallery/stackoverflow-api-grails/stackapps-demo_1275451039026.png' alt='stackapps-demo' class='ngg-singlepic ngg-none'  width='860' height='480'/><br />
Did I mention it was ugly?  Where the API call requires a parameter, you get a text field. Each domain gets its own submit button, officially representing my first attempted usage of g:actionSubmit in Grails. Doesn&#8217;t work quite the way I expected, but it does certainly work. Click a button and you&#8217;re shown the raw JSON result, along with the call you made. In an ugly fashion, or did I say that already? Still, for a couple of hours of hacking, I&#8217;m not unhappy with the result.</p>
<p></p>
<h2>StackApps</h2>
<p>This is where the apps in the contest get shown off and, in a wonderful display of <a href="http://en.wikipedia.org/wiki/Eating_your_own_dog_food">dogfooding</a>, it&#8217;s guess what &#8211; another StackOverflow clone! The page for this application <a href="http://stackapps.com/questions/491/a-grails-app-demoing-the-stackexchange-api">can be found here</a>. And the <a href="http://github.com/kellyrob99/stackoverflow-api-grails">source code is up on github</a> if you&#8217;d like to take a look.</p>
<p></p>
<h2>Next Steps?</h2>
<p>Toying with the idea of porting this to appengine, which would unfortunately mean replacing HttpBuilder and the underlying Apache HttpClient in favor of something that didn&#8217;t use a threaded approach, most likely using <a href="http://code.google.com/appengine/docs/java/urlfetch/">URL fetch</a>. That and some slightly less ugly tabular and/or tree views of the JSON responses would be kinda nice to have.<br />
 <img src='http://www.kellyrob99.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2011/03/27/hooking-into-the-jenkinshudson-api/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API'>Hooking into the Jenkins(Hudson) API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/10/25/grails-ui-datatable-using-xml-for-a-model/' rel='bookmark' title='Grails-UI DataTable using XML for a model'>Grails-UI DataTable using XML for a model</a></li>
<li><a href='http://www.kellyrob99.com/blog/2012/02/26/hooking-into-the-jenkinshudson-api-part-2/' rel='bookmark' title='Hooking into the Jenkins(Hudson) API, Part 2'>Hooking into the Jenkins(Hudson) API, Part 2</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Achieving Groovy-like Fluency in Java with Google Collections</title>
		<link>http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=achieving-groovy-like-fluency-in-java-with-google-collections</link>
		<comments>http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/#comments</comments>
		<pubDate>Sun, 16 May 2010 05:33:19 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Collections]]></category>
		<category><![CDATA[DataProvider]]></category>
		<category><![CDATA[fluent syntax]]></category>
		<category><![CDATA[GMaven]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[google-collections]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[guava]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[kellyrob99]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[MultiMap]]></category>
		<category><![CDATA[mvn]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[TestNG]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1261</guid>
		<description><![CDATA[One of the most compelling things about using Groovy is the fluent and concise syntax, as well as the productivity and readability gains that come out of it. But there&#8217;s no reason not to take advantage of some of the same techniques and some library support, in this case google-collections, to make Java code easy [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2009/04/19/groovy-and-glazed-lists-with-grape/' rel='bookmark' title='Groovy and Glazed Lists with Grape'>Groovy and Glazed Lists with Grape</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/11/21/different-flavors-of-embedded-groovy-in-java-apps-or-how-to-make-your-java-groovier/' rel='bookmark' title='Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;'>Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/02/08/my-favorite-new-groovy-trick/' rel='bookmark' title='My favorite new Groovy trick'>My favorite new Groovy trick</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>One of the most compelling things about using Groovy is the fluent and concise syntax, as well as the productivity and readability gains that come out of it. But there&#8217;s no reason not to take advantage of some of the same techniques and some library support, in this case <a href="http://code.google.com/p/google-collections/">google-collections</a>, to make Java code easy to write AND to read.</p>
<h2>Creating Collections</h2>
<p>Groovy really shines for this one, making the creation of Lists and Maps, empty or populated, an absolute breeze. Java has some help for creating basic Lists but begins to struggle when creating maps. This is an area that google-collections can help in, especially in regards to creating immutable Maps.</p>
<pre class="brush: groovy; title: ; notranslate">
//Empty Lists
        List&lt;String&gt; groovyList = []
        List&lt;String&gt; javaList = new ArrayList&lt;String&gt;()
        List&lt;String&gt; googleList = Lists.newArrayList()  //can omit generics

//Populated Lists
        List&lt;String&gt; groovyList = [&quot;1&quot;, &quot;2&quot;]
        List&lt;String&gt; javaList = Arrays.asList(&quot;1&quot;, &quot;2&quot;)
        List&lt;String&gt; googleList = Lists.newArrayList(&quot;1&quot;, &quot;2&quot;)

//Immutable Lists
        List&lt;String&gt; groovyList = [&quot;1&quot;, &quot;2&quot;].asImmutable()
        List&lt;String&gt; javaList = Collections.unmodifiableList(Arrays.asList(&quot;1&quot;, &quot;2&quot;))
        List&lt;String&gt; googleList = ImmutableList.of(&quot;1&quot;, &quot;2&quot;)

//Empty Maps
        Map&lt;String, String&gt; groovyMap = [:]
        Map&lt;String, String&gt; javaMap = new LinkedHashMap&lt;String,String&gt;()
        Map&lt;String, String&gt; googleMap = Maps.newLinkedHashMap()

//Immutable Maps
        Map&lt;String, String&gt; groovyMap = [&quot;a&quot;:&quot;1&quot;, &quot;b&quot;:&quot;2&quot;].asImmutable()

        Map&lt;String, String&gt; javaMap = new LinkedHashMap&lt;String,String&gt;()
        javaMap.put(&quot;a&quot;, &quot;1&quot;)
        javaMap.put(&quot;b&quot;, &quot;2&quot;)
        javaMap = Collections.unmodifiableMap(javaMap)

        //OR(works only in Java, will not compile in Groovy)
        Map&lt;String, String&gt; javaMap = new LinkedHashMap&lt;String, String&gt;()
        {
            {
                put(&quot;a&quot;, &quot;1&quot;);
                put(&quot;b&quot;, &quot;2&quot;);
            }
        };

        Map&lt;String, String&gt; googleMap = ImmutableMap.of(&quot;a&quot;, &quot;1&quot;, &quot;b&quot;, &quot;2&quot;)  //clunky syntax but it works
</pre>
<h2>Filtering Collections</h2>
<p>Groovy provides the very handy &#8216;findAll&#8217; method that allows you to filter a Collection by applying a Closure. Google-collections provides similar facilities using the Predicate interface and two filter methods available statically on Collections2 and Iterables. This would also be a lot more readable if the Predicate definition were extracted but I wanted to show that it&#8217;s still possible to create them in-line quickly.</p>
<pre class="brush: groovy; title: ; notranslate">
import static com.google.common.collect.Collections2.*

List&lt;Integer&gt; toFilter = [1, 2, 3, 4, 5]
List&lt;Integer&gt; groovyVersion = toFilter.findAll{ it &lt; 3}
List&lt;Integer&gt; googleVersion = filter(toFilter, new Predicate&lt;Integer&gt;()
    {
        public boolean apply(Integer input)
        {
            return input &lt; 3;
        }
    };)
</pre>
<h2>Joining Collections into a String Representation</h2>
<p>This one has come up pretty often over the years, and it&#8217;s not surprising that where the JDK hasn&#8217;t provided, enterprising developers have added support through libraries. The problem is: given a Collection of objects, create a String representation of that Collection suitable for view from a consumer of the system. And admit it &#8211; the first time you hand-coded the algorithm you left a trailing comma, didn&#8217;t you? I know I did.<br />
Groovy has fantastic support for this use-case by simply including the &#8216;join&#8217; method on Lists. Google-collections utilizes static calls on the Joiner class along with a simple DSL-like syntax to achieve the same effect. Throw in a static import to make it even more concise and it does a fine job. These two examples yield the same result.</p>
<pre class="brush: groovy; title: ; notranslate">
import static com.google.common.base.Joiner.*
def toJoin = ['a', 'b', 'c']
def separator = ', '

//groovy version
def groovyJoin = toJoin.join(separator)

//google-collections version
def googleJoin = on(separator).join(toJoin)
</pre>
<p>And google-collections also supports join for Maps, something missing from Groovy(although not very hard to implement).</p>
<pre class="brush: groovy; title: ; notranslate">
import static com.google.common.base.Joiner.*
def toJoin = [1: 'a', 2: 'b', 3: 'c']
def separator = ', '
def keyValueSeparator = ':'

//results in '1:a, 2:b, 3:c' which is essentially what is returned from Groovy map.toMapString()
def googleVersion = on(separator).withKeyValueSeparator(keyValueSeparator).join(map)

//results in '1 is a and 2 is b and 3 is c'
googleVersion = on(&quot; and &quot;).withKeyValueSeparator(&quot; is &quot;).join(map)

//doing the same in Groovy is slightly more involved, but really not that bad
def groovyVersion = toJoin.inject([]) {builder, entry -&gt;
            builder &lt;&lt; &quot;${entry.key} is ${entry.value}&quot;
            builder
        }.join(' and ')
</pre>
<h2>Multimaps</h2>
<p>Multimaps are one of the more interesting parts of google-collections, at least to me. Have you ever found yourself writing code to create a Map of keys to Lists? Well the various <a href="http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Multimap.html">Multimap</a> implementations in google-collections mean you never have to write that boilerplate kind of code again. Here&#8217;s a &#8220;first-stab&#8221; effort to simulate a fairly generic Multimap with pure Java code.</p>
<pre class="brush: groovy; title: ; notranslate">
public class JavaMultimap
{
    private Map&lt;Object, List&lt;Object&gt;&gt; multimap = new LinkedHashMap&lt;Object, List&lt;Object&gt;&gt;();

    public boolean put(Object key, Object value)
    {
        List&lt;object&gt; objects = multimap.get(key);
        objects = objects != null ? objects : new ArrayList&lt;object&gt;();
        objects.add(value);
        multimap.put(key, objects);
        return true;
    }
}
</pre>
<p>
And the same thing in Groovy, achieving a slightly smaller version.</p>
<pre class="brush: groovy; title: ; notranslate">
class GroovyMultimap
{
    Map map = [:]

    public boolean put(Object key, Object value)
    {
        List list = map.get(key, [])
        list.add(value)
        map.&quot;$key&quot; = list
    }
}
</pre>
<p>I did some primitive timings comparing Java, Groovy and google-collections Multimaps implementations and, as you&#8217;d pretty much expect, google clearly takes the lead.  Where things really start to get interesting though is when you start using the Multimap in Groovy code. Imagine if you will that you want to iterate over a collection of Objects and map some of the properties to a List. Here&#8217;s a contrived example of what I&#8217;m talking about, but applying this same pattern to domain objects in your application or even a directory full of xml files is pretty much the same. If you look closely you&#8217;ll notice that Groovy actually makes this a one liner to extract all values for a property across a List of Objects(used in the assertion), but I imagine that Multimap is probably a better alternative for large data sets.</p>
<pre class="brush: groovy; title: ; notranslate">
class GoogleCollectionsMultiMapTest
{
    private Random random = new Random()

    @Test
    public void testMultimap()
    {
        def list = []
        10.times {
            list &lt;&lt; createObject()
        }
        List properties = ['value1', 'value2', 'value3']
        Multimap multimap = list.inject(LinkedListMultimap.create()) {Multimap map, object -&gt;
            properties.each {
                map.put(it, object.&quot;$it&quot;)
            }
            map
        }
        properties.each {
            assertEquals (multimap.get(it), list.&quot;$it&quot;)
        }
    }

    Object createObject()
    {
        Expando expando = new Expando()
        expando.value1 =  random.nextInt(10) + 1
        expando.value2 =  random.nextInt(100) + 100
        expando.value3 =  random.nextInt(50) + 50
        return expando
    }
}
</pre>
<h2>So Where Does This Get Us?</h2>
<p>Between google-collections and <a href="http://code.google.com/p/guava-libraries/">the newer guava-libraries</a> that contain it, there&#8217;s lots of help available for simplifying programming problems and making your code more readable. I haven&#8217;t even touched on the newly available support for primitives, Files, Streams and more, but if you&#8217;re interested in reducing the amount of code you write AND simultaneously making it more readable you should probably take a look. There&#8217;s a very nice overview available in <a href="http://codemunchies.com/2009/10/beautiful-code-with-google-collections-guava-and-static-imports-part-1/">part one</a> and <a href="http://codemunchies.com/2009/10/diving-into-the-google-guava-library-part-2/">part two</a> by <a href="http://twitter.com/astensby">Aleksander Stensby</a>.  And here&#8217;s a <a href="http://bwinterberg.blogspot.com/2009/09/introduction-to-google-collections.html">closer look at what google-collections can do for you</a>.</p>
<h2>Source Code</h2>
<p>As per usual, source code is <a href="http://github.com/kellyrob99/groovy-google-collections">available at github as a maven project</a>. Big thanks to the Spock team for sharing <a href="http://old.nabble.com/GMaven-and-Groovy-1.7-td27378212.html">how they configure GMaven to properly utilize Groovy 1.7</a>.  Please feel free to take a look and comment here. Thanks!</p>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2009/04/19/groovy-and-glazed-lists-with-grape/' rel='bookmark' title='Groovy and Glazed Lists with Grape'>Groovy and Glazed Lists with Grape</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/11/21/different-flavors-of-embedded-groovy-in-java-apps-or-how-to-make-your-java-groovier/' rel='bookmark' title='Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;'>Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/02/08/my-favorite-new-groovy-trick/' rel='bookmark' title='My favorite new Groovy trick'>My favorite new Groovy trick</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Groovy and Hibernate Validator for Dynamic Constraints</title>
		<link>http://www.kellyrob99.com/blog/2010/04/17/groovy-and-hibernate-validator-for-dynamic-constraints/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=groovy-and-hibernate-validator-for-dynamic-constraints</link>
		<comments>http://www.kellyrob99.com/blog/2010/04/17/groovy-and-hibernate-validator-for-dynamic-constraints/#comments</comments>
		<pubDate>Sun, 18 Apr 2010 06:17:50 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Hibernate]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JBoss Seam]]></category>
		<category><![CDATA[kellyrob99.com]]></category>
		<category><![CDATA[Languages]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Source code]]></category>
		<category><![CDATA[theKaptain]]></category>
		<category><![CDATA[User interface]]></category>
		<category><![CDATA[validator]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1263</guid>
		<description><![CDATA[I was reading this article about hibernate validator today and it inspired me to apply a little Groovy to the problem of validating a bean. More specifically, finding out how hard it would be to apply different validation rules to the same classes at runtime. Turns out it&#8217;s really pretty simple. Hibernate validator, if you [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/' rel='bookmark' title='Groovy and CSV: How to Get Your Data Out?'>Groovy and CSV: How to Get Your Data Out?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/12/16/jira-grails-plugin/' rel='bookmark' title='Jira Grails Plugin'>Jira Grails Plugin</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/03/14/breaking-weak-captcha-in-slightly-more-than-26-lines-of-groovy-code/' rel='bookmark' title='Breaking Weak CAPTCHA in&#8230; slightly more than 26 Lines of Groovy Code'>Breaking Weak CAPTCHA in&#8230; slightly more than 26 Lines of Groovy Code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I was reading <a href="http://java.dzone.com/articles/using-hibernate-validator">this article about hibernate validator</a> today and it inspired me to apply a little <a class="zem_slink" href="http://groovy.codehaus.org" title="Groovy (programming language)" rel="homepage">Groovy</a> to the problem of validating a bean.  More specifically, finding out how hard it would be to apply different validation rules to the same classes at runtime. Turns out it&#8217;s really pretty simple.<br />
<a href="http://www.hibernate.org/subprojects/validator/download.html">Hibernate validator</a>, if you didn&#8217;t already know, is the reference implementation of <a href="http://jcp.org/en/jsr/detail?id=303">JSR-303</a> and it provides the ability to specify by xml or annotation configuration validation rules for pojos.</p>
<p></p>
<h2>Where Hibernate Validator Shines</h2>
<p>Annotations on domain classes allow for easily validating object state at the time of persistence. Excellent integration with frameworks like <a class="zem_slink" href="http://www.seamframework.org" title="JBoss Seam" rel="homepage">JBoss Seam</a> allow this same ability to be utilized for validating web forms on the client-side with little more than an <a href="http://docs.jboss.org/seam/1.2.1.GA/reference/en/html/validation.html">&lt;s:validateAll/&gt; tag</a>. Seam practically hides the entire interaction with validation components from the developer. Since validation rules are defined directly in the domain class, you can (almost) guarantee that no objects with inconsistent state will ever end up being saved in your database.  There are certain validations that aren&#8217;t possible to verify without actually looking in the database, unique constraints for example, but generally in my experience hibernate validator is extremely easy to configure and work with. Implementing <a class="zem_slink" href="http://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete" title="Create, read, update and delete" rel="wikipedia">CRUD</a> functionality is pretty trivial, and UIs can achieve consistency since all validations are applied equally.<br />
Alternatively, if you can&#8217;t or don&#8217;t want to use annotations for some reason, you can specify your validation rules in an xml file. Usually a singleton validation.xml file is made available on the classpath and picked up automagically when a ValidationFactory is created. A simple xml configuration looks like this:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;constraint-mappings xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemalocation=&quot;http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd&quot; xmlns=&quot;http://jboss.org/xml/ns/javax/validation/mapping&quot;&gt;
    &lt;default-package&gt;org.kar.test&lt;/default-package&gt;
    &lt;bean class=&quot;ValidateTestableClass&quot;&gt;
        &lt;field name=&quot;name&quot;&gt;
            &lt;constraint annotation=&quot;javax.validation.constraints.NotNull&quot;&gt;
        &lt;/constraint&gt;
    &lt;/field&gt;
&lt;/bean&gt;
</pre>
<p>and is meant to be applied to this simple class:</p>
<pre class="brush: groovy; title: ; notranslate">
class ValidateTestableClass
{
    int id
    String name
    String description
    boolean enabled
}
</pre>
<p></p>
<h2>Comparing with <a class="zem_slink" href="http://grails.org" title="Grails (framework)" rel="homepage">Grails</a> Validation</h2>
<p>Grails automatically provides <a href="http://www.grails.org/doc/latest/guide/7.%20Validation.html">validation capabilities</a> for domain classes and command objects, and enables adding the same behavior to any pogo through a combination of the @Validateable annotation and a static constraints closure. Adding validation support to arbitrary classes also requires specifying which packages to scan for the annotation.<br />
Plugin support from projects like <a href="http://www.grails.org/plugin/bean-fields">bean-fields</a> simplifies the handling of client-side validation and rendering error markers in the UI, an ability which the Grails framework provides natively by adding an &#8216;errors&#8217; field directly onto the domain or command object class instances bound to a web form.</p>
<p></p>
<h2>Dynamic Constraints</h2>
<p>Both the hibernate validator and the Grails strategies for applying validation described here have the same limitation: both are universally applied to all instances of a class. There&#8217;s no easy apparent way to override those constraints at runtime, although I suspect that some fancy MOP&#8217;ing or configuration could probably be used to accomplish overrides at runtime.<br />
Hibernate validator also supports creation of ad hoc validators by seeding with one or more xml documents. Or if you&#8217;re like me and hate hand editing xml, you can leverage Groovy to take a bit of the pain away. Here&#8217;s the same xml snippet from above in a Groovy Closure, generated simply by turning the DomToGroovy class loose on the raw xml:</p>
<pre class="brush: groovy; title: ; notranslate">
looseConstraint = {
        mkp.declareNamespace(xsi: 'http://www.w3.org/2001/XMLSchema-instance')
        'constraint-mappings'(xmlns: 'http://jboss.org/xml/ns/javax/validation/mapping',
                'xsi:schemaLocation': 'http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd') {
            'default-package'('org.kar.test.objects')
            bean('class': 'ValidateTestableClass') {
                field(name: 'name') {
                    constraint(annotation: 'javax.validation.constraints.NotNull')
                }
            }
        }
    }
</pre>
<p>Losing all the angle brackets is a good start, but we really haven&#8217;t saved a lot of typing. Until you start taking advantage of the ability to define more complicated structures. Note the use of a list structure here to apply NotNull constraints to multiple fields.</p>
<pre class="brush: groovy; title: ; notranslate">
strictConstraint = {
        mkp.declareNamespace(xsi: 'http://www.w3.org/2001/XMLSchema-instance')
        'constraint-mappings'(xmlns: 'http://jboss.org/xml/ns/javax/validation/mapping',
                'xsi:schemaLocation': 'http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd') {
            'default-package'('org.kar.test.objects')
            bean('class': 'ValidateTestableClass') {
                ['name', 'description'].each {
                    field(name: it) {
                        constraint(annotation: 'javax.validation.constraints.NotNull')
                    }
                }
                field(name: 'id') {
                    constraint(annotation: 'javax.validation.constraints.DecimalMin') {
                        element(name: 'value', '2')
                    }
                }
                field(name: 'enabled') {
                    constraint(annotation: 'javax.validation.constraints.AssertTrue')
                }
            }
        }
    }
</pre>
<p></p>
<h2>Applying Dynamic Constraints</h2>
<p>Applying constraints is as simple as converting Closures to xml and mapping them to a Configuration object, which then supplies a Validator to use. StreamingMarkupBuilder is utilized to create the xml behind the scenes.</p>
<pre class="brush: groovy; title: ; notranslate">
    /**
     * Create a configuration object passing closures as validation mapping documents.
     * @param closures closures to render into validation mapping documents
     * @return config
     */
    public Configuration createConfig(Closure... closures)
    {
        Configuration config = Validation.byDefaultProvider().configure()
        closures.each {
            config.addMapping(new ByteArrayInputStream(GroovyXmlConversionUtil.convertToXml(it).bytes))
        }
        config
    }
</pre>
<p>I haven&#8217;t tested the use of multiple mappings extensively, but minimally each class you&#8217;re configuring must be confined to a single mapping &#8211; you can&#8217;t extend the validations by layering configurations on top of one another. You should however be able to map constraints for different classes in separate Closures.</p>
<p></p>
<h2>Crying out for a Builder!</h2>
<p>Going from Closures to xml is a quick and dirty way to test out this functionality, but what would really be nice is a Builder that could create an appropriate validation environment more directly. At the least it would allow for removing the namespace declarations and explicit package naming that make up the bulk of the content.</p>
<h2>So what do you get?</h2>
<ol>
<li>1. Ability to declare validations against any existing <a class="zem_slink" href="http://java.sun.com" title="Java (programming language)" rel="homepage">Java</a> or Groovy class without changing the source code</li>
<li>2. Programmatic ability to create the configuration of validations</li>
<li>3. A choice of which validations to apply at runtime</li>
<li>4. Consistency with the behavior of domain class validation</li>
</ol>
<p>And what&#8217;d I get? A fun bit of quick coding on a Saturday afternoon. Nice! Source code is <a href="http://github.com/kellyrob99/groovy-hibernate-validator">available on git-hub</a> if you want to check it out.</p>
<div class="zemanta-pixie"><a class="zemanta-pixie-a" title="Reblog this post [with Zemanta]" href="http://reblog.zemanta.com/zemified/c83e9a1b-1079-49a8-80cb-95c76b967f69/"><img class="zemanta-pixie-img" src="http://img.zemanta.com/reblog_c.png?x-id=c83e9a1b-1079-49a8-80cb-95c76b967f69" alt="Reblog this post [with Zemanta]" /></a><span class="zem-script more-related pretty-attribution"><script src="http://static.zemanta.com/readside/loader.js" type="text/javascript"></script></span></div>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/07/01/groovy-and-csv-how-to-get-your-data-out/' rel='bookmark' title='Groovy and CSV: How to Get Your Data Out?'>Groovy and CSV: How to Get Your Data Out?</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/12/16/jira-grails-plugin/' rel='bookmark' title='Jira Grails Plugin'>Jira Grails Plugin</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/03/14/breaking-weak-captcha-in-slightly-more-than-26-lines-of-groovy-code/' rel='bookmark' title='Breaking Weak CAPTCHA in&#8230; slightly more than 26 Lines of Groovy Code'>Breaking Weak CAPTCHA in&#8230; slightly more than 26 Lines of Groovy Code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/04/17/groovy-and-hibernate-validator-for-dynamic-constraints/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>30 Days with a Roomba</title>
		<link>http://www.kellyrob99.com/blog/2010/03/21/30-days-with-a-roomba/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=30-days-with-a-roomba</link>
		<comments>http://www.kellyrob99.com/blog/2010/03/21/30-days-with-a-roomba/#comments</comments>
		<pubDate>Mon, 22 Mar 2010 02:31:26 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Cool Toys]]></category>
		<category><![CDATA[Robots]]></category>
		<category><![CDATA[Roomba]]></category>
		<category><![CDATA[theKaptain]]></category>
		<category><![CDATA[Three Laws of Robotics]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1194</guid>
		<description><![CDATA[Recently I got suckered into buying a Roomba at a local store. It was the very basest model on special at $96CD and I gave in to the indulgence to see how it might fare in my home. Fair Disclosure We live with 3 pets. Two dogs and one cat. The dogs are black and [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>Recently I got suckered into buying a <a class="zem_slink" href="http://en.wikipedia.org/wiki/Roomba" title="Roomba" rel="wikipedia">Roomba</a> at a local store. It was the very basest model on special at $96CD and I gave in to the indulgence to see how it might fare in my home.</p>
<h2>Fair Disclosure</h2>
<p>We live with 3 pets. Two dogs and one cat. The dogs are black and tan. The cat is gray and white. The end result is that we have hair of every pretty much every possible color just about everywhere, all the time. If you too are a multi-pet owner you may know of the fur dust-devils that can take over your kitchen floor, the inevitable dirt by the door where the dogs come in or the &#8220;track proof&#8221; kitty litter that inevitably gets tracked. Over time, in order to properly clean up after the pets we&#8217;ve invested in a very good vacuum cleaner and a steam cleaner to go with it. Not to mention a weekly two hour visit from a professional cleaner. The Roomba has taken a lot of the work out of it, and that&#8217;s a good thing because if there&#8217;s one thing I hate it&#8217;s doing dishes by hand. Followed closely by vacuuming.</p>
<h2>First Impressions</h2>
<p>After fully charging the unit, we set it loose on the main traffic areas of our house &#8211; the living room and the kitchen. The first time we ran it the area was left pretty much &#8216;as is&#8217; and the Roomba needed help a couple of times when it got caught up around chairs, table legs and an edge of a couch. The dirt trap was also stuffed to the max with pet hair after an hour. I blame the fact that the couch it DID fit under had probably gone way too long without us pulling it away from the wall. My Bad. At the end of an hour, the floor was very visibly and noticeably cleaner.</p>
<h2>Week One</h2>
<p>Let me let you in on a little secret. I&#8217;m a tech guy. In practical terms what does that boil down to?  Wires. I&#8217;ve got em. Where I can, they&#8217;re routed, fixed and generally hidden, but in the shady recesses behind desks where Roombas might venture they can be found, exposed and vulnerable to the elements. Turns out it&#8217;s not that big a deal. As things the Roomba got stuck on revealed themselves, they slowly got evaluated, assessed, and in some cases removed. Getting caught under the edge of one couch? Solved by some $1.99 plastic furniture lifts. Stuck rotating around the legs of a wooden chair for 10 minutes? Chair ended up in the garage. The dirt trap was still pretty much full after every run, one per day in a different part of the house everyday, but at the end of a week the house was looking much better overall. And as a side benefit there was a whole lot less crap lying around on the floor.<br />
 <img src='http://www.kellyrob99.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>Week Two</h2>
<p>So by this time there&#8217;s a really big bonus to note about the Roomba over a conventional vacuum. Not only does it not require a lot of direct interaction to get your floors clean, but it also doesn&#8217;t make the pets go mental when you turn it on. One sure way to make the dogs and cat disperse in an immediate fashion is to turn on the vacuum cleaner. The noise drives them out of the room as far as they can get and it takes awhile for them to settle down when you finally shut it off. </p>
<p>The Roomba on the other hand doesn&#8217;t appear to phase them in the least. It&#8217;s (reasonably) quiet for starters. It also isn&#8217;t really all that &#8220;aggressive&#8221;. Our big dog, Aurora, a 13 year old Black Labrador/Dane cross, has a habit of falling asleep right in the middle of the room. The Roomba happily cleans right around her, so much so that she might turn her head to look at it, but rarely is it enough to make her actually stand up. Keep in mind this is when it runs directly into her. The pressure sensor on this piece of hardware is pretty impressive. Before I let it run into Aurora, I did test it out myself first of course &#8211; what do you people think I am, a monster? In this way the robot most definitely appears to obey the first law; when it runs into something it immediately stops and tries a different direction. And it stops on a dime. The cat thinks that we bought him a new toy. He chases and jumps on the Roomba like it was covered in catnip. LMAO.</p>
<p>After two weeks we figured out how to properly clean out the unit. Finally breaking down and RTFM helped to figure out how to clean the filter. The chassis is pretty much hollow so a lot of dirt and hair had accumulated there, something you can fix by pretty much shaking the whole thing over a garbage can, preferably outside. The carpet rollers also needed a little attention by this point, as pet hair and assorted other things(thread, plastic twist tie, etc) had been rooted out from the floors of our home and wrapped around the spindle. At this point our cleaning lady, a wonderful woman who has contributed much to keeping our house livable, not only noticed the difference but took the initiative to suggest tackling some of the less &#8220;essential&#8221; cleaning aspects and generally taking things up a notch: removing all the sink and shower knobs, cleaning in and around them; cleaning the oven(more than the once/yr average); removing all of the window screens, cleaning both them and the windows they&#8217;re covering. Did I mention she also takes the dogs out for us once a week? If you&#8217;re around #yyj and you want someone hard-working and dependable to give you a hand I can highly recommend this lady. Thank you Sandy! </p>
<p>The trap is still pretty much always full.</p>
<h2>Week Three</h2>
<p>Stepping it up a notch, the Roomba is now being run 2 or 3 times a day, as much as possible covering the 3 primary areas of the house: the bedroom/bathroom areas, the dining room, and the living room and kitchen. All told it&#8217;s about 1800 square feet, broken down in order to roughly 600, 400 and 800 sq/ft chunks. The dirt trap now is starting to not be full. The Roomba is covering more territory every day, all of the electrical cords are tucked away where they can&#8217;t be harmed. The cat is now bored with the Roomba and merely takes one tiny step back when it threatens to run into him. The dogs continue to mostly ignore it. The house actually looks better than when we moved in. Despite the two month lifetime stated for the filter, I retired the first filter because it was, quite frankly, toast. Fortunately along with the &#8220;special&#8221; price 3 more filters were included in the box with the original purchase.</p>
<h2>Week Four</h2>
<p>Keeping up with things now is a lot easier. The Roomba runs once or twice a day, usually either right after we get home from work(in an area of the house we&#8217;re not in), and at night when we&#8217;re going to bed. The noise is pretty minimal so running it downstairs in the high-traffic areas at night while we&#8217;re sleeping works out really well.  It&#8217;s also become kind of automatic to deal with. If we&#8217;re walking past the place where we charge the Roomba and the light is &#8220;green&#8221; we pretty much grab it and take it somewhere to do it&#8217;s job. If we&#8217;re walking past it in the hallway and the light is &#8220;red&#8221; then it&#8217;s time to empty it out and take it back to the charging station. That and remembering to pick up the limited number of things left hanging around on the floor that it can get caught on is pretty much it. </p>
<h2>Is it worth it?</h2>
<p>Well unless you&#8217;ve been paying really bad attention so far, you already know that I think it is. <img src='http://www.kellyrob99.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /><br />
 So here&#8217;s the breakdown as I see it. </p>
<p>The Roomba cleans for between 7 and 14 hours per week. I can&#8217;t honestly say we ever averaged that much time pushing a vacuum before. The physical effort involved I would estimate at about 5 minutes or so per run, to clear up the floor, turn it loose and then come back and empty it later. So that&#8217;s about 30 to 60 minutes a week. And that is kinda the point of a robot isn&#8217;t it? Nothing like a division of labor that is 10% planning and 90% labor.</p>
<p>It cleans for approximately an hour every time, and takes a couple or three hours to charge back up to full. I haven&#8217;t been explicitly timing this, but it hasn&#8217;t varied a lot from this pattern.</p>
<p>It occasionally gets stuck. Personally I just think it&#8217;s kinda cool to watch it edge around the top of a staircase, and usually it&#8217;s sensible enough to pull back, but every once and awhile it just goes a teeny tiny bit too far and gets stuck slanted and bleating at the edge of the stair. Third Law in full effect here.</p>
<p>Similarly it seemingly hates the half inch foam mat we have under a painting easel, getting caught half-on/half-off the corners of it. With the more expensive Roomba models these are the kind of problems that seem to be usually solved by virtual &#8220;walls&#8221; which mark boundaries the Roomba should not cross. For us that bought the cheap version, some pretty simple measures close the gap. For the top of the stairs? We already have a gate(s) in place for the pets and it works equally well to keep the Roomba away from places it should not go. For the for mat? Sometimes a chair, sometimes a pair of shoes &#8211; whichever one happens to be closer at hand at the time. Weight really isn&#8217;t a big factor, but height is. If a surface is high enough and the Roomba bumps into it, it just back off. If it&#8217;s too low, the Roomba tries to climb it. Long story short is if there&#8217;s anywhere you don&#8217;t want your Roomba to go, you can gate it easily with just about anything that will stand in its way and is more than an inch high. As already stated this includes, but is not limited to, pet gates, furniture, people and pets themselves. This pretty much covers the Second Law.</p>
<p>If it gets stuck, well&#8230; so what? So far this thing has been so careful that it&#8217;s trained us to better behavior. Less things to get stuck on means less things on the floor means less clutter and more things in a closet or on a shelf. If it gets stuck on what&#8217;s left, it beeps to tell you so. Give it a kick and set it back on its merry way. The most you lose is &lt;1hr of cleaning time, and you get the balance back with nothing more than moving it around and hitting the button again. Honestly with one simple color/status changing button you get all the information you need about the Roomba just by happening to walk by it. Simple UI FTW!</p>
<p>For the truly interested out there, I tracked this month or Roomba in a Google spreadsheet. Blame my OCD if you want. And for those of you who might be reading this who aren&#8217;t total geeks, the <a href="http://en.wikipedia.org/wiki/Three_Laws_of_Robotics">Three Laws of Robotics</a> are classic Asimov science fiction.<br />

<table id="wp-table-reloaded-id-3-no-1" class="wp-table-reloaded wp-table-reloaded-id-3">
<thead>
	<tr class="row-1 odd">
		<th class="column-1">Date</th><th class="column-2">Duration</th><th class="column-3">Areas</th><th class="column-4">#TimesEmptied</th><th class="column-5">Notes</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2 even">
		<td class="column-1">1/30/2010</td><td class="column-2">~45m</td><td class="column-3">Den + kitchen</td><td class="column-4">1</td><td class="column-5">Totally full</td>
	</tr>
	<tr class="row-3 odd">
		<td class="column-1">1/31/2010</td><td class="column-2">~30m</td><td class="column-3">Master bedroom + dressing room</td><td class="column-4">1</td><td class="column-5">Totally full</td>
	</tr>
	<tr class="row-4 even">
		<td class="column-1">2/1/2010</td><td class="column-2">60m+</td><td class="column-3">Den + kitchen + storage </td><td class="column-4">3</td><td class="column-5">had trouble getting into storeroom past easel; stuck under couch in places; sticks in corner of couch by glass doors consistently; realized I wasn't emptying trap properly after 2nd time; trap was full even after emptying properly the last time</td>
	</tr>
	<tr class="row-5 odd">
		<td class="column-1">2/2/2010</td><td class="column-2">60m+</td><td class="column-3">3 bedrooms upstairs</td><td class="column-4">3</td><td class="column-5">trap was half to 3/4 full each time</td>
	</tr>
	<tr class="row-6 even">
		<td class="column-1">2/3/2010</td><td class="column-2"><60m</td><td class="column-3">front room</td><td class="column-4">2</td><td class="column-5">trap was full first time(40m), almost empty the 2nd; light turned yellow after 20m, orange after 40m; tray got caught on persian rug and fell off; first time died under 1h</td>
	</tr>
	<tr class="row-7 odd">
		<td class="column-1">2/4/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom</td><td class="column-4">2</td><td class="column-5"></td>
	</tr>
	<tr class="row-8 even">
		<td class="column-1">2/5/2010</td><td class="column-2">~60m</td><td class="column-3">3 bedrooms + bathroom</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-9 odd">
		<td class="column-1">2/6/2010</td><td class="column-2">~45m</td><td class="column-3">living room</td><td class="column-4">1</td><td class="column-5">pretty full, not totally</td>
	</tr>
	<tr class="row-10 even">
		<td class="column-1">2/7/2010</td><td class="column-2">~60 m</td><td class="column-3">living room</td><td class="column-4">1</td><td class="column-5">full</td>
	</tr>
	<tr class="row-11 odd">
		<td class="column-1">2/7/2010</td><td class="column-2">~60 m</td><td class="column-3">2 bdrms &amp; bath</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-12 even">
		<td class="column-1">2/8/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; bath</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-13 odd">
		<td class="column-1">2/8/2010</td><td class="column-2">~60m</td><td class="column-3">Den + kitchen + storage </td><td class="column-4">2</td><td class="column-5"></td>
	</tr>
	<tr class="row-14 even">
		<td class="column-1">2/9/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-15 odd">
		<td class="column-1">2/10/2010</td><td class="column-2">unattended</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5">got caught under couch, weren't home to correct it</td>
	</tr>
	<tr class="row-16 even">
		<td class="column-1">2/10/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom</td><td class="column-4">3</td><td class="column-5"></td>
	</tr>
	<tr class="row-17 odd">
		<td class="column-1">2/11/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-18 even">
		<td class="column-1">2/11/2010</td><td class="column-2">~60m</td><td class="column-3">whole downstairs</td><td class="column-4">3</td><td class="column-5">chassis was filled with crud, need to be more thorough when emptying; trap fell off</td>
	</tr>
	<tr class="row-19 odd">
		<td class="column-1">2/12/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">2</td><td class="column-5">caught under couch</td>
	</tr>
	<tr class="row-20 even">
		<td class="column-1">2/12/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-21 odd">
		<td class="column-1">2/13/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5">trap was only about a third full!</td>
	</tr>
	<tr class="row-22 even">
		<td class="column-1">2/14/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-23 odd">
		<td class="column-1">2/14/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-24 even">
		<td class="column-1">2/14/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom</td><td class="column-4">1</td><td class="column-5">run when going to bed, minimal noise </td>
	</tr>
	<tr class="row-25 odd">
		<td class="column-1">2/15/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-26 even">
		<td class="column-1">2/16/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5">full, but not compact full</td>
	</tr>
	<tr class="row-27 odd">
		<td class="column-1">2/16/2010</td><td class="column-2">25 min</td><td class="column-3">den, back room</td><td class="column-4">1</td><td class="column-5">1/2 full</td>
	</tr>
	<tr class="row-28 even">
		<td class="column-1">2/17/2010</td><td class="column-2">~60 min</td><td class="column-3">den</td><td class="column-4">1</td><td class="column-5">1/2 full</td>
	</tr>
	<tr class="row-29 odd">
		<td class="column-1">2/17/2010</td><td class="column-2">~60m</td><td class="column-3">3 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5">1/2 full, finished completely underneath a bed :(</td>
	</tr>
	<tr class="row-30 even">
		<td class="column-1">2/18/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5">1/2 full</td>
	</tr>
	<tr class="row-31 odd">
		<td class="column-1">2/19/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5">Got stuck once early on, 1/2 full</td>
	</tr>
	<tr class="row-32 even">
		<td class="column-1">2/20/2010</td><td class="column-2">~60m</td><td class="column-3">whole downstairs</td><td class="column-4">2</td><td class="column-5">Full both times</td>
	</tr>
	<tr class="row-33 odd">
		<td class="column-1">2/20/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-34 even">
		<td class="column-1">2/21/2010</td><td class="column-2">~60m</td><td class="column-3">3 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5">full. Got stuck hanging on lip of stairs.</td>
	</tr>
	<tr class="row-35 odd">
		<td class="column-1">2/21/2010</td><td class="column-2">60m+</td><td class="column-3">den+kitchen+storeroom<br />
</td><td class="column-4">3</td><td class="column-5">full each time</td>
	</tr>
	<tr class="row-36 even">
		<td class="column-1">2/21/2010</td><td class="column-2">~60m</td><td class="column-3">whole downstairs</td><td class="column-4"></td><td class="column-5">run when going to bed, minimal noise</td>
	</tr>
	<tr class="row-37 odd">
		<td class="column-1">2/22/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-38 even">
		<td class="column-1">2/23/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-39 odd">
		<td class="column-1">2/24/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom<br />
</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-40 even">
		<td class="column-1">2/24/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom<br />
</td><td class="column-4">1</td><td class="column-5">fun for the guests!</td>
	</tr>
	<tr class="row-41 odd">
		<td class="column-1">2/25/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom<br />
</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-42 even">
		<td class="column-1">2/26/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-43 odd">
		<td class="column-1">2/27/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-44 even">
		<td class="column-1">2/28/2010</td><td class="column-2">~60m</td><td class="column-3">whole downstairs</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-45 odd">
		<td class="column-1">2/28/2010</td><td class="column-2">~60m</td><td class="column-3">2 bdrms &amp; both baths &amp; closet</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
	<tr class="row-46 even">
		<td class="column-1">3/1/2010</td><td class="column-2">~60m</td><td class="column-3">front room</td><td class="column-4">1</td><td class="column-5">real problems with the couch and persian carpet; got stuck on both more than once</td>
	</tr>
	<tr class="row-47 odd">
		<td class="column-1">3/1/2010</td><td class="column-2">~60m</td><td class="column-3">den+kitchen+storeroom<br />
</td><td class="column-4">1</td><td class="column-5"></td>
	</tr>
</tbody>
</table>
 </p>
<div class="zemanta-pixie"><a class="zemanta-pixie-a" href="http://reblog.zemanta.com/zemified/e28f7773-829b-4c59-bf72-e00827dff9ff/" title="Reblog this post [with Zemanta]"><img class="zemanta-pixie-img" src="http://img.zemanta.com/reblog_c.png?x-id=e28f7773-829b-4c59-bf72-e00827dff9ff" alt="Reblog this post [with Zemanta]" /></a><span class="zem-script more-related pretty-attribution"><script type="text/javascript" src="http://static.zemanta.com/readside/loader.js" defer="defer"></script></span></div>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/03/21/30-days-with-a-roomba/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Gource Visualizations of the Groovy/Grails/Griffon Projects</title>
		<link>http://www.kellyrob99.com/blog/2010/03/21/gource-visualizations-of-the-groovygrailsgriffon-projects/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=gource-visualizations-of-the-groovygrailsgriffon-projects</link>
		<comments>http://www.kellyrob99.com/blog/2010/03/21/gource-visualizations-of-the-groovygrailsgriffon-projects/#comments</comments>
		<pubDate>Mon, 22 Mar 2010 01:58:41 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Cool Toys]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Concurrent Versions System]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[git-svn]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Griffon]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[MacPorts]]></category>
		<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Open source]]></category>
		<category><![CDATA[Revision control]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[SVN]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1226</guid>
		<description><![CDATA[That&#8217;s a whole lot of Gr8ness in the title, don&#8217;t you think? Ok, ok &#8211; I apologize for the pun. I whipped these up using the open source Gource visualization software and with HD content free on Youtube now, I just had to try that out too. Gource Gource is a software version control visualization [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/01/07/bamboo-grails-and-git-for-continuous-integration/' rel='bookmark' title='Bamboo, Grails and Git for Continuous Integration'>Bamboo, Grails and Git for Continuous Integration</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/' rel='bookmark' title='A Grails App Demoing the StackExchange API'>A Grails App Demoing the StackExchange API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/10/25/grails-ui-datatable-using-xml-for-a-model/' rel='bookmark' title='Grails-UI DataTable using XML for a model'>Grails-UI DataTable using XML for a model</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>That&#8217;s a whole lot of Gr8ness in the title, don&#8217;t you think? Ok, ok &#8211; I apologize for the pun. I whipped these up using the open source <a href="http://code.google.com/p/gource/">Gource</a> visualization software and with <a href="http://www.google.com/support/youtube/bin/answer.py?hl=en&amp;answer=153665">HD content free on Youtube</a> now, I just had to try that out too.</p>
<h2>Gource</h2>
<p>Gource is a software <a class="zem_slink" href="http://en.wikipedia.org/wiki/Revision_control" title="Revision control" rel="wikipedia">version control</a> visualization program with support for <a class="zem_slink" href="http://git-scm.com/" title="Git (software)" rel="homepage">Git</a>, <a class="zem_slink" href="http://mercurial.selenic.com/" title="Mercurial" rel="homepage">Mercurial</a>, and with a tiny bit of extra effort, <a class="zem_slink" href="http://www.nongnu.org/cvs" title="Concurrent Versions System" rel="homepage">CVS</a> and <a href="http://subversion.tigris.org/">Subversion</a>. It&#8217;s available as a macport install, as is the ffmpeg program used to stitch together a series of screenshots into the final video.&nbsp; All of these visualizations were created with Git or Git-svn. When the Gource program is running it brings up an interactive window so you can interact with it to a degree, affecting the camera, zoom, etc.&nbsp; The default resolution is 1024&#215;768 but I tried to bump it up to 1920&#215;1080. I&#8217;m assuming that stretched the limits of the rendering, as there&#8217;s quite a bit of pixellation in the final product; either that or the ffmpeg params need to be tweaked. The three visualizations on this page were all created using this one-liner command line invocation (broken down here over several lines for readability).</p>
<pre class="brush: bash; title: ; notranslate">
gource -1920x1080 --highlight-all-users --bloom-intensity .3 -a 1 -s .01
     --hide-dirnames --hide-filenames --stop-at-end --output-ppm-stream -
     | ffmpeg -y -b 3000K -r 60 -f image2pipe -vcodec ppm -i -
     -vcodec libx264 -vpre default  gource.mp4
</pre>
<h2>Groovy</h2>
<p>From the initial commit on August 28, 2003, Groovy has come a long way. I tried the Gource provided python script to translate an svn log file into the required format the first time, but cloning the repository with git-svn seemed to produce better results. It&#8217;s interesting to see that the overall structure of the Groovy project doesn&#8217;t look like is has changed very much over the years. I recognized a lot of the names you see on the screen. Be sure to watch the <a class="zem_slink" href="http://en.wikipedia.org/wiki/1080p" title="1080p" rel="wikipedia">1080p</a> version if you&#8217;re having trouble reading the text and see if you can&#8217;t find some recognizable committers.</p>
<p><a href="http://www.kellyrob99.com/blog/2010/03/21/gource-visualizations-of-the-groovygrailsgriffon-projects/"><em>Click here to view the embedded video.</em></a></p>
<h2>Grails</h2>
<p>Starting on Sunday March 14, 2004 here&#8217;s how it all unfolded, at least  from the perspective of the public Git repository. Grails looks like it grew a little more spontaneously than Groovy; there are more frequent restructurings and some transitions that seem to cover a whole lot of ground in very little time. In particular watch out for the burst of activity in the summer of 2009.</p>
<p><a href="http://www.kellyrob99.com/blog/2010/03/21/gource-visualizations-of-the-groovygrailsgriffon-projects/"><em>Click here to view the embedded video.</em></a></p>
<h2>Griffon</h2>
<p>September 17, 2008 marks the first commit for Griffon and this short but sweet video explodes right from the start. Benefiting greatly from the Grails model this project shows a lot of progress in a very short time indeed. Most, if not all, of the names you see here you probably caught in the previous two videos as well.<br />
<p><a href="http://www.kellyrob99.com/blog/2010/03/21/gource-visualizations-of-the-groovygrailsgriffon-projects/"><em>Click here to view the embedded video.</em></a></p></p>
<h2>What&#8217;s the Point?</h2>
<p>Watching these gives me (and maybe you) some appreciation for how much effort over time it takes to bring a new language/framework/technology to maturity and all of the individual contributions that go into it. Gource provides a lot more detail into the actual structure if you want to dig in. For purposes of keeping these images fairly uncluttered I removed file and directory names and just focused on the committers and the general structure. I think I gained a git of insight into how a codebase can evolve, and properly appreciate that organizing the increasing complexity of an ever-growing system over time is not a simple job. Plus they look cool. Enjoy!</p>
<div class="zemanta-pixie"><a class="zemanta-pixie-a" href="http://reblog.zemanta.com/zemified/2f2c3e09-4284-4248-b30f-d47242fd57a1/" title="Reblog this post [with Zemanta]"><img class="zemanta-pixie-img" src="http://img.zemanta.com/reblog_c.png?x-id=2f2c3e09-4284-4248-b30f-d47242fd57a1" alt="Reblog this post [with Zemanta]" /></a><span class="zem-script more-related pretty-attribution"><script type="text/javascript" src="http://static.zemanta.com/readside/loader.js" defer="defer"></script></span></div>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2010/01/07/bamboo-grails-and-git-for-continuous-integration/' rel='bookmark' title='Bamboo, Grails and Git for Continuous Integration'>Bamboo, Grails and Git for Continuous Integration</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/06/02/a-grails-app-demoing-the-stackexchange-api/' rel='bookmark' title='A Grails App Demoing the StackExchange API'>A Grails App Demoing the StackExchange API</a></li>
<li><a href='http://www.kellyrob99.com/blog/2009/10/25/grails-ui-datatable-using-xml-for-a-model/' rel='bookmark' title='Grails-UI DataTable using XML for a model'>Grails-UI DataTable using XML for a model</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/03/21/gource-visualizations-of-the-groovygrailsgriffon-projects/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Breaking Weak CAPTCHA in&#8230; slightly more than 26 Lines of Groovy Code</title>
		<link>http://www.kellyrob99.com/blog/2010/03/14/breaking-weak-captcha-in-slightly-more-than-26-lines-of-groovy-code/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=breaking-weak-captcha-in-slightly-more-than-26-lines-of-groovy-code</link>
		<comments>http://www.kellyrob99.com/blog/2010/03/14/breaking-weak-captcha-in-slightly-more-than-26-lines-of-groovy-code/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 23:44:59 +0000</pubDate>
		<dc:creator>TheKaptain</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Application programming interface]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Java2D]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[Open source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Source code]]></category>
		<category><![CDATA[tesseract-ocr]]></category>
		<category><![CDATA[theKaptain]]></category>

		<guid isPermaLink="false">http://www.kellyrob99.com/blog/?p=1158</guid>
		<description><![CDATA[I read an interesting article recently about using python and open source software to defeat a particular captcha implementation and I set out to see how hard it would be to do the same in Groovy. In particular, coming from the Java side of the fence, I was impressed by how the available libraries in [...]
Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2009/11/21/different-flavors-of-embedded-groovy-in-java-apps-or-how-to-make-your-java-groovier/' rel='bookmark' title='Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;'>Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/' rel='bookmark' title='Achieving Groovy-like Fluency in Java with Google Collections'>Achieving Groovy-like Fluency in Java with Google Collections</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/04/17/groovy-and-hibernate-validator-for-dynamic-constraints/' rel='bookmark' title='Groovy and Hibernate Validator for Dynamic Constraints'>Groovy and Hibernate Validator for Dynamic Constraints</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I read <a href="http://www.bonsai-sec.com/blog/index.php/breaking-weak-captcha-in-26-lines-of-code/">an interesting article</a> recently about using python and <a class="zem_slink" href="http://www.wikinvest.com/concept/Open_Source" title="Open Source" rel="wikinvest">open source</a> software to defeat a particular captcha implementation and I set out to see how hard it would be to do the same in Groovy. In particular, coming from the <a class="zem_slink" href="http://java.sun.com" title="Java (programming language)" rel="homepage">Java</a> side of the fence, I was impressed by how the available libraries in python made loading, mutating and saving images so easy. Admittedly I have limited experience working with image data, but when I have it has always seemed like a complex(and easy to get wrong) process. Maybe there&#8217;s a Java library out there that provides a simple &#8216;image_resize&#8217; method, but it&#8217;s certainly not in the BufferedImage <a class="zem_slink" href="http://en.wikipedia.org/wiki/Application_programming_interface" title="Application programming interface" rel="wikipedia">API</a>. Still, when porting the 26 lines of code over to Groovy, I was able to get it considerably less verbose than the Java equivalent.</p>
<p></p>
<h2>The Pretty Pictures</h2>
<p>Here are the three images to test against. In order to put them in a suitable format for <a href="http://code.google.com/p/tesseract-ocr">the open source tesseract-ocr program</a> to process we need to make them bigger, remove the background noise and transform them into a &#8216;tif&#8217; format. The python program we&#8217;re porting utilizes the PIL library for image handling and the pytesseract library for wrapping tesseract; I didn&#8217;t look very hard for java equivalents and just coded the required functions directly.</p>
<table>
<tbody>
<tr>
<td>
<a href="http://www.kellyrob99.com/blog/wp-content/gallery/captcha-breaker/9koo.gif" title="Original image for 9koO" class="shutterset_singlepic58" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/58__x_9koo.gif" alt="9koo" title="9koo" />
</a>
</td>
<td>
<a href="http://www.kellyrob99.com/blog/wp-content/gallery/captcha-breaker/jxt9.gif" title="Original image for jxt9" class="shutterset_singlepic60" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/60__x_jxt9.gif" alt="jxt9" title="jxt9" />
</a>
</td>
<td>
<a href="http://www.kellyrob99.com/blog/wp-content/gallery/captcha-breaker/e4ya.gif" title="Original image for e4ya" class="shutterset_singlepic59" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/59__x_e4ya.gif" alt="e4ya" title="e4ya" />
</a>
</td>
</tr>
</tbody>
</table>
<p></p>
<h2>Reading in the Image</h2>
<p>The python code for this is three lines, one to load the image and a couple more to convert it into a format suitable for directly manipulating pixel color through <a class="zem_slink" href="http://en.wikipedia.org/wiki/RGB_color_model" title="RGB color model" rel="wikipedia">RGB</a> values. Groovy takes a bit more to do the same, but being able to use a &#8216;with&#8217; block makes interacting with the Graphics object a lot cleaner than the same Java code</p>
<pre class="brush: groovy; title: ; notranslate">
//python
from PIL import Image
img = Image.open('input.gif')
img = img.convert(&quot;RGBA&quot;)
pixdata = img.load()

//Groovy
BufferedImage image = ImageIO.read(new File(fileName))
BufferedImage dimg = new BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_ARGB)
dimg.createGraphics().with {
    setComposite(AlphaComposite.Src)
    drawImage(image, null, 0, 0)
    dispose()
}
</pre>
<p></p>
<h2>Removing the Background Noise</h2>
<p>In both cases we&#8217;re doing essentially the same thing: finding all non-black pixels and setting them to white. This leaves only the actual embedded text to stand out. Being able to utilize the Java Color constants makes the Groovy version a little more readable, IMO, but otherwise the two pieces of code are generally equivalent.</p>
<pre class="brush: groovy; title: ; notranslate">
//python
for y in xrange(img.size[1]):
    for x in xrange(img.size[0]):
        if pixdata[x, y] != (0, 0, 0, 255):
            pixdata[x, y] = (255, 255, 255, 255)

//Groovy
(0..&lt;dimg.height).each {i=&quot;&quot; -=&quot;&quot;&gt;
    (0..&lt;dimg.width).each {j=&quot;&quot; -=&quot;&quot;&gt;
        if (dimg.getRGB(j, i) != Color.BLACK.RGB)
        {
            dimg.setRGB(j, i, Color.WHITE.RGB)
        }
    }
}
</pre>
<p></p>
<h2>Resizing the Image</h2>
<p><a class="zem_slink" href="http://www.python.org/" title="Python (programming language)" rel="homepage">Python</a>&#8216;s library usage really shines here, making this a one line call. Not quite the same in Java-land, although again there&#8217;s probably a better way to do this(I just don&#8217;t know it offhand).</p>
<pre class="brush: groovy; title: ; notranslate">
//python
big = im_orig.resize((116, 56), Image.NEAREST)

//Groovy
dimg = resizeImage(dimg, 116, 56)
...
def resizeImage = {BufferedImage image, int w, int h -&amp;gt;
    BufferedImage dimg = new BufferedImage(w, h, image.type)
    dimg.createGraphics().with {
        setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
        drawImage(image, 0, 0, w, h, 0, 0, image.width, image.height, null)
        dispose()
    }
    return dimg
}
</pre>
<p>By this point the original images now look like this, and are <i>almost</i> ready for OCR.</p>
<table>
<tbody>
<tr>
<td>
<a href="http://www.kellyrob99.com/blog/wp-content/gallery/captcha-breaker/tmp0.gif" title="Resized and cleaned for OCR" class="shutterset_singlepic61" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/61__x_tmp0.gif" alt="9koO-readyForOCR" title="9koO-readyForOCR" />
</a>
</td>
<td>
<a href="http://www.kellyrob99.com/blog/wp-content/gallery/captcha-breaker/tmp1.gif" title="Resized and cleaned for OCR" class="shutterset_singlepic62" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/62__x_tmp1.gif" alt="jxt9-readyForOCR" title="jxt9-readyForOCR" />
</a>
</td>
<td>
<a href="http://www.kellyrob99.com/blog/wp-content/gallery/captcha-breaker/tmp2.gif" title="Resized and cleaned for OCR" class="shutterset_singlepic63" >
	<img class="ngg-singlepic" src="http://www.kellyrob99.com/blog/wp-content/gallery/cache/63__x_tmp2.gif" alt="e4ya-readyForOCR" title="e4ya-readyForOCR" />
</a>
</td>
</tr>
</tbody>
</table>
<p></p>
<h2>Converting to a tif File</h2>
<p>This one turns out to be a bit of a PITA in Java and particularly on a Mac, and represents the bulk of the Groovy code. Unfortunately it is also the only format that tesseract appears to accept &#8216;out of the box&#8217;.  After googling the fun that is JAI and working with the <a class="zem_slink" href="http://en.wikipedia.org/wiki/Tagged_Image_File_Format" title="Tagged Image File Format" rel="wikipedia">.tif</a>(f) format with it on a Mac,   I ended up taking the code kindly provided in <a href="http://www.ideyatech.com/2009/05/converting-to-tiff-on-mac-using-java-advanced-imaging/">this blog post</a> and Groovified it a bit to make a working transformation. Thanks very much to Allan Tan for that. One more time, there&#8217;s likely a better/easier way to do this, but honestly it&#8217;s more effort than I&#8217;m willing to put in on a weekend afternoon just to satisfy my curiosity.<br />
 <img src='http://www.kellyrob99.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<pre class="brush: groovy; title: ; notranslate">
//python
ext = &quot;.tif&quot;
big.save(&quot;input-NEAREST&quot; + ext)

//Groovy
void convertToTiff(String inputFile, String outputFile)
{
    OutputStream ios
    try
    {
        ios = new BufferedOutputStream(new FileOutputStream(new File(outputFile)))
        ImageEncoder enc = ImageCodec.createImageEncoder(&quot;tiff&quot;, ios, new TIFFEncodeParam(compression: TIFFEncodeParam.COMPRESSION_NONE, littleEndian: false))
        RenderedOp src = JAI.create(&quot;fileload&quot;, inputFile)

        //Apply the color filter and return the result.
        ColorConvertOp filterObj = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null)
        BufferedImage dst = new BufferedImage(src.width, src.height, BufferedImage.TYPE_3BYTE_BGR)
        filterObj.filter(src.getAsBufferedImage(), dst)

        // save the output file
        enc.encode(dst)
    }
    catch (Exception e)
    {
        println e
    }
    finally
    {
        ios.close()
    }
}
</pre>
<p></p>
<h2>OCR with Tesseract-OCR</h2>
<p>Finally we need to pass the processed image to tesseract so it can &#8216;read&#8217; it for us. Again, the python library makes this a breeze, but calling out to a command line program with Groovy is so simple that it ends up being about the same. Tesseract itself is available as a macport, as well in downloadable unix binaries and a windows executable so installing the software is a breeze.</p>
<pre class="brush: groovy; title: ; notranslate">
//python
from pytesser import *
image = Image.open('input-NEAREST.tif')
print image_to_string(image)

//Groovy
def tesseract = ['/opt/local/bin/tesseract', tmpTif, tmpTesseract].execute()
tesseract.waitFor()
return new File(&quot;${tmpTesseract}.txt&quot;).readLines()[0]
</pre>
<p></p>
<h2>Testing it out</h2>
<p>To test it out I implemented the code in a maven project, iterate over the images and write out intermediate results to a temp directory. And it only works on two out of three of the cases. For some reason tesseract insists on consistently seeing &#8216;e4ya&#8217; as &#8216;e4ga&#8217;.  I tried to see if I could get it working by tweaking the image manipulation parameters and the order of operations(resizing before removing the background noise for instance) but that just caused the other cases to fail as well. Since in the final image the &#8216;y&#8217; seems pretty clear, it&#8217;s more likely that tweaking tesseract configuration might yield better results.</p>
<pre class="brush: groovy; title: ; notranslate">
public void testPrintImage()
{
    def breaker = new CaptchaBreaker()
    /* tesseract interprets &quot;e4ya&quot; as &quot;e4ga&quot; unfortunately */
    ['9koO', 'jxt9'/*,'e4ya'*/].each {String imageName -&amp;gt;
        def fileName = &quot;src/test/resources/${imageName}.gif&quot;
        assertEquals(&quot;Testing $imageName&quot;,imageName, breaker.imageToString(fileName))
    }
}
</pre>
<p></p>
<h2>C&#8217;est Finis</h2>
<p>I had some fun playing with areas of Java that I don&#8217;t usually interact with, and gained some appreciation for the diversity and ease-of-use exposed by just a couple of python libraries. It&#8217;s comforting to note that I was able to implement all of the required functionality from those libraries in &lt; 90 lines of Groovy. With a little more effort I think the final product could be tweaked to avoid the intermediate file system reads/writes as well, but that&#8217;s for another day.<br />
Source code is <a href="http://github.com/kellyrob99/catcha-breaker">available on github</a> if you&#8217;d care to take a look, and thanks for stopping by!</p>
<div class="zemanta-pixie"><a class="zemanta-pixie-a" href="http://reblog.zemanta.com/zemified/7a2ae51d-774b-47b5-894b-592c38ebf542/" title="Reblog this post [with Zemanta]"><img class="zemanta-pixie-img" src="http://img.zemanta.com/reblog_c.png?x-id=7a2ae51d-774b-47b5-894b-592c38ebf542" alt="Reblog this post [with Zemanta]" /></a><span class="zem-script more-related pretty-attribution"><script type="text/javascript" src="http://static.zemanta.com/readside/loader.js" defer="defer"></script></span></div>
<p>Related posts:<ol>
<li><a href='http://www.kellyrob99.com/blog/2009/11/21/different-flavors-of-embedded-groovy-in-java-apps-or-how-to-make-your-java-groovier/' rel='bookmark' title='Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;'>Different Flavors of Embedded Groovy in Java Apps or &#8220;How To Make your Java Groovier!&#8221;</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/05/15/achieving-groovy-like-fluency-in-java-with-google-collections/' rel='bookmark' title='Achieving Groovy-like Fluency in Java with Google Collections'>Achieving Groovy-like Fluency in Java with Google Collections</a></li>
<li><a href='http://www.kellyrob99.com/blog/2010/04/17/groovy-and-hibernate-validator-for-dynamic-constraints/' rel='bookmark' title='Groovy and Hibernate Validator for Dynamic Constraints'>Groovy and Hibernate Validator for Dynamic Constraints</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.kellyrob99.com/blog/2010/03/14/breaking-weak-captcha-in-slightly-more-than-26-lines-of-groovy-code/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 5.904 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2012-05-18 15:06:01 -->

