Core Java

Creating files and directories in NIO.2

Great number of applications nowadays create files or directories for very wide range of purposes. Whether it is to generate a report, export piece of configuration or simply to store some data it is important to be able to handle these tasks. Creating files and directories is one of the most heavily used functionality while working with a file system. This part of library underwent quite a modernization. Updates in this area include guarantee of atomicity of certain operations, creation of files and directories with preset file attributes, performance optimization as well as introduction of exception hierarchy that replaced boolean returning methods from prior versions of IO library.
 
 

Checking methods

Before we get down to any code or explanation, let me take a step back and focus on something that will be essential not only to this post but also a number of posts to come. I find it important to be familiar a few methods usually called checking methods. Checking methods include all those methods used to perform various checks before calling the actual file system manipulation code. For convenience, they are all in class java.nio.file.Files. Using these methods will help you prevent unexpected behavior of your application. Since these methods are really simple, I will skip examples dedicated to them and instead use them in later examples.

Checking methods
Method nameDescription
exists(Path path, LinkOption... options)Tests whether a file exists.
isExecutable(Path path)Tests whether a file is executable.
isHidden(Path path)Tells whether or not a file is considered hidden.
isReadable(Path path)Tests whether a file is readable.
isRegularFile(Path path, LinkOption... options)Tests whether a file is a regular file with opaque content.
isSameFile(Path path, Path path2)Tests if two paths locate the same file.
isWritable(Path path)Tests whether a file is writable.
notExists(Path path, LinkOption... options)Tests whether the file located by this path does not exist.

 

Creating a new directory

One of the most important uses of class Files is to create new directories using method createDirectory. Directory creation is pretty simple and straight forward process so there is not much to explain. As usual it is always a good idea to use checking method exists from class Files to ensure that it is possible to create a directory with given path and also to prevent FileAlreadyExistsException. Whole situation is presented in the following code snippet:

Path newDirectoryPath = Paths.get("/home/jstas/directory");

if (!Files.exists(newDirectoryPath)) {
    try {
        Files.createDirectory(newDirectoryPath);
    } catch (IOException e) {
        System.err.println(e);
    }
}

The code sample is pretty simple – it creates a directory with provided path given no other file system entry resides on provided path. If we need to create whole directory hierarchy then we need to switch to method createDirectories which behaves similarly and creates whole hierarchy defined by a path instance. Since a directory is a type of file we are able to set its own metadata (file attributes). Not only are we able to do this, we might even create metadata definition beforehand and create a directory with initial file attributes in an atomic operation preventing any inconsistencies along the way. As mentioned in my previous article, there are two supported standards for managing file system permissions: POSIX and ACL.

POSIX file permissions

First, lets look at how we can manage file system permissions on POSIX-compliant systems like Linux-based systems and Mac OS. Thanks to the fact that POSIX file permissions are rather simple to understand, library creators provide us with convenience tools such as direct translation from string representation to a set of PosixFilePermissions or conversion tool to convert said set into FileAttribute object. This is not the only way to create FileAttribute object as we will see in next chapter.

Getting back to the example at hand, lets look at the following code. Using convenience method fromString of class PosixFilePermissions we are able to create a set of PosixFilePermissions. Now it is necessary to create FileAttribute instance to be passed to createDirectory method that creates our test directory. Lets look at following snippet of code:

Path newDirectoryPath = Paths.get("/home/jstas/testPosix");

if (!Files.exists(newDirectoryPath)) {
    Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("r-xr-----");
    FileAttribute<Set<PosixFilePermission>> fileAttributes = PosixFilePermissions.asFileAttribute(permissions);

    try {
        Files.createDirectory(newDirectoryPath, fileAttributes);
    } catch (IOException e) {
        System.err.println(e);
    }
}

It is easy to validate whether our permissions were set correctly. You can either read file attributes directly from Java code like I presented in File attributes article or do it manually. I used systems terminal to check them with following output:

dr-xr-----.  2 jstas jstas   4096 Jan  5 13:34 testPosix

ACL file permissions

Things get a little bit more complex when managing file system permissions on ACL-compliant systems such as Windows (NT, 2000, XP and newer). ACL lists can get pretty complex and robust so there are no shortcuts here like with POSIX file permissions. The key here is to use an anonymous class definition based on the interface FileAttribute. This interface defines only two methods: name returns the name of a file attribute and value returns value of this attribute. When working with ACL, the name of an attribute we are interested in is ‘acl:acl’. value method just returns list of constructed ACL entries.

Lets take a look at what’s hidden inside an ACL entry and how to create an instance of AclEntry. First of all, ACL entry consists of several objects:

Given the complexity of a single ACL entry, creators of NIO.2 library saw a very suitable candidate for implementation of a builder pattern. Visit following page for more information on design patterns and builder pattern. So the implementation selects appropriate flags and permissions, binds them with an user principal and sets the type of entry. Please study following code snippet to get familiar with ACL permissions:

Path newDirectoryPath = Paths.get("c:", "testACL");

if (!Files.exists(newDirectoryPath)) {
    FileAttribute<List<AclEntry>> fileAttributes = new FileAttribute<List<AclEntry>>() {

        @Override
        public List<AclEntry> value() {
            // lookup user principal
            FileSystem fileSystem = FileSystems.getDefault();
            UserPrincipalLookupService userPrincipalLookupService = fileSystem.getUserPrincipalLookupService();
            UserPrincipal userPrincipal = null;
            try {
                userPrincipal = userPrincipalLookupService.lookupPrincipalByName("JStas");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            // select ACL flags
            Set<AclEntryFlag> flags = EnumSet.of(AclEntryFlag.FILE_INHERIT, AclEntryFlag.DIRECTORY_INHERIT);

            // select ACL permission
            Set<AclEntryPermission> permissions = EnumSet.of(AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_DATA, AclEntryPermission.EXECUTE);

            // build ACL entry
            Builder builder = AclEntry.newBuilder();
            builder.setFlags(flags);
            builder.setPermissions(permissions);
            builder.setPrincipal(userPrincipal);
            builder.setType(AclEntryType.DENY);

            AclEntry entry = builder.build();
            List<AclEntry> aclEntryList = new ArrayList<>();
            aclEntryList.add(entry);

            return aclEntryList;
        }

        @Override
        public String name() {
            return "acl:acl";
        }
    };

    try {
        Files.createDirectory(newDirectoryPath, fileAttributes);
    } catch (IOException e) {
        System.err.println(e);
    }
}

To verify successful creation of a directory and its file attributes in Windows 7, select security tab in properties of given folder and click on Advanced. Your newly created entry should be listed in presented table with detail view similar to this one:

ACL entry example (Windows 7)
ACL entry example (Windows 7)

Creating a new file

The core part of any file system related code usually involves code that creates single or more files. To create a file we need to use class Files again and call method createFile. Just like a directory, a file can be created with initial file attributes and same restrictions apply. Having said that I’m not going to demonstrate the work with file attributes since it is the same as in directory example. Once again this is really simple method with no catch to it so everything is presented in following example:

Path newFilePath = Paths.get("C:", "a.txt");

if (!Files.exists(newFilePath)) {
    try {
        Files.createFile(newFilePath);
    } catch (IOException e) {
        System.err.println(e);
    }
}

Please notice the use of exists checking method that prevents FileAlreadyExistsException.

Reference: Creating files and directories in NIO.2 from our JCG partner Jakub Stas at the Jakub Stas blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button