Wednesday, July 18, 2007

Method missing in Groovy - Part 2

In my previous post I showed how to use invokeMethod to provide "method missing" behaviour in Groovy. Well, if you're prepared to live on the cutting edge and install the latest Groovy 1.1 beta 3 code from SVN then you can try out Groovy's new "method missing" feature that will be more familiar to Ruby programmers.

This came about after I had a productive discussion with Charles Nutter of JRuby, and in the spirit of cross pollination here is the example from my previous post using the new "method missing" feature of the upcoming Groovy 1.1 release:


1 def dynamicMethods = [...]
2 Book.metaClass.'static'.methodMissing = { String methodName, args ->
3 StaticMethodInvocation method =
4 dynamicMethods.find { it.isMethodMatch(methodName) }
5 if(method) {
6 Book.metaClass.'static'."$methodName" = { Object[] varArgs ->
7 method.invoke(Book.class, methodName, varArgs)
8 }
9 result = method.invoke(Book.class, methodName, args)
10 }
11 else {
12 throw new MissingMethodException(methodName, Book.class, args)
13 }
14 result
15 }


So what is the difference between this and invokeMethod? Well invokeMethod will deal with every method call and hence has a certain amount of overhead. The behaviour of invokeMethod is more useful for AOP type use cases where you need to intercept a method call and wrap behaviour around the invocation.

The "method missing" approach will only take effect when a method is not found to invoke using the normal runtime and hence has no additional overhead plus the code is a bit more concise. Thanks for the help Charles! ;-)

Monday, July 16, 2007

Dealing with method missing with Groovy's MetaClass system

One of the new features coming up in Groovy 1.1 to be released later this year is ExpandoMetaClass. Its an elegant API to programatically extend a class' functionality with Groovy's Meta Object Protocol (MOP).

An example of how we take advantage of this is Grails' dynamic finders in GORM. Dynamic finders allow you to do things like Book.findByTitleAndAuthor("It", "Stephen King"). So how are these implemented in Grails?

First we defined an interface that allows the matching of a method signature to a given method pattern. That interface goes something like this:

interface StaticMethodInvocation {
boolean isMethodMatch(String methodName)
Object invoke(Class theClass, String methodName, Object[] args)
}

The implementation of this interface just uses regular expressions to match the method signature. Simple enough. So how do we use this from the MetaClass itself? Well, this is where the magic of ExpandoMetaClass comes in as it allows us to override a static "invokeMethod" in Groovy:

1 def dynamicMethods = [...]
2 Book.metaClass.'static'.invokeMethod = { String methodName, args ->
3 def metaMethod = Book.metaClass.getStaticMetaMethod(methodName, args)
4 def result
5 if(metaMethod) {
6 result = metaMethod.invoke(dc.clazz, args)
7 }
8 else {
9 StaticMethodInvocation method =
10 dynamicMethods.find { it.isMethodMatch(methodName) }
11 if(method) {
12 Book.metaClass.'static'."$methodName" = { Object[] varArgs ->
13 method.invoke(Book.class, methodName, varArgs)
14 }
15 result = method.invoke(Book.class, methodName, args)
16 }
17 else {
18 throw new MissingMethodException(methodName, Book.class, args)
19 }
20 }
21 result
22 }

So what is actually happening here? First we look to see if the method already exists
on the line:

3 def metaMethod = Book.metaClass.getStaticMetaMethod(methodName, args)

If it does we simply invoke the method:

6 result = metaMethod.invoke(dc.clazz, args)

Otherwise we attempt to find a method that matches the method signature using a previously defined list of StaticMethodInvocation instances:

9 StaticMethodInvocation method =
10 dynamicMethods.find { it.isMethodMatch(methodName) }

If the method exists we dynamically register a new method on the MetaClass so that the next time the method is invoked it doesn't have to go through the matching process and will simply dispatch like normal:

12 Book.metaClass.'static'."$methodName" = { Object[] varArgs ->
13 method.invoke(dc.clazz, methodName, varArgs)
14 }

Finally, we invoke the method itself, or if the method isn't matched we throw a MethodMissingException:

15 result = method.invoke(dc.clazz, methodName, args)

Job done. As simple as that ;-)

I'm doing a talk at the Grails eXchange about the Grails plug-in system and how we dynamically extend the behaviour of classes. See you there!

Friday, July 06, 2007

Groovy 1.1 beta 2 out. Configuration made easy with ConfigSlurper

So we've put out the 1.1 beta 2 release of Groovy, which is a really significant milestone for the project. Guillaume has the lowdown of all the new features in his blog.

One of the things I worked on for this release was a new utility class called ConfigSlurper. It allows you to write configuration files as Groovy scripts in a Java properties file like format. What advantage does this give you?

1) You get to take advantage of native Java types and not do type conversion from properties
2) You can use "global variables" and write more DRY configurations
3) You can take advantage of Groovy's advanced syntax for lists and maps to make config easier

Here is an example of configuring log4j with a ConfigSlurper script:


log4j.appender.stdout = "org.apache.log4j.ConsoleAppender"
log4j.appender."stdout.layout"="org.apache.log4j.PatternLayout"
log4j.rootLogger="error,stdout"
log4j.logger.org.springframework="info,stdout"
log4j.additivity.org.springframework=false


Not that disimilar from standard Java properties files and you can convert to and from Java properties files, merge configurations and serialize them back to disk. To use this this script you can use the ConfigSlurper class to parse it:


def config = new ConfigSlurper().parse(new File('myconfig.groovy').toURL())

assert "info,stdout" == config.log4j.logger.org.springframework
assert false == config.log4j.additivity.org.springframework


We're using this as our primary means to deal with necessary configuration (not possible by convention in Grails. Checkout some more examples of its usage here.

Getting JetGroovy up and running with IntelliJ IDEA

I've blogged about it before, but I can't express how impressed I am by the work JetBrains have done on their Groovy plugin. Code completion, CTRL+click navigation from Groovy to Java and Java to Groovy, some refactoring support, joint compilation of Groovy and Java. JetGroovy really makes mixing Groovy & Java in the same codebase completely seamless.

How do you get setup? Follow these steps:

1) Download and install the IDEA 7.0 EAP from here
2) Download a stable binary of JetGroovy from here
3) Unzip the zip file into the IDEA_HOME/plugins directory
4) Start IDEA
5) Open up the JetGroovy preferences dialog in Settings/Groovy&Grails and point IDEA to your Groovy & Grails installs
6) You're done

Make sure you checkout the feature tour here. If you're not a IntelliJ user, don't forget their is also a lot of excellent work happening around the Eclipse plugin. Enjoy!

Thursday, July 05, 2007

5 More Misconceptions About Grails

Update: InfoQ have featured the two article's in a Grails Misconceptions post. Discuss!

Grails committer Marc Palmer posted a nice entry expressing 10 common misconceptions about Grails and what it is about.

This was linked to from another interesting post entitled Blasphemy: The case against Rails, which highlights the strength of Java and the how "Groovy/Grails is proving itself to be a formidable challenger".

An interesting aspect of the above post however, was the reaction of Ruby/Rails users to the outrageous comment that Grails is a more realistic alternative in the enterprise. Some of the comments including even more classic misconceptions and knee-jerk reactions which I will address in this post. My 5 misconceptions are a bit more long-winded than Marc's, but I think it is needed to address each one completely:

1) "Who needs Grails when we have JRuby on Rails?"

This is a classic and is the foundations for one of the biggest misconceptions about what Grails is. JRuby on Rails is an excellent way to run Rails apps on a Java EE container like GlassFish. End of story. Grails has very different goals. It is not a port of Rails to the Groovy language. It is the act of bringing together solid industrial strength components like Spring, Hibernate, Quartz, Compass, Sitemesh etc. and making them DRY by embracing convention-over-configuration.

We're not re-inventing the wheel and because the vast majority of the core of Grails is Java it is more robust and performant. Grails is actually a Spring MVC application at its core and is deployable onto all major containers, not just Glassfish, including the big commercial ones such as WebLogic, WebSphere and Oracle AS.

2) "Why use Groovy 'the half-way house' instead of just going for Ruby?"

This is another one that is often quoted all over the place. Why bother choosing Groovy when Ruby has the same features (some argue more)? Well this one misses the aims and goals of Groovy completely. Groovy IS NOT intended as a replacement for Java. Groovy (and Grails) are designed to be used symbiotically with Java.

Java is a great programming language for many general tasks, don't let the zealots persuade you otherwise. In the same sense Groovy is excellent at what its good at. For example Java is great for writing complex business logic or low-level plumbing code. Groovy is better at a higher level. Groovy and Java are meant to be used together.

This is starting to become more clear with the development of Groovy IDE support. The JetGroovy plug-in from IntelliJ allows you to have circular references between Groovy and Java code and ctrl-click navigate from Groovy classes/methods to Java and vica versa. It is awesome stuff. Groovy developers use Groovy because we love Java, the language, the libraries and the platform.

3) "Why is Grails more suitable than Rails for the enterprise?"

A number of reasons. Two of the biggest ones are Spring & Hibernate. As it stands to day a enormous number of organisations are using Spring & Hibernate. They have existing Spring context, existing Hibernate domain models, and so on.

I was in this same situation before I started working in Grails. Grails is designed to integrate with these frameworks as seamlessly as possible. So for example you can drop a Hibernate domain model written in Java and mapping files into a Grails app and start using dynamic finders and GORM straight away.

In addition Grails controllers use standard Servlet API objects like request, response, session etc. and can sit alongside other servlets. It is after all just a Spring MVC application under the covers. Rails on the other hand is designed almost in a way EJB2 was designed (shock, horror bear with me while I qualify this). In other words you extend framework objects like ActiveController, ActiveRecord etc. which bind you to the framework.

There is also no such thing as a domain model in Rails. Rails models are database tables. This is all well and good, but in enterprises the same domain model is often re-used in multiple applications both desktop and web. This is effectively accomplished in Java by packaging the classes with the mapping files in a JAR.

4) "Groovy/Grails are not a credible competitor to Ruby/Rails"

This one is entertaining. Often the credibility of Grails is questioned because it is newer and has a smaller userbase. However my view on this takes me back to the foundations of Grails. Grails is built on very credible technologies. This point was also addressed nicely in Marc's post.

The other area to consider is the arenas that are being competed in. Grails has two goals primary goals. The first goal is to create a web application framework that is easy, elegant and DRY without sacrificing the platform, technologies and tools on which the Java community is built on. A framework that is suitable for enterprise scenarios (see above). DHH was quite happy to dismiss the importance of the enterprise, we're not.

The second goal is to lower the barrier of entry for Java web development. Make Java web development as easy as Ruby/Rails development. Now these are generally conflicting goals, but on the whole we've done a pretty good job at meeting both requirements up until now. Whether Grails will receive more adoption in one man shops and small companies is up for debate, personally I see it being adopted more in larger organisations.

5) "Why bother with Groovy when Ruby is backed by Sun?"

Sun were kind enough to lend a hand to the JRuby project in their hour of need and should be applauded for doing so. However my standard answer to this one is the best things rarely come directly from Sun (Spring, Hibernate etc. etc.).