blue64.net

Living in a 64 bit world

With Grape, Groovy Is On Par with Native Scripting Languages

with 18 comments

If you haven’t heard, the latest version of Groovy was released this week and included with it, among many other great features, was Grape (Groovy Advanced Packaging Engine). Grape is an annotation based dependency management system that provides functionality similar to that of Maven and Ivy with one clear advantage, namely, no build file.

If Grape doesn’t use a build file, how does it know what dependencies are necessary to run the code? Does it figure it out for you on the fly? Unfortunately, it is not that smart (yet), perhaps the next release. If it doesn’t figure it out for you, then how do you specify your dependencies? You configure your dependencies by using the@Grapes or @Grab annotations.

What is so good about being able to configure your dependencies via annotations?

If you are working with Groovy scripts, it frees you up from having to worry about dependency management and allows you to focus more on what the script needs to do much like when working with other scripting languages like Ruby or Perl. In order to clearly demonstrate the advantages of Grape, lets walk through an example.

The problem
Trying to keep up with my ever changing IP address after switching ISP’s earlier this year. There are several services running at my home that I need access to on a daily basis. If my IP changes over night, after a brown out, or for some other reason, I need to know about it asap.

In order to keep up with my IP address, I wrote a set of scripts that perform the following:

  • Obtains the current IP address of the server where it is running
  • Looks up the most recent IP address of the server in a log file
  • If the current IP address is different that the most recent IP address:
    • Updates the log file with the current IP address
    • Send the new IP address in a customizable email to a configurable address
  • If the IP address’s are the same, it does nothing.

The Solution
It took a total of three Groovy classes/scripts to solve this problem. We are not going to get in to the details of the solution because I want to stay focused on Grape.

You can find all of the code discussed in this post on github. Please feel free to download and use it. Feedback is welcome as well.

This simple Groovy class first connects to a mail server, and then sends the change of address message.

import javax.mail.Session
import javax.mail.Message
import javax.mail.internet.MimeMessage
import javax.mail.internet.InternetAddress

@Grapes([
  @Grab(group = 'javax.activation', module = 'activation', version = '1.1'),
  @Grab(group = 'javax.mail', module = 'mail', version = '1.4')
])

class Mailer {

  static def s_config = new ConfigSlurper("message").parse(new File('MailProperties.groovy').toURL())

  static def deliverIpAddressChangeMessage(ipAddress) {
    def subject = "IP Address Changed to ${ipAddress}"
    def message = "IP Address changed to ${ipAddress}.\nPlease update your configurations."
    sendMail("${s_config.message.to}".toString(), "${s_config.message.from}".toString(), subject, message)
  }
  //...
 }

Download Source: Mailer.groovy

The most interesting things to pay attention to are:

  • The @Grapes block after all of the imports, you can see this groovy class depends on javax.activation and javax.mail jars.
  • Thanks to Grapes, you can compile this class simply by invoking groovyc Mailer.groovy as opposed to having to configure either Maven, Gant, Ant, or some other build tool to manage the dependencies and classpath for you.
  • What’s the big deal? Read more to find out!

This next code snipped represents the “main” entry point of my solution. It simply obtains the current IP address of the machine it is running on, checks the current address against the most recent known address stored in a log file, and then uses the previous class to send an email if the IP address has changed.

#!/usr/bin/env groovy

// IP Address Regex http://www.regular-expressions.info/examples.html
currentIp = ("http://whatsmyip.us/".toURL().text =~ /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/)[0]

println currentIp

def ipLog = new File("ip-log.txt")

recentIp = ipLog.readLines().last().tokenize(",").last().trim()

if (currentIp != recentIp) {
  Mailer.deliverIpAddressChangeMessage currentIp
  println "IP Address has changed, it is now: ${currentIp}. Sending Message."
  ipLog << "${new Date()}, ${currentIp}\n"
}

Download Source: whatsMyIp.groovy

The most interesting thing to pay attention to in this script is:

  • The #!/usr/bin/env groovy on the first line on the script.
  • This line enables the script to be called directly from the command line like: ./whatsMyIp.groovy instead of groovy whatsMyIp.groovy

The Big Deal!
If Grape didn’t exist the only way to invoke this script would be to invoke it with a build tool such as Maven, GAnt, or some other. If a build tool didn’t suit you then you would have to invoke groovy -classpath=/path/activation.jar... and manage the dependencies there. Both of these solutions work fine, but are clunky.

If you were to solve this problem using a language such as Ruby, you would not have to worry about dependency management since Ruby is so closely integrated with the OS. You would simply run gem install some gem, and this would install the dependencies at the OS level. Thus allowing you to focus on your script and letting the Ruby runtime focus on the dependencies. Invoking ./someScript.rb is common in Ruby.

Grape gives Groovy scripts the same clean dependency abstraction. It is possible to invoke ./whatsMyIp.groovy without having to worry about any dependency management. Once the groovy runtime comes across the Grape annotations, it loads the dependencies on demand freeing the Groovy script from having to be wrapped with a dependency management layer.

This is a huge deal because now simple Groovy scripts can leverage the entire Java ecosystem from the command line without having to wrap the invocation with a build tool. Groovy Scripts are now clean, simple, and easy. I hope this inspires you to go out and convert some Ruby or Perl script to Groovy.

Related Posts

This entry was posted by Steve on Thursday, December 24th, 2009 at 2:31 pmand is filed under: , , , . You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

18 Responses to 'With Grape, Groovy Is On Par with Native Scripting Languages'

Subscribe to comments with RSS or TrackBack to 'With Grape, Groovy Is On Par with Native Scripting Languages'.

  1. [...] This post was Twitted by aalmiray [...]

  2. I’ve been waiting for this capability from the earliest days of Groovy. I can’t wait to try it out.

    ReplyReply

    Sean Gilligan

    30 Dec 09 at 2:07 am

  3. Great post – thanks! (Actually, I may even hawk your code for personal and educational use =])

    ReplyReply

    Steve

    30 Dec 09 at 12:06 pm

  4. Grape rocks. I consider it a superior solution to ‘gem install’ because it keeps the dependency and version in the code.

    ReplyReply

    Rick

    30 Dec 09 at 12:31 pm

  5. @Rick: RubyGems can optionally also specify gem dependencies (including version) in the code, as in:

    require ‘rubygems’
    gem ‘rails’, ‘>= 2.3′

    And of course it supports all transitive dependencies, offline installation, bundling into an existing app, comparative version requirements (>=, etc), and if you run JRuby…Maven artifacts are even gem-installable.

    ReplyReply
  6. [...] With Grape, Groovy Is On Par with Native Scripting Languages Wow, Grape ist ja wirklich spitze [...]

  7. Does that “#!/usr/bin/env groovy” work in Windows, or just Linux? If not, is there something similar?
    Thanks!

    ReplyReply

    Steve Olsen

    30 Dec 09 at 5:40 pm

  8. @Steve Olsen: To get the same sort of functionality on Windows you would need to install a bash environment such as Cygwin.

    ReplyReply

    Steve

    30 Dec 09 at 8:51 pm

  9. @Steve Olsen: first line is ignored on windows, you can go on with association of groovy interpreter with the .groovy extension or simply loading it with groovy console if you mind. Cheers

    ReplyReply

    Ales Najmann

    31 Dec 09 at 7:03 am

  10. Nice post, but i have question. if you rely now on the groovyc compiler or interpreter to load the dependency automatically without the need to specified them manually in the classpath variable, from which directory it takes the jar?

    I guess im not clear on how that works.

    Thanks!

    ReplyReply

    jcgarciam

    31 Dec 09 at 10:54 am

  11. @jcgarciam: Great question.

    The simple answer is Grape will automatically download the dependencies for you and store them in your ~/.groovy/grapes directory. If you were to run the above code your grapes directory would consist of two directories, javax.activation and javax.mail, and the dependent jar’s would be stored there. Once the dependent jars are downloaded, Grape will automatically add them to the runtime classpath since it knows where the jar files are located.

    The more complex answer is you can configure Grape to leverage existing dependency management tools such as Maven by updating your ~/.groovy/grapeConfig.xml to point to your existing repository. Details on how to configure Grape to work in this fashion can be found here.

    ReplyReply

    Steve

    31 Dec 09 at 12:47 pm

  12. “Once the groovy runtime comes across the Grape annotations, it loads the dependencies on demand freeing the Groovy script from having to be wrapped with a dependency management layer.”

    So I take it that groovy Grapes is compatible with typical Maven-related artifact repositories, such as Sonatype Nexus or JFrog Artifactory?

    Does that imply then that there is a one-time configuration of some setup file for the Groovy install so that remote artifact repositories can be referenced? Does it happen to also work with a local Maven repository as stored under the user’s .m2 directory subtree? (Such Maven friendliness for dependency management would be truly awesome.)

    ReplyReply

    RogerV

    31 Dec 09 at 1:09 pm

  13. Looks like some of my question got answered as I was off typing it up and posting it. Thanks, Steve. :-)

    ReplyReply

    RogerV

    31 Dec 09 at 1:13 pm

  14. @Steve, thanks for replying.

    While im seeing how useful this could be(indeed), it worries me how this functionality could lead to a disorganized dependency spider web spread all over the place when having a medium sized project (with few developers) and not having an specific place to set all dependencies at once.

    IMHO we may need to just educate the team where to place them and not leave the developers going wild declaring @Grapes all over the place.

    ReplyReply

    jcgarciam

    31 Dec 09 at 4:51 pm

  15. The one problem I have with this model of dependency management is that I don’t get intellisense. Not a big deal for such nice functionality.

    ReplyReply

    Kris

    31 Dec 09 at 8:35 pm

  16. Groovy isn’t on par because it still lacks good calls to the native OS command line. Can’t do “cd /opt/tomcat;./catalina.sh run”.execute()

    I don’t understand why Groovy doesn’t steal JRuby’s native code for doing command line calls.

    ReplyReply

    phil swenson

    3 Jan 10 at 10:02 am

  17. This post was Twitted by me

    ReplyReply
  18. me also twitted it. nice thanks

    ReplyReply

Leave a Reply