Oracle Blog

Alan Bateman

Copy that

Copy.java is one of several new samples included in JDK 7 to demonstrate its new API to the
file system. If you have any recent snapshot installed then you'll find these samples in the
$JAVA_HOME/sample/nio/file directory. Copy.java works like the Unix cp
program to copy a file or file tree to a target location. It supports the -r, -p
and -i options to do a recursive copy, preserve attributes, or work interactively to
prompt whenever an existing file would be overwritten. The sample code is relatively simple and
demonstrates many aspects of the API that are worth looking at.

At its heart, the
copyTo method is used to do the copy. The Copy sample uses the COPY_ATTRIBUTES
option when invoked with -p to preserve attributes. This is useful when you want the file
attributes/meta-data copied to the target file. This means the file timestamps, permissions, Access
Control List, extended attributes, etc. The other option used in the sample code is
REPLACE_EXISTING; this is used to replace the target file if it exists. One thing to
point out is that the copyTo method doesn't copy the contents of a directory. If you invoke
it on a directory then it creates an empty directory. On the surface this might appearing limiting but
work through the sample and it should becomes clear.

Another interesting thing to point out is that you'll see code like:

target.resolve(source.relativize(file))

The relativize
method used to compute a relative path between source and file. This is then
resolved
against target. For example, suppose we are doing a recusive copy from /users/gus to
/users/joe/backup_of_gus. As we walk the tree we will need to copy /users/gus/stamps/rare/black_penny.html.
In this sample source.relativize(file) yields the relative path stamps/rare/black_penny.html.
Resolving this against the target directory (/users/joe/backup_of_gus) yields us the path
in the target tree: /users/joe/backup_of_gus/stamps/rare/black_penny.html.

The other important API used in the sample is the
Files.walkFileTree method. This method is used to implement recursive file operations,
starting at a given starting point. The method is passed a
FileVisitor
that is invoked for each file or directory in the file tree. The Copy sample has an inner class
TreeCopier that implements FileVisitor . TreeCopier's preVisitDirectory
method is invoked for each directory before the entries in the directory are visited. It simply invokes
the copyTo method to copy the directory (creating an empty directory as I mentioned above). When
the copy succeeds or the directory already exists the preVisitDirectory returns CONTINUE
to instruct the iterator to continue and visit the files in the directory. If the copy fails, then
preVisitDirectory returns SKIP_SUBTREE to tell the iterator to skip the directory.
You'll see that postVisitDirectory is also implemented. This is invoked after all of the
entries in the directory have been visited. In this code it is implemented so that the directory's
last-modified-time can be fixed up after we are done with the directory. Most recursive operations will only need to implement one
of preVisitDirectory or postVisitDirectory. The visitFile method is very
simple and just copies the file to the corresponding file in the target tree. One interesting thing to point out is that visitFile is invoked if a cycle is detected (a cycle being
a symbolic link to a parent directory). Copying is one of the few cases where you want to follow
symbolic links and so you need to be concerned with the possibility of a cycle.

Hopefully this sample will be useful when learning the new API. The
$JAVA_HOME/sample/nio/file directory has a number of other examples that demonstrate other
parts of the API.