--- old/src/share/classes/java/util/logging/FileHandler.java 2014-06-25 10:24:06.000000000 +0200 +++ new/src/share/classes/java/util/logging/FileHandler.java 2014-06-25 10:24:06.000000000 +0200 @@ -25,6 +25,7 @@ package java.util.logging; +import static java.nio.file.StandardOpenOption.APPEND; import static java.nio.file.StandardOpenOption.CREATE_NEW; import static java.nio.file.StandardOpenOption.WRITE; @@ -34,10 +35,15 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.channels.FileChannel; +import java.nio.channels.OverlappingFileLockException; import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.HashSet; +import java.util.Set; /** * Simple file logging Handler. @@ -149,7 +155,7 @@ private FileChannel lockFileChannel; private File files[]; private static final int MAX_LOCKS = 100; - private static final java.util.HashMap locks = new java.util.HashMap<>(); + private static final Set locks = new HashSet<>(); /** * A metered stream is a subclass of OutputStream that @@ -428,34 +434,59 @@ // between processes (and not within a process), we first check // if we ourself already have the file locked. synchronized(locks) { - if (locks.get(lockFileName) != null) { + if (locks.contains(lockFileName)) { // We already own this lock, for a different FileHandler // object. Try again. continue; } + final Path lockFilePath = Paths.get(lockFileName); + boolean fileCreated; try { - lockFileChannel = FileChannel.open(Paths.get(lockFileName), + lockFileChannel = FileChannel.open(lockFilePath, CREATE_NEW, WRITE); + fileCreated = true; } catch (FileAlreadyExistsException ix) { - // try the next lock file name in the sequence - continue; + // This may be a zombie file left over by a previous + // execution. Reuse it - but only if we can actually + // write to its directory. + if (Files.isRegularFile(lockFilePath) + && Files.isWritable(lockFilePath) + && Files.isWritable(lockFilePath.getParent())) { + lockFileChannel = FileChannel.open(lockFilePath, + WRITE, APPEND); + fileCreated = false; + } else { + // try the next lock file name in the sequence + continue; + } } boolean available; try { available = lockFileChannel.tryLock() != null; // We got the lock OK. + // At this point we could call File.deleteOnExit(). + // However, this could have undesirable side effects + // as indicated by JDK-4872014. So we will instead + // rely on the fact that close() will remove the lock + // file and that whoever is creating FileHandlers should + // be responsible for closing them. } catch (IOException ix) { // We got an IOException while trying to get the lock. // This normally indicates that locking is not supported // on the target directory. We have to proceed without - // getting a lock. Drop through. - available = true; + // getting a lock. Drop through, but only if we did + // create the file... + available = fileCreated; + } catch (OverlappingFileLockException x) { + // someone already locked this file in this VM. + // continue searching for an available lock. + available = false; } if (available) { // We got the lock. Remember it. - locks.put(lockFileName, lockFileName); + locks.add(lockFileName); break; }