content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.activemq.util;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.RandomAccessFile;
  import java.nio.channels.FileLock;
  import java.nio.channels.OverlappingFileLockException;
  import java.util.Date;
  
  /**
   * Used to lock a File.
   *
   * @author chirino
   */
  public class LockFile {
  
      private static final boolean DISABLE_FILE_LOCK = Boolean.getBoolean("java.nio.channels.FileLock.broken");
      final private File file;
      private long lastModified;
  
      private FileLock lock;
      private RandomAccessFile randomAccessLockFile;
      private int lockCounter;
      private final boolean deleteOnUnlock;
      private volatile boolean locked;
      private String lockSystemPropertyName = "";
  
      private static final Logger LOG = LoggerFactory.getLogger(LockFile.class);
  
      public LockFile(File file, boolean deleteOnUnlock) {
          this.file = file;
          this.deleteOnUnlock = deleteOnUnlock;
      }
  
      /**
       * @throws IOException
       */
      synchronized public void lock() throws IOException {
          if (DISABLE_FILE_LOCK) {
              return;
          }
  
          if (lockCounter > 0) {
              return;
          }
  
          IOHelper.mkdirs(file.getParentFile());
          synchronized (LockFile.class) {
              lockSystemPropertyName = getVmLockKey();
              if (System.getProperty(lockSystemPropertyName) != null) {
                  throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm. Value: " + System.getProperty(lockSystemPropertyName));
              }
              System.setProperty(lockSystemPropertyName, new Date().toString());
          }
          try {
              if (lock == null) {
                  randomAccessLockFile = new RandomAccessFile(file, "rw");
                  IOException reason = null;
                  try {
                      lock = randomAccessLockFile.getChannel().tryLock(0, Math.max(1, randomAccessLockFile.getChannel().size()), false);
                  } catch (OverlappingFileLockException e) {
                      reason = IOExceptionSupport.create("File '" + file + "' could not be locked.", e);
                  } catch (IOException ioe) {
                      reason = ioe;
                  }
                  if (lock != null) {
                      //track lastModified only if we are able to successfully obtain the lock.
                      randomAccessLockFile.writeLong(System.currentTimeMillis());
                      randomAccessLockFile.getChannel().force(true);
                      lastModified = file.lastModified();
                      lockCounter++;
                      System.setProperty(lockSystemPropertyName, new Date().toString());
                      locked = true;
                  } else {
                      // new read file for next attempt
                      closeReadFile();
                      if (reason != null) {
                          throw reason;
                      }
                      throw new IOException("File '" + file + "' could not be locked.");
                  }
  
              }
          } finally {
              synchronized (LockFile.class) {
                  if (lock == null) {
                      System.getProperties().remove(lockSystemPropertyName);
                  }
              }
          }
      }
  
      /**
       */
      synchronized public void unlock() {
          if (DISABLE_FILE_LOCK) {
              return;
          }
  
          lockCounter--;
          if (lockCounter != 0) {
              return;
          }
  
          // release the lock..
          if (lock != null) {
              try {
                  lock.release();
              } catch (Throwable ignore) {
              } finally {
                  if (lockSystemPropertyName != null) {
                      System.getProperties().remove(lockSystemPropertyName);
                  }
                  lock = null;
              }
          }
          closeReadFile();
  
          if (locked && deleteOnUnlock) {
              file.delete();
          }
      }
  
      private String getVmLockKey() throws IOException {
          return getClass().getName() + ".lock." + file.getCanonicalPath();
      }
  
      private void closeReadFile() {
          // close the file.
          if (randomAccessLockFile != null) {
              try {
                  randomAccessLockFile.close();
              } catch (Throwable ignore) {
              }
              randomAccessLockFile = null;
          }
      }
  
      /**
       * @return true if the lock file's last modified does not match the locally cached lastModified, false otherwise
       */
      private boolean hasBeenModified() {
          boolean modified = false;
  
          //Create a new instance of the File object so we can get the most up to date information on the file.
          File localFile = new File(file.getAbsolutePath());
  
          if (localFile.exists()) {
              if(localFile.lastModified() != lastModified) {
                  LOG.info("Lock file " + file.getAbsolutePath() + ", locked at " + new Date(lastModified) + ", has been modified at " + new Date(localFile.lastModified()));
                  modified = true;
              }
          }
          else {
              //The lock file is missing
              LOG.info("Lock file " + file.getAbsolutePath() + ", does not exist");
              modified = true;
          }
  
          return modified;
      }
  
      public boolean keepAlive() {
          locked = locked && lock != null && lock.isValid() && !hasBeenModified();
          return locked;
      }
  
  }
  