- •Credits
- •About the Authors
- •About the Reviewers
- •www.PacktPub.com
- •Table of Contents
- •Preface
- •Introduction
- •Installing Groovy on Windows
- •Installing Groovy on Linux and OS X
- •Executing Groovy code from the command line
- •Using Groovy as a command-line text file editor
- •Running Groovy with invokedynamic support
- •Building Groovy from source
- •Managing multiple Groovy installations on Linux
- •Using groovysh to try out Groovy commands
- •Starting groovyConsole to execute Groovy snippets
- •Configuring Groovy in Eclipse
- •Configuring Groovy in IntelliJ IDEA
- •Introduction
- •Using Java classes from Groovy
- •Embedding Groovy into Java
- •Compiling Groovy code
- •Generating documentation for Groovy code
- •Introduction
- •Searching strings with regular expressions
- •Writing less verbose Java Beans with Groovy Beans
- •Inheriting constructors in Groovy classes
- •Defining code as data in Groovy
- •Defining data structures as code in Groovy
- •Implementing multiple inheritance in Groovy
- •Defining type-checking rules for dynamic code
- •Adding automatic logging to Groovy classes
- •Introduction
- •Reading from a file
- •Reading a text file line by line
- •Processing every word in a text file
- •Writing to a file
- •Replacing tabs with spaces in a text file
- •Deleting a file or directory
- •Walking through a directory recursively
- •Searching for files
- •Changing file attributes on Windows
- •Reading data from a ZIP file
- •Reading an Excel file
- •Extracting data from a PDF
- •Introduction
- •Reading XML using XmlSlurper
- •Reading XML using XmlParser
- •Reading XML content with namespaces
- •Searching in XML with GPath
- •Searching in XML with XPath
- •Constructing XML content
- •Modifying XML content
- •Sorting XML nodes
- •Serializing Groovy Beans to XML
- •Introduction
- •Parsing JSON messages with JsonSlurper
- •Constructing JSON messages with JsonBuilder
- •Modifying JSON messages
- •Validating JSON messages
- •Converting JSON message to XML
- •Converting JSON message to Groovy Bean
- •Using JSON to configure your scripts
- •Introduction
- •Creating a database table
- •Connecting to an SQL database
- •Modifying data in an SQL database
- •Calling a stored procedure
- •Reading BLOB/CLOB from a database
- •Building a simple ORM framework
- •Using Groovy to access Redis
- •Using Groovy to access MongoDB
- •Using Groovy to access Apache Cassandra
- •Introduction
- •Downloading content from the Internet
- •Executing an HTTP GET request
- •Executing an HTTP POST request
- •Constructing and modifying complex URLs
- •Issuing a REST request and parsing a response
- •Issuing a SOAP request and parsing a response
- •Consuming RSS and Atom feeds
- •Using basic authentication for web service security
- •Using OAuth for web service security
- •Introduction
- •Querying methods and properties
- •Dynamically extending classes with new methods
- •Overriding methods dynamically
- •Adding performance logging to methods
- •Adding transparent imports to a script
- •DSL for executing commands over SSH
- •DSL for generating reports from logfiles
- •Introduction
- •Processing collections concurrently
- •Downloading files concurrently
- •Splitting a large task into smaller parallel jobs
- •Running tasks in parallel and asynchronously
- •Using actors to build message-based concurrency
- •Using STM to atomically update fields
- •Using dataflow variables for lazy evaluation
- •Index
Using Groovy Language Features
There's more...
Extension methods existed in Groovy before v2.0. Most of the magical GDK functionality (for example, additional methods available on the java.lang.String or java.io.File classes) is implemented in that way. But since Groovy 2.0, creating and packaging your own extension modules became possible.
Extension modules also work if you use the @Grab annotations to append classpath dependencies to your scripts (see the Simplifying dependency management with Grape recipe in Chapter 2, Using Groovy Ecosystem).
You can also append static methods to the classes. The mechanism works in the exactly same way with the only small exception that you need to use staticExtensionClasses in the module descriptor to refer to the class implementing those extensions. Also, if your module defines both static and non-static extension points, then those should be located in different classes.
That's not the only way to extend existing classes. Another approach is to use metaclass facilities at runtime. This technique is discussed in more detail in Chapter 9, Metaprogramming and DSLs in Groovy.
See also
ff Groovy's User Guide pages about extension modules: http://docs.codehaus. org/display/GROOVY/Creating+an+extension+module
Defining type-checking rules for dynamic code
The defining characteristic of Groovy is probably the dynamic nature of the language. Dynamic languages possess the capacity to extend a program at runtime, including changing types, behaviors, and object structures. With these languages, the things that static languages do at compile time can be done at runtime; we can even execute program statements that are created on the fly at runtime. Another trait of Groovy, typical of other dynamic languages such as Ruby or Python, is dynamic typing. With dynamic typing, the language automatically tests properties when expressions are being (dynamically) evaluated (that is, at runtime).
In this recipe, we will present how Groovy can be instructed to apply static typing checks to code elements to warn you about potential violations at compiletime; that is, before code is executed.
126
www.it-ebooks.info
Chapter 3
Getting ready
Let's look at some script as follows:
def name = 'john' printn naame
The Groovy compiler groovyc is perfectly happy to compile the previous code. Can you spot the error?
But, if we try to execute the code we get, not surprisingly, the following exception:
Caught: groovy.lang.MissingPropertyException: No such property: naame
for class: DynamicTypingExample
Dynamic typing is a very controversial topic. Proponents of static typing argue that the benefits of static typing encompass:
1.Easier to spot programming mistakes (for example, preventing assigning a Boolean to an integer).
2.Self-documenting code thanks to type signatures.
3.The compiler has more opportunities to optimize the code (for example, replacing a virtual call with a direct one when the receiver's type is statically known).
4.More efficient development tools (for example, autocompletion of members of a class).
Defenders of dynamic typing return fire by claiming that static typing is inflexible and has a negative impact on prototyping systems with volatile or unknown requirements. Furthermore, writing code in dynamic languages requires more discipline than writing in statically typed languages. This is often translated into a greater awareness for testing—and unit testing in particular—among developers who decide to produce code with dynamic languages.
It is worth noting that Groovy also supports optional static typing, even though the name is slightly misleading. In Groovy, we can declare a variable's type as follows:
int var = 10
or
String computeFrequency(List<String> listOfWords) { }
Still, the compiler would not catch any compilation error if we assigned the wrong type to a typed variable:
int var = "Hello, I'm not a number!"
127
www.it-ebooks.info
Using Groovy Language Features
Groovy has optional typing mostly for Java compatibility and to support better code readability.
To mitigate some of the criticism inherent to dynamic typing, the Groovy team has introduced static type-checking since v2.0 of the language. Static type-checking enables the verification of the proper type and ensures that the methods we call and properties we access are valid for the type at compile time, hence decreasing the number of bugs pushed to the runtime.
How to do it...
Forcing the compiler to enable static type-checking it is simply a matter of annotating the code that we want to be checked with the @groovy.transform.TypeChecked annotation. The annotation can be placed on classes or individual methods:
1.Create a new Groovy file (with the .groovy extension) with the following code:
def upperInteger(myInteger) { println myInteger.toUpperCase()
}
upperInteger(10)
2.From the shell, compile the code using the Groovy compiler groovyc. The compiler shouldn't report any error.
3.Now, run the script using groovy. The runtime should throw an exception: caught: groovy.lang.MissingMethodException:
No signature of method: java.lang.Integer.toUpperCase() is applicable for argument types: () values: []
4.Add the type-checking annotation to the upperInteger function:
@groovy.transform.TypeChecked def upperInteger(myInteger) {
println myInteger.toUpperCase()
}
5.Compile the code again with groovyc. This time, the compiler reports the type error:
DynamicTypingExample.groovy: 3: [Static type checking] - Cannot find matching method java.lang.Object#toUpperCase(). Please check if the declared type is right
and if the method exists. @ line 3, column 11.
println myInteger.toUpperCase()
^
128
www.it-ebooks.info
Chapter 3
How it works...
If we place the annotation on a class, then type-checking is performed on all the methods, closures, and inner classes in the class. If we place it on a method, the type-checking is performed only on the members of the target method, including closures.
Static type-checking is implemented through an AST Transformation (see more on AST Transformations in Chapter 9, Metaprogramming and DSLs in Groovy) and can be used to enforce proper typing for a number of different cases. The following list shows a collection of bugs that would not be caught by the compiler but are caught by the TypeChecked annotation.
1.Variable names:
@TypeChecked
def variableName() { def name = 'hello' println naame
}
The variable named name is misspelled in the second line. The compiler outputs:
MyClass.groovy: 7: [Static type checking] - The variable [naame] is undeclared..
2.GStrings:
@TypeChecked def gstring() {
def name = 'hello'
println "hello, this is $naame"
}
Similar to the previous bug, the compiler complains about the undeclared variable naame.
3.Collections:
@TypeChecked
def collections() { List myList = []
myList = "I'm a String"
}
Here, the compiler detects that we are assigning a String to a List and throws an error: [Static type checking] - Cannot assign value of type java. lang.String to variable of type java.util.List.
129
www.it-ebooks.info
Using Groovy Language Features
4.Collection type:
@TypeChecked
def moreCollections() { int[] array = new int[2] array[0] = 100
array[1] = '100'
}
Similarly to the previous example, the compiler detects the invalid assignment of a String to an element of an array of ints.
5.Return type:
@TypeChecked
int getSalary() {
'fired on ' + new Date().format('yyyy-MM-dd')
}
Return types are also verified by the static type-checking algorithm. In this example, the compiler throws the following error:
[Static type checking] - Cannot return value of type java.lang. String on method returning type int
6.Return type propagation:
boolean test() { true
}
@TypeChecked void m() {
int x = 1 + test()
}
Wrong return types are also detected when the type is propagated from another method. In the previous example, the compiler fails with:
Static type checking] - Cannot find matching method int#plus(void). Please check if the declared type is right and if the method exists
7.Generics:
@TypeChecked def generics() {
List<Integer> aList = ['a','b']
}
Generics are supported as well. We are not allowed to assign the wrong type to a typed List.
130
www.it-ebooks.info
Chapter 3
There's more...
As you may have suspected, static type-checking doesn't play very nice with a dynamic language. Metaprogramming capabilities are effectively shut down when using the @TypeChecked annotation on a class or method.
Here is an example that uses metaprogramming to inject a new method into the class String
(the Dynamically extending classes with new methods recipe in Chapter 9, Metaprogramming and DSLs in Groovy has more information about method injections):
class Sample { def test() {
String.metaClass.hello = { "Hello, $delegate" } println 'some String'.hello()
}
}
new Sample().test()
When executed, this script yields the following output:
Hello, some String
If we add the @TypeChecked annotation to the class and try to run the script, we get the following error from the compiler:
[Static type checking] -
Cannot find matching method java.lang.String#hello().
Please check if the declared type is right and if the method exists.
The reason for the error is that the compiler doesn't see the new dynamic method and therefore fails. Another case where static type-checking is too picky is when using implicit parameters in closures. Let's take an example:
class Sample { def test() {
['one','two','three'].collect { println it.toUpperCase() }
}
}
new Sample().test()
This example, when executed, prints the following output:
ONE
TWO
THREE
131
www.it-ebooks.info