/* * #! * Ontopoly Editor * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed 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 ontopoly; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.ontopia.utils.ObjectUtils; public class LockManager implements Serializable { public static final long DEFAULT_LOCK_TIMESPAN_MINUTES = 5; // 5 minutes public static final long DEFAULT_LOCK_REACQUIRE_TIMESPAN_MINUTES = DEFAULT_LOCK_TIMESPAN_MINUTES - 1; // 4 minutes private final Map<String,Lock> locks = new HashMap<String,Lock>(); private long lockTimespan; private int accessCount; private long nextPrune; public LockManager() { this(DEFAULT_LOCK_TIMESPAN_MINUTES * 1000 * 60); } public LockManager(long lockTimespan) { this.lockTimespan = lockTimespan; nextPrune = System.currentTimeMillis() + lockTimespan; } public long getLockTimeSpan() { return lockTimespan; } public Lock lock(String lockKey, String lockerId) { synchronized (locks) { long expiryTime = System.currentTimeMillis(); if (expiryTime >= nextPrune && (++accessCount % 100 == 0)) pruneLocks(nextPrune); Lock lock = locks.get(lockKey); // return existing lock, if not expired if (lock != null && !lock.expired(expiryTime, getLockTimeSpan())) { // if the existing lock was owned by us, then update the expiry if (lock.ownedBy(lockerId)) lock.setLockTime(expiryTime); return lock; } else { // create new lock lock = new Lock(lockerId, lockKey); locks.put(lockKey, lock); } return lock; } } public LockManager.Lock forcedUnlock(String lockKey) { synchronized (locks) { long expiryTime = System.currentTimeMillis(); if (expiryTime >= nextPrune && (++accessCount % 100 == 0)) pruneLocks(nextPrune); return locks.remove(lockKey); } } public LockManager.Lock unlock(String lockKey, String lockerId) { synchronized (locks) { long expiryTime = System.currentTimeMillis(); if (expiryTime >= nextPrune && (+accessCount % 100 == 0)) pruneLocks(nextPrune); Lock lock = locks.get(lockKey); if (lock != null && lock.ownedBy(lockerId)) return locks.remove(lockKey); else return null; } } public void expireLocksForOwner(String lockerId) { synchronized (locks) { Iterator<String> iter = locks.keySet().iterator(); while (iter.hasNext()) { String lockKey = iter.next(); Lock lock = locks.get(lockKey); if (lock.ownedBy(lockerId)) iter.remove(); } } } private void pruneLocks(long expiryTime) { // don't prune locks if there are few of them if (locks.size() < 200) { Iterator<String> iter = locks.keySet().iterator(); while (iter.hasNext()) { String lockKey = iter.next(); Lock lock = locks.get(lockKey); if (lock.expired(expiryTime, getLockTimeSpan())) iter.remove(); } } accessCount = 0; nextPrune = System.currentTimeMillis() + lockTimespan; } public static class Lock implements Serializable { private String lockedBy; private String lockKey; private long lockedTime; public Lock(String lockedBy, String lockKey) { this.lockedBy = lockedBy; this.lockKey = lockKey; this.lockedTime = System.currentTimeMillis(); } public String getLockedBy() { return lockedBy; } public long getLockTime() { return lockedTime; } public void setLockTime(long lockedTime) { this.lockedTime = lockedTime; } public String getLockKey() { return lockKey; } public boolean expired(long expiryTime, long lockTimeSpan) { return (lockedTime + lockTimeSpan) < expiryTime; } public boolean ownedBy(String ownerId) { return ObjectUtils.equals(lockedBy, ownerId); } } }