- •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
Chapter 3
The @Canonical annotation has only two parameters, includes and excludes, to select which properties to include or exclude in the computation. Since @Canonical doesn't take any additional parameters and uses some sensible defaults, how can we customize certain aspects of the code generation? For example, if you want to use @Canonical but customize the @ToString behavior, then annotate the class with both @Canonical and @ToString. The @ToString definition and parameters take precedence over @Canonical.
@Canonical
@ToString(excludes='age') class Person {
String name int age
}
Inheriting constructors in Groovy classes
In Java, class inheritance doesn't support the inheriting of constructors for a number of good reasons (leaving the details of constructing an object to the programmer is generally a good idea). There are times when automatically inheriting the constructors of a class would be really useful and make the code less verbose. One of these cases is when inheriting from a class that extends Exception, where all the constructors are just calling super. Groovy has a convenient annotation for doing just that, @InheritConstructors.
In this recipe, we will explore how to use this annotation.
How to do it...
Let's demonstrate the features that the @InheritConstructors annotation gives:
1.Create an Exception class: one of the classes that are used to communicate that something is horribly wrong with the code or the data:
class BusinessException extends Exception {
}
2.Try to instantiate the class using one of the default constructors of the Exception class, for instance:
def be = new BusinessException('missing resource')
The code fails at runtime with the following error:
Could not find matching constructor for: BusinessException(java.lang.String)
99
www.it-ebooks.info
Using Groovy Language Features
3.Add the groovy.transform.InheritConstructors annotation to the
BusinessException and try to instantiate the class again with a String message. This time, the code will execute without errors:
@groovy.transform.InheritConstructors
class BusinessException extends Exception {
}
How it works...
The @InheritConstructors annotation removes the boilerplate of writing matching constructors for a superclass. By adding the @InheritConstructors annotation to the class, we can create the BusinessException by using any of the constructors exposed by
java.lang.Exception:
import groovy.transform.InheritConstructors
@InheritConstructors
class BusinessException extends Exception { }
assert new BusinessException('hello').message == 'hello'
def |
b1 |
= |
new BusinessException('missing resource') |
def |
b2 |
= |
new BusinessException('catastrophic failure', b1) |
assert |
b2.cause.message == 'missing resource' |
The annotation is smart enough to detect your existing constructors, if any, and avoid overriding them during the constructor generation phase.
Adding the cloning functionality to Groovy Beans
There are several strategies to clone an object in Java. To clone an object means the ability to create an object with the same state as the original object.
A widely used strategy to clone an object is for the class to be cloned to implement the Cloneable interface, and implement a method, clone, in which the cloning code is executed. Naturally, Groovy supports this semantic but makes it even easier to implement with the @AutoClone annotation, which will be demonstrated in this recipe.
100
www.it-ebooks.info
Chapter 3
How to do it...
The following steps will show the power of the @AutoClone annotation:
1.Let's define an object Vehicle and annotate the class with the @AutoClone annotation:
import groovy.transform.AutoClone
@AutoClone
class Vehicle { String brand String type
Long wheelsNumber
}
2.In the same script where the Vehicle object is defined, add the following code:
def v1 = new Vehicle() v1.brand = 'Ferrari' v1.type = 'Testarossa' v1.wheelsNumber = 4 def v2 = v1.clone()
assert v1 instanceof Cloneable assert v1.brand == v2.brand
The script should execute without errors.
3.Let's add a second object to the mix:
class Engine { int horseEngine Number liter
}
4.Let's add the engine property to the Vehicle class:
Engine engine
5.Modify the test code, so that the vehicle is created with an engine:
def v1 = new Vehicle(
brand: 'Ferrari', type: 'Testarossa', wheelsNumber: 4
)
def e1 = new Engine( horseEngine: 390,
101
www.it-ebooks.info
Using Groovy Language Features
liter: 4.9
)
v1.engine = e1 // assign engine to car def v2 = v1.clone() // clone
println 'Original vehicle engine liters: ' + v1.engine.liter println 'Cloned vehicle engine liters: ' + v2.engine.liter v2.engine.liter = 8
println 'Original vehicle engine liters: ' + v1.engine.liter
6.The result of the modified script yields:
Original vehicle engine liters: 4.9 Cloned vehicle engine liters: 4.9 Original vehicle engine liters: 8
How it works...
The snippet in step 2 shows how easy is to clone an object without having to implement any interface. The default cloning strategy implemented by the annotation will call super.clone before calling clone on each Cloneable property of the class. If a field or property is not cloneable, it is simply copied in a bitwise fashion. If some properties don't support cloning, a
CloneNotSupportedException is thrown.
The decompiled Vehicle class has the following aspect (the code is cleaned up a bit for the sake of readability, as the Groovy-generated bytecode uses reflection heavily):
public class Vehicle implements Cloneable, GroovyObject {
public Object clone() throws CloneNotSupportedException { Object result = super.clone()
if (brand instanceof Cloneable) { result.brand = brand.clone()
}
if (type instanceof Cloneable) { result.type = type.clone()
}
if (wheelsNumber instanceof Cloneable) { result.wheelsNumber = wheelsNumber.clone()
}
return result
}
}
102
www.it-ebooks.info
Chapter 3
In step 6, we can observe that the cloning process didn't quite work as expected. An attribute of the cloned object is modified, and the same attribute on the original object gets modified as well. Not surprisingly, the reason for this behavior is that the Engine class is not annotated with the @AutoClone annotation; therefore, the cloning process only clones the reference
to the object, not the actual object. Simply annotate the Engine object with @AutoClone to have the actual object cloned.
There's more...
The @AutoClone annotation supports some additional cloning styles. The first cloning style is based on the Copy Constructor pattern:
import groovy.transform.*
@AutoClone(style=AutoCloneStyle.COPY_CONSTRUCTOR) class Vehicle {
...
}
The clone method implementation is moved into the body of a constructor that takes a parameter of the same type as the class. Then, calls to clone simply return the result of calling this constructor. Enabling the COPY_CONSTRUCTOR pattern produces Java code similar to the following code snippet:
public class Vehicle implements Cloneable {
private String brand; private String type; private Long wheelsNumber;
protected Vehicle(Vehicle other) { brand = other.brand;
type = other.type;
wheelsNumber = other.wheelsNumber;
}
public Object clone() throws CloneNotSupportedException { return new Vehicle(this);
}
}
103
www.it-ebooks.info