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 046 private static final Logger LOG = LoggerFactory.getLogger(LockFile.class); 047 048 public LockFile(File file, boolean deleteOnUnlock) { 049 this.file = file; 050 this.deleteOnUnlock = deleteOnUnlock; 051 } 052 053 /** 054 * @throws IOException 055 */ 056 synchronized public void lock() throws IOException { 057 if (DISABLE_FILE_LOCK) { 058 return; 059 } 060 061 if (lockCounter > 0) { 062 return; 063 } 064 065 IOHelper.mkdirs(file.getParentFile()); 066 synchronized (LockFile.class) { 067 if (System.getProperty(getVmLockKey()) != null) { 068 throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm."); 069 } 070 System.setProperty(getVmLockKey(), new Date().toString()); 071 } 072 try { 073 if (lock == null) { 074 randomAccessLockFile = new RandomAccessFile(file, "rw"); 075 IOException reason = null; 076 try { 077 lock = randomAccessLockFile.getChannel().tryLock(0, Math.max(1, randomAccessLockFile.getChannel().size()), false); 078 } catch (OverlappingFileLockException e) { 079 reason = IOExceptionSupport.create("File '" + file + "' could not be locked.", e); 080 } catch (IOException ioe) { 081 reason = ioe; 082 } 083 if (lock != null) { 084 //track lastModified only if we are able to successfully obtain the lock. 085 randomAccessLockFile.writeLong(System.currentTimeMillis()); 086 randomAccessLockFile.getChannel().force(true); 087 lastModified = file.lastModified(); 088 lockCounter++; 089 System.setProperty(getVmLockKey(), new Date().toString()); 090 locked = true; 091 } else { 092 // new read file for next attempt 093 closeReadFile(); 094 if (reason != null) { 095 throw reason; 096 } 097 throw new IOException("File '" + file + "' could not be locked."); 098 } 099 100 } 101 } finally { 102 synchronized (LockFile.class) { 103 if (lock == null) { 104 System.getProperties().remove(getVmLockKey()); 105 } 106 } 107 } 108 } 109 110 /** 111 */ 112 public void unlock() { 113 if (DISABLE_FILE_LOCK) { 114 return; 115 } 116 117 lockCounter--; 118 if (lockCounter != 0) { 119 return; 120 } 121 122 // release the lock.. 123 if (lock != null) { 124 try { 125 lock.release(); 126 System.getProperties().remove(getVmLockKey()); 127 } catch (Throwable ignore) { 128 } 129 lock = null; 130 } 131 closeReadFile(); 132 133 if (locked && deleteOnUnlock) { 134 file.delete(); 135 } 136 } 137 138 private String getVmLockKey() throws IOException { 139 return getClass().getName() + ".lock." + file.getCanonicalPath(); 140 } 141 142 private void closeReadFile() { 143 // close the file. 144 if (randomAccessLockFile != null) { 145 try { 146 randomAccessLockFile.close(); 147 } catch (Throwable ignore) { 148 } 149 randomAccessLockFile = null; 150 } 151 } 152 153 /** 154 * @return true if the lock file's last modified does not match the locally cached lastModified, false otherwise 155 */ 156 private boolean hasBeenModified() { 157 boolean modified = false; 158 159 //Create a new instance of the File object so we can get the most up to date information on the file. 160 File localFile = new File(file.getAbsolutePath()); 161 162 if (localFile.exists()) { 163 if(localFile.lastModified() != lastModified) { 164 LOG.info("Lock file " + file.getAbsolutePath() + ", locked at " + new Date(lastModified) + ", has been modified at " + new Date(localFile.lastModified())); 165 modified = true; 166 } 167 } 168 else { 169 //The lock file is missing 170 LOG.info("Lock file " + file.getAbsolutePath() + ", does not exist"); 171 modified = true; 172 } 173 174 return modified; 175 } 176 177 public boolean keepAlive() { 178 locked = locked && lock != null && lock.isValid() && !hasBeenModified(); 179 return locked; 180 } 181 182}