Monday, July 8, 2013

Java SE 7: Refactoring your legacy java.io.File Code to elegant java.nio.file (NIO.2)


In my previous article, I have introduced all of the new functionalities, features and APIs, which are shipped with Java SE 7 release, and here we will see how to migrate or re-write your old IO to new NIO.2 code.

In my daily projects I am developing, I am depending extensively on I/O usage for exchanging files among my systems for integration, and there are a lot of boilerplate code, validations, exceptions checking and security related issues. Therefor I have decided to refactor my code especially critical one to Java SE 7 NIO.2 APIs.

In this article, I will show you a quick guide on how to refactor and map your legacy IO (Input/Output) code, which is prior to Java SE 7 ( java.io.File class was the only mechanism used for file I/O), to the new java.nio.file APIs which comes with rich and tremendous functionalities as part of Java SE 7 and above.

The Java 7 file I/O API JSR 203, has been completely re-architected in the Java SE 7 release; and because of that, you cannot swap one method for another method.

To use the rich functionality offered by the java.nio.file package, the easiest solution is to use the File.toPath method. If the previous statement is not sufficient for your needs, you must rewrite your file I/O code.

Before we dive into the mapping of the functionalities from legacy IO to the new one from Java 7 NIO, I have question to ask.

Why should I migrate from old IO to new NIO.2?

If you have developed more than a few applications based on java.io.File, then you should be familiar with not only its methods, but also its methods’ drawbacks.

For example, many of these methods do not throw exceptions when they fail, there is no real support for symbolic links, metadata access is inefficient, file renaming across platforms is inconsistent, and some methods do not scale, enhanced security and so on.

Let us begin the refactoring process:

The first milestone of refactoring java.io.File code may be considered the conversion of File objects into java.nio.file.Path objects through the java.io.File.toPath() method:


After conversion, you can exploit the Path features.

However, while this is the easiest solution, it may not always satisfy your needs.

Sometimes you will need to rewrite your file I/O code and align code to java.nio.file classes, and for this, you can use the one-by-one correspondence between the two APIs.

The following Table shows this correspondence; this table will make your code transition from Java 5 or 6 to Java 7 much easier:

Javadoc Description

java.io.File

java.nio.file

Class name correspondence

java.io.File

java.nio.file.Path

Tests this abstract pathname for equality with the given object

File.equals(Object)

Path.equals(Object)

Compares two abstract pathnames lexicographically

File.compareTo(File)

Path.compareTo(Path)

Returns the absolute pathname string of this abstract pathname

File.getAbsolutePath()

Path.toAbsolutePath()

Returns the absolute form of this abstract pathname

File.getAbsoluteFile()

Path.toAbsolutePath()

Returns the canonical pathname string of this abstract pathname

File.getCanonicalPath()

Path.toRealPath(LinkOption...)

Path.normalize()

Returns the canonical form of this abstract pathname

File.getCanonicalFile()

Path.toRealPath(LinkOption...)

Path.normalize()

Constructs a file: URI that represents this abstract pathname

File.toURI()

Path.toUri()

Tests whether the file denoted by this abstract pathname is a normal file

File.isFile()

Files.isRegularFile(Path, LinkOption ...)

Tests whether the file denoted by this abstract pathname is a directory

File.isDirectory()

Files.isDirectory(Path, LinkOption...)

Tests whether the file named by this abstract pathname is a hidden file

File.isHidden()

Files.isHidden(Path)

Tests whether the application can read the file denoted by this abstract pathname

File.canRead()

Files.isReadable(Path)

Tests whether the application can modify the file denoted by this abstract pathname

File.canWrite()

Files.isWritable(Path)

Tests whether the application can execute the file denoted by this abstract pathname

File.canExecute()

Files.isExecutable(Path)

Tests whether the file or directory denoted by this abstract pathname exists

File.exists()

Files.exists(Path, LinkOption...)

Files.notExists(Path, LinkOption ...)

Creates the directory named by this abstract pathname

File.mkdir()

Files.createDirectory(Path,

FileAttribute<?> ...)

Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories

File.mkdirs()

Files.createDirectories(Path,

FileAttribute<?> ...)

Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist

File.createNewFile()

Files.createFile(Path, FileAttribute<?> ...)

Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname

File.list()

File.listFiles()

Files.newDirectoryStream(Path)

Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter

File.list(FilenameFilter)

File.listFiles(FileFilter)

File.listFiles(FilenameFilter)

Files.newDirectoryStream(Path, DirectoryStream.Filter<? super Path>)

Files.newDirectoryStream(Path, String)

The length of the file denoted by this abstract pathname

File.length()

Files.size(Path)

Deletes the file or directory denoted by this abstract pathname

File.delete()

Files.delete(Path)

Files.deleteIfExists(Path)

Renames the file denoted by this abstract pathname

File.renameTo(File)

Files.move(Path, Path, CopyOption)

Sets the owner or everybody’s execute permission for this abstract pathname

File.setExecutable(boolean, boolean)

Files.setAttribute(Path, String, Object, LinkOption...)

Sets the owner or everybody’s read permission for this abstract pathname

File.setReadable(boolean, boolean)

Files.setAttribute(Path, String, Object, LinkOption...)

Marks the file or directory named by this abstract pathname so that only read operations are allowed

File.setReadOnly()

Files.setAttribute(Path, String, Object, LinkOption...)

Sets the owner or everybody’s write permission for this abstract pathname

File.setWritable(boolean, boolean)

Files.setAttribute(Path, String, Object, LinkOption...)

Returns the time that the file denoted by this

abstract pathname was last modified

File.lastModified()

Files.getLastModifiedTime(Path path, LinkOption... options)

Sets the last-modified time of the file or directory named by this abstract pathname

File.setLastModified(long)

Files.setLastModifiedTime(Path, FileTime)

Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its name

File.createTempFile(String, String)

Files.createTempFile(String prefix, String suffix, FileAttribute<?>... attrs)

Creates a new empty file in the specified directory, using the given prefix and suffix strings to generate its name

File.createTempFile(String, String, File)

Files.createTempFile(Path dir, String prefix, String suffix, FileAttribute<?>... attrs)

Returns the size of the partition named by this abstract pathname

File.getTotalSpace()

FileStore.getTotalSpace()

Returns the number of unallocated bytes in the partition named by this abstract path name

File.getFreeSpace()

FileStore.getUnallocatedSpace()

Returns the number of bytes available to this

virtual machine on the partition named by this abstract pathname

File.getUsableSpace()

FilesStore.getUsableSpace()

Lists the available file system roots

File.listRoots()

FileSystem.getRootDirectories()

Random access file

java.io.RandomAccessFile

java.nio.channels.SeekableByteChannel

Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates

File.deleteOnExit()

Replaced by the DELETE_ON_CLOSE option

Combines two paths

new File(parent,"new_file")

parent.resolve("new_file")

Happy conversion, refactoring, mapping, and migrating IO from earlier JDKs to new JDK7 NIO2 :)

References:
  1. JDK7: Part 1- The power of java 7 NIO.2(JSR 203)(important concepts).
  2. File I/O(Featuring NIO.2).
  3. Package java.nio.file.
  4. Package java.io.
  5. File class.

No comments :

Post a Comment