One of the best new features of Groovy is Grape. It allows a script to connect to Ivy or Maven style repositories and download the dependencies it needs. This lets you bang out a quick script, ship it to another computer, and run it there without having to fiddle around with classpaths, jars, or any other sort of file. I put together a simple demo that shows some of this. The script grabs my RSS feed using ROME and then splits out words longer than 3 characters if they show up more than once. The important part is at the top, however. You can see that I use the Grab annotation to get ROME and Apache Commons Lang. I’m then free to import the classes I need from those jars. Further down, I use an inline Grab to get the Google Collections jar and then create the class I want with a fully qualified name. Pretty cool, I think. You can try it yourself by cutting and pasting this example. You’ll need access to Groovy from your command line or IDE.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env groovy
@Grab(group='rome', module='rome', version='0.9') //Long-style grab
@Grab('commons-lang:commons-lang:2.4') // Gradle-style grab
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;

class GrapeTest {
  public static final int MIN_WORD_LEN = 4;
 
  static main(args) {
    //Inline grab using fully qualified name (no import statement)
    @Grab('com.google.collections:google-collections:1.0')
    def words = new com.google.common.collect.TreeMultiset()
   
    def feed = getFeed('http://codegoop.com/feed/')
    getCleanFeedLines(feed).each { line ->
      line.split(' ').each { word ->
        if (validWord(word)) {
          words.add(word)
        }
      }
    }
    printResult(words)
  }
 
  def static getFeed(url) {
    def input = new SyndFeedInput()
    return input.build(new XmlReader(new URL(url)))
  }
 
  def static getCleanFeedLines(feed) {
    def lines = new ArrayList()
    feed.entries.each { entry ->
      entry.contents.each { content ->
        cleanLine(content.value).each { line ->
          lines.add(line)
        }
      }
    }
    return lines
  }
 
  def static validWord(word) {
    return StringUtils.isNotBlank(word) && word.size() >= MIN_WORD_LEN;
  }
 
  def static printResult(result) {
    result.elementSet().each {
      if (result.count(it) > 1) {
        println it + " found " + result.count(it) + " times"
      }
    }
  }
 
  //Only use the code in the <p> blocks
  //Remove html tags and non-alpha (leaving space)
  def static cleanLine(str) {
    def cleanedLines = new ArrayList()
    def subLines = StringUtils.substringsBetween(str, "<p>", "</p>")
    subLines.each { subLine ->
      def line = StringEscapeUtils.unescapeHtml(subLine)
      line = line.replaceAll("<(.*?)*>", "").replaceAll("[^a-zA-Z\\s]", "")
                          .toLowerCase()
      cleanedLines.add(line)
    }
    return cleanedLines
  }
}

Running the script gives you something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ./GrapeTest.groovy (Unix or Cygwin) vs C:\>groovy GrapeTest.groovy (Windows)

about found 6 times
arent found 2 times
around found 2 times
automatic found 2 times
basically found 2 times
been found 6 times
better found 3 times
bring found 2 times
<snip>
  several more lines
</snip>
work found 2 times
write found 2 times
written found 3 times
year found 5 times
youre found 5 times

Now, that’s all great, if the libraries you need are in the default repositories, but what if they aren’t? Well, you can either edit ~/.groovy/grapeConfig.xml and add in the repo you want or, even better, use @GrabResolver(name=’myRepoName’, root=’myRepoUrl’) at the top of your script to retain that “copy/paste” the file feature that seems so nice. GrabResolver is new in Groovy 1.7. See the release notes here