viernes, abril 01, 2011

'Cos The Only Thing Misplaced Was Direction And I Found Direction There Is No Childhood's End (Childhoods End - Marillion)




JSR 203 is a new specification implemented in JDK 7, and is about NIO 2.0. JSR 203 defines three points of improvement:
  • Filesystem interface.
  • Complete Socket-Channel Functionality.
  • Support for Asynchronous I/O.

In this post I will talk about filesystem interface, because there are new classes that implements some file functionalities that more often than not I had developed myself in projects. In summary we will take a look to Path class and Files class.

Path is "an object that may be used to locate a file in a file system. It will typically represent a system dependent file path". Well in common language a path is the route to a resource. More or less like old File class, in fact java.io.File has a new method toPath() that returns a Path instance.

Files class is an utility class that implements common file operations like creating, finding (you say finding? yes), deleting, reading contents ...

For me typical operations that are implemented in Files class are:

* Delete a file (/tmp/test/sec.txt)

Path file = Paths.get("/tmp/test", "sec.txt");
Files.delete(file);

Files.delete method deleates passed file path, and that's important because with not empty directory a DirectoryNotEmptyException is thrown.

And how about deleting a directory with contents? let me introduce another new concept. FileVisitor interface. Files class has a method for walking along all directories tree. This is useful if you wanted for example implements the tree *nix command (prints all directory structure for given root directory). But in my case I will implement a complete delete function (*nix equivalent command would be rm -Rf /tmp/test).



Previous code is equivalent to command rm -Rf /tmp/test. SimpleFileVisitor is an implementation of FileVisitor. It implements default behavior for all FileVisitor methods, and in this case we override visitFile for deleting files. After all files of current directory are visited postVisitDirectory method is called and current directory can be deleted safetly. See that the result in this case is CONTINUE (continuing walking through tree, but as you can imagine you have other options like SKIP_SIBLINGS, SKIP_SUBTREE or TERMINATE).


* Copy a File

Files class has three main methods for copying files. It is important to note that you can copy files and directories, however files inside the directory are not copied, so the new directory is emptied even when the original directory contains files. 

Files.copy(source, target, REPLACE_EXISTING);

Moreover two methods are also present for copying files. Files.copy(InputStream, Path, CopyOptions...) and Files.copy(Path, OutputStream). As you can imagine first method takes an inputstream and copy its content into given Path, and the second one, reads given Path and sends file content to passed outputstream.

Same is applied for Files.move(Path, Path, CopyOption...).

For copying all directory structure with their files, a FileVisitor could be implemented as in previous example.


* Manage Metadata Information

Two main classes are created for managing files metadata. One class for reading metadata attributes like permissions, is hidden, is read-only, owner, modified-time, ... Because each OS manages file attributes in different manner, an implementation of that class are provided for each OS. For example there is an implementation for DOS systems, another one for POSIX systems, or even a helper class for implemting yourself attributes reader.

Posix example for printing group which belongs given Path:


The other class is FileStore. A FileStore represents a storage pool, device, partition, volume, concrete file system or other implementation specific means of file storage. This class is used for calculating disk usage.



* Reading, Writing, Random Access

Files class has methods for creating inputstreams, outputstream, readers, channels, ... because this features are not new I won't explain extensively. For example for acquiring an InputStream instance Files.newInputStream, Files.newOutputStream, ...

* Symbolic and Hard Link

With NIO 2.0 you can create a symbolic link http://en.wikipedia.org/wiki/Symbolic_link or a hard link http://en.wikipedia.org/wiki/Hard_link from a path.

Creating a symbolic link to a directory:



Creating a hard link to a file:



* Finding Files

Sometimes my programs should find all files with extension xml contained in a directory and manage them (for example create a zip with them). Before java NIO 2, a FileFilter was created and then create a matcher for only selecting those with xml extension. Now PathMatcher class can be used instead of creating yourself matcher. This matcher accepts Glob Syntax http://en.wikipedia.org/wiki/Glob_(programming) or Regular Expression. Glob syntax is easy to use and flexible and most of us have been used  (in console operations) although we didn't know it was Glob Syntax.

Let's implement the next *nix script ls -R *.html in java.



As you can see this implementation of FileVisitor follows all directories structure finding all html files. See that there are two important lines:

matcher = FileSystems.getDefault().getPathMatcher("glob:*.html");

where we are creating a matcher that only returns true if file ends with html.

if (name != null && matcher.matches(name))

that returns true if current file matches the the glob condition.

Thanks of FileVisitor and PathMatcher this routine can be reused for any kind of files (modifying glob expression). Think now if you should do the same with java.io.File class, you would create a FileFilter where if you wanted to create a flexible solution you should create a Pattern Matcher, iterate over matches, and deal with recursive navigation through directory tree. See with PathMatcher how easy is changing between selecting all files ending with html extension to selecting all files starting with word 'Test'.

* Watcher Service

This new feature is really useful for receiving events when a directory has been changed by creation, modification or deletion of a file. Before NIO 2.0 you should create an "infinite-loop" that was watching if a new file was created. And this was done listening all files and comparing modification date or comparing with previous loop execution. Now this service automatizes all logic, and simply throws an event when registered change occurs.



Two final notes, first is that events are only thrown for files not for directories, and second one, watch service is not recursive, so it will only throws an event in case of files created into /tmp not /tmp/my-directory. As with all java NIO 2.0 you have FileVisitor interface for dealing with recursive directory tree.


* Determine MIME Type

If your application requires to know MIME type, Files class  has a probeContentType. The implementation of this method is highly platform specific and is not infallible.

Path path = Paths.get("/tmp/dataset.xml"); System.out.println(Files.probeContentType(path));

It returns text/xml.


And those are all new features of NIO 2 that have changed my developer life when I have to develop an application where managing files are required. I wish you find them as usually as I do.

0 comentarios: