/* Copyright (c) 2003 eInnovation Inc. All rights reserved This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. */ package com.openedit.modules.edit; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.openedit.users.User; /** * This registry allows clients to claim, query, and release edit locks for paths. * * @author Eric Galluzzo */ public class EditLockRegistry { /** Locks expire in 30 minutes. */ protected static final long LOCK_EXPIRATION_DURATION = 30 * 60 * 1000; protected Map fieldPathToLockMap; /** * Constructor for EditLockRegistry. */ public EditLockRegistry() { super(); } /** * Retrieve the owner of the lock for the specified path, if any. * * @param inPath The path for which to query the lock * * @return The user who has the given path locked, or <code>null</code> if none */ public User getLockOwner(String inPath) { unlockIfExpired(inPath); EditLock lock = getEditLock(inPath); return (lock == null) ? null : lock.getLockedUser(); } /** * Determine whether the given path is locked by any user. * * @param inPath The path to query * * @return <code>true</code> if the path is locked, <code>false</code> if not */ public boolean isLocked(String inPath) { unlockIfExpired(inPath); return getPathToLockMap().containsKey(inPath); } /** * Determine whether the given user can lock the given path via this edit lock registry. * * @param inPath The path * @param inUser The user * * @return <code>true</code> if so, <code>false</code> if not */ public boolean canLock(String inPath, User inUser) { if ((inPath == null) || (inUser == null)) { return false; } unlockIfExpired(inPath); User owner = getLockOwner(inPath); boolean ok = (owner == null) || owner.equals(inUser); if ( !ok ) { } return ok; } /** * Lock the given path with the given user, regardless of who already has the path locked. * * @param inPath The path to lock * @param inUser The user with which to lock the path */ public synchronized void forciblyLockPath(String inPath, User inUser) { forciblyLockPath(inPath, inUser, new Date()); } /** * Claim a lock for the given path with the given user. * * @param inPath The path to lock * @param inUser The user with which to lock the path * * @throws AlreadyLockedException If the path is already locked by a different user */ public synchronized void lockPath(String inPath, User inUser) throws AlreadyLockedException { lockPath(inPath, inUser, new Date()); } /** * Release the lock for the given path, if the given user has claimed it. * * @param inPath The path to conditionally unlock * @param inUser The user to check for * * @return <code>true</code> if the path was unlocked, <code>false</code> otherwise */ public synchronized boolean unlockPath(String inPath, User inUser) { unlockIfExpired(inPath); User oldUser = getLockOwner(inPath); if ((oldUser != null) && oldUser.equals(inUser)) { getPathToLockMap().remove(inPath); return true; } else { return false; } } /** * Retrieve the edit lock for the given path, if any. * * @param inPath The path * * @return The edit lock, or <code>null</code> if the given path is not locked */ protected EditLock getEditLock(String inPath) { if (inPath == null) { return null; } return (EditLock) getPathToLockMap().get(inPath); } /** * Returns the map from locked paths to their {@link EditLock}s. * * @return Map */ protected Map getPathToLockMap() { if (fieldPathToLockMap == null) { fieldPathToLockMap = new HashMap(); } return fieldPathToLockMap; } /** * Lock the given path with the given user, regardless of who already has the path locked, and * setting the lock creation date to the given date. * * @param inPath The path to lock * @param inUser The user with which to lock the path * @param inDate The lock creation date * * @throws IllegalArgumentException DOCUMENT ME! */ protected synchronized void forciblyLockPath(String inPath, User inUser, Date inDate) { if (inUser == null) { throw new IllegalArgumentException("Cannot claim an edit lock without a user"); } if (inPath == null) { throw new IllegalArgumentException("Cannot claim a lock on a null path"); } getPathToLockMap().put(inPath, new EditLock(inPath, inUser, inDate)); } /** * Claim a lock for the given path with the given user, setting the lock creation date to the * given date. * * @param inPath The path to lock * @param inUser The user with which to lock the path * @param inDate The lock creation date * * @throws AlreadyLockedException If the path is already locked by a different user * @throws IllegalArgumentException DOCUMENT ME! */ protected synchronized void lockPath(String inPath, User inUser, Date inDate) throws AlreadyLockedException { if (inUser == null) { throw new IllegalArgumentException("Cannot claim an edit lock without a user"); } if (inPath == null) { throw new IllegalArgumentException("Cannot claim a lock on a null path"); } unlockIfExpired(inPath); User oldUser = getLockOwner(inPath); if (!canLock(inPath, inUser)) { throw new AlreadyLockedException(inPath, oldUser); } forciblyLockPath(inPath, inUser, inDate); } /** * Remove the lock for the given path if it has expired. * * @param inPath The path whose lock to conditionally remove */ protected void unlockIfExpired(String inPath) { EditLock lock = getEditLock(inPath); if ((lock != null) && lock.isExpired()) { getPathToLockMap().remove(inPath); } } protected class EditLock { protected Date fieldCreationDate; protected String fieldPath; protected User fieldLockedUser; /** * Create a new edit lock. The path and user must be filled in later. */ public EditLock() { this(null, null); } /** * Create a new edit lock for the given path and user, locked at the present moment. * * @param inPath The path to lock * @param inLockedUser The user locking the path */ public EditLock(String inPath, User inLockedUser) { this(inPath, inLockedUser, new Date()); } /** * Create a new edit lock for the given path and user, created at the given date. * * @param inPath The path to lock * @param inLockedUser The user locking the path * @param inCreationDate The date at which this lock was created */ public EditLock(String inPath, User inLockedUser, Date inCreationDate) { setPath(inPath); setCreationDate(inCreationDate); setLockedUser(inLockedUser); } /** * Sets the date this lock was created. * * @param creationDate The creationDate to set */ public void setCreationDate(Date creationDate) { fieldCreationDate = creationDate; } /** * Returns the date this lock was created. * * @return Date */ public Date getCreationDate() { return fieldCreationDate; } /** * Determine whether this lock has expired. * * @return <code>true</code> if it has expired, <code>false</code> otherwise */ public boolean isExpired() { return (new Date().getTime() - getCreationDate().getTime()) >= LOCK_EXPIRATION_DURATION; } /** * Sets the user that has the path locked. * * @param lockedUser The user to set */ public void setLockedUser(User lockedUser) { fieldLockedUser = lockedUser; } /** * Returns the user that has the path locked. * * @return User */ public User getLockedUser() { return fieldLockedUser; } /** * Sets the locked path. * * @param path The path to set */ public void setPath(String path) { fieldPath = path; } /** * Returns the locked path. * * @return String */ public String getPath() { return fieldPath; } } }