blob: 43a8c455a9f78ff9cc953c84023e9074164512fc [file] [log] [blame]
//////////////////////////////////////////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
//////////////////////////////////////////
= Working with IO
Groovy provides a number of
link:gdk.html[helper methods] for working
with I/O. While you could use standard Java code in Groovy to deal with those,
Groovy provides much more convenient ways to handle files, streams, readers, ...
In particular, you should take a look at methods added to:
* the `java.io.File` class : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
* the `java.io.InputStream` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
* the `java.io.OutputStream` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html
* the `java.io.Reader` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html
* the `java.io.Writer` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html
* the `java.nio.file.Path` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html
The following section focuses on sample idiomatic constructs using helper methods available above but is not meant
to be a complete description of all available methods. For that, please read the link:gdk.html[GDK API].
== Reading files
As a first example, let's see how you would print all lines of a text file in Groovy:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=print_file_lines,indent=0]
----
The `eachLine` method is a method added to the `File` class automatically by Groovy and has many variants, for example
if you need to know the line number, you can use this variant:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=print_file_lines2,indent=0]
----
If for whatever reason an exception is thrown in the `eachLine` body, the method makes sure that the resource
is properly closed. This is true for all I/O resource methods that Groovy adds.
For example in some cases you will prefer to use a `Reader`, but still benefit from the automatic resource management
from Groovy. In the next example, the reader *will* be closed even if the exception occurs:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=withreader_exception,indent=0]
----
Should you need to collect the lines of a text file into a list, you can do:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=collect_lines,indent=0]
----
Or you can even leverage the `as` operator to get the contents of the file into an array of lines:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=lines_as_strings,indent=0]
----
How many times did you have to get the contents of a file into a `byte[]` and how much code does it require? Groovy
makes it very easy actually:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=file_bytes,indent=0]
----
Working with I/O is not limited to dealing with files. In fact, a lot of operations rely on input/output streams,
hence why Groovy adds a lot of support methods to those, as you can see in the
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html[documentation].
As an example, you can obtain an `InputStream` from a `File` very easily:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=newinputstream,indent=0]
----
However you can see that it requires you to deal with closing the inputstream. In Groovy it is in general a better
idea to use the `withInputStream` idiom that will take care of that for you:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=withinputstream,indent=0]
----
== Writing files
Of course in some cases you won't want to read but write a file. One of the options is to use a `Writer`:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=withwriter_example,indent=0]
----
But for such a simple example, using the `<<` operator would have been enough:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=file_leftshift,indent=0]
----
Of course we do not always deal with text contents, so you could use the `Writer` or directly write bytes as in
this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=file_setbytes,indent=0]
----
Of course you can also directly deal with output streams. For example, here is how you would create an output
stream to write into a file:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=newoutputstream,indent=0]
----
However you can see that it requires you to deal with closing the output stream. Again it is in general a better
idea to use the `withOutputStream` idiom that will handle the exceptions and close the stream in any case:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=withoutputstream,indent=0]
----
== Traversing file trees
In scripting contexts it is a common task to traverse a file tree in order to find some specific files and do
something with them. Groovy provides multiple methods to do this. For example you can perform something on all files
of a directory:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=eachfile,indent=0]
----
<1> executes the closure code on each file found in the directory
<2> executes the closure code on files in the directory matching the specified pattern
Often you will have to deal with a deeper hierarchy of files, in which case you can use `eachFileRecurse`:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=eachfilerecurse,indent=0]
----
<1> executes the closure code on each file or directory found in the directory, recursively
<2> executes the closure code only on files, but recursively
For more complex traversal techniques you can use the `traverse` method, which requires you to set a special flag
indicating what to do with the traversal:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=traverse,indent=0]
----
<1> if the current file is a directory and its name is `bin`, stop the traversal
<2> otherwise print the file name and continue
== Data and objects
In Java it is not uncommon to serialize and deserialize data using the `java.io.DataOutputStream` and
`java.io.DataInputStream` classes respectively. Groovy will make it even easier to deal with them. For example, you could
serialize data into a file and deserialize it using this code:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=data_in_out,indent=0]
----
And similarily, if the data you want to serialize implements the `Serializable` interface, you can proceed with
an object output stream, as illustrated here:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=object_in_out,indent=0]
----
[[process-management]]
== Executing External Processes
The previous section described how easy it was to deal with files, readers or streams in Groovy. However in domains
like system administration or devops it is often required to communicate with external processes.
Groovy provides a simple way to execute command line processes. Simply
write the command line as a string and call the `execute()` method.
E.g., on a *nix machine (or a windows machine with appropriate *nix
commands installed), you can execute this:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=process_list_files,indent=0]
----
<1> executes the `ls` command in an external process
<2> consume the output of the command and retrieve the text
The `execute()` method returns a `java.lang.Process` instance which will
subsequently allow the in/out/err streams to be processed and the exit
value from the process to be inspected etc.
e.g. here is the same command as above but we will now process the
resulting stream a line at a time:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=process_list_files_line_by_line,indent=0]
----
<1> executes the `ls` command in an external process
<2> for each line of the input stream of the process
<3> print the line
It is worth noting that `in` corresponds to an input stream to the standard output of the command. `out` will refer
to a stream where you can send data to the process (its standard input).
Remember that many commands are shell built-ins and need special
handling. So if you want a listing of files in a directory on a Windows
machine and write:
[source,groovy]
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=dir_windows,indent=0]
----
you will receive an `IOException` saying  _Cannot run program "dir":
CreateProcess error=2, The system cannot find the file specified._
This is because `dir` is built-in to the Windows shell (`cmd.exe`) and
can’t be run as a simple executable. Instead, you will need to write:
[source,groovy]
----------------------------------
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=dir_windows_fixed,indent=0]
----------------------------------
Also, because this functionality currently makes use of
`java.lang.Process` undercover, the deficiencies of that class
must be taken into consideration. In particular, the javadoc
for this class says:
________________________________________________________________________
Because some native platforms only provide limited buffer size for
standard input and output streams, failure to promptly write the input
stream or read the output stream of the subprocess may cause the
subprocess to block, and even deadlock
________________________________________________________________________
Because of this, Groovy provides some additional helper methods which
make stream handling for processes easier.
Here is how to gobble all of the output (including the error stream
output) from your process:
[source,groovy]
----------------------------------
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=consumeoutput,indent=0]
----------------------------------
There are also variations of `consumeProcessOutput` that make use of `StringBuffer`, `InputStream`,
`OutputStream` etc... For a complete list, please read the
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Process.html[GDK API for java.lang.Process]
In addition, these is a `pipeTo` command (mapped to `|`
to allow overloading) which lets the output stream of one process be fed
into the input stream of another process.
Here are some examples of use:
[source,groovy]
.Pipes in action
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=pipe_example_1,indent=0]
----
[source,groovy]
.Consuming errors
----
include::{projectdir}/src/spec/test/gdk/WorkingWithIOSpecTest.groovy[tags=pipe_example_2,indent=0]
----