Groovy is an open-source scripting language that is implemented in Java and is tightly integrated with it. It requires Java's JDK 1.4. Groovy adds some features of the Ruby and Python scripting languages to Java. Features of Groovy include dynamic typing, closures, easy object navigation and more compact syntax for working with Lists and Maps. These features and more are described in detail in this article.
Here's a quote from the Groovy web site. "Groovy is designed to help you get things done on the Java platform in a quicker, more concise and fun way - bringing the power of Python and Ruby inside the Java Platform."
Groovy scripts can use any Java classes. They can be compiled to Java bytecode (in .class files) that can be invoked from normal Java classes. The Groovy compiler, groovyc, compiles both Groovy scripts and Java source files, however some Java syntax (such as nested classes) is not supported yet.
In theory, entire applications could be written in Groovy that would have similar performance characteristics to an equivalent Java application. This distinguishes Groovy from other scripting languages such as Ruby, Python, Perl and BeanShell. One of the things that makes Groovy perform more slowly than Java today is that the generated bytecode uses reflection to call constructors and private/protected methods. This will be addressed over upcoming releases.
Groovy was created by James Strachan and Bob McWhirter. James is also involved in the development of many other open source products including Jelly, dom4j, Jaxen, Betwixt and Maven. Bob is the creator of Jaxen and Drools (an open source, object-oriented, Java rules engine).
This article doesn't cover every feature of Groovy, but it covers a large number of them. It assumes that you are familiar enough with Java syntax to be able to compare Java syntax to Groovy syntax.
If we could all agree on what makes a good programming language syntax then we wouldn't need so many of them. Based on the number of programming languages out there, we obviously don't agree. After reading this article, you may decide that you like Java syntax just fine and that Groovy syntax is just too much syntactic sugar for your tastes. If that is your conclusion, I encourage you to investigate BeanShell from Pat Niemeyer at http://www.beanshell.org. It sticks much closer to standard Java syntax. On the other hand, if you prefer the shorter syntax of Groovy then that's just groovy!
To download Groovy, follow these steps.
To install Groovy, follow these steps.
There are four ways to execute Groovy scripts. In all of these cases, the script lines are parsed, converted to Java source and compiled to Java bytecode.
The command groovysh starts an interactive shell where
Groovy statements can be entered.
Enter any number of statements, pressing the enter key after each.
Statements are not evaluated or executed until
the execute command is entered.
The command groovyConsole launches a Swing window.
Enter Groovy statements in the bottom part of the window.
Select Run from the Actions menu to execute them.
Output appears in the top part of the window.
Scripts can be opened and saved using the File menu.
A Groovy script file (typically named with a .groovy file extension)
can be executed using the command
groovy script-name.groovy.
A Groovy script file can be compiled to a Java .class file using
the command groovyc script-name.groovy.
If there are loose statements in the script,
this .class file will contain a main method, so it can be executed as a
Java application using the command java script-name
.
The contents of the main method will be discussed later.
The classpath must contain the
groovy*.jar and asm*.jar files
from the Groovy lib directory.
There’s even a custom Ant task to do this!
The class is org.codehaus.groovy.ant.Groovyc.
Here are some of the key differences between Java and Groovy syntax. Others will be covered later.
java.lang, groovy.lang and
groovy.util are automatically imported.
Types are optional for variables, properties, method/closure parameters and method return types. They take on the type of whatever was last assigned to them. Different types can be used later. Any type can be used, even primitives (through autoboxing). Many type coersions occur automatically when needed such as conversions between these types: String, primitive types (like int) and type wrapper classes (like Integer). This allows primitives to be added to collections.
Groovy adds many methods to standard Java classes such as java.lang.Object and java.lang.String. See http://groovy.codehaus.org/groovy-jdk.html. Those added to Object are covered here. Others will be discussed later.
This returns a string of the form
<class-name@hashcode property-name=property-value ...>
For example, <Car@ef5502 make=Toyota model=Camry>.
These static methods print the toString value of object.
For example, print car or println car.
This provides dynamic method invocation using reflection.
The syntax is object.invokeMethod(method-name, argument-array).
The following example prints the value 4.
s = 'abcabc' // a java.lang.String
method = 'indexOf'
args = ['b', 2]
println s.invokeMethod(method, args)
Literal strings can be surrounded with single or double quotes.
When double quotes are used, they can contain embedded values.
The syntax for an embedded value is ${expression}
which is just like Ruby except $ is used instead of #.
Strings surrounded by double quotes that contain at least one embedded value
are represented by a groovy.lang.GString object.
Other strings are represented by a java.lang.String.
GStrings are automatically coerced to java.lang.String
when needed.
Javadoc for Groovy classes like groovy.lang.GString can be found at http://groovy.codehaus.org/gapi/.
Embedded values are very useful for implementing toString methods. For example,
String toString() { "${name} is ${age} years old." }
Multi-line strings can be created in three ways. The following examples are equivalent. The last example uses what is called a "here-doc". After the three less-than characters, a delimiter string is specified. The string value includes all the characters between the first and second occurrence of the delimiter string. "EOS" (standing for "End Of String") is a common delimiter value, but others can be used.
s = " This string
spans three \"lines\"
and contains two newlines."
s = """ This string
spans three "lines"
and contains two newlines."""
s = <<<EOS
This string
spans three "lines"
and contains two newlines.
EOS
Note that the last newline character in the previous code snippet is NOT retained.
The following methods are added to java.lang.String.
This determines whether a string contains a substring.
'Groovy'.contains('oo') returns true.
This counts occurrences of a substring within a string.
'Groovy Tool'.count('oo') returns 2.
This tokenizes a string using a given delimeter
and returns a list of the tokens.
Specifying the delimiter parameter is optional.
It defaults to whitespace characters.
'apple^banana^grape'.tokenize('^')
returns ['apple', 'banana', 'grape'].
This removes first occurrence of a substring from a string.
'Groovy Tool' - 'oo' returns 'Grvy Tool'.
This repeats a string a given number of times.
'Groovy' * 3 returns 'GroovyGroovyGroovy'.
First, let's review the support for regular expressions in J2SE 1.4. Then we'll discuss how Groovy builds on this.
In J2SE 1.4, regular expressions are supported by classes in
java.util.regex package.
Pattern objects represent a compiled regex.
They are created with Pattern.compile("pattern").
The javadoc for this class describes regular expression syntax.
Matcher objects hold the results of matching a Pattern against a String.
They are created with pattern.matcher("text").
To simply determine if the text matches the pattern,
matcher.matches() is used.
Backslashes in patterns must be escaped with an extra backslash.
Groovy supports regular expressions with three operators.
~"pattern" creates a Pattern object
and is equivalent to Pattern.compile("pattern").
"text" =~ "pattern" creates a Matcher object
and is equivalent to
Pattern.compile("pattern").matcher("text").
"text" ==~ "pattern" -
returns a boolean match indication and is equivalent to
Pattern.compile("pattern").matcher("text").matches().
See the javadoc of Pattern and Matcher for additional methods.
For example,
pattern = "\\d{5}" // matches zip codes (5 digits)
text = "63304" // a zip code
println text ==~ pattern // prints "true"
m = text =~ pattern
println m.matches() // prints "true"
// The next line requires a literal string for the pattern.
// A variable can't used.
p = ~"\\d{5}"
m = p.matcher(text)
println m.matches() // prints "true"
Groovy scripts are source files that typically have a ".groovy" file extension. They can contain (in any order) loose statements, method definitions not associated with a class, and class definitions.
For example,
// These are loose statements.
println 'loose statement'
myMethod 'Mark', 19
println new MyClass(a1:'Running', a2:26.2)
// This is a method definition that
// is not associated with a class.
def myMethod(p1, p2) {
println "myMethod: p1=${p1}, p2=${p2}"
}
// This is a definition of a class that
// has two properties and one method.
class MyClass {
a1; a2
String toString() { "MyClass: a1=${a1}, a2=${a2}" }
}
Method and class definitions do not have to appear before their first use.
Loose methods get compiled to static methods
in the class that corresponds to the script file.
For example, a loose method named foo
in a script called Bar.groovy
will get compiled to a static method named
foo in the class Bar.
Loose statements are collected in a run method
that is invoked by a generated main method
When groovyc is used to compile a script,
the generated class will have a run method
that contains all the loose statements
and a main method that invokes run.
Currently scripts cannot invoke code in other scripts unless they are compiled and imported. This should be fixed soon.
Groovy supports operator overloading for a fixed set of operators. Each operator is mapped to a particular method name. Implementing these methods in your classes allows the corresponding operators to be used with objects from those classes. The methods can be overloaded to work with various parameter types.
a == b maps to a.equals(b)
a != b maps to !a.equals(b)
a === b maps to a == b in Java
a <=> b maps to a.compareTo(b)
a > b maps to a.compareTo(b) > 0
a >= b maps to a.compareTo(b) >= 0
a < b maps to a.compareTo(b) < 0
a <= b maps to a.compareTo(b) <= 0
The comparison operators handle null values and never generate a NullPointerException. Null is treated as less than everything else.
Notice that in Groovy
the == operator is used to test
whether two objects have the same value and
the === operator is used to test
whether they are the same object in memory.
The compareTo method returns an int
less than 0 if a < b,
greater than 0 if a > b,
and 0 if a is equal to b.
a + b maps to a.plus(b)
a - b maps to a.minus(b)
a * b maps to a.multiply(b)
a / b maps to a.divide(b)
a++ and ++a maps to a.increment(b)
a-- and --a maps to a.decrement(b)
a[b] maps to a.get(b)
a[b] = c maps to a.put(b, c)
A closure is a snippet of code that optionally accepts parameters.
Each closure is compiled into a new class that extends
groovy.lang.Closure.
This class has a call method that can be used
to invoke a closure and pass parameters to it.
They can access and modify variables
that are in scope when the closure is created.
Variables created inside a closure are available
in the scope where the closure is invoked.
Closures can be held in variables and passed as parameters to methods.
This is particularly useful in certain
list, map and string methods that are described later.
The syntax for defining a closure is
{ comma-separated-parameter-list | statements }
For example,
closure = { bill, tipPercentage | bill * tipPercentage / 100 }
tip = closure.call(25.19, 15)
tip = closure(25.19, 15) // equivalent to previous line
Passing the wrong number of parameters
results in an IncorrectClosureArgumentException.
The keyword it is used in closures with one parameter.
The parameter list can be omitted and referred to
in statements with it.
For example, the following closures are equivalent.
{ x | println x }
{ println it }
Here's an example of a method that takes a List and a Closure as parameters.
It is written as a "loose method", but it also
could be written as a method of some class.
The method iterates over the List,
invoking the Closure on each item in the List.
It populates a new List with the items for which the closure returns true
and then returns the new List.
Note that Groovy provides find and findAll
methods that can be used instead of this.
def List myFind(List list, Closure closure) {
List newList = []
for (team in list) {
if (closure.call team) newList.add team
}
newList
}
Here's an example of using this method.
class Team { name; wins; losses }
teams = []
teams.add new Team(name:'Rams', wins:12 , losses:4)
teams.add new Team(name:'Raiders', wins:4 , losses:12)
teams.add new Team(name:'Packers', wins:10 , losses:6)
teams.add new Team(name:'49ers', wins:7 , losses:9)
winningTeams = myFind(teams) { it.wins > it.losses }
winningTeams.each { println it.name }
There’s no need to write a method like myFind
since the List class already has a findAll method.
To use it,
winningTeams = teams.findAll { it.wins > it.losses }
Here's an example of a simple Groovy Bean.
class Car {
String make
String model
}
This class declares two properties and no methods.
However, there is a lot going on behind the scenes.
Classes, properties and methods are public by default.
Public and protected properties result in private fields for which
public/protected get and set methods are automatically generated.
These can be overridden to provide custom behavior.
For properties that are explicitly declared to be private,
get and set methods are not generated.
The above Groovy code is equivalent to the following Java code.
public class Car {
private String make;
private String model;
public String getMake() {
return make;
}
public String getModel() {
return model;
}
public void setMake(String make) {
this.make = make;
}
public void setModel(String model) {
this.model = model;
}
}
The classes generated by Groovy Beans extend java.lang.Object
and implement groovy.lang.GroovyObject.
This adds methods the methods
getProperty, setProperty,
getMetaClass, setMetaClass
and invokeMethod.
groovy.lang.MetaClass allows methods to be added at runtime.
Groovy Beans can be created using named parameters. For example, the following code calls the Car no-arg constructor and then a set method for each specified property.
myCar = new Car(make:'Toyota', model:'Camry')
Groovy lists are instances of java.util.ArrayList.
They can be created using a comma-separted list
of values in square brackets.
For example,
cars = [new Car(make:'Honda', model:'Odyssey'),
new Car(make:'Toyota', model:'Camry')]
println cars[1] // refers to Camry
for (car in cars) { println car } // invokes Car toString method
class Car {
make; model
String toString() { "Car: make=${make}, model=${model}" }
}
To locate list items starting from the end of the list, use negative indexes.
Empty lists can be created with []. For example,
cars = []
Items can be added to lists in two ways.
cars.add car
cars << car
Lists can be created from arrays with
array.toList().
Arrays can be created from lists with
list.toArray().
Groovy adds several methods to java.util.List.
This counts the elements in a list that are equal to a given object.
[1, 2, 3, 1].count(1) returns 2.
This creates an immutable copy of a collection
using the static unmodifiableList method
in java.util.Collections. For example,
list = [1, 2, 3].immutable()
list.add 4 // throws java.lang.UnsupportedOperationException
This creates a list containing the common elements of two lists.
[1, 2, 3, 4].intersect([2, 4, 6]) returns [2, 4].
This concatenates list item toString values
with a given string between each.
For example, this places a caret delimiter
between all the strings in a List.
['one', 'two', 'three'].join('^')
returns "one^two^three".
This sorts list elements and creates a new list.
It accepts a java.util.Comparator
or a closure for custom ordering.
fruits = ['kiwi', 'strawberry', 'grape', 'banana']
// The next line returns [banana, grape, kiwi, strawberry].
sortedFruits = fruits.sort()
// The next line returns [kiwi, grape, banana, strawberry].
sortedFruits =
fruits.sort {l, r | return l.length() <=> r.length()}
The last call to sort above is an example of a method
that takes a closure as a parameter.
There are many methods in Groovy that do this.
Groovy Beans can easily be sorted on multiple properties.
Suppose there is a Player bean with properties
name, age and score.
To sort a list of these beans called players
based on age and then score,
players.sort { [it.age, it.score] }
These find the minimum or maximum list item or string character.
They accept a java.util.Comparator
or a closure for custom ordering.
For example, these find the minimum and maximum number in a list.
[5, 9, 1, 6].min() returns 1.
[5, 9, 1, 6].max() returns 9.
This reverses the order of elements in a list or characters in a string.
[1, 2, 3].reverse() returns [3, 2, 1].
Groovy overloads the plus and minus operators
for working with java.util.List objects.
This creates a union of two lists, but duplicates are not removed.
[1, 2, 3] + [2, 3, 4] returns [1, 2, 3, 2, 3, 4].
This removes all elements from the first list that are in the second.
[1, 2, 3, 4] - [2, 4, 6] returns [1, 3].
When the list items are not primitives,
the equals method is used to compare them.
Groovy maps are instances of java.util.HashMap.
They can be created using a comma-separated list
of key/value pairs in square brackets.
The keys and values are separated by a colon.
For example,
players = ['baseball':'Albert Pujols',
'golf':'Tiger Woods']
println players['golf'] // prints Tiger Woods
println players.golf // prints Tiger Woods
for (player in players) {
println "${player.value} plays ${player.key}"
}
// This has the same result as the previous loop.
players.each {player |
println "${player.value} plays ${player.key}"
}
Empty maps can be created with [:]. For example,
players = [:]
The Groovy switch statement takes any kind of object
including Class, List, Range and Pattern.
case statements compare values
using the isCase method.
Many overloaded versions of isCase are provided.
Unless overloaded for specific types, isCase
uses the equals method.
When a case is followed by a class name,
isCase uses instanceof.
The isCase method can be overrriden in your own classes.
Here's an example of a switch statement that operates on many different types of values.
switch (x) {
case 'Mark':
println "got my name"
break
case 3..7:
println 'got a number in the range 3 to 7 inclusive'
break
case ['Moe', 'Larry', 'Curly']:
println 'got a Stooge name'
break
case java.util.Date:
println 'got a Date object'
break
case ~"\\d{5}":
println 'got a zip code'
break
default:
println "got unexpected value ${x}"
}
Ranges are created by using the ".." and "..." operators. Here are some examples.
3..7 creates a range from 3 to 7
3...7 creates a range from 3 to 6
"A".."D" creates a range from "A" to "D"
"A"..."D" creates a range from "A" to "C"
Ranges are represented by objects from classes that
implement the groovy.lang.Range interface
which extends java.util.AbstractList.
A Range is an immutable List.
The Range interface adds getFrom and getTo
methods to get lower and upper values.
Two implementations of the Range interface are provided.
groovy.lang.IntRange is used when bounds are integers.
It adds a contains method to test
whether a value is in the range.
groovy.lang.ObjectRange is used when bounds are any other type.
It also adds a contains method, but it is only useful
when the objects used for the bounds
implement java.lang.Comparable.
Ranges are useful in loops. See the examples in the next section.
Here are six ways to loop through a range of values.
for (i in 1..1000) { println i }
i = 1
while (i <= 1000) { println i; i++ }
(1..1000).each { println it }
1000.times { println it }
// values go from 0 to 999
1.upto(1000) { println it }
1.step(1001, 1) { println it }
// values go from 1 to 1000;
// stopping one before the parameter value
Several List, Map and String methods accept a closure as a parameter.
This iterates through collection items or string characters.
It is an alternative to using java.util.Iterator
that results in more compact code.
For example, to print each number in a List
[5, 9, 1, 6].each {x | println x}
or
[5, 9, 1, 6].each {println it}
This transforms a collection or string into a new one.
For example, to double each number in a List and create a new List
doubles = [5, 9, 1, 6].collect {x | x * 2}
This sets doubles to [10, 18, 2, 12].
This finds the first occurrence of a collection item or string character
that meets some criteria.
For example, to find the first number in a list that is greater than 5
[5, 9, 1, 6].find {x | x > 5} returns 9.
This finds all occurrences of a collection item or string character
that meet some criteria.
For example, to find all the numbers in a list that are greater than 5
[5, 9, 1, 6].findAll {x | x > 5}
returns [9, 6].
This determines whether every collection item or string character
meets some criteria.
For example, to determine whether all the numbers in a List
are less than 7
[5, 9, 1, 6].every {x | x < 7} returns false.
This determines whether any collection item or string character
meets some criteria.
For example, to determine whether any of the numbers in a List
are less than 7
[5, 9, 1, 6].any {x | x < 7} returns true.
This passes a value into the first iteration and passes the result of each iteration into the next one. For example, to find 5 factorial (in an unusual way)
factorial = [2, 3, 4, 5].inject(1) {
prevResult, x | prevResult * x
}
This closure is executed four times.
1) 1 * 2
2) 2 * 3
3) 6 * 4
4) 24 * 5
It returns 120.
The ellipses (...) in the code examples below indicate omitted code.
file = new File('myFile.txt')
file.eachLine { println it }
lineList = file.readLines()
file = new File('myFile.txt')
file.eachByte { println it }
byteList = file.readBytes()
dir = new File('directory-path')
dir.eachFile { file | . . . }
These methods of reading from a Reader or InputStream insure that it gets closed regardless of whether an exception is thrown.
file.withReader { reader | . . . }
reader.withReader { reader | . . . }
inputStream.withStream { is | . . . }
These methods of writing to a Writer or OutputStream insure that it gets closed regardless of whether an exception is thrown.
file.withWriter { writer | . . . }
file.withPrintWriter { pw | . . . }
file.withOutputStream { os | . . . }
writer.withWriter { writer | . . . }
outputStream.withStream { os | . . . }
To append strings
s = 'foo'
s = s << 'bar'
To append to a StringBuffer
sb = new StringBuffer('foo')
sb << 'bar'
To add to lists
colors = ['red', 'green']
colors << 'blue'
To write to the end of streams
w = new File('myFile.txt').newWriter()
w << 'foo' << 'bar'
w.close()
Object graphs can be walked with XPath-like syntax
using the dot (".") operator.
To avoid risk of NullPointerException,
use the "->" operator instead of ".".
For example,
class Team {
String name
Person coach
players = []
}
class Person {
String name
}
p = new Person(name:'Mike Martz')
t = new Team(name:'Rams', coach:p)
// The next line prints the same as team.getCoach().getName().
println "coach = ${t.coach.name}"
t = new Team(name:'Blues')
// The next line returns null,
// it doesn't throw NullPointerException.
println "coach = ${t->coach->name}"
// The next line throws NullPointerException.
println "coach = ${t.coach.name}"
Suppose you want to get a Class object from an object.
In Java this is done with
someObject.getClass().
In Groovy, this is done with
someObject.class.
Suppose you want to get a Class object from a class name.
In both Java and Groovy this is done with
SomeClass.class or
Class.forName("pkg.SomeClass").
To print a list of methods
in the Groovy class GString,
GString.class.methods.each { it.name }
To print a list of method names in
the Java interface java.util.List,
java.util.List.class.methods.each { it.name }
Classes can be written to catch calls to unimplemented methods. For example,
o = new CatchCall()
// The next line prints "unknown method Mark called with [19]".
println o.foo("Mark", 19)
class CatchCall {
invokeMethod(String name, Object args) {
try {
return metaClass.invokeMethod(this, name, args)
} catch (MissingMethodException e) {
// Can insert logic here to deal with certain
// method names and arguments in special ways.
return "unknown method ${name} called with ${args}"
}
}
}
Groovy Markup utilizes the invokeMethod method just described
to catch calls to non-existent methods and convert them to "nodes".
Parameters to the methods are treated as attributes of the nodes.
Closures after the methods are treated as the content of the nodes.
This has many uses including
NodeBuilder)DOMBuilder)SAXBuilder)MarkupBuilder)AntBuilder)SwingBuilder)
In addition, custom builders can be created by
extending the class groovy.util.BuilderSupport.
Here's an example of using MarkupBuilder to generate HTML.
import groovy.xml.MarkupBuilder
mb = new MarkupBuilder()
mb.html() {
head() {
title("This is my title.")
}
body() {
p("This is my paragraph.")
}
}
println mb
This generates the following HTML.
<html>
<head>
<title>This is my title.</title>
</head>
<body>
<p>This is my paragraph.</p>
</body>
</html>
Here's an example of using MarkupBuilder to generate XML.
import groovy.xml.MarkupBuilder;
mb = new MarkupBuilder()
mb.autos() {
auto(year:2001, color:'blue') {
make('Toyota')
model('Camry')
}
}
println mb
This generates the following XML.
<autos>
<auto year='2001' color='blue'>
<make>Toyota</make>
<model>Camry</model>
</auto>
</autos>
Groovy makes JDBC easier.
The class groovy.sql.Sql provides an easy way to
execute a query and iterate through ResultSet rows.
In the following example,
MusicCollection is the name of a database
(in this case, registered as an ODBC data source),
Artists is the name of a table in that database,
and Name is the name of a column in that table.
import groovy.sql.Sql
dbURL = 'jdbc:odbc:MusicCollection'
jdbcDriver = 'sun.jdbc.odbc.JdbcOdbcDriver'
sql = Sql.newInstance(dbURL, jdbcDriver)
sql.eachRow('select * from Artists') {
println it.Name
}
Groovlets are the Groovy alternative to Servlets and JSP. They provide the following implicit variables.
out - what is returned by the
HttpServletResponse getWriter method
request - the HttpServletRequest
session - the HttpSession
Here is an example Groovlet.
This can be saved in a file with a name like
SimpleGroovlet.groovy.
It uses a "here-doc" to output HTML.
out.println <<<EOS
<html>
<head>
<title>My Simple Groovlet</title>
</head>
<body>
<h1>My Simple Groovlet</h1>
<p>Today is ${new java.util.Date()}.</p>
</body>
</html>
EOS
GroovyServlet compiles Groovlets
and caches them until they are changed.
It automatically recompiles them if they are changed.
GroovyServlet must be registered in web.xml.
Here's an example web.xml file showing
only the part to register GroovyServlet.
<?xml version="1.0"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
</web-app>
Groovlets can be deployed using Ant. The basic steps are
*.groovy) at the topweb.xml in the WEB-INF directory
groovy*.jar and asm*.jar files
in the WEB-INF/lib directory
Here's an example Ant build file for doing this.
build.dir=build
src.dir=src
# Directory that contains Groovlets
groovy.dir=${src.dir}/groovy
# Directory that contains web.xml
web.dir=${src.dir}/web
# Path to WAR that will be produced
war.file=${build.dir}/${ant.project.name}.war
# Where the WAR should be deployed
webapps.dir=${env.TOMCAT_HOME}/webapps
# JARs that must be in the WAR
asm.jar=${env.GROOVY_HOME}/lib/asm-1.4.1.jar
groovy.jar=${env.GROOVY_HOME}/lib/groovy-1.0-beta-4-snapshot.jar
<project name="GroovletExample" default="deploy">
<property environment="env"/>
<property file="build.properties"/>
<target name="prepare">
<mkdir dir="${build.dir}"/>
</target>
<target name="war" depends="prepare"
description="creates WAR file">
<war destfile="${war.file}" webxml="${web.dir}/web.xml">
<fileset dir="${groovy.dir}"/>
<lib file="${groovy.jar}"/>
<lib file="${asm.jar}"/>
</war>
</target>
<target name="deploy" depends="war"
description="deploys WAR file">
<delete dir="${webapps.dir}/${ant.project.name}"/>
<delete file="${webapps.dir}/${war.file}"/>
<copy file="${war.file}" todir="${webapps.dir}"/>
</target>
</project>
Once this example Groovlet is deployed,
it can be displayed in a web browser by visiting a URL like
http://localhost:8080/GroovletExample/SimpleGroovlet.groovy.
GroovletExample is the name of the web app.
SimpleGroovlet.groovy is the name of the Groovlet.
This matches the url-pattern specified for
GroovyServlet in web.xml.
Groovy isn't perfect yet. To view issues with Groovy, visit http://groovy.codehaus.org and click the Issue Tracker link. Here are some of the reported issues along with their assigned issue numbers.
* imports aren't supported yet (84).
So there it is ...
a quick run through of some of the syntax and features of Groovy.
Will the shortcuts provided over Java allow you to get more work done?
Will you have more fun doing it?
Will your code be easier or harder to understand?
I'd love to hear your feedback.
Email me at mark@ociweb.com.