001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.util; 018 019import org.slf4j.Logger; 020import org.slf4j.LoggerFactory; 021 022import java.io.File; 023import java.io.IOException; 024import java.io.RandomAccessFile; 025import java.nio.channels.FileLock; 026import java.nio.channels.OverlappingFileLockException; 027import java.util.Date; 028 029/** 030 * Used to lock a File. 031 * 032 * @author chirino 033 */ 034public class LockFile { 035 036 private static final boolean DISABLE_FILE_LOCK = Boolean.getBoolean("java.nio.channels.FileLock.broken"); 037 final private File file; 038 private long lastModified; 039 040 private FileLock lock; 041 private RandomAccessFile randomAccessLockFile; 042 private int lockCounter; 043 private final boolean deleteOnUnlock; 044 private volatile boolean locked; 045 private String lockSystemPropertyName = ""; 046 047 private static final Logger LOG = LoggerFactory.getLogger(LockFile.class); 048 049 public LockFile(File file, boolean deleteOnUnlock) { 050 this.file = file; 051 this.deleteOnUnlock = deleteOnUnlock; 052 } 053 054 /** 055 * @throws IOException 056 */ 057 synchronized public void lock() throws IOException { 058 if (DISABLE_FILE_LOCK) { 059 return; 060 } 061 062 if (lockCounter > 0) { 063 return; 064 } 065 066 IOHelper.mkdirs(file.getParentFile()); 067 synchronized (LockFile.class) { 068 lockSystemPropertyName = getVmLockKey(); 069 if (System.getProperty(lockSystemPropertyName) != null) { 070 throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm. Value: " + System.getProperty(lockSystemPropertyName)); 071 } 072 System.setProperty(lockSystemPropertyName, new Date().toString()); 073 } 074 try { 075 if (lock == null) { 076 randomAccessLockFile = new RandomAccessFile(file, "rw"); 077 IOException reason = null; 078 try { 079 lock = randomAccessLockFile.getChannel().tryLock(0, Math.max(1, randomAccessLockFile.getChannel().size()), false); 080 } catch (OverlappingFileLockException e) { 081 reason = IOExceptionSupport.create("File '" + file + "' could not be locked.", e); 082 } catch (IOException ioe) { 083 reason = ioe; 084 } 085 if (lock != null) { 086 //track lastModified only if we are able to successfully obtain the lock. 087 randomAccessLockFile.writeLong(System.currentTimeMillis()); 088 randomAccessLockFile.getChannel().force(true); 089 lastModified = file.lastModified(); 090 lockCounter++; 091 System.setProperty(lockSystemPropertyName, new Date().toString()); 092 locked = true; 093 } else { 094 // new read file for next attempt 095 closeReadFile(); 096 if (reason != null) { 097 throw reason; 098 } 099 throw new IOException("File '" + file + "' could not be locked."); 100 } 101 102 } 103 } finally { 104 synchronized (LockFile.class) { 105 if (lock == null) { 106 System.getProperties().remove(lockSystemPropertyName); 107 } 108 } 109 } 110 } 111 112 /** 113 */ 114 synchronized public void unlock() { 115 if (DISABLE_FILE_LOCK) { 116 return; 117 } 118 119 lockCounter--; 120 if (lockCounter != 0) { 121 return; 122 } 123 124 // release the lock.. 125 if (lock != null) { 126 try { 127 lock.release(); 128 } catch (Throwable ignore) { 129 } finally { 130 if (lockSystemPropertyName != null) { 131 System.getProperties().remove(lockSystemPropertyName); 132 } 133 lock = null; 134 } 135 } 136 closeReadFile(); 137 138 if (locked && deleteOnUnlock) { 139 file.delete(); 140 } 141 } 142 143 private String getVmLockKey() throws IOException { 144 return getClass().getName() + ".lock." + file.getCanonicalPath(); 145 } 146 147 private void closeReadFile() { 148 // close the file. 149 if (randomAccessLockFile != null) { 150 try { 151 randomAccessLockFile.close(); 152 } catch (Throwable ignore) { 153 } 154 randomAccessLockFile = null; 155 } 156 } 157 158 /** 159 * @return true if the lock file's last modified does not match the locally cached lastModified, false otherwise 160 */ 161 private boolean hasBeenModified() { 162 boolean modified = false; 163 164 //Create a new instance of the File object so we can get the most up to date information on the file. 165 File localFile = new File(file.getAbsolutePath()); 166 167 if (localFile.exists()) { 168 if(localFile.lastModified() != lastModified) { 169 LOG.info("Lock file " + file.getAbsolutePath() + ", locked at " + new Date(lastModified) + ", has been modified at " + new Date(localFile.lastModified())); 170 modified = true; 171 } 172 } 173 else { 174 //The lock file is missing 175 LOG.info("Lock file " + file.getAbsolutePath() + ", does not exist"); 176 modified = true; 177 } 178 179 return modified; 180 } 181 182 public boolean keepAlive() { 183 locked = locked && lock != null && lock.isValid() && !hasBeenModified(); 184 return locked; 185 } 186 187}