/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.io.fs; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; import java.util.Map; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.util.SVNHashMap; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNFileType; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class FSWriteLock { private static final Map ourThreadDBLocksCache = new SVNHashMap(); private static final Map ourThreadRepositoryLocksCache = new SVNHashMap(); private static final Map ourThreadLogLocksCache = new SVNHashMap(); private static final int DB_LOCK = 1; private static final int LOGS_LOCK = 2; private static final int REPOS_LOCK = 3; private File myLockFile; private RandomAccessFile myLockRAFile; private FileLock myLock; private String myToken; private int myLockType; private boolean myIsShared; private int myReferencesCount = 0; private FSWriteLock(String token, File lockFile, int lockType, boolean shared) { myToken = token; myLockFile = lockFile; myLockType = lockType; myIsShared = shared; } public static synchronized FSWriteLock getWriteLockForDB(FSFS owner) throws SVNException { String uuid = owner.getUUID(); FSWriteLock lock = (FSWriteLock) ourThreadDBLocksCache.get(uuid); if (lock == null) { lock = new FSWriteLock(uuid, owner.getWriteLockFile(), DB_LOCK, false); ourThreadDBLocksCache.put(uuid, lock); } lock.myReferencesCount++; return lock; } public static synchronized FSWriteLock getWriteLockForCurrentTxn(String token, FSFS owner) throws SVNException { if (token == null || token.length() == 0){ SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.FS_NO_LOCK_TOKEN, "Incorrect lock token for current transaction"), SVNLogType.FSFS); } String uuid = owner.getUUID() + token; FSWriteLock lock = (FSWriteLock) ourThreadDBLocksCache.get(uuid); if (lock == null) { lock = new FSWriteLock(uuid, owner.getTransactionCurrentLockFile(), DB_LOCK, false); ourThreadDBLocksCache.put(uuid, lock); } lock.myReferencesCount++; return lock; } public static synchronized FSWriteLock getWriteLockForTxn(String txnID, FSFS owner) throws SVNException { if (txnID == null || txnID.length() == 0){ SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.FS_NO_LOCK_TOKEN, "Incorrect txn id while locking"), SVNLogType.FSFS); } String uuid = owner.getUUID() + txnID; FSWriteLock lock = (FSWriteLock) ourThreadDBLocksCache.get(uuid); if (lock == null) { lock = new FSWriteLock(uuid, owner.getTransactionProtoRevLockFile(txnID), DB_LOCK, false); ourThreadDBLocksCache.put(uuid, lock); } lock.myReferencesCount++; return lock; } public static synchronized FSWriteLock getDBLogsLock(FSFS owner, boolean exclusive) throws SVNException { String uuid = owner.getUUID(); FSWriteLock lock = (FSWriteLock) ourThreadLogLocksCache.get(uuid); if (lock == null) { lock = new FSWriteLock(uuid, owner.getDBLogsLockFile(), LOGS_LOCK, !exclusive); ourThreadLogLocksCache.put(uuid, lock); } lock.myReferencesCount++; return lock; } public synchronized void lock() throws SVNException { boolean errorOccured = false; Exception childError = null; if (myLock != null) { errorOccured = true; } try { SVNFileType type = SVNFileType.getType(myLockFile); if (type == SVNFileType.UNKNOWN || type == SVNFileType.NONE) { SVNFileUtil.createEmptyFile(myLockFile); } myLockRAFile = new RandomAccessFile(myLockFile, "rw"); myLock = myLockRAFile.getChannel().lock(0L, Long.MAX_VALUE, myIsShared); } catch (IOException ioe) { unlock(); errorOccured = true; childError = ioe; } if (errorOccured) { String msg = childError == null ? "file already locked" : childError.getMessage(); SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can''t get exclusive lock on file ''{0}'': {1}", new Object[]{ myLockFile, msg }); SVNErrorManager.error(err, childError, SVNLogType.FSFS); } } public static synchronized void release(FSWriteLock lock) { if (lock == null) { return; } if ((--lock.myReferencesCount) == 0) { if (lock.myLockType == DB_LOCK) { ourThreadDBLocksCache.remove(lock.myToken); } else if (lock.myLockType == REPOS_LOCK) { ourThreadRepositoryLocksCache.remove(lock.myToken); } else if (lock.myLockType == LOGS_LOCK) { ourThreadLogLocksCache.remove(lock.myToken); } } } public synchronized void unlock() throws SVNException { if (myLock != null) { try { myLock.release(); } catch (IOException ioex) { SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Unexpected error while releasing file lock on ''{0}''", myLockFile); SVNErrorManager.error(error, ioex, SVNLogType.FSFS); } myLock = null; } SVNFileUtil.closeFile(myLockRAFile); } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("{["); buffer.append("File:"); buffer.append(myLockFile); buffer.append("]["); buffer.append("RefCount:"); buffer.append(myReferencesCount); buffer.append("]["); buffer.append("Token:"); buffer.append(myToken); buffer.append("]}"); return buffer.toString(); } }