Appendix B. Groovy for Gradle users

Gradle’s core functionality is built with Java. On top of this functionality sits a domain-specific language (DSL) written in the dynamic programming language Groovy. When writing a Gradle build script, you automatically use the language constructs exposed by this DSL to express the desired build instructions. Gradle build scripts are executable Groovy scripts, but they can’t be run by the Groovy runtime. When the need to implement custom logic arises, you can use Groovy’s language features to build out the desired functionality directly in the Gradle build script. This appendix is a primer to Groovy and explains why it’s important for users of Gradle to learn the language. Later, I’ll also demonstrate how some of Gradle’s configuration elements are implemented with Groovy.

B.1. What is Groovy?

Groovy is a dynamic programming language for the Java Virtual Machine (JVM). Its syntax is close to the one provided by Java. The language integrates with existing Java classes and libraries, which makes it easy for Java developers to learn it. Not only does Groovy build upon the strengths of Java, it also provides powerful programming features inspired by those of Ruby, Python, and others. Groovy can be used as a scripting language without having to compile the code. Alternatively, Groovy code can be compiled to Java bytecode. In this book, you use both approaches.

While this appendix will give you a head start on Groovy’s most important language features, it’s highly recommended that you explore it further on your own. There are great resources out there in the form of books and dedicated Groovy web pages. The definitive guide to Groovy is the book Groovy in Action, Second Edition by Dierk Konig, et al (Manning, 2009). It explains all language features in great detail. The DZone reference guide on Groovy provides a cheat sheet that’s handy to every beginner of the language. You can download it for free at http://refcardz.dzone.com/refcardz/groovy. A useful resource for people experimenting with Groovy is the blog Groovy Goodness by Hubert A. Klein Ikkink, at http://mrhaki.blogspot.com/search/label/Groovy%3AGoodness. Each posting demonstrates a unique Groovy language feature by example. The author recently condensed his blog posts into a book titled Groovy Goodness Notebook (Leanpub, 2013).

B.2. How much Groovy do I need to know?

If you’re new to Gradle you may wonder how much Groovy you actually need to know to write your first build scripts. The simple answer is very little. However, it’s highly recommended that you know some Java. Groovy is almost 100% compatible with Java. When implementing trivial task actions within your build script, you can choose to write plain Java code or use Groovy’s expressive language constructs.

Let’s look at an example. Assume you want to determine all files within a directory and write their names to a new file named allfiles.txt. Sounds pretty simple, right? The following listing demonstrates the Java version of the task action doLast.

Listing B.1. Gradle task written with Java syntax

Apart from the task definition itself, which is defined with Gradle’s DSL, no Groovy code was required to implement the scenario. Next, we’ll compare this task implementation with the Groovy version. In the following listing, the task action code is much shorter and more concise than the Java version.

Listing B.2. Gradle task written with Groovy syntax

A mixture of Java and Groovy works just fine for trivial builds, especially if you’re in the process of learning Groovy. For more complex builds that include custom tasks and plugins, you’ll need to know more about the language. And for a deep understanding of how Gradle works internally, knowing about advanced Groovy features is key.

B.3. Comparing Java and Groovy syntax

In the last section, we compared a task implementation written in Java and Groovy. In Groovy you can become extremely productive while at the same time writing less code. To get a good understanding of how this plays out in practice, let’s talk about the main differentiators between both languages. As an example, we’ll look at the class ProjectVersion.java from chapter 4 of this book. The class describes the major and minor version of a Gradle project. The next listing shows a simplified version of the class.

Listing B.3. A typical Java POJO class
package com.manning.gia;

public class ProjectVersion {
   private Integer major;
   private Integer minor;

   public ProjectVersion(Integer major, Integer minor) {
      this.major = major;
      this.minor = minor;
   }

   public Integer getMajor() {
      return major;
   }

   public void setMajor(Integer major) {
      this.major = major;
   }

   public Integer getMinor() {
      return minor;
   }

   public void setMinor(Integer minor) {
      this.minor = minor;
   }
}

As a Java developer, you probably see this kind of boilerplate code every day. Plain old Java objects (POJOs), classes that don’t implement or extend third-party library classes, are often used to represent data. The class exposes two private fields via getter and setter methods. The following listing demonstrates how to express the same logic in Groovy.

Listing B.4. Project version class written in Groovy
package com.manning.gia

class ProjectVersion {
   Integer major
   Integer minor

   ProjectVersion(Integer major, Integer minor) {
      this.major = major
      this.minor = minor
   }
}

I think we can agree that the Groovy version of the class looks far less noisy. Groovy assumes sensible defaults for any class you write, particularly the following optimizations:

  • The use of semicolons at the end of an expression is optional.
  • Every class, constructor, and method is public by default.
  • In Groovy, the last expression in the body of a method is returned. This means that the use of the return statement is optional.
  • The Groovy compiler adds getter/setter methods automatically so you don’t have to write them yourself.
  • Fields of a class (also called properties in Groovy) can be accessed by dot notation, which looks exactly as if they were public in Java. However, under the hood Groovy calls the auto-generated corresponding getter/setter method.
  • If you compare two instances of a class with ==, Groovy automatically calls the method equals() under the hood. This operation also avoids potential NullPointerExceptions.

B.4. Essential Groovy features

So far, we’ve only scratched the surface of the feature set Groovy provides. In this section, we’ll explore language aspects used on a regular basis when programming in Groovy. The features we’ll discuss in the following sections aren’t listed in a particular order. Feel free to follow along and try out the examples one by one on your computer. There are two tools that are extremely helpful in running code snippets.

The Groovy console is automatically available after installing the Groovy runtime. It provides an interactive user interface for entering and executing Groovy scripts. The console can be launched by running the command groovyConsole or groovyConsole.bat, located in the directory <GROOVY_HOME>/bin.

The Groovy web console (http://groovyconsole.appspot.com/) provides an even easier way to try out Groovy code snippets. You can run Groovy scripts on the web without installing the Groovy runtime. Keep in mind that the language features used in your script are bound to the bundled Groovy version of the web console indicated on the page.

B.4.1. Assert statement

If you’re coming from Java, you may know the keyword assert. It’s used to verify pre- and post-conditions in your code. Unlike Java’s pendant, which only works if you enable assertion checking by setting a runtime flag (-ea or –enableassertion), Groovy’s assert statement is always evaluated. The following listing shows a usage example.

Listing B.5. Groovy’s power assert

Careful readers will note that the second assertion should fail, because you incremented the version variable by 1. Groovy’s assert statement, also called power assert, provides helpful output to identify the root cause of an issue. The following output demonstrates a sample output for the listing:

Exception thrown
Jul 29, 2013 8:06:04 AM org.codehaus.groovy.runtime.StackTraceUtils
sanitize
WARNING: Sanitizing stacktrace:
Assertion failed:
assert version == 12
       |       |
       13      false

You’ll use the assert statement in the upcoming code examples as a tool to verify and document the desired behavior.

B.4.2. Optional data type declaration

Groovy doesn’t force you to explicitly declare a type of variable, method parameter, or return type. Instead, you can simply mark it with the keyword def, a placeholder for java.lang.Object. At runtime, Groovy figures out the type based on the assigned value. The next listing demonstrates some examples of optional typing.

Listing B.6. Optional typing for variables and methods
def buildTool = 'Gradle'
assert buildTool.class == java.lang.String

def initProjectVersion(major, minor) {
   new ProjectVersion(major, minor)
}

assert initProjectVersion(1, 2).class == com.manning.gia.ProjectVersion

This feature is helpful for certain use cases, but you may still prefer explicit, strong typing. Particularly in projects that expose a public API, strong typing automatically improves documentation, makes it more obvious what parameter types need to be provided, and enables IDE code completion. For the same reasons, declaring void instead of def as a return type should be used if a method doesn’t return a value.

B.4.3. Optional parentheses

Method calls in Groovy can omit the parentheses if the method signature requires at least one parameter. Without going into too much detail, this feature is often used to create more natural-looking DSLs, a human-readable language understandable by domain experts. The following listing compares two method calls with and without the parentheses.

Listing B.7. Omitting parentheses for top-level expressions
initProjectVersion(1, 2)
initProjectVersion 1, 2

println('Groovy is awesome!')
println 'Groovy is awesome!'

B.4.4. Strings

There are three different ways to declare Strings in Groovy. A single-quoted String always creates the Java equivalent of a String. The second form follows the Java way of creating a String. It wraps the text with double quotes. Multiline Strings, wrapped by triple double quotes, are helpful if you want to assign wordy text or impose formatting (for example, multiline SQL statements). The next listing shows the full range of creating Strings in Groovy.

Listing B.8. String notations in Groovy
def myString1 = 'This is a single-quoted String'
def myString2 = "This is a double-quoted String"
def myString3 = """
   This
   is a
   multiline
   String
"""

B.4.5. Groovy Strings (GStrings)

Double-quoted Strings in Groovy are more powerful than traditional Java Strings. They can interpolate embedded variables or expressions denoted by a dollar sign and curly braces. At runtime, the expression is evaluated and forms a String. In Groovy, these types of Strings are also known as GStrings. The following listing gives an example of their practical use.

Listing B.9. String interpolation with GStrings
def language = 'groovy'
def sentence = "$language is awesome!"
assert sentence == 'groovy is awesome!'

def improvedSentence = "${language.capitalize()} is awesome!"
assert improvedSentence == 'Groovy is awesome!'

B.4.6. Collections API

Groovy offers a concise syntax for implementations of the Collections API, which makes them easier to use than their Java equivalent. Next, we’ll discuss Lists and Maps.

Lists

By putting a comma-separated list of values in square brackets, you can initialize new Lists. Under the hood, Groovy creates an instance of java.util.ArrayList. Groovy also adds syntactic sugar to simplify the use of a List. A perfect example is the left shift operator (<<) that allows for adding a new element to the List. Under the hood, Groovy calls the method add. The following listing shows some of this functionality.

Listing B.10. Managing Lists in Groovy

Maps

Maps are easier to handle than Lists. To initialize a new Map with a new value, create a comma-separated list of key-value pairs in square brackets. The default implementation of a Map is java.lang.LinkedHashMap. The next listing shows usage examples of a Groovy Map.

Listing B.11. Managing Maps in Groovy

B.4.7. Named parameters

Earlier we talked about the simple bean class ProjectVersion. The class exposes a constructor to initialize its fields with values. Assume you didn’t define a constructor. Groovy provides another handy way of setting property values, called named parameters. This mechanism first calls the default constructor of the class and then calls the setter methods for each of the provided parameters. The following listing shows how to set the values for the fields major and minor through named parameters.

Listing B.12. Setting field values with named parameters
class ProjectVersion {
   Integer major
   Integer minor
}

ProjectVersion projectVersion = new ProjectVersion(major: 1, minor: 10)
assert projectVersion.minor == 10
projectVersion.minor = 30
assert projectVersion.minor == 30

B.4.8. Closures

A closure is a block of code of type groovy.lang.Closure, similar to lambdas in other programming languages. Closures can be assigned to variables, passed to methods as parameters, and called like regular methods.

Implicit closure parameter

Every closure that doesn’t explicitly define any parameters has access to an implicit parameter named it. The parameter it refers to the first parameter passed to a closure when calling it. If no parameter is provided, the value of the parameter is null. Let’s look at an example to make this concept less abstract. The following listing shows the definition and invocation of a closure, including the implicit parameter it.

Listing B.13. Closure with single, implicit parameter

Explicit closure parameters

Instead of using the implicit closure parameter, you can be more descriptive by assigning your own parameter name. In the next listing, you define a parameter named version of type ProjectVersion.

Listing B.14. Closure with single, explicit parameter

Keep in mind that typing is optional in Groovy. You could have used the identifier version without the type. Groovy doesn’t limit the number of parameters a closure can define. The following listing shows how to declare multiple, untyped parameters for a closure.

Listing B.15. Closure with multiple, untyped parameters

Closure return value

A closure always returns a value. This is either the last statement of the closure if no explicit return statement is declared, or the value of the executed return statement. If the last statement of a closure doesn’t have a value, null is returned. The closure shown in the following listing returns the value of the last statement, the project’s minor version.

Listing B.16. Closure return value

Closure as method parameter

As mentioned earlier, you can also use a closure as a method parameter. The following listing shows an example.

Listing B.17. Closure as method parameter

Closure delegation

The code of a closure is executed against the delegate of a closure. By default, the delegate is the owner of the closure. For example, if you define a closure within a Groovy script, the owner is an instance of groovy.lang.Script. The implicit variable delegate of a closure allows for redefining the default owner. Consider the scenario in the next listing. You set the delegate to the instance of ProjectVersion. This means that every closure is executed against it.

Listing B.18. Setting the delegate for a closure

B.4.9. Groovy Development Toolkit

The Groovy Development Kit (GDK) extends the Java Development Kit (JDK) by providing convenience methods to the standard JDK classes. You can find a Javadoc-style HTML catalog at http://groovy.codehaus.org/groovy-jdk/. You’ll find many useful methods in classes like String, Collection, File, and Stream. Many of these methods use a closure as a parameter to add a functional flavor to the language. You already saw one of these methods in action in listings B.10 and B.11: each. You used the method to iterate over the elements of a Collection. Let’s look at some other examples in the following listing.

Listing B.19. Examples of methods added by the GDK

B.5. Applied Groovy in Gradle build scripts

Gradle build scripts are valid Groovy scripts. Within the build script, you can use every Groovy feature the language provides. This means that the code has to strictly adhere to Groovy’s syntax. Invalid code will automatically result in a runtime error when executing the build.

Gradle comes with a DSL written in Groovy for modeling typical build concerns. You learned in chapter 4 that every build script corresponds to at least one instance of org.gradle.api.Project. In most cases, properties and methods you invoke in the build script automatically delegate to the Project instance.

Let’s examine the sample build script shown in listing B.20. After getting to know some of Groovy’s language features by example, you may have a hunch how they work internally. I hope this example can demystify some of the “magic” beginners to Gradle and Groovy encounter.

Listing B.20. Applied Groovy syntax in a sample Gradle build script

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.101.95