ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Groovy, Java's New Scripting Language
Pages: 1, 2

Groovy Is Not Java

Groovy provides some syntax extensions beyond standard Java. One of the most interesting is what are called closures. This term has been overloaded in a lot of places in computer science, but for now just think of Groovy closures as being similar to anonymous inner classes. One difference is that variables can be passed in or out; Groovy closures do not constitute a "lexical scope" the way an inner class does. The example below, closures.groovy, shows how to break a String into a List and print it one word per line, both the "long way" and using closures.



Example 4. closures.groovy

# without closures
x = "When in the course of human events ...".tokenize()
for (val in x) println val

# with closures
"When in the course of human events ...".tokenize().each { val | println val }

# with closures, default parameter name
"When in the course of human events ...".tokenize().each { println it }

The closure code is used to print each value of the list. In the first version, a parameter, val, is declared (before the |), and used in the body. In the second version, I use Groovy's default parameter name it (presumably short for "item").

Another interesting syntax enhancement is the use of variable substitution within strings. Almost any String assignment can contain the pattern ${variablename}, and the value of the named variable will be interpolated into the String. This is a much more convenient form than Java's extensive use of the + operator for string concatenation. This can be used in printing, as in this hello.groovy example:

Example 5. hello.groovy and date.groovy

i = 42
println "Hello ${i}"

This prints "Hello 42". The interpolated value can be a property, as shown in date.groovy:

i = new java.util.Date();
println "Today is day ${i.day} of month ${i.month}"

I said that Groovy doesn't bring a lot of new API. But it does offer a lot of convenience methods added onto the standard API. For example, the Groovy version of String adds the tokenize() method used in the closure.groovy example above. The file docs/groovy-jdk.html in the Groovy distribution lists all of the methods added to the JDK classes; it's quite an extensive list, and adds up to a great deal of time saved when using Groovy compared to using "raw Java."

As another example, Groovy's java.io.File class has a newReader method that returns a BufferedReader (and newPrintWriter(), which returns a PrintWriter). java.io.File also has eachLine, which opens the file and reads each line for you; now that's high-level! The example below is wc.groovy, my implementation of wc, a traditional Unix tool that counts the lines, words, and characters in a text file:

Example 6. wc.groovy

# simple implementation of Unix "wc" in groovy.
# could simplify with File.splitEachLine() but this way gets "chars" right.
filename=args[0]
chars=0; lines=0; words=0;
new java.io.File(filename).eachLine {
	chars+=it.length() + 1
	words+=it.tokenize().size();
	lines++;
}
println "\t${lines}\t${words}\t${chars}\t${filename}"

Running the script produces output that is close to the original:

Example 7. wc.groovy versus the original

ian:136$ wc test.txt
       2      25     130 test.txt
ian:137$ groovy wc.groovy test.txt
        2       25      130     test.txt
ian:138$ 

Groovy also adds several convenience methods for lists and maps; some of these are shown in the lists.groovy example below:

Example 8. lists.groovy

# List operations
list = ["ian","brian","brain"]
println list;
println "Maximum value: ${list.max()}"

# inject is like an iterator with carryover of last value
list.inject("foo", { val, elem|println "${val} -- ${elem}"; return elem })
# findAll returns all the elements for which the closure returns true.
println "findAll: "+list.findAll({it.contains("ian")})

# Ruby-style ranges, +- operators, etc.
validCardYears = 2004..2008
validCardYears.each {println "Valid year: ${it}"}
lastValidCardYear = validCardYears[-1]
println "Last valid year is currently ${lastValidCardYear}"

Running this catch-all script produces the following output.

groovy src/lists.groovy
[ian, brian, brain]
Maximum value: ian
foo -- ian
ian -- brian
brian -- brain
findAll: [ian, brian]
Valid year: 2004
Valid year: 2005
Valid year: 2006
Valid year: 2007
Valid year: 2008
Last valid year is currently 2008

Finally, just to prove it can be done, I back-ported wc.groovy into Java; the WC.java example gives the right answer, but has roughly twice the amount of code.

Example 9. WC.java

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

/** simple implementation of unix "wc" in Java. */
public class WC {
	public static void main(String[] args) throws IOException {
		int chars=0, lines=0, words=0;
		String filename=args[0];
		BufferedReader r = new BufferedReader(new FileReader(filename));
		String it;
		while ((it = r.readLine()) != null) {
			chars+=it.length() + 1;
			words+=new StringTokenizer(it).countTokens();
			lines++;
		}
		System.out.println("\t" + lines + "\t" + words + 
			"\t" + chars + "\t" + filename);
	}
}

XML, Swing, Builder, and All That

Groovy includes a tree-based markup generator called GroovyMarkup that can be subclassed to make a variety of tree-based object representations, including XML markup, HTML markup, SAX event firing, and even Swing user interfaces! The example below, swingbuilder.groovy, shows a user interface with a variety of labels and text fields built from a list, a pop-up "About" box, and two ways of exiting: a push button or just closing the main window (since defaultCloseOperation is set to EXIT_ON_CLOSE).

Example 10. swingbuilder.groovy

import groovy.swing.SwingBuilder;

theMap = ["color":"green", "object":"pencil", "location":"home"];

// create a JFrame with a label and text field for each key in the Map
swing = new SwingBuilder();
frame = swing.frame(title:'A Groovy Swing', location:[240,240],
	defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE) {
	panel() {
		for (entry in theMap) {
			label(text:entry.key)
			textField(text:entry.value)
		}
		button(text:'About', actionPerformed:{ 
			pane = swing.optionPane(message:'SwingBuilder Demo v0.0')
			dialog = pane.createDialog(null, 'About')
			dialog.show()
		})
		button(text:'Quit', actionPerformed:{ System.exit(0) });
	}
}
frame.pack();
frame.show();

There are no actions yet for the text fields, so changing them doesn't actually do anything. A more elaborate Swing demo, and other demos, can be found at the Groovy web site.

Here's how it looks in action:

swing demo

Servlet Support

Another interesting API that Groovy supports is the J2EE servlet API. Like standard JSPs, GroovyServlet provides an out object that is used here. The handleform.groovy example below is a simple GroovyServlet in action that receives the content of a simple HTML form (with name, address, phone number, and the like) and stores the fields' contents into a JavaBean, which is then persisted using an already written data accessor.

Example 11. handleform.groovy

import java.util.Date
import beans.Person
import beans.PersonDAO

bean = new Person();

bean.firstName = ${firstName}
// etc for other properties

out.println(<<<EOS
<html>
<head>
	<title>Thanks for Signing Up Via the Groovy Servlet</title>
</head>
<body>
The Groovy Servlet thanks you, 
${firstName} at ${request.remoteHost}, 
for signing up with us at ${new Date()}.
<hr/>
</body>
</html>
EOS)
out.println("Bean = " + bean);
new PersonDAO().insert(bean);
out.println("<hr>");
out.println("(database updated)");

This example also shows use of the here document, an idea borrowed from the Bourne shell and its many derivatives on Unix. Everything from the line following the <<< up to the string that ends that line is copied, in this case to the argument of the out.println(), which causes the whole mess of HTML--after the substitution of ${firstName} and so on--back to the user's browser.

GroovyServlet does not pretend to be a full MVC framework like Struts or JSF, but it is good for some servlet tasks. To make the above Groovy script, one only needs a servlet and servlet-mapping elements in the web.xml file, and two .jar files, groovy.jar and asm.jar, from the distribution.

Read more about GroovyServlet at the Groovy Servlets page.

Mix and Match

How about using Groovy from within Java? There are several ways.

You can use groovyc just like javac to produce bytecode files. Suppose I have this complicated Groovy script, g.groovy:

Example 12. g.groovy

i = 42
println "Hello ${i}"

The command to run this as a script is groovy g.groovy, which produces this output:

Hello 42

Now to compile it to bytecode, I just say groovyc g.groovy. Then I can call it from within a Java class. For example:

public class j {
	public static void main(String args[]) {
		new g().run();
	}
}

Then I can compile and run class j using normal javac and java, remembering to include the Groovy .jar and its helper, asm.jar, in my classpath (I know there are better ways of doing so than shown, but I chose the most transparent one here for readers trying it out by hand):

$ javac -classpath /home/ian/groovy/lib/groovy-1.0-beta-6.jar:/home/ian/groovy/lib/asm-1.4.1.jar:. j.java
$ java -classpath /home/ian/groovy/lib/groovy-1.0-beta-6.jar:/home/ian/groovy/lib/asm-1.4.1.jar:. j
Hello 42
$ 

What's Next?

There is far more to Groovy than I have room to explain in this article. If you're a Java developer and you're not already a raving fan of Jython, PNuts, or any of the other Java-related scripting languages, I urge you to download Groovy and give it a try. Even if you prefer one of those other scripting languages, check out Groovy--you might like it.

Because Groovy is new, there is still a lot to be done. The Groovy developers are always looking for help.

Groovy needs to do a better job of error handling. The groovysh interpreter prints a caught exception, and the Groovy runtime prints a stack trace. But most exceptions are the result of user error, and it would be good if Groovy could report on such errors a little better.

As Groovy continues to mature, expect to see more and more about it in the Java media, and more developers using it for scripting tasks.

Ian F. Darwin has worked in the computer industry for three decades: with Unix since 1980, Java since 1995, and OpenBSD since 1998. He is the author of two O'Reilly books, Checking C Programs with lint and Java Cookbook, and co-author of Tomcat: The Definitive Guide with Jason Brittain.


In June 2004, O'Reilly Media released Java Cookbook, 2nd Edition.


Return to ONJava.com.