So HLS recently gave Groovy a shot by trying it out with the brand-spanking-new Tapestry 5, and the results were relatively promising. Then Marc Guillemot, of HtmlUnit and Canoo WebTest fame, started trying to get me to buy Dierk’s recently-published Groovy In Action
I had considered using Groovy to reduce the LOC in the domain objects for one of my side projects, but I’m using JPA annotations as part of my persistence configuration. As of this writing, only the most bleeding-edge Groovy bits will support annotations, so I decided it wasn’t worth pursuing. However, I recently realized that this particular project is going to require integration with different legacy database systems, which means I’d probably be better off doing my mapping in hbm.xml files.
Now that it’s highly unlikely that I will be using JPA annotations to map my domain objects to the backing database, I decided that it was time to give Groovy a try, and see if I could get rid of all the boilerplate getters and setters in my domain objects. I chose one of the simpler domain objects for the trial. It contained a little over 150 LOC, comprising 7 properties, overridden equals() / hashCode() / toString(), and a couple of trivial convenience methods.
Initial setup consisted of installing the Groovy Eclipse Plugin and took about 1 minute. My initial experience was similar to Howard’s — everything just worked. I immediately halved the number of lines in the class, since I could get rid of all my getters and setters. I should mention that before moving this particular domain object over into Groovy, I wrote extensive unit tests for it. I just can’t get over the feeling that giving up all the compile-time checks that Java has to offer somehow puts me on shaky ground.
After making sure that my unit tests still passed, I tried to figure out if there was anywhere else I could cut down on the LOC. I settled on the equals() and hashCode() methods, and decided that surely there must be a way in Groovy to declaratively express the fields that should be used as a bean’s identity. Unfortunately, the only thing I was able to find was GROOVY-27, an open enhancement wish filed four years ago. Eventually I had a pass at writing a GroovyBean superclass with generic equals() and hashCode() methods relying on an abstract getIdentity() method, which returns a list of the values comprising the bean’s identity. I could have done this in Java, and it was 5 times slower than the older implementation (which was already relatively slow due to the use of EqualsBuilder and HashCodeBuilder), but it reduced my LOC to 35. Plus, the root of all evil is premature optimization, right?
Up to this point everything had gone relatively smoothly, but unfortunately what lay ahead was frustration upon frustration. First, in implementing the equals() and hashCode() methods as described above, I naively decided to use Groovy’s cool closure support for looping. Bad decision. Not surprisingly, there’s a lot of magic going on under the covers to get closures to work, and I got ClassNotFoundExceptions, ClassCastExceptions and StackOverflowExceptions until I got rid of the closures. Apparently some of the magic involves these essential methods, and Groovy is not happy to be using magic at this basic level.
Second, I’m using Maven 2 to manage this project, and apparently most Groovy enthusiasts use Ant. The result? Second-class support for Maven. Supposedly there’s a Maven plugin out there somewhere that worked every third Tuesday in 2004, but the “standard” way of integrating Maven 2 and Groovy is via Groovy’s ant task. It was painful enough to see the 100 lines of Java code I had just annihilated resurrected as ghastly XML code in my pom.xml file, but unfortunately the Groovy compilation ends up occurring after the standard Java compilation. This would be OK if I were writing Groovy code that never got used by Java code, but unfortunately, domain objects are the bedrock of any application and are used everywhere else. I suppose I could bind the Groovy compilation to some other build lifecycle phase which occurs before the standard compile phase, or put all my Groovy files in a sub-module referenced by the main project, or any number of other hacks, but the point is that I don’t want to spend my time fighting Groovy so that I can be three times as productive writing my domain objects.
Finally, there is the issue of the Groovy Eclipse plugin itself. It works well out of the box, but there is some room for improvement. For example, why must it compile into the bin-groovy directory? Why can’t it just use the project’s default output folder? Also, while you can right-click on a project and choose “Add Groovy Nature”, there’s not an option to “Remove Groovy Nature”. I’m also slightly OCD when it comes to code formatting, and it’s nice to be able to auto-format my code. Allowing the IDE to remove those trailing tabs for me probably doubles my productivity. However, the Groovy editor can’t auto-format.
All in all, aside from the undocumented no-closures-in-hashCode-or-equals issue, I found Groovy to be a well-polished alternative to Java on the JVM. As I mentioned earlier, the LOC in my domain objects was cut in half, and a declarative identity feature for beans would be absolutely killer. However, when you consider the need to interact with the entire Java ecosystem, in this case Eclipse and Maven 2, it’s obvious that Groovy could use some lovin’. A solid Maven 2 plugin that doesn’t require 50 lines of XML (convention over configuration, baby!) and some polish on the Eclipse plugin would go a long way to making a la carte Groovy use possible. As things stand, however, I’m going to have to continue to writing my domain objects in Java.
UPDATE: Apparently the JRuby guys are working on IDE integration and a Maven 2 plugin…
Guillaume Laforge said,
March 18, 2007 at 5:45 am
Hi Daniel,
Thanks a lot for this great and detailed feedback.
IDE integration is progressing nicely for Groovy as there are now 4-5 persons working on the Eclipse plugin, and I’m sure they’d be happy to hear about your feedback. And JetBrains is investigating to provide full support for Groovy in IntelliJ IDEA. So on the IDE side of things, you should see some interesting things coming in the following months.
Regarding the Maven 2 plugin, there’s also a Maven 2 expert working on the topic (http://mojo.codehaus.org/groovy-maven-plugin/) so you could also provide your feedback there or on our mailing-lists to see what you need for your projects.
Now that we have a nice and stable 1.0 of Groovy, we’re focusing a lot on tooling support. Stay tuned!
Slava Pestov said,
March 19, 2007 at 3:55 pm
ClassCastExceptions, StackOverflowErrors, 7 times slower, 100 lines of XML… looks like Groovy has all the check list features one expects from an ‘open sores’ Java project. This will surely be a hit in the ‘enterprise’ market, guys.
Daniel Gredler said,
March 19, 2007 at 7:01 pm
That’s a bit of a mischaracterization, Slava. Granted there’s some sort of trouble using closures in specific places. However, the speed hit was the result of my choice of a generic algorithm. It would have been just as slow in pure Java. The 100 lines of XML did indeed suck, but apparently a new Maven 2 plugin was released just a couple of days ago. According to the docs, all that’s needed now is the standard minimal XML required for any Maven plugin.
Robert said,
March 20, 2007 at 12:14 pm
Slava likes to lash out against anything Java or not his language of choice.
Mark Menard said,
March 20, 2007 at 7:31 pm
Daniel,
You’ve experienced some of the things I have getting started with Groovy. I haven’t run into the closure issue on hashCode and equals. (Don’t ask me why, ok?)
I understand your issue with Java code depending on Groovy code. The compilation order is an issue. There are some methods of working with it though.
1. Interfaces: Have your domain entities implement interfaces that are written in Java. Then your domain entity clients can just depend on the interfaces.
2. Use a maven sub-project for your domain model. Then you can just code away in Groovy on the domain mode, and package it into a jar file. Your other code can depend on that artifact. This would overcome the compilation order issue, and at the same time make your domain model a releasable artifact.
I’m in the process of implementing a project using Groovy for my Struts 2 actions, service beans, and DAO’s. I haven’t done my domain model, because I implemented it using JPA annotations. It would be a lot of work to redo in XML files.
My experience with Groovy has been really good to date. So much so that I made a Struts 2 plugin to instantiate my action classes on the fly, with reload on change to the source file. I’ve also started using Spring reloadable scripted beans too. This allows me to set up my interfaces for a process in Java, then code away in Groovy without needing to restart my application server. (Details on my site.)
As to the Maven and plugin to run groovyc… is your config really 100 lines? Mine is 41 lines, and that has two life cycle hooks in it, to compile and test-compile.
Take care,
Mark
Karik said,
June 14, 2011 at 9:22 pm
Glad I\’ve fialnly found something I agree with!
Daniel Gredler said,
March 20, 2007 at 7:55 pm
Mark,
Thanks for the info, it sounds like your Groovy experiment is going well. One of the commenters to HLS’s post linked above (relating to Tapestry 5 and Groovy) also mentioned using Groovy with Struts 2. Do you know if this is a common choice?
I may give Groovy another try in the near future, with tempered expectations as to interoperability within a single project module. The Spring support for Groovy looks very nice, but I’m not sure if I’m ready to implement my service or DAO layers with Groovy yet
As far as the XML, I estimated it at 50 in my last paragraph, so the 41 may be accurate. However, if you are in fact using the Ant task via Maven, as I was, I’d encourage you to check out the new Maven 2 plugin, which looks much less verbose and was just released a couple of days ago.
Cliff said,
March 26, 2007 at 8:39 am
Daniel,
I feel your pain regarding the M2 stuff. Incidentally I did manage to post an article on my blog regarding the old Groovy plugin for M2 shortly after I wrote the M2-Ant-Groovy-compile docs you linked to above. That support worked relatively well until I hit a snag myself. I’m happy to be bailed out with the more recent Groovy M2 plugin submitted by Jason. Good luck getting your groove on and keep spreading the good news! By the way, even though JRuby is planning M2 plugins and IDE integration I’d still advocate the Groovy stuff because it just fits the Java ecosystem better overall.
Daniel Gredler said,
March 26, 2007 at 11:32 am
I agree with the assertion that Groovy enjoys a slight advantage over JRuby in terms of ecosystem integration, given that JRuby needs to remain compatible with C Ruby. However, only time will tell how large that advantage actually is, or whether it will tip the scales one way or another for us users.