Groovy file operations - execute around pattern in action

In an older post, I described Execute Around Pattern. In this post, let's understand how this pattern simplifies the solution while reading from files and writing to files.

Suppose we have a text file that contains a few names one name per line. We want our program to read the file and print the names comma separated. Let's start with the following code.

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

Path sourcePath = Paths.get('/Users/naresha/temp/names.txt')

BufferedReader reader = Files.newBufferedReader(sourcePath);
List<String> names = reader.readLines()
println names.join(", ")

Note that I have used Java nio2 APIs. If you are used to the traditional java.io.File based APIs, this post can help you to transition to the nio2 APIs.

java.nio.file.Files provides newBufferedReader static factory method. I choose to use this factory method instead of invoking the constructor. You may want to recall the first recommendation from the famous book 'Effective Java' - consider static factory methods instead of constructors.

Also Groovy provides a convenient readLines() extension method on the objects of Reader.

Now the code looks quite simple, isn't it? Until we realise that we forgot to close the reader. While what you want to do with the text file(read the entire content or read part of it) varies from context to context, you always create a reader object before reading the content and close the reader post read operation. Now, do you see the pattern here? We can easily reuse the pre and post reading code by using the Execute Around pattern.

import java.nio.file.Path
import java.nio.file.Paths

Path sourcePath = Paths.get('/Users/naresha/temp/names.txt')
String commaSeperatedNames = sourcePath.withReader { reader ->
    List<String> names = reader.readLines()
    names.join(", ")
}
println commaSeperatedNames

In the above code, withReader method creates an instance of BufferedReader from the path on which the method is invoked. Then it invokes the closure by passing the reader object as the argument. Finally, it closes the reader object before returning the value obtained by invoking the closure.

Similarly, Groovy provides withWriter method for writing to a text file, which creates a writer object and closes it after performing the operations as specified by the closure that you pass as the argument.