Nathan
Nathan

Reputation: 8940

Files.createDirectories() throws FileAlreadyExistsExceptions but no directory

This question asks a similar question. However, in my case the directory does not exist before or after calling Files.createDirectories(). This happens on Oracle JDK 10.0.2.

Here is my code...

Set<PosixFilePermission> perms;
FileAttribute<?> attr;
Path path;
File directory;

directory = new File("/test/http/localhost_4452/UCF2b/Live");
path      = directory.toPath();
perms     = EnumSet.noneOf(PosixFilePermission.class);

perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);

attr = PosixFilePermissions.asFileAttribute(perms);

try
{
   if (!directory.exists())
      Files.createDirectories(path, attr);
}
catch (IOException e)
{
   if (!directory.exists())
   {
      ... collect more information about the state of the directory and its parent path ...
      ... add this information as a suppressed exception ...
      throw e;
   }
   // else do nothing and assume another thread created the directory
}

Here is the exception...

java.nio.file.FileAlreadyExistsException: /test/http/localhost_4452/UCF2b/Live
at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:94)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
at java.base/sun.nio.fs.UnixFileSystemProvider.createDirectory(UnixFileSystemProvider.java:385)
at java.base/java.nio.file.Files.createDirectory(Files.java:682)
at java.base/java.nio.file.Files.createAndCheckIsDirectory(Files.java:789)
at java.base/java.nio.file.Files.createDirectories(Files.java:735)
at ...my code...

The current user is root. Here is the collected diagnostic information about the directory and its parent directories. This information is collected in the catch block if the directory does not exist.

+--------------------------------------+--------+----------+--------------------------+---------+-----------+-------+--------+---------+-------+-------+
|            Path                      | Exists |  Length  |        Modified          |  Owner  | Directory | File  | Hidden | Execute | Read  | Write |
+--------------------------------------+--------+----------+--------------------------+---------+-----------+-------+--------+---------+-------+-------+
| /test/http/localhost_4452/UCF2b/Live | false  |   0.00 B | 1970-01-01T00:00:00Z     | (null)  | false     | false | false  | false   | false | false |
| /test/http/localhost_4452/UCF2b      | true   |  4.00 kB | 2018-11-04T20:32:09.769Z | root    | true      | false | false  | true    | true  | true  |
| /test/http/localhost_4452            | true   |  4.00 kB | 2018-11-04T20:18:26.849Z | root    | true      | false | false  | true    | true  | true  |
| /test/http                           | true   |  4.00 kB | 2018-11-04T20:11:42.605Z | root    | true      | false | false  | true    | true  | true  |
| /test/                               | true   | 20.00 kB | 2018-11-04T20:32:09.768Z | root    | true      | false | false  | true    | true  | true  |
| /                                    | true   |  4.00 kB | 2018-11-04T20:09:22.061Z | root    | true      | false | false  | true    | true  | true  |
+--------------------------------------+--------+----------+--------------------------+---------+-----------+-------+--------+---------+-------+-------+

As you can see, the code checks if the directory exists before calling Files.createDirectories() and verifies that the directory does not exist after the call. The exception only rarely happens. What am I not understanding? How do I create the directory? If I simply repeat the call to Files.createDirectories(), it continues to fail.

Edit: This code is called by multiple threads. This means multiple threads could call Files.createDirectories(), but the code does not rethrow the exception if the directory ends up existing. In other words, other thread(s) would have to create and delete the directory at the right moment since directory.exists() is false before and after Files.createDirectories(). Furthermore, this perfect timing would have to persist since once the program hits this problem, it keeps happening for the specific directory.

Upvotes: 3

Views: 3795

Answers (2)

Nathan
Nathan

Reputation: 8940

I cannot reproduce the issue very frequently. I decided to replace Files.createDirectories() with Files.createDirectory() (i.e. create each directory in the path as needed). Maybe this will work. Maybe this will shed light on the underlying issue.

Edit: It has been 1 week since trying the above and about 1,000 unit test executions. The problem has not reoccurred. I am going to assume that the above answer works.

Upvotes: 0

Kartik
Kartik

Reputation: 7917

Most likely this code is being used by multiple threads because only that can explain why would the code enter an if block if the condition is false and also that it happens rarely. A thread checks that the directory doesn't exist, and before it could create it, another thread creates it. You should use synchronization if that's the case.

synchronized (this) {
    if (!directory.exists())
        Files.createDirectories(path, attr);
}

Upvotes: 1

Related Questions