Maximum Zeal ~ Emphatic prose on indulged fascinations

JDK7 NIO.2 FS API Primer

Earlier today I was watching Deep Dive: JDK 7 With Danny Coward and it prompted me to play around with the new NIO.2 java.nio.file.* APIs in JDK7 for filesystem access. They are one of the major new features in JDK7 being worked upon under JSR-203.

Here I enumerate almost all the new filesystem APIs by example using the latest binary snapshot of OpenJDK7. Asynchronous IO, socket operations and multicast datagrams are not covered in this article. The sample code is available as an eclipse maven project.

Note that java.io.File is effectively deprecated in JDK7 although it is not explicitly marked as being such. It has been replaced by the much improved java.nio.file.Path. Interoperability is maintained using File.toPath().

Search a directory

The new java.nio.file.Files class provides a walkFileTree() method that allow easy control of the traversal of directories along with pseudo functional predicate based closure style of programming. Note the distinct and welcome lack of for loops.

package jdk7.examples.nio;

import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SearchDirectory {

    final static Logger logger = LoggerFactory.getLogger(SearchDirectory.class);

    public static void main(String[] args) {
        searchDirectory("/home/dhruba/.mozilla", "userChrome-example.css");
    }

    public static void searchDirectory(String searchDir, String searchFile) {

        FileSystem fs = FileSystems.getDefault();
        final Path dirPath = fs.getPath(searchDir);
        final Path filePath = fs.getPath(searchFile);
        logger.info("looking for '{}' in dir '{}'", filePath, dirPath);

        FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                if (file.getName().equals(filePath)) {
                    logger.info("found '{}'", file);
                    return FileVisitResult.TERMINATE;
                }
                logger.info("visited {}", file);
                return FileVisitResult.CONTINUE;
            }
        };

        Files.walkFileTree(dirPath, visitor);
        logger.info("search complete");

    }

}

Look up file metadata

The java.nio.file.attribute package allows cross platform lookup of metadata.

package jdk7.examples.nio;

import java.io.IOException;
import java.nio.file.FileRef;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.attribute.Attributes;
import java.nio.file.attribute.BasicFileAttributes;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LookupFileMetadata {

    static final Logger logger = LoggerFactory.getLogger(LookupFileMetadata.class);

    public static void main(String[] args) throws IOException {
        lookupFileMetadata("/etc/passwd");
    }

    public static void lookupFileMetadata(String file) throws IOException {

        FileSystem fs = FileSystems.getDefault();
        FileRef fileRef = fs.getPath(file);
        logger.info("looking up basic attributes for file {}", fileRef);

        BasicFileAttributes attributes = Attributes.readBasicFileAttributes(fileRef);
        logger.info("isDirectory: {}", attributes.isDirectory());
        logger.info("isOther: {}", attributes.isOther());
        logger.info("isRegularFile: {}", attributes.isRegularFile());
        logger.info("isSymbolicLink: {}", attributes.isSymbolicLink());
        logger.info("creationTime: {}", attributes.creationTime());
        logger.info("lastAccessTime: {}", attributes.lastAccessTime());
        logger.info("lastModifiedTime: {}", attributes.lastModifiedTime());

    }

}

Iterate a directory

Previously the list() and listFiles() methods of java.io.File would hang for long periods when working upon directories over the network or large directories. NIO.2 now offers efficient stream based iteration.

package jdk7.examples.nio;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class IterateDirectory {

    static final Logger logger = LoggerFactory.getLogger(IterateDirectory.class);

    public static void main(String[] args) throws IOException {
        iterateDirectory("/home/dhruba/downloads");
    }

    public static void iterateDirectory(String dir) throws IOException {

        FileSystem fs = FileSystems.getDefault();
        Path tmp = fs.getPath(dir);

        DirectoryStream<Path> stream = tmp.newDirectoryStream("*.pdf");

        for (Path path : stream) {
            logger.info("found path: {}", path);
        }

    }

}

Watch a directory

Instead of polling directories to be notified of any new activity NIO.2 offers efficient event driven directory change notification.

package jdk7.examples.nio;

import static java.nio.file.StandardWatchEventKind.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKind.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKind.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKind.OVERFLOW;

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKind;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WatchDirectory {

    static final Logger logger = LoggerFactory.getLogger(WatchDirectory.class);

    private final WatchService watchService;
    private final Path directory;

    public WatchDirectory(String searchDir) throws IOException {
        FileSystem fs = FileSystems.getDefault();
        watchService = fs.newWatchService();
        directory = fs.getPath(searchDir);
        directory.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE, OVERFLOW);
        logger.info("registered watchService on {}", directory);
    }

    public static void main(String[] args) throws IOException {
        new WatchDirectory("/home/dhruba").watchFile();
    }

    public void watchFile() throws IOException {

        for (;;) {
            WatchKey key;
            try {
                key = watchService.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
                return;
            }

            for (WatchEvent<?> e : key.pollEvents()) {

                @SuppressWarnings("unchecked")
                WatchEvent<Path> event = (WatchEvent<Path>) e;

                Path fileName = event.context();
                Path child = directory.resolve(fileName);
                String contentType = Files.probeContentType(child);

                if (e.kind() == StandardWatchEventKind.ENTRY_CREATE) {
                    logger.info("file was created: {}; contentType: {}", fileName, contentType);
                } else if (e.kind() == StandardWatchEventKind.ENTRY_MODIFY) {
                    logger.info("file was modified: {}; contentType: {}", fileName, contentType);
                } else if (e.kind() == StandardWatchEventKind.ENTRY_DELETE) {
                    logger.info("file was deleted: {}", fileName);
                } else if (e.kind() == StandardWatchEventKind.OVERFLOW) {
                    logger.info("overflow occurred");
                    continue;
                }

                boolean valid = key.reset();
                if (!valid) {
                    logger.info("object no longer registered");
                    break;
                }

            }
        }

    }

}

Manipulate files

The manipulation of disk items is now done using java.nio.file.Path which effectively replaces java.io.File.

package jdk7.examples.nio;

import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.EnumSet;
import java.util.Set;

public class ManipulateFile {

    public static void main(String[] args) throws IOException {
        manipulateFile("/home/dhruba/test");
    }

    public static void manipulateFile(String file) throws IOException {
        FileSystem fs = FileSystems.getDefault();
        Path dir = fs.getPath(file);

        // CREATE
        Path foo = dir.resolve("foo");
        EnumSet<PosixFilePermission> permissions = EnumSet.of(OWNER_READ, OWNER_WRITE,
                OWNER_EXECUTE);
        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions
                .asFileAttribute(permissions);
        foo.createFile(attr);

        // APPEND TO FOO
        OutputStream os = foo.newOutputStream(StandardOpenOption.APPEND);
        os.write("Hello Worldn".getBytes());
        os.close();

        // COPY FOO TO BAR
        Path bar = dir.resolve("bar");
        foo.copyTo(bar);

        // APPEND TO BAR
        SeekableByteChannel sbc = bar.newByteChannel(StandardOpenOption.APPEND);
        sbc.write(ByteBuffer.wrap("Hello Worldn".getBytes()));
        sbc.close();

        // DELETE
        foo.delete();
        bar.delete();

    }

}

Find symbolic links

There is new APIs to interact with symbolic links in a cross platform fashion.

package jdk7.examples.nio;

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FindSymbolicLinks {

    static final Logger logger = LoggerFactory.getLogger(FindSymbolicLinks.class);

    public static void main(String[] args) {
        findSymbolicLinks("/opt");
    }

    public static void findSymbolicLinks(String searchDir) {

        FileSystem fs = FileSystems.getDefault();
        final Path searchPath = fs.getPath(searchDir);

        FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
                if (attributes.isSymbolicLink()) {
                    try {
                        logger.info("found symbolic link: [{}] which resolves to: [{}]", file, file
                                .readSymbolicLink());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return FileVisitResult.CONTINUE;
            }
        };
        Files.walkFileTree(searchPath, visitor);

    }

}

In summary NIO.2 address many of the issues of older jdk versions and provides APIs that were previously sorely missing. To try out the above examples download openjdk and use with your favourite editor.

Update [14/09/2009]: Previously the code examples weren’t html entity encoded; they are now.

7 Responses to JDK7 NIO.2 FS API Primer

  1. Good examples, thanks,
    In “Manipulate files” you have “FileAttribute> attr” which should probably be without “>”.
    Would also be good the pages was little wider, unless it’s designed for best view by mobile phone users.

  2. Brian – thanks for your comments and suggestions. Regarding the mistakes you found it was because I’d forgotten to html entity encode all code snippets (a tedious task!). This is now fixed. And as per your suggestions the width of the blog is now wide enough to accommodate the code. I’d been pondering this for a while and have finally done it. :-)

  3. Looks better now.
    I hoped the devs went the extra mile to also enable NIO2 to detect character and block devices, socket and pipe files under Linux. Since I’m working over a Java file browser (for Ubuntu), that would allow to better detect the file type.

  4. Hi Dhruba,

    Excellent article, I have question for you. The organization that I work for is still using JDK6, is there anyway I can just extract the java.nio package and create a new jar of it, say nio2.jar and run your examples ? I tried and got
    [Exception in thread “main” java.lang.SecurityException: Prohibited package name : java.nio.file]
    I tried playing around by manually moving the classed to different packages and re-jarring, but didn’t work as some of the classes have references to /java/nio/… embedded in them. Would greatly appreciate your input.

    Thanks!

  5. On Windows 7:

    at java.nio.file.Files.setPosixFilePermissions(Files.java:1974)
    	at powerpack.controler.ManipulateFile.(ManipulateFile.java:35)
    

    I would like create a simple program. Pragram will be compressing file. Buat when I use java.io.File i get Acces Denie
    For example:

    …..
    path = “C://New Folder”;
    File f = null

    f = new File(path);

    How Can I resolve my problem?

  6. Your SearchDirectory code contains two errors that prevent compilation:
    Line 33 reads if (file.getName().equals(filePath)) {
    but should instead be if (file.getFileName().equals(filePath)) {

    and the line 42 statement Files.walkFileTree(dirPath, visitor);
    must be surrounded with a try/catch block

Leave a Reply