Java 7: Managing Files and Directories

Exclusive offer: get 50% off this eBook here
Java 7 New Features Cookbook

Java 7 New Features Cookbook — Save 50%

Over 100 comprehensive recipes to get you up-to-speed with all the exciting new features of Java 7 with this book and ebook

$23.99    $12.00
by Jennifer L. Reese Richard M. Reese | February 2012 | Java

In this article by Richard M.Reese and Jennifer L.Reese authors of Java 7 New Features Cookbook , we will cover the following:

  • Creating files and directories
  • Controlling how a file is copied
  • Managing temporary files and directories
  • Setting time-related attributes of a file or directory
  • Managing file ownership
  • Managing ACL file permissions
  • Managing POSIX attributes

(For more resources on Java, see here.)

Introduction

It is often necessary to perform file manipulations, such as creating files, manipulating their attributes and contents, or removing them from the filesystem. The addition of the java.lang.object.Files class in Java 7 simplifies this process. This class relies heavily on the use of the new java.nio.file.Path interface. The methods of the class are all static in nature, and generally assign the actual file manipulation operations to the underlying filesystem.

Many of the operations described in this chapter are atomic in nature, such as those used to create and delete files or directories. Atomic operations will either execute successfully to completion or fail and result in an effective cancellation of the operation. During execution, they are not interrupted from the standpoint of a filesystem. Other concurrent file operations will not impact the operation.

To execute many of the examples in this chapter, the application needs to run as administrator. To run an application as administrator under Windows, right-click on the Command Prompt menu and choose Run as administrator. Then navigate to the appropriate directory and execute using the java.exe command. To run as administrator on a UNIX system, use the sudo command in a terminal window followed by the java command.

Basic file management is covered in this chapter. The methods required for the creation of files and directories are covered in the Creating Files and Directories recipe. This recipe focuses on normal files. The creation of temporary files and directories is covered in the Managing temporary files and directories recipe and the creation of linked files is covered in the Managing symbolic links recipe.

The options available for copying files and directories are found in the Controlling how a file is copied recipe. The techniques illustrated there provide a powerful way of dealing with file replication. Moving and deleting files and directories are covered in the Moving a file or directory and Deleting files and directories recipes, respectively.

The Setting time-related attributes of a file or directory recipe illustrates how to assign time attributes to a file. Related to this effort are other attributes, such as file ownership and permissions. File ownership is addressed in the Managing file ownership recipe. File permissions are discussed in two recipes: Managing ACL file permissions and Managing POSIX file permissions.

Creating files and directories

The process of creating new files and directories is greatly simplified in Java 7. The methods implemented by the Files class are relatively intuitive and easy to incorporate into your code. In this recipe, we will cover how to create new files and directories using the createFile and createDirectory methods.

Getting ready

In our example, we are going to use several different methods to create a Path object that represents a file or directory. We will do the following:

  1. Create a Path object.
  2. Create a directory using the Files class' createDirectory method.
  3. Create a file using the Files class' createFile method.

The FileSystem class' getPath method can be used to create a Path object, as can the Paths class' get method. The Paths class' static get method returns an instance of a Path based on a string sequence or a URI object. The FileSystem class' getPath method also returns a Path object, but only uses a string sequence to identify the file.

How to do it...

  1. Create a console application with a main method. In the main method, add the following code that creates a Path object for the directory /home/test in the C directory. Within a try block, invoke the createDirectory method with your Path object as the parameter. This method will throw an IOException if the path is invalid. Next, create a Path object for the file newFile.txt using the createFile method on this Path object, again catching the IOException as follows:
  2. try{

    Path testDirectoryPath = Paths.get("C:/home/test");
    Path testDirectory = Files.createDirectory(testDirectoryPath);
    System.out.println("Directory created successfully!");
    Path newFilePath = FileSystems.getDefault().

    getPath("C:/home/test/newFile.txt");
    Path testFile = Files.createFile(newFilePath);
    System.out.println("File created successfully!");
    }
    catch (IOException ex){

    ex.printStackTrace();
    }

  3. Execute the program. Your output should appear as follows:
    Directory created successfully!
    File created successfully!
  4. Verify that the new file and directory exists in your filesystem. Next, add a catch block prior to the IOException after both methods, and catch a FileAlreadyExistsException:
  5. }
    catch (FileAlreadyExistsException a){

    System.out.println("File or directory already exists!");
    }
    catch (IOException ex){

    ex.printStackTrace();
    }

  6. When you execute the program again, your output should appear as follows:
    File or directory already exists!

How it works...

The first Path object was created and then used by the createDirectory method to create a new directory. After the second Path object was created, the createFile method was used to create a file within the directory, which had just been created. It is important to note that the Path object used in the file creation could not be instantiated before the directory was created, because it would have referenced an invalid path. This would have resulted in an IOException.

When the createDirectory method is invoked, the system is directed to check for the existence of the directory first, and if it does not exist, create it. The createFile method works in a similar fashion. The method fails if the file already exists. We saw this when we caught the FileAlreadyExistsException. Had we not caught that exception, an IOException would have been thrown. Either way, the existing file would not be overwritten.

There's more...

The createFile and createDirectory methods are atomic in nature. The createDirectories method is available to create directories, as discussed next. All three methods provide the option to pass file attribute parameters for more specific file creation.

Using the createDirectories method to create a hierarchy of directories

The createDirectories method is used to create a directory and potentially other intermediate directories. In this example, we build upon the previous directory structure by adding a subtest and a subsubtest directory to the test directory. Comment out the previous code that created the directory and file and add the following code sequence:

Path directoriesPath = Paths.

get("C:/home/test/subtest/subsubtest");
Path testDirectory = Files.createDirectories(directoriesPath);

Verify that the operation succeeded by examining the resulting directory structure.

See also

Creating temporary files and directories is covered in the Managing temporary files and directories recipe. The creation of symbolic files is illustrated in the Managing symbolic links recipe.

Controlling how a file is copied

The process of copying files is also simplified in Java 7, and allows for control over the manner in which they are copied. The Files class' copy method supports this operation and is overloaded providing three techniques for copying those which differ by their source or destination.

Getting ready

In our example, we are going to create a new file and then copy it to another target file. This process involves:

  1. Creating a new file using the createFile method.
  2. Creating a path for the destination file.
  3. Copying the file using the copy method.

How to do it...

  1. Create a console application with a main method. In the main method, add the following code sequence to create a new file. Specify two Path objects, one for your initial file and one for the location where it will be copied. Then add the copy method to copy that file to the destination location as follows:

  2. Path newFile = FileSystems.getDefault().

    getPath("C:/home/docs/newFile.txt");
    Path copiedFile = FileSystems.getDefault().

    getPath("C:/home/docs/copiedFile.txt");
    try{

    Files.createFile(newFile);
    System.out.println("File created successfully!");
    Files.copy(newFile, copiedFile);
    System.out.println("File copied successfully!");
    }
    catch (IOException e){

    System.out.println("IO Exception.");
    }

  3. Execute the program. Your output should appear as follows:
    File created successfully!
    File copied successfully!
  4. When you execute the program again, your output should appear as follows:
    File copied successfully!

How it works...

The createFile method created your initial file, and the copy method copied that file to the location specified by the copiedFile variable. If you were to attempt to run that code sequence twice in a row, you would have encountered an IOException, because the copy method will not, by default, replace an existing file. The copy method is overloaded. The second form of the copy method used the java.lang.enum.StandardCopyOption enumeration value of REPLACE_EXISTING, which allowed the file to be replaced.

The three enumeration values for StandardCopyOption are listed in the following table:

Value

Meaning

ATOMIC_MOVE

Perform the copy operation

atomically

COPY_ATTRIBUTES

Copy the source file attributes

to the destination file

REPLACE_EXISTING

Replace the existing file

if it already exists

Replace the copy method call in the previous example with the following:

Files.copy(newFile, copiedFile, StandardCopyOption.REPLACE_EXISTING);

When the code executes, the file should be replaced. Another example of the use of the copy options is found in the There's more... section of the Moving a file and directory recipe.

There's more...

If the source file and the destination file are the same, then the method completes, but no copy actually occurs. The copy method is not atomic in nature.

There are two other overloaded copy methods. One copies a java.io.InputStream to a file and the other copies a file to a java.io.OutputStream. In this section, we will examine, in more depth, the processes of:

  • Copying a symbolic link file
  • Copying a directory
  • Copying an input stream to a file
  • Copying a file to an output stream

Copying a symbolic link file

When a symbolic link file is copied, the target of the symbolic link is copied. To illustrate this, create a symbolic link file called users.txt in the music directory to the users.txt file in the docs directory.

Use the following code sequence to perform the copy operation:


Path originalLinkedFile = FileSystems.getDefault(). getPath("C:/home/music/users.txt");
Path newLinkedFile = FileSystems.getDefault(). getPath("C:/home/music/users2.txt");
try{

Files.copy(originalLinkedFile, newLinkedFile);
System.out.println("Symbolic link file copied successfully!");
}
catch (IOException e){

System.out.println("IO Exception.");
}

Execute the code. You should get the following output:
Symbolic link file copied successfully!

Examine the resulting music directory structure. The user2.txt file has been added and is not connected to either the linked file or the original target file. Modification of the user2.txt does not affect the contents of the other two files.

Copying a directory

When a directory is copied, an empty directory is created. The files in the original directory are not copied. The following code sequence illustrates this process:

Path originalDirectory = FileSystems.getDefault(). getPath("C:/home/docs");
Path newDirectory = FileSystems.getDefault(). getPath("C:/home/tmp");
try{

Files.copy(originalDirectory, newDirectory);
System.out.println("Directory copied successfully!");
}
catch (IOException e){

e.printStackTrace();
}

When this sequence is executed, you should get the following output:
Directory copied successfully!

Examine the tmp directory. It should be empty as any files in the source directory are not copied.

Copying an input stream to a file

The copy method has a convenient overloaded version that permits the creation of a new file based on the input from an InputStream. The first argument of this method differs from the original copy method, in that it is an instance of an InputStream.

The following example uses this method to copy the jdk7.java.net website to a file:

Path newFile = FileSystems.getDefault(). getPath("C:/home/docs/java7WebSite.html");
URI url = URI.create("http://jdk7.java.net/");
try (InputStream inputStream = url.toURL().openStream())

Files.copy(inputStream, newFile);
System.out.println("Site copied successfully!");
}
catch (MalformedURLException ex){

ex.printStackTrace();
}
catch (IOException ex){

ex.printStackTrace();
}

When the code executes, you should get the following output:
Site copied successfully!

A java.lang.Object.URI object was created to represent the website. Using the URI object instead of a java.lang.Object.URL object immediately avoids having to create a separate try-catch block to handle the MalformedURLException exception.

The URL class' openStream method returns an InputStream, which is used as the first parameter of the copy method.

The copy method was then executed. The new file can now be opened with a browser or otherwise can be processed as needed. Notice that the method returns a long value representing the number of bytes written.

Copying a file to an output stream

The third overloaded version of the copy method will open a file and write its contents to an OutputStream. This can be useful when the content of a file needs to be copied to a non-file object such as a PipedOutputStream. It can also be useful when communicating to other threads or writing to an array of bytes, as illustrated here. In this example, the content of the users.txt file is copied to an instance of a ByteArrayOutputStream>. Its toByteArray method is then used to populate an array as follows:


Path sourceFile = FileSystems.getDefault(). getPath("C:/home/docs/users.txt");
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

Files.copy(sourceFile, outputStream);
byte arr[] = outputStream.toByteArray();
System.out.println("The contents of " + sourceFile.getFileName());
for(byte data : arr) {

System.out.print((char)data);
}
System.out.println();
}
catch (IOException ex) {

ex.printStackTrace();
}

Execute this sequence. The output will depend on the contents of your file, but should be similar to the following:

The contents of users.txt
Bob
Jennifer
Sally
Tom
Ted

Notice the use of the try-with-resources block that handles the opening and closing of the file. It is always a good idea to close the OutputStream, when the copy operation is complete or exceptions occur. The try-with-resources block handles this nicely. The method may block until the operation is complete in certain situations. Much of its behavior is implementation-specific. Also, the output stream may need to be flushed since it implements the Flushable interface. Notice that the method returns a long value representing the number of bytes written.

See also

See the Managing symbolic links recipe for more details on working with symbolic links.

Java 7 New Features Cookbook Over 100 comprehensive recipes to get you up-to-speed with all the exciting new features of Java 7 with this book and ebook
Published: February 2012
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

(For more resources on Java, see here.)

Managing temporary files and directories

The process of creating temporary files and directories can be an essential part of many applications. Temporary files may be used for intermediate data or as a temporary store to be cleaned up later. The process of managing temporary files and directories can be accomplished simply via the Files class. In this recipe, we will cover how to create temporary files and directories using the createTempDirectory and createTempFile methods.

Getting ready

In our example, we are going to create a temporary directory and then create a temporary file within the directory as follows:

  1. Create Path<>> objects representing the temporary file and directory.
  2. Create a temporary directory using the createTempDirectory method.
  3. Create a temporary file using the createTempFile method.

How to do it...

  1. Create a console application with a main method. In the main method, create a Path object rootDirectory using the getPath method. Invoke the createTempDirectory method using rootDirectory as the first argument, and an empty string as the second argument. Then use the toString method to convert the returning Path object dirPath to a String and print it to the screen. Next, add the createTempFile method using dirPath as the first argument with empty strings as the second and third arguments. Use the toString method again to print out this resulting path as follows:

    try {

    Path rootDirectory = FileSystems.getDefault().getPath("C:/home/docs");
    Path tempDirectory = Files.createTempDirectory(rootDirectory, "");
    System.out.println("Temporary directory created successfully!");

    String dirPath = tempDirectory.toString();
    System.out.println(dirPath);
    Path tempFile = Files.createTempFile(tempDirectory,"", "");
    System.out.println("Temporary file created successfully!");

    String filePath = tempFile.toString();
    System.out.println(filePath);
    }
    catch (IOException e) {

    System.out.println("IO Exception.");
    }

  2. This code sequence will result in an output similar to the following:
    Temporary directory created successfully!
    C:\home\docs\7087436262102989339
    Temporary file created successfully!
    C:\home\docs\7087436262102989339\3473887367961760381

How it works...

The createTempDirectory method creates an empty directory and returns a Path object representing the location of this new directory. Likewise, the createTempFile method creates an empty file and returns a Path object representing this new file. In our previous example, we used the toString method to see the path where our directory and file were created. The previous numeric directory and filenames are assigned by the system and are platform-specific.

This createTempDirectory method requires at least two parameters, namely, the Path object directing the location for the new directory and a String variable specifying the directory prefix. In our previous example, we left the prefix blank. However, if we had wanted to specify text to precede the filename assigned by the system, the second variable could have been populated with this prefix string.

The createTempFile method works in a similar manner as the createTempDirectory method, and had we wanted to assign a prefix to our temporary file, we could have used the second parameter to specify the string. The third parameter of this method could have also been used to specify a suffix, or file type, for our file, such as .txt.

It is important to note that, although in our example we specified the Path in which we wanted our directory and file created, there is another version of each method in which the initial argument, the Path object, could be omitted, and the directory and/or file would be created in the system's default temporary directory. Additionally, these methods do not check for the file or directory's existence before creating them, and will overwrite any existing file or directory with the same temporary, system-assigned name.

There's more...

File attribute names can also be passed to the overloaded createTempDirectory or createTempFile methods. These attributes are optional, but can be used to specify how the temporary files will be handled, such as whether the file should be deleted upon closing.

The createTempDirectory and the createTempFile methods are intended to have a limited existence. If it is desirable to delete these files or directories automatically, a shut-down hook or the java.io.File class' deleteOnExit method can be used. These two techniques will result in the deletion of the element, when the application or the JVM terminates.

Setting time-related attributes of a file or directory

The timestamp for a file can be critical for some applications. For example, the order in which operations execute may be dependent on the time a file was last updated. There are three dates supported by the BasicFileAttributeView:

  • The last modified time
  • The last access time
  • The creation time

They can be set using the BasicFileAttributeView interface's setTimes method. As we will see in the There's more... section, the Files class can be used to set or get only the last modified time.

Getting ready

In order to set the times using the setTimes method. We need to do the following:

  1. Obtain a Path object, which represents the file of interest.
  2. Obtain a BasicFileAttributeView object.
  3. Create FileTime objects for the times needed.
  4. Use these FileTime objects as arguments of the setTimes method.

How to do it...

  1. Create a new console application using the following main method. We will update the last modified time of our favorite file users.txt to the current time:

    public static void main(String[] args) throws Exception {

    Path path = Paths.get("C:/home/docs/users.txt");
    BasicFileAttributeView view = Files. getFileAttributeView(path, BasicFileAttributeView.class);
    FileTime lastModifedTime;
    FileTime lastAccessTime;
    FileTime createTime;

    BasicFileAttributes attributes = view.readAttributes();
    lastModifedTime = attributes.lastModifiedTime();
    createTime = attributes.creationTime();

    long currentTime = Calendar.getInstance().getTimeInMillis();
    lastAccessTime = FileTime.fromMillis(currentTime);

    view.setTimes(lastModifedTime, lastAccessTime, createTime);
    System.out.println(attributes.lastAccessTime());
    }

  2. Execute the application. Unless you have access to a time machine, or have otherwise manipulated your system's clock, your output should reflect a time later than the time shown as follows:
    2011-09-24T21:34:55.012Z

How it works...

A Path was first created for the users.txt file. Next, an instance of the BasicFileAttributeView interface was obtained using the getFileAttributeView method. A try block was used to catch any IOExceptions that might be thrown by the readAttributes or setTimes methods.

Within the try block, FileTime objects were created for each of the three types of time. The lastModifedTime and createTime times were not changed for the file. These were obtained using the corresponding methods of the BasicFileAttributes class, which was obtained using the view method.

The currentTime long variable was assigned the current time expressed in milliseconds. Its value was obtained using the getTimeInMillis method executed against an instance of the Calendar class. The three FileTime objects were then used as arguments to the setTimes method, effectively setting these time values.

There's more...

There is more to the use of the FileTime class than presented so far. In addition, the Files class provides alternative approaches for maintaining times. Here we will further explore the following:

  • Understanding the FileTime class
  • Using the Files class' setLastModifiedTime to maintain the last modified time
  • Using the Files class' setAttribute method to set individual attributes

Understanding the FileTime class

The java.nio.file.attribute.FileTime class represents the time for use with several of the java.nio package methods. To create a FileTime object, we need to use either of the following two static FileTime methods:

  • The from method, which accepts a long number representing a duration and a TimeUnit object representing a unit of time measurement
  • The fromMillis method, which accepts a long argument representing the number of milliseconds based on the epoch

TimeUnit is an enumeration found in the java.util.concurrent package. It represents a time duration, as defined in the following table. It is used in conjunction with another parameter whose combination represents a time duration:

Enumeration Value

Meaning

NANOSECONDS

One thousandth of

a microsecond

MICROSECONDS

One thousandth of

a millisecond

MILLISECONDS

One thousandth of

a second

SECONDS

A second

MINUTES

Sixty seconds

HOURS

Sixty minutes

DAYS

Twenty four hours

The from method returns a TimeUnit object. Its value is computed by adding the first long argument, whose unit of measure is specified by the second TimeUnit argument, to the epoch.

The epoch is 1970-01-01T00:00:00Z, which is the base time used for specifying time on most computers. This base time represents midnight, Coordinate Universal Time on January 1, 1970.

For example, the from method can be used to present a point in time, which is 1000 days from the epoch using the following code sequence:

FileTime fileTime = FileTime.from(1000, TimeUnit.DAYS);
System.out.println(fileTime);

When executed, you should get the following output:
1972-09-27T00:00:00Z

The fromMillis method is used to create a FileTime object, whose time is represented by adding its argument to the epoch where the argument is a long number representing a value in milliseconds. If we used the following fromMillis method instead of the from method as follows:

FileTime fileTime = FileTime.fromMillis(1000L*60*60*24*1000);

We will get the same results. Notice that the first argument is a long literal, which forces the result of the expression to be a long number. If we did not promote our results to be long values, we would have received an integer value, which would have resulted in overflow and an incorrect date. The first argument of either method can be negative.

A good reference for more details regarding the use of time in Java is at

Using the Files class' setLastModifiedTime to maintain the last modified time

The Files class' getLastModifiedTime and setLastModifiedTime methods provide an alternative approach for setting the last modified attribute of a file. In the following code sequence, the set method uses the lastModifedTime object to set the time as follows:

Files.setLastModifiedTime(path, lastModifedTime);

The Files class' getLastModifiedTime returns a FileTime object. We could have this method to assign a value to the lastModifedTime variable as follows:

lastModifedTime = Files.getLastModifiedTime(path);

The method has an optional LinkOption argument that indicates whether symbolic links should be followed or not.

Using the Files class' setAttribute method to set individual attributes

The setAttribute method provides a flexible and dynamic approach for setting certain file attributes. To set the last modified time, we could have used the following code sequence:

Files.setAttribute(path, "basic:lastAccessTime", lastAccessTime);

Managing file ownership

The owner of a file or directory can be modified after the file has been created. This is accomplished by using the java.nio.file.attribute.FileOwnerAttributeView interface's setOwner method, which can be useful when ownerships change and need to be controlled programmatically.

A java.nio.file.attribute.UserPrincipal object is used to represent a user. A Path object is used to represent a file or directory. Using these two objects with the Files class' setOwner method enables us to maintain file ownerships.

Getting ready

In order to change the owner of a file or directory:

  1. Obtain a Path object, which represents the file or directory.
  2. Use the path as the argument to the getFileAttributeView method.
  3. Create a UserPrincipal object representing the new owner.
  4. Use the FileOwnerAttributeView interface's setOwner method to change the file's owner.

How to do it...

  1. In this example, we will assume that the current owner of the users.txt file is richard. We will change the owner to a user called jennifer. To do this, create a new user on your system called jennifer. Create a new console application with the following main method. In the method, we will use the FileOwnerAttributeView and a UserPrincipal object to change the owner as follows:
  2. public static void main(String[] args) throws Exception{

    Path path = Paths.get("C:/home/docs/users.txt");
    FileOwnerAttributeView view = Files.getFileAttributeView(path,

    FileOwnerAttributeView.class);
    UserPrincipalLookupService lookupService = FileSystems.getDefault

    ().getUserPrincipalLookupService();
    UserPrincipal userPrincipal = lookupService.lookupPrincipalByName

    ("jennifer");

    view.setOwner(userPrincipal);
    System.out.println("Owner: " + view.getOwner().getName());
    }

  3. In order to modify the ownership of a file, we must have appropriate privileges. The introduction to this chapter explains how to get administrator privileges. When the application is executed using Windows 7, the output should reflect the PC name and the file's owners, shown as follows. The PC name is separated from the owner with a backslash:
    Owner: Richard-PC\Richard
    Owner: Richard-PC\Jennifer

How it works...

A Path was first created for the users.txt file. Next, an instance of the FileOwnerAttributeView interface was obtained using the getFileAttributeView method. Within the try block, a UserPrincipalLookupService object was created using the default FileSystem class' getUserPrincipalLookupService method. The lookupPrincipalByName method was passed the string jennifer, which returned a UserPrincipal object representing that user.

The last step was to pass the UserPrincipal object to the setOwner method. A message was displayed to indicate that the change had been made. It then used the getOwner method to retrieve the current owner verifying the change.

There's more...

Any interface derived from FileOwnerAttributeView can use the getOwner or setOwner methods. These include the AclFileAttributeView and PosixFileAttributeView interfaces. In addition, the Files class' setOwner method can also be used to change ownership of a file.

Using the Files class' setOwner method

The Files class' setOwner method works in the same way as the FileOwnerAttributeView interfaces' setOwner method. It differs in that it has two arguments: a Path object representing the file and a UserPrincipal object. The following sequence illustrates the process of setting the owner of the users.txt to jennifer:

Path path = Paths.get("C:/home/docs/users.txt");
try {

UserPrincipalLookupService lookupService = FileSystems.getDefault

().getUserPrincipalLookupService();
UserPrincipal userPrincipal = lookupService.lookupPrincipalByName

("jennifer");

Files.setOwner(path, userPrincipal);
System.out.println("Owner: " + view.getOwner().getName());
}
catch (IOException ex) {

ex.printStackTrace();
}

Java 7 New Features Cookbook Over 100 comprehensive recipes to get you up-to-speed with all the exciting new features of Java 7 with this book and ebook
Published: February 2012
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

(For more resources on Java, see here.)

Managing ACL file permissions

In this recipe, we will examine how ACL permissions can be set. The ability to set these permissions is important for many applications. For example, when we need to control who can modify or execute a file, we can affect this change programmatically. What we can change is indicated by the AclEntryPermission enumeration values listed later.

Getting ready

To set a new ACL permission for a file:

  1. Create a Path object for the file whose attributes we want to change.
  2. Obtain an AclFileAttributeView for that file.
  3. Obtain a UserPrincipal object for the user.
  4. Obtain a list of ACL entries currently assigned to the file.
  5. Create a new AclEntry.Builder object holding the permission that we want to add.
  6. Add the permission to the ACL list.
  7. Use the setAcl method to replace the current ACL list with a new one.

How to do it...

  1. Create a new console application with the following main method. In this method, we will initially simply display the current ACL list for the file users.txt as follows:
  2. public static void main(String[] args) throws Exception {

    Path path = Paths.get("C:/home/docs/users.txt");
    AclFileAttributeView view = Files.getFileAttributeView(path,

    AclFileAttributeView.class);
    List<AclEntry> aclEntryList = view.getAcl();
    displayAclEntries(aclEntryList);
    }

  3. To illustrate the process of adding and deleting ACL attributes, we will use a series of helper methods:
    • displayAclEntries: This displays the principal and entry type and then calls the other two helper methods
    • displayEntryFlags: This displays the entry flags, if present
    • displayPermissions: This displays the entry permissions, if any
  4. Add the methods, as shown in the following code, to your application:
  5. private static void displayAclEntries(List<AclEntry> aclEntryList) {

    System.out.println("ACL Entry List size: " + aclEntryList.size());
    for (AclEntry entry : aclEntryList) {

    System.out.println("User Principal Name: " + entry.principal

    ().getName());
    System.out.println("ACL Entry Type: " + entry.type());
    displayEntryFlags(entry.flags());
    displayPermissions(entry.permissions());
    System.out.println();
    }
    }

    private static void displayPermissions(Set<AclEntryPermission>

    permissionSet){

    if (permissionSet.isEmpty()) {

    System.out.println("No Permissions present");
    }
    else {

    System.out.println("Permissions");
    for (AclEntryPermission permission : permissionSet) {

    System.out.print(permission.name() + " ");
    }
    System.out.println();
    }
    }

    private static void displayEntryFlags(Set<AclEntryFlag> flagSet) {

    if (flagSet.isEmpty()) {

    System.out.println("No ACL Entry Flags present");
    }
    else {

    System.out.println("ACL Entry Flags");
    for (AclEntryFlag flag : flagSet) {
    System.out.print(flag.name() + " ");
    }
    System.out.println();
    }
    }

  6. The ACL list contains the ACL entries for a file. When the displayAclEntries method is executed, it will display the number of entries as a convenience and then each entry will be separated by a blank line. The following illustrates a possible list for the users.txt file:
    Owner: Richard-PC\Richard
    ACL Entry List size: 4
    User Principal Name: BUILTIN\Administrators
    ACL Entry Type: ALLOW
    No ACL Entry Flags present
    Permissions
    READ_DATA DELETE READ_NAMED_ATTRS READ_ATTRIBUTES WRITE_OWNER DELETE_CHILD WRITE_DATA APPEND_DATA SYNCHRONIZE EXECUTE WRITE_ATTRIBUTES WRITE_ACL WRITE_NAMED_ATTRS READ_ACL
    User Principal Name: NT AUTHORITY\SYSTEM
    ACL Entry Type: ALLOW
    No ACL Entry Flags present
    Permissions
    READ_DATA DELETE READ_NAMED_ATTRS READ_ATTRIBUTES WRITE_OWNER DELETE_CHILD WRITE_DATA APPEND_DATA SYNCHRONIZE EXECUTE WRITE_ATTRIBUTES WRITE_ACL WRITE_NAMED_ATTRS READ_ACL
    User Principal Name: BUILTIN\Users
    ACL Entry Type: ALLOW
    No ACL Entry Flags present
    Permissions
    READ_DATA SYNCHRONIZE EXECUTE READ_NAMED_ATTRS READ_ATTRIBUTES READ_ACL
    User Principal Name: NT AUTHORITY\Authenticated Users
    ACL Entry Type: ALLOW
    No ACL Entry Flags present
    Permissions
    APPEND_DATA READ_DATA DELETE SYNCHRONIZE EXECUTE READ_NAMED_ATTRS READ_ATTRIBUTES WRITE_ATTRIBUTES WRITE_NAMED_ATTRS READ_ACL WRITE_DATA
  7. Next, use the UserPrincipalLookupService class' lookupService method to return an instance of the UserPrincipalLookupService class. Use its lookupPrincipalByName method to return a UserPrincipal object based on a user's name. Add the following code after the displayAclEntries method is called:
  8. UserPrincipalLookupService lookupService = FileSystems.getDefault

    ().getUserPrincipalLookupService();
    UserPrincipal userPrincipal = lookupService.lookupPrincipalByName

    ("users");

  9. Next, add the following code to create and set up an AclEntry.Builder object. This will be used to add WRITE_ACL and DELETE permissions for the user. Add the entry to the ACL list and use the setAcl method to attach it to the current file as follows:
  10. AclEntry.Builder builder = AclEntry.newBuilder();
    builder.setType(AclEntryType.ALLOW);
    builder.setPrincipal(userPrincipal);
    builder.setPermissions(
    AclEntryPermission.WRITE_ACL,
    AclEntryPermission.DELETE);

    AclEntry entry = builder.build();
    aclEntryList.add(0, entry);
    view.setAcl(aclEntryList);

  11. Execute the application. In order to modify some ACL attributes of a file, we must have the appropriate privileges. The introduction to this chapter gives the details of how to run the application as the administrator. Next, comment out the code that adds the ACL entry and verify that the ACL entry has been made. You should see the following entry added to the list:
    ACL Entry List size: 5
    User Principal Name: BUILTIN\Users
    ACL Entry Type: ALLOW
    No ACL Entry Flags present
    Permissions
    WRITE_ACL DELETE

How it works...

In the main method, we created the Path object, and then used it to obtain an instance of the java.nio.file.attribute.AclFileAttributeView interface. The file presented by the Path object was the users.txt file. The AclFileAttributeView object can be used for several purposes. Here, we were only interested in using its getAcl method to return a list of the ACL attributes associated with the file.

We displayed the list of current ACLs only to see what they were, and to eventually verify that the attributes for the file have been changed. ACL attributes are associated with a user. In this example, we created a UserPrincipal object that represented users.

A new ACL entry can be created using the build method of the java.nio.file.attribute.AclEntry.Builder class. The static newBuilder method created an instance of an AclEntry.Builder class. The setPrincipal method was executed to set users as the principal for the attribute. The setPermissions method takes either a set of AclEntryPermission objects or a variable number of AclEntryPermission objects. In this example, we used a list consisting of two permissions separated by a comma: AclEntryPermission.WRITE_ACL and AclEntryPermission.DELETE.

The AclEntry.Builder object was then added to the existing ACL for the file. The entry was added at the beginning of the list. The last step was to use the setAcl method to replace the old ACL list with this new one.

There's more...

To remove an ACL attribute, we need to obtain the current list and then identify the position of the attribute that we want to remove. We can use the java.util.List interface's remove method to remove that item. The setAcl method can then be used to replace the old list with the new one.

ACL attributes are explained in more detail in the RFC 3530: Network File System (NFS) version 4 Protocol. The following tables provide additional information and insight into the ACL permissions that are available. The enumeration AclEntryType has the following values:

Value

Meaning

ALARM

Results in an alarm being generated

in a system-specific manner,

when an attempt is made to

access the attributes specified

ALLOW

Grants permissions

AUDIT

Logs the access requested in a

system-dependent way, when an attempt

s made to access

the attributes specified

DENY

Denies access

The AclEntryPermission enumeration values are summarized in the table that follows:

Value

Meaning

APPEND_DATA

Ability to append data to a file

DELETE

Ability to delete the file

DELETE_CHILD

Ability to delete a file or

directory within a directory

EXECUTE

Ability to execute a file

READ_ACL

Ability to read the ACL attribute

READ_ATTRIBUTES

Ability to read (non-ACL) file

attributes

READ_DATA

Ability to read the data of the file

READ_NAMED_ATTRS

Ability to read the named attributes

of a file

SYNCHRONIZE

Ability to access files locally

at the server with synchronous

reads and writes

WRITE_ACL

Ability to write the ACL attribute

WRITE_ATTRIBUTES

Ability to write (non-ACL) file

attributes

WRITE_DATA

Ability to modify the file's data

WRITE_NAMED_ATTRS

Ability to write the named attributes

of a file

WRITE_OWNER

Ability to change the owner

The AclEntryFlag enumeration is applied to directory entries. There are four values summarized as follows:

Value

Meaning

DIRECTORY_INHERIT

The ACL entry should be added

to each new directory created

FILE_INHERIT

The ACL entry should be added

to each new non-directory

file created

INHERIT_ONLY

The ACL entry should be added

to each new file or

directory created

NO_PROPAGATE_INHERIT

The ACL entry should not be placed

on the newly created directory,

hich is inheritable by subdirectories

of the created directory

Currently, there are no flags associated with the AclEntryType.AUDIT or AclEntryType.ALARM.

Managing POSIX attributes

The POSIX attributes available include a group owner, a user owner, and a set of permissions. In this recipe, we will investigate how to maintain these attributes. The management of these attributes makes it easier to develop applications designed to execute on multiple operating systems. While the number of attributes is limited, they may be sufficient for many applications.

There are three approaches that can be used to manage POSIX attributes:

  • The java.nio.file.attribute.PosixFileAttributeView interface
  • The Files class set/get POSIX file permission methods
  • The Files class' setAttribute method

Getting ready

To maintain POSIX permission attributes for a file, we need to:

  1. Create a Path object representing the file or directory of interest.
  2. Obtain a PosixFileAttributes object for that file.
  3. Get a set of permissions for that file using the permissions method.
  4. Modify the set of permissions.
  5. Replace the permission using the setPermissions method.

How to do it...

  • We will create an application that obtains a PosixFileAttributes object and uses it to display the current permissions set for the users.txt file, and then add the PosixFilePermission.OTHERS_WRITE permission to the file. Create a new console application and add the following main method:
  • public static void main(String[] args) throws Exception{

    Path path = Paths.get("home/docs/users.txt");
    FileSystem fileSystem = path.getFileSystem();
    PosixFileAttributeView view = Files.getFileAttributeView(path,

    PosixFileAttributeView.class);

    PosixFileAttributes attributes = view.readAttributes();
    Set<PosixFilePermission> permissions = attributes.permissions();
    listPermissions(permissions);

    permissions.add(PosixFilePermission.OTHERS_WRITE);
    view.setPermissions(permissions);

    System.out.println();
    listPermissions(permissions);
    }

    private static void listPermissions(Set<PosixFilePermission> permissions){

    System.out.print("Permissions: ");
    for (PosixFilePermission permission : permissions){

    System.out.print(permission.name() + " ");
    }
    System.out.println();
    }

  • Execute the application on a system that supports POSIX. When executed under Ubuntu 11.04, you should get the results similar to the following:
    Permissions: GROUP_READ OWNER_WRITE OTHERS_READ OWNER_READ
    Permissions: GROUP_READ OWNER_WRITE OTHERS_WRITE OTHERS_READ OWNER_READ
  • How it works...

    In the main method, we obtained a Path for the users.txt file and then used the getFileAttributeView method to get an instance of the PosixFileAttributeView. The readAttributes method was then used to obtain an instance of the PosixFileAttributes object, representing the file's POSIX attributes.

    The listPermissions method was used to list the permissions for the file. This method was executed once before, and once after the new permission was added to the file. We did this simply to show the change in permissions.

    The PosixFilePermission.OTHERS_WRITE permission was added to the permission set using the add method. The following table lists the PosixFilePermission enumeration values:

    Value

    Level

    Permission Granted

    GROUP_EXECUTE

    Group

    Execute and search

    GROUP_READ

    Read

    GROUP_WRITE

    Write

    OTHERS_EXECUTE

    Others

    Execute and search

    OTHERS_READ

    Read

    OTHERS_WRITE

    Write

    OWNER_EXECUTE

    Owner

    Execute and search

    OWNER_READ

    Read

    OWNER_WRITE

    Write

    In this example, we added a PosixFilePermission.OTHERS_WRITE permission. In the next section, we will illustrate how to remove a permission.

    There's more...

    There are several other operations of interest including:

    • Removing a file permission
    • Modifying the POSIX ownership of a file
    • Using the Files class' set/get POSIX file permission methods
    • Using the Files class' setAttribute method
    • Using the PosixFilePermissions class to create PosixFilePermissions

    Removing a file permission

    Removing a permission is simply a matter of:

    • Obtaining a set of permissions for the file
    • Using the Set interface's remove method to remove the permission
    • Reassigning the set to the file

    This is illustrated in the following code sequence, where the PosixFilePermission.OTHERS_WRITE permission is removed:

    Set<PosixFilePermission> permissions = attributes.permissions();
    Permissions.remove(PosixFilePermission.OTHERS_WRITE);
    view.setPermissions(permissions);

    Modifying the POSIX ownership of a file

    The POSIX owners are specified at the group and user level. The PosixFileAttributes method's group and owner will return objects representing the group and user owners of the file. The setGroup and setOwner methods will set the corresponding memberships.

    In the example that follows, the owners for the users.txt file are displayed and then changed. The UserPrincipal objects are created to support the set methods:

    Path path = Paths.get("home/docs/users.txt");
    try {

    FileSystem fileSystem = path.getFileSystem();
    PosixFileAttributeView view = Files.getFileAttributeView(path,

    PosixFileAttributeView.class);

    PosixFileAttributes attributes = view.readAttributes();
    Set<PosixFilePermission> permissions = attributes.permissions();

    System.out.println("Old Group: " + attributes.group().getName());
    System.out.println("Old Owner: " + attributes.owner().getName());
    System.out.println();

    UserPrincipalLookupService lookupService = FileSystems.getDefault

    ().getUserPrincipalLookupService();
    UserPrincipal userPrincipal = lookupService.lookupPrincipalByName

    ("jennifer");
    GroupPrincipal groupPrincipal =

    lookupService.lookupPrincipalByGroupName(("jennifer");
    view.setGroup(groupPrincipal);
    view.setOwner(userPrincipal);

    attributes = view.readAttributes();
    System.out.println("New Group: " + attributes.group().getName());
    System.out.println("New Owner: " + attributes.owner().getName());
    System.out.println();

    }
    catch (IOException ex) {

    ex.printStackTrace();
    }

    When executed, your output should appear as follows:

    Setting owner for users.txt
    Old Group: richard
    Old Owner: richard
    New Group: jennifer
    New Owner: jennifer

    You may need to execute the code as an administrator, as detailed in the introduction.

    Using the Files class' set/get POSIX file permission methods

    This approach uses the Files class' setPosixFilePermissions and getPosixFilePermissions methods. The getPosixFilePermissions method returns a set of PosixFilePermissions for the file specified by its first argument. Its second argument is a LinkOption, which is used to determine how symbolic link files are handled. Links are not normally followed, unless the LinkOption.NOFOLLOW_LINKS is used. We could use the following code sequence to list the permissions associated with a file:

    Path path = Paths.get("home/docs/users.txt");
    try {

    Set<PosixFilePermission> permissions = Files.getPosixFilePermissions

    (path);
    System.out.print("Permissions: ");
    for (PosixFilePermission permission : permissions) {

    System.out.print(permission.name() + " ");
    }
    System.out.println();

    }
    catch (IOException ex) {

    ex.printStackTrace();
    }

    The setPermissions method takes a Path object representing the file and a set of PosixFilePermission. Instead of using the previous method:

    view.setPermissions(path, permissions);

    We can use the Files class' setPosixFilePermissions method:

    Files.setPosixFilePermissions(path, permissions);

    The use of the Files class simplifies the process by avoiding the creation of a PosixFileAttributes object.

    Using the Files class setAttribute method

    The setAttribute method will set an attribute and has the following four arguments:

    • A Path object representing the file
    • A String containing the attribute to be set
    • An object representing the value of the attribute
    • An option LinkOption value specifying how symbolic links are handled

    The following illustrates adding the PosixFilePermission.OTHERS_WRITE permission to the users.txt file:

    Path path = Paths.get("home/docs/users.txt");
    try {

    Files.setAttribute(path, "posix:permission,

    PosixFilePermission.OTHERS_WRITE);
    }
    catch (IOException ex) {

    ex.printStackTrace();
    }

    The LinkOption value was not used in this example.

    Using the PosixFilePermissions class to create PosixFilePermissions

    The PosixFilePermissions class possesses three methods:

    • asFileAttribute, which returns a FileAttribute object that contains a set of PosixFilePermissions
    • fromString, which also returns a set of PosixFilePermissions based on a String argument
    • toString, which performs the inverse operation of the fromString method

    All three methods are static. The first method returns a FileAttribute object, which can be used with the createFile or createDirectory method

    On Unix systems, file permissions are frequently expressed as a nine-character string. The string is grouped in three character groups. The first set represents permission of the user, the second represents permission of the group, and the last set represents the permission of all others. Each of the three character groups represents the read, write, or execute permissions granted for that set. An r in the first position grants read permission, a w in the second position indicates write permission, and an x in the last position grants execute permission. A - in any of these positions means that the permission is not set.

    To illustrate these methods, execute the following code sequence:

    Path path = Paths.get("home/docs/users.txt");
    try {

    FileSystem fileSystem = path.getFileSystem();
    PosixFileAttributeView view = Files.getFileAttributeView(path,

    PosixFileAttributeView.class);

    PosixFileAttributes attributes = view.readAttributes();
    Set<PosixFilePermission> permissions = attributes.permissions();

    for(PosixFilePermission permission : permissions) {

    System.out.print(permission.toString() + ' ');
    }
    System.out.println();

    FileAttribute<Set<PosixFilePermission>> fileAttributes =

    PosixFilePermissions.asFileAttribute(permissions);
    Set<PosixFilePermission> fileAttributeSet = fileAttributes.value();
    for (PosixFilePermission posixFilePermission : fileAttributeSet) {

    System.out.print(posixFilePermission.toString() + ' ');
    }

    System.out.println();
    System.out.println(PosixFilePermissions.toString(permissions));
    permissions = PosixFilePermissions.fromString("rw-rw-r--");
    for(PosixFilePermission permission : permissions) {

    System.out.print(permission.toString() + ' ');
    }
    System.out.println();

    }
    catch (IOException ex) {

    }

    Your output should be similar to the following:
    OTHERS_READ OWNER_READ GROUP_READ OWNER_WRITE
    OTHERS_READ OWNER_READ OWNER_WRITE GROUP_READ
    rw-r--r--
    OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE OTHERS_READ

    The first section of the code obtains a set of permissions for the users.txt file, as detailed earlier in this recipe. The permissions were then displayed. Next, the asFileAttribute method was executed to return the FileAttribute for the file. The value method was used to obtain a set of the attributes, which were then displayed. The two sets of permissions were displayed but in a different order.

    Next, the toString method was used to display this same set of permissions as a string. Notice, each character reflects a permission granted for the users.txt file.

    The last code segment created a new set of permissions using the fromString method. These permissions were then displayed to verify the conversion.

    Summary

    In this article, we learnt the following:

    • Creating files and directories
    • Controlling how a file is copied
    • Managing temporary files and directories
    • Setting time-related attributes of a file or directory
    • Managing file ownership
    • Managing ACL file permissions
    • Managing POSIX attributes

    Further resources on this subject:


    About the Author :


    Jennifer L. Reese

    Jennifer L. Reese holds a B.S. degree from Tarleton State University. She currently works as a software engineer for Local Government Solutions in Waxahachie, Texas, developing software for county government. Prior to graduation, she worked for the Center for Agribusiness Excellence at Tarleton where she used Java in conjunction with GIS software to analyze crop and weather data.

    Richard M. Reese

    Richard M. Reese holds a Ph.D. in Computer Science from Texas A & M University and is currently an Associate Professor in the Department of Engineering and Physics at Tarleton State University in Stephenville, Texas. In addition to his experience in academia, Richard has over 17 years experience in industry including operating system development at GTE Automatic Electric Labs and at Lockheed Martin in Fort Worth, Texas, where he supervised a tool development group and oversaw various research and development projects. Prior to his industrial experience he served four years in the United States Air Force.

    Richard has been involved with Java since 1996 and is a certified Java SE 7 Associate Programmer. He has worked as consultant/instructor of software languages in private and public classes providing him with a variety of insight into industry applications. He has published numerous papers and has developed courseware for a variety of topics including advanced Java technologies. He has also written the EJB 3.1 Cookbook and Java 7 New Features Cookbook for Packt Publishing.

    Books From Packt


    Java EE 6 with GlassFish 3 Application Server
    Java EE 6 with GlassFish 3 Application Server

    JavaFX 1.2 Application Development Cookbook
    JavaFX 1.2 Application Development Cookbook

    Java EE6 Cookbook for securing, tuning, and extending enterprise applications
    Java EE6 Cookbook for securing, tuning, and extending enterprise applications

    Java EE 5 Development with NetBeans 6
    Java EE 5 Development with NetBeans 6

    Java EE 5 Development using GlassFish Application Server
    Java EE 5 Development using GlassFish Application Server

    EJB 3.1 Cookbook
    EJB 3.1 Cookbook

    JDBC 4.0 and Oracle JDeveloper for J2EE Development
    JDBC 4.0 and Oracle JDeveloper for J2EE Development

    EJB 3.0 Database Persistence with Oracle Fusion Middleware 11g
    EJB 3.0 Database Persistence with Oracle Fusion Middleware 11g


    No votes yet

    Post new comment

    CAPTCHA
    This question is for testing whether you are a human visitor and to prevent automated spam submissions.
    f
    L
    p
    j
    A
    e
    Enter the code without spaces and pay attention to upper/lower case.
    Code Download and Errata
    Packt Anytime, Anywhere
    Register Books
    Print Upgrades
    eBook Downloads
    Video Support
    Contact Us
    Awards Voting Nominations Previous Winners
    Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
    Resources
    Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software