src/share/classes/java/util/logging/FileHandler.java

Print this page

        

@@ -23,23 +23,29 @@
  * questions.
  */
 
 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;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 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 <tt>Handler</tt>.
  * <p>
  * The <tt>FileHandler</tt> can either write to a specified file,

@@ -147,11 +153,11 @@
     private String pattern;
     private String lockFileName;
     private FileChannel lockFileChannel;
     private File files[];
     private static final int MAX_LOCKS = 100;
-    private static final java.util.HashMap<String, String> locks = new java.util.HashMap<>();
+    private static final Set<String> locks = new HashSet<>();
 
     /**
      * A metered stream is a subclass of OutputStream that
      * (a) forwards all its output to a target stream
      * (b) keeps track of how many bytes have been written

@@ -426,38 +432,63 @@
             // Now try to lock that filename.
             // Because some systems (e.g., Solaris) can only do file locks
             // 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) {
+                    // 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;
                 }
 
                 // We failed to get the lock.  Try next file.
                 lockFileChannel.close();