/* documentr - Edit, maintain, and present software documentation on the web. Copyright (C) 2012-2013 Maik Schreiber This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.blizzy.documentr.repository; import java.util.Map; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import com.google.common.collect.Maps; @Component public class LockManager { private Map<LockKey, Lock> locks = Maps.newHashMap(); private Lock allLock; ILock lockAll() { LockKey key = new LockKey(null, null, false); return lock(key, true); } ILock lockProjectCentral(String projectName) { Assert.hasLength(projectName); LockKey key = new LockKey(projectName, null, true); return lock(key, false); } ILock lockProjectBranch(String projectName, String branchName) { Assert.hasLength(projectName); Assert.hasLength(branchName); LockKey key = new LockKey(projectName, branchName, false); return lock(key, false); } private ILock lock(LockKey key, boolean all) { Lock lock; synchronized (locks) { lock = all ? lockAllInternal() : lockInternal(key); lock.increaseUseCount(); } return lock; } private Lock lockAllInternal() { Lock lock; Thread thread = Thread.currentThread(); for (;;) { lock = allLock; if (((lock == null) || (lock.getLockingThread() == thread)) && areAllLocksHeldByCurrentThread()) { break; } try { locks.wait(); } catch (InterruptedException e) { // ignore } } if (lock == null) { lock = new Lock(thread); allLock = lock; } return lock; } private Lock lockInternal(LockKey key) { Lock lock; Thread thread = Thread.currentThread(); for (;;) { lock = locks.get(key); if (((allLock == null) || (allLock.getLockingThread() == thread)) && ((lock == null) || (lock.getLockingThread() == thread))) { break; } try { locks.wait(); } catch (InterruptedException e) { // ignore } } if (lock == null) { lock = new Lock(thread); locks.put(key, lock); } return lock; } private boolean areAllLocksHeldByCurrentThread() { Thread thread = Thread.currentThread(); if (!locks.isEmpty()) { for (Lock lock : locks.values()) { if (lock.getLockingThread() != thread) { return false; } } } return true; } void unlock(ILock lock) { Assert.notNull(lock); Assert.isInstanceOf(Lock.class, lock); Lock l = (Lock) lock; if (l.getLockingThread() != Thread.currentThread()) { throw new IllegalStateException("current thread is not lock owner"); //$NON-NLS-1$ } synchronized (locks) { boolean isRegular = locks.values().contains(l); boolean isAll = l == allLock; if (!isRegular && !isAll) { throw new IllegalStateException("unknown lock"); //$NON-NLS-1$ } int newUseCount = l.decreaseUseCount(); if (newUseCount == 0) { if (isRegular) { locks.values().remove(l); } else { allLock = null; } } locks.notifyAll(); } } }