Some JSR 203 Examples

12

I’ve been preparing for some upcoming Java SE 7 Preview presentations I’m doing on the No Fluff tour in Reston, VA and Chicago, IL. I worked up some JSR 203 (the new new I/O JSR) examples and thought I would drop them here in case they were useful. These are based on the latest Javadocs available on the project page (b97).

I’m not putting much context here. You can read a well written summary from Elliotte Rusty Harold or watch the JavaOne presentation. Or come see my talks. :)

I actually did a talk like this about 18 months ago and went back and looked at it and the API has changed a lot since then. In many ways, it’s been simplified and consolidated much to its overall benefit. In just the last few months, there’s been a lot of polishing, especially making better use of enums and little things like that. Some of those changes are good and some are pretty long-winded in use.

Path is the new File. It’s a little more nuanced than that, but Path is definitely the center of the new filesystem API. Here’s some basic fiddling with it:

import java.nio.file.*;

// FileSystems -> FileSystem -> Path
FileSystem fileSystem = FileSystems.getDefault();
Path homeDir = fileSystem.getPath(“/Users/amiller”);

// Shortcut with Paths helper class
Path homeDir = Paths.get(“/Users/amiller”);

// Resolve one path in terms of another
Path relativeTemp = Paths.get(“temp”);
Path absoluteTemp = relativeTemp.resolve(homeDir);

// Get relative path from a base
Path absoluteProfile = Paths.get(“/Users/amiller/.profile”);
Path relativeProfile = absoluteProfile.relativize(homeDir);
assert relativeProfile.isRelative();
assert relativeProfile.getNameCount() == 1;

And here’s an example of opening and appending to a file:

import java.io.*;
import java.nio.file.*;
import static java.nio.file.StandardOpenOption.*;

Path journal = Paths.get(“/Users/amiller/journal.txt”);

OutputStream stream = journal.newOutputStream(CREATE, APPEND);

try { 

writeEntry(stream); // normal stuff
} finally {
stream.close();
}

One nice thing is that copy and move are built-in (at long, long last):

import java.nio.file.*;

Path home = Paths.get(“/Users/amiller”);
Path secrets = home.resolve(“secrets.txt”);

// Steal secrets
secrets.copyTo(home.resolve(“stolenSecrets.txt”));

// Hide secrets
secrets.moveTo(Paths.get(“/Users/dvader/secrets.txt”));

There is lots of support now for walking directories – here are some internal and external iterator versions:

Path music = Paths.get(“/Users/amiller/files/music”);

// External iterator
DirectoryStream mp3s = music.newDirectoryStream(“*.mp3”);
try {
for(Path entry : mp3s)
System.out.println(entry.getName());
} finally {
mp3s.close();
}

// Internal iterator
Files.withDirectory(music, “*.mp3”, new FileAction<Path>() {
public void invoke(Path entry) {
System.out.println(entry.getName());
}
}

And there is even a visitor pattern for walking a directory recursively (with support for following symbolic links, checking for cycles, etc):

Path itunes = Paths.get(“/Users/amiller/Music/iTunes/iTunes Music”);

public class Mp3Visitor extends SimpleFileVisitor<Path> {
private Path root;
public Mp3Visitor(Path root) {
this.root = root;
}

public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println(root.relativize(file));
}
}

Files.walkFileTree(itunes, new Mp3Visitor(itunes));

And of course, we also finally have support for accessing posix file attributes as part of the API (and some other common attribute sets and extensible support for other pluggable file systems):

Path file = Paths.get(“/usr/bin/perl”);

// true here means follow symbolic links
BasicFileAttributes attrs =
Attributes.readPosixFileAttributes(file, true);
Set perms = attrs.permissions();

System.out.format(“%s %s %s”, PosixFilePermission.toString(attrs.permissions()), attrs.owner(), attrs.group());

// rwxr-xr-x root wheel

Finally, there is now support for generic “watch services” to watch file events in a directory (with efficient support depending on the platform):

import static java.nio.file.StandardWatchEventKind.*;

Path deploy = Paths.get(“deploy”);
WatchService watcher = FileSystems.getDefault().newWatchService();
WatchKey key = deploy.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY } );

for(;;) {
key = watcher.take(); // blocks, also can poll
for(WatchEvent<?> ev : key.pollEvents()) {
switch(event.kind()) {
case ENTRY_CREATE:
Path file = (Path)ev.getContext(); //relative to deploy
// deploy new stuff
case ENTRY_MODIFY: …
case ENTRY_DELETE: …
}
}
key.reset(); // reset after processing
}

If anyone spots any bugs, please let me know.

Comments

12 Responses to “Some JSR 203 Examples”
  1. Tuomas Kiviaho says:

    I’ve been a bit skeptic how JSR 203 this number one of it’s major components turns out to be. I my self am more exited about numbers 2 and 3 but I hope still than Apache Commons VFS would be obsoleted by number one.

    I was afraid that we get yet another not so satisfying reimplementation such as java.util.logging.

  2. Alan says:

    Thanks Alex – this is a good taster for this part of the API. In the binary download there are more examples in the $JDK_HOME/sample/nio/file directory that may be useful.

    One nit, is that you use “~/Music” in one or two examples on the assumption that you are getting shell-like expansion. I assume you are on Linux or Unix, and in that case, ‘~’ is a valid character to have in a file name isn’t expanded by the file system — eg: Paths.get(“~”).createFile() will create a file named “~” in the current directory rather than fail because your home directory exists.

    Personally, I find it useful to do a static import of StandardOpenOption.* to keep the code that opens files a bit shorter.

  3. Buddy Casino says:

    At last, Java has a useful filesystem API. And it only took us ~50 felt years. I can hardly contain my feelings… *sniff*

  4. Mittal says:

    good stuff…

  5. Alex says:

    Thanks Alan – I made some tweaks based on your suggestions.

  6. Anonymous says:

    shouldn’t stream.close(); be mp3s.close()?

  7. Alex says:

    @Anonymous: Right on. Fixed.

  8. Walter says:

    Seems odd that Path would have methods to get, e.g., an OutputStream. There are plenty of ways to open a file, why would they encumber Path with all of them or (alternately) declare some to be better than others? Seems to suck in a lot of otherwise unnecessary type linkage. Convenient if that’s your use case, though.

    Do illegally named Paths barf during creation or during access?

  9. Bruce says:

    new StandardWatchEventKind[ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY]

    or

    new StandardWatchEventKind[] {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}

    ??

  10. Alex says:

    @Walter: I’d say opening a file is an awfully common use case. There are really only a few basic ways to open access to a file – the classic Input/OutputStreams of course, and the new SeekableByteChannel which works from ByteBuffers.

    When you create a path (from a Filesystem or the helper Paths class), if the path is invalid it will throw an InvalidPathException (unchecked). The rules defining what paths are invalid depends on the underlying file system (and possibly the file store as well).

    @Bruce: right on. This is why I ask for feedback… :) And also why I don’t write code in a text editor anymore. Fixed!

  11. Orion Letizi says:

    Have there been discussions about how to mock this stuff up for tests? One of the things that chaps me about the existing java filesystem api is that they are all concrete classes and very difficult to mock up for unit tests.

  12. Alex says:

    @Orion: I’ve actually had discussions about that with various folks over the last couple years. Because the underpinnings of the new file API are based on the concept of pluggable filesystems, there is actually the potential for an enormous amount of support.

    For instance, you could build a virtual file system that actually worked in-memory so that as your tests wrote to files, they were not actually touching disk, which of course means that it’s easy to clean up, easy to write tests that don’t depend on the runtime filesystem environment, and easy to avoid things like Windows file locking madness. If I recall correctly, no such thing will ship with the jdk but someone has already built a proof of concept.