| /** |
| * 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; |
| } |
| |
| } |