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!