Combining Groovy and Java

Combining Groovy and Java

Everybody refactors (I hope). But what if your standard refactoring just isn’t good enough? Take the next step in refactoring into Groovy code and see how easy it is to integrate Groovy into your existing Java projects.

The challenge

While working on a project lately, I encountered (something like) the following code:

// The result list 
ArrayList result = new ArrayList(); 
// First, we create a document based on a URL 
URL url = new URL(configLocation); 
URLConnection connection = url.getConnection();
InputStream in = connection.getInputStream(); 
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
factory.setValidating(false); 
DocumentBuilder builder = factory.newDocumentBuilder(); 
Document document = builder.parse(inputStream); 
// ..  then, do some XPath queries 
XPathFactory xPathFactory = XPathFactory.newInstance(); 
XPath xpath = xPathFactory.newXPath(); 
NodeList nodeList = (NodeList) xpath.evaluate("/user/address", document, XPathConstants.NODESET); 
// And loop through the results 
for (int i = 0; i < nodeList.getLength(); i++) { 
    final Node node = nodeList.item(i); 
    XPath xpath = XPathFactory.newInstance().newXPath(); 
    // Read parameters from XML file using XPath 
    String name = (String) xpath.evaluate("name", node, XPathConstants.STRING); 
    result.add(name); 
}

First, this code tries to read from a URL to retrieve an XML file. Then it tries to execute some XPath queries to retrieve a NodeList, and for each item in the NodeList it adds an item to a result List. I omitted the error handling, because that would make the code example even longer.

As you might agree, the above code is quite hard to read, and gives a lot of overhead in trying to accomplish the task at hand.

While this code could probably be optimized and shorted considerably, I thought this code was ugly enough to be replaced completely. I considered my options, and I decided it would be a nice exercise for using a bit of Groovy to do the XML parsing for me, and replace the current Java code by Groovy code while maintaining the same level of functionality.

For those totally unfamiliar with Groovy, I suggest checking the Groovy website. It contains a lot of documentation and code examples. In short, Groovy is a Dynamic Scripting Language. It’s not a port, like JRuby, but has been specifically designed for working with Java.

To replace the code, I had to make sure it would:

  1. Provide the same level of functionality afterward
  2. Have less overhead
  3. Would integrate into our build system (Maven 2)

The solution

So, let’s begin at point 1. We need a bit of Groovy code to load a url, parse some XML, and build some Java objects with it. The following code does just that:

package groovy 

import com.xebia.domain.Address 
import com.xebia.domain.AddressDownloader 

class GroovyAddressDownloader implements AddressDownloader { 
    // implement the method just like in java 
    public List download() { 
        // define the location of the source url 
        def url = "http://www.foobar.com/address.xml" 
        // use the automatically imported groovy.util.XmlParser 
        // to parse the source 
        def xmlFeed = new XmlParser().parse(url); 
        // create a new list 
        def list = [] 
        // loop through all the xml elements using dynamic methods 
        xmlFeed.address.each {item -> 
            // retrieve the content of the root/address/name element 
            def value = item.name.text() 
            // create a new address using a dynamic constructor 
            // and add it to the list 
            list << new Address(name: value); 
        } 
        return list 
    } 
}

As you can see, even with the above example consisting of 50% comments, it’s still shorter and more complete than the Java version. The Groovy code is very consistent and provides almost no overhead in parsing the XML. The above example uses quite some Groovy magic, like closures, dynamic methods, shorthand method, etc.

Getting it to work

When I type the following in my favorite IDE:

import groovy.GroovyAddressDownloader; 
AddressDownloader addressDownloader = new GroovyAddressDownloader();
List addresses = addressDownloader.download();

And press CTRL+SHIFT+F10 (Run), it works! IntelliJ 7.0 supports Groovy (and Grails) out of the box, and does a very good job at providing a lot of the functionality Java developers are used to these days (syntax highlighting, code completion, refactoring, etc).

Integrating it with Maven

But we’re not there yet! We don’t use IntelliJ as our build, but we use Maven 2. Fortunately for us, a the beginning of December a new version of the Groovy Maven Plugin was released. This plugin makes it easy to compile Java, Groovy or a combination of both in one project. All we need to do for this is edit our Maven pom.xml to include the Groovy plugin, and change the settings.xml to include the codehaus repository.

The following lines of XML need to be added to the pom.xml:

 
	org.codehaus.mojo.groovy 
	groovy-maven-plugin 
	 
		 
			 
				generateStubs 
				compile 
			 
		 
	 

A small explanation from the Groovy Maven Plugin:

It works by adding an extra goal execution to generate stubs during the generate-sources and generate-test-sources phases. These stubs are created from your Groovy source files and translated into minimal Java sources (containing JavaDoc, classes, fields, methods, etc. but omitting the implementation details. This allows Maven's maven-compiler-plugin to be invoked as normal to compile Java sources and the Groovy Java stubs, thus allowing the compilation to resolve classes. After the normal Java compile, the Groovy compiler kicks in and then re-generates the real classes into places and everything is happy happy. In short: this makes sure your Java code can use the Groovy code, and vice verse.

After adding the final lines of XML to our settings (found in your home directory/.m2):


    < profiles>
        < profile>
            codehaus
            
                
                    Codehaus
                    http://repository.codehaus.org/
                    
                        false
                    
                    
                        true
                    
                
            
            < pluginRepositories>
                < pluginRepository>
                    Codehaus
                    http://repository.codehaus.org/
                    
                        false
                    
                    
                        true
                    
                
            
        
    
    
        Codehaus
    

You should be all set to go. Running mvn install should compile both your Java as well as your Groovy classes, and if everything went allright, you should see something like this in the Maven output:

[INFO] [groovy:generateStubs {execution: default}]
[INFO] Generated 1 Java stub
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to D:\Projects\GroovyDemot\target\classes
[INFO] [groovy:compile {execution: default}]
[INFO] Compiled 2 Groovy source files
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.

Conclusion

It isn't hard to get Groovy working in combination with your existing Java project. So download Groovy, read the docs and have a go at it!

Comments (7)

  1. Lars Vonk - Reply

    December 27, 2007 at 11:34 am

    Really nice one Erik. What criteria would you use to introduce groovy instead of a "plain" java refactoring?

  2. Guillaume Laforge - Reply

    December 27, 2007 at 5:20 pm

    I think the criteria varies from one project to another, so there's no single rule to apply. But, basically, whenever Groovy's APIs or syntax makes dealing with a certain task easier, and makes the code more readable, then it probably makes sense to apply this Groovy refactoring. Groovy's goal has always been to simplify the life of the developers (especially the Java developers), so we've tried hard to simplify the usage of common APIs such as XML parsing, our unit testing, JDBC handling, etc. Whenever such tasks arise, it's very easy to switch to Groovy which offers nice wrapper APIs and a neater syntax. That's probably one of the most common scenario where Groovy is introduced in a Java project.

    Beyond this, there are other areas where Groovy makes a lot of sense, for instance for your business logic. With Groovy's concise and malleable syntax, with its meta-programming capabilities, you can derive your own business languages out of Groovy's own grammar. You create a language which is much easier to understand and read by the developers, and business domain experts can even understand it as well -- that's what we've experienced on some projects.

    Groovy is a nice "refactoring" in several opportunities, but it really depends on your project, its goals, its audience, its technical aspects, etc.

  3. John Wilson - Reply

    January 6, 2008 at 12:50 pm

    Excellent example!

    How about using collect rather than each?

  4. Erik Pragt - Reply

    January 6, 2008 at 11:47 pm

    Hi John,

    Thanks for the compliment. You are right, collect might be better in this case, but I thought .each was more readable in this case.

    When using \'collect\', the example would look like this:

    def url = \"http://www.foobar.com/address.xml\"
    def xmlFeed = new XmlParser().parse(url)
    return xmlFeed.address.collect {  new Address(name: it.name.text()) }
    

    Which nicely transforms an 16 line Java code program into a 3 line Groovy code sniplet.

    Thanks for the hint!

    Note: When copying this code, please remove the \\ character. WordPress inserts them automatically

  5. Erik Pragt - Reply

    January 7, 2008 at 12:06 am

    Hi Lars,

    Thanks for the compliment. I've been thinking about this, and actually I find it hard to find a reason when not to refactor to Groovy. Actually, I find it hard to find a reason to program in Java instead of Groovy. Maybe this goes for JRuby too.

    When picking a dynamic language instead of Java, you get a lot of extra flexibility and elegance in your code (as you can see above), while maintaining the power of the Java platform, such as your application servers, IDE, runtime enviroment and Java libraries.

    Ofcourse I wouldn't suggest throwing away your existing Java codebase, or plugging in Groovy when all your are refactoring a the names of some properties, but when picking up a new project, I would definetly consider Groovy.

    There is ofcourse a learning curve, but I don't know if learning a (Java) framework like Spring or Hibernate is really harder... Maybe that's just the price of progress?

  6. johaneltes - Reply

    January 8, 2008 at 10:59 pm

    Maybe to Groovy:
    return xmlFeed.address.name*.text().collect {new Address(name: it)}

  7. links for 2008-01-14 - Reply

    January 14, 2008 at 10:30 am

    [...] Xebia Blog Everybody refactors (I hope). But what if your standard refactoring just isn’t good enough? Take the next step in refactoring into Groovy code and see how easy it is to integrate Groovy into your existing Java projects. [...]

Add a Comment