org.opencms.lock.CmsLockManager Maven / Gradle / Ivy
Show all versions of opencms-core Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* 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.
*
* For further information about Alkacon Software GmbH & Co. KG, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.lock;
import org.opencms.db.CmsDbContext;
import org.opencms.db.CmsDriverManager;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsUser;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.I_CmsResource;
import org.opencms.i18n.CmsMessageContainer;
import org.opencms.main.CmsException;
import org.opencms.main.OpenCms;
import org.opencms.util.CmsUUID;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The CmsLockManager is used by the Cms application to detect
* the lock state of a resource.
*
* The lock state depends on the path of the resource, and probably
* locked parent folders. The result of a query to the lock manager
* are instances of CmsLock objects.
*
* @since 6.0.0
*
* @see org.opencms.file.CmsObject#getLock(CmsResource)
* @see org.opencms.lock.CmsLock
*/
public final class CmsLockManager {
/** The driver manager instance. */
private CmsDriverManager m_driverManager;
/** The flag to indicate if the locks should be written to the db. */
private boolean m_isDirty;
/** The flag to indicate if the lock manager has been started in run level 4. */
private boolean m_runningInServlet;
/**
* Default constructor, creates a new lock manager.
*
* @param driverManager the driver manager instance
*/
public CmsLockManager(CmsDriverManager driverManager) {
m_driverManager = driverManager;
}
/**
* Adds a resource to the lock manager.
*
* @param dbc the current database context
* @param resource the resource
* @param user the user who locked the resource
* @param project the project where the resource is locked
* @param type the lock type
*
* @throws CmsLockException if the resource is locked
* @throws CmsException if something goes wrong
*/
public void addResource(CmsDbContext dbc, CmsResource resource, CmsUser user, CmsProject project, CmsLockType type)
throws CmsLockException, CmsException {
// check the type
if (!type.isSystem() && !type.isExclusive()) {
// invalid type
throw new CmsLockException(Messages.get().container(Messages.ERR_INVALID_LOCK_TYPE_1, type.toString()));
}
// get the current lock
CmsLock currentLock = getLock(dbc, resource);
if (currentLock.getType().isShallow() && !type.isShallow() && type.isExclusive()) {
throw new CmsLockException(
Messages.get().container(Messages.ERR_LOCK_CANT_UPGRADE_SHALLOW_LOCK_1, currentLock.toString()));
}
// check lockability
checkLockable(dbc, resource, user, project, type, currentLock);
boolean needNewLock = true;
// prevent shared locks get compromised
if ((type.isExclusive()) && !(type.isTemporary() && currentLock.isInherited())) {
if (!currentLock.getEditionLock().isUnlocked()) {
needNewLock = false;
}
}
CmsLock newLock = CmsLock.getNullLock();
if (needNewLock) {
// lock the resource
newLock = new CmsLock(resource.getRootPath(), user.getId(), project, type);
lockResource(newLock);
}
// handle collisions with exclusive locked sub-resources in case of a folder
if (resource.isFolder() && newLock.getSystemLock().isUnlocked() && !type.isShallow()) {
String resourceName = resource.getRootPath();
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
String lockedPath = lock.getResourceName();
if (lockedPath.startsWith(resourceName) && !lockedPath.equals(resourceName)) {
unlockResource(lockedPath, false);
}
}
}
}
/**
* Counts the exclusive locked resources in a project.
*
* @param project the project
*
* @return the number of exclusive locked resources in the specified project
*/
public int countExclusiveLocksInProject(CmsProject project) {
int count = 0;
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
if (lock.getEditionLock().isInProject(project)) {
count++;
}
}
return count;
}
/**
* Returns the lock state of the given resource.
*
* In case no lock is set, the null lock
which can be obtained
* by {@link CmsLock#getNullLock()} is returned.
*
* @param dbc the current database context
* @param resource the resource
*
* @return the lock state of the given resource
* @throws CmsException if something goes wrong
*/
public CmsLock getLock(CmsDbContext dbc, CmsResource resource) throws CmsException {
return getLock(dbc, resource, true);
}
/**
* Returns the lock state of the given resource.
*
* In case no lock is set, the null lock
which can be obtained
* by {@link CmsLock#getNullLock()} is returned.
*
* @param dbc the current database context
* @param resource the resource
* @param includeSiblings if siblings (shared locks) should be included in the search
*
* @return the lock state of the given resource
* @throws CmsException if something goes wrong
*/
public CmsLock getLock(CmsDbContext dbc, CmsResource resource, boolean includeSiblings) throws CmsException {
// resources are never locked in the online project
// and non-existent resources are never locked
if ((resource == null) || (dbc.currentProject().isOnlineProject())) {
return CmsLock.getNullLock();
}
// check exclusive direct locks first
CmsLock lock = getDirectLock(resource.getRootPath());
if ((lock == null) && includeSiblings && (resource.getSiblingCount() > 1)) {
// check if siblings are exclusively locked
List siblings = internalReadSiblings(dbc, resource);
lock = getSiblingsLock(siblings, resource.getRootPath());
}
if (lock == null) {
// if there is no parent lock, this will be the null lock as well
lock = getParentLock(resource.getRootPath());
}
if (!lock.getSystemLock().isUnlocked()) {
lock = lock.getSystemLock();
} else {
lock = lock.getEditionLock();
}
return lock;
}
/**
* Returns all exclusive locked resources matching the given resource and filter.
*
* @param dbc the database context
* @param resource the resource
* @param filter the lock filter
*
* @return a list of resources
*
* @throws CmsException if something goes wrong
*/
public List getLockedResources(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter)
throws CmsException {
List lockedResources = new ArrayList();
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
CmsResource lockedResource;
boolean matchesFilter = filter.match(resource.getRootPath(), lock);
if (!matchesFilter && !filter.isSharedExclusive()) {
// we don't need to read the resource if the filter didn't match and we don't need to look at the siblings
continue;
}
try {
lockedResource = m_driverManager.readResource(dbc, lock.getResourceName(), CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
OpenCms.getMemoryMonitor().uncacheLock(lock.getResourceName());
continue;
}
if (filter.isSharedExclusive() && (lockedResource.getSiblingCount() > 1)) {
Iterator itSiblings = internalReadSiblings(dbc, lockedResource).iterator();
while (itSiblings.hasNext()) {
CmsResource sibling = itSiblings.next();
CmsLock siblingLock = internalSiblingLock(lock, sibling.getRootPath());
if (filter.match(resource.getRootPath(), siblingLock)) {
lockedResources.add(sibling);
}
}
}
if (matchesFilter) {
lockedResources.add(lockedResource);
}
}
Collections.sort(lockedResources, I_CmsResource.COMPARE_ROOT_PATH);
return lockedResources;
}
/**
* Returns all exclusive locked resources matching the given resource and filter, but uses a cache for resource loookups.
*
* @param dbc the database context
* @param resource the resource
* @param filter the lock filter
* @param cache a cache to use for resource lookups
*
* @return a list of resources
*
* @throws CmsException if something goes wrong
*/
public List getLockedResourcesWithCache(
CmsDbContext dbc,
CmsResource resource,
CmsLockFilter filter,
Map cache)
throws CmsException {
List lockedResources = new ArrayList();
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
CmsResource lockedResource;
boolean matchesFilter = filter.match(resource.getRootPath(), lock);
if (!matchesFilter && !filter.isSharedExclusive()) {
// we don't need to read the resource if the filter didn't match and we don't need to look at the siblings
continue;
}
lockedResource = cache.get(lock.getResourceName());
if (lockedResource == null) {
try {
lockedResource = m_driverManager.readResource(dbc, lock.getResourceName(), CmsResourceFilter.ALL);
cache.put(lock.getResourceName(), lockedResource);
} catch (CmsVfsResourceNotFoundException e) {
OpenCms.getMemoryMonitor().uncacheLock(lock.getResourceName());
// we put a dummy resource object in the map so we won't need to read the nonexistent resource again
CmsResource dummy = new CmsResource(
null,
null,
"",
0,
false,
0,
null,
null,
0,
null,
0,
null,
0,
0,
0,
0,
0,
0);
cache.put(lock.getResourceName(), dummy);
continue;
}
} else if (lockedResource.getStructureId() == null) {
// dummy resource, i.e. the resource was not found in a previous readResource call
continue;
}
if (filter.isSharedExclusive() && (lockedResource != null) && (lockedResource.getSiblingCount() > 1)) {
Iterator itSiblings = internalReadSiblings(dbc, lockedResource).iterator();
while (itSiblings.hasNext()) {
CmsResource sibling = itSiblings.next();
CmsLock siblingLock = internalSiblingLock(lock, sibling.getRootPath());
if (filter.match(resource.getRootPath(), siblingLock)) {
lockedResources.add(sibling);
}
}
}
if (matchesFilter) {
lockedResources.add(lockedResource);
}
}
Collections.sort(lockedResources, I_CmsResource.COMPARE_ROOT_PATH);
return lockedResources;
}
/**
* Returns all exclusive locked resources matching the given resource name and filter.
*
* @param dbc the database context
* @param resourceName the resource name
* @param filter the lock filter
*
* @return a list of root paths
*
* @throws CmsException if something goes wrong
*/
public List getLocks(CmsDbContext dbc, String resourceName, CmsLockFilter filter) throws CmsException {
List locks = new ArrayList();
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
if (filter.isSharedExclusive()) {
CmsResource resource;
try {
resource = m_driverManager.readResource(dbc, lock.getResourceName(), CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
OpenCms.getMemoryMonitor().uncacheLock(lock.getResourceName());
continue;
}
if (resource.getSiblingCount() > 1) {
Iterator itSiblings = internalReadSiblings(dbc, resource).iterator();
while (itSiblings.hasNext()) {
CmsResource sibling = itSiblings.next();
CmsLock siblingLock = internalSiblingLock(lock, sibling.getRootPath());
if (filter.match(resourceName, siblingLock)) {
locks.add(siblingLock);
}
}
}
}
if (filter.match(resourceName, lock)) {
locks.add(lock);
}
}
return locks;
}
/**
* Returns true
if the given resource contains a resource that has a system lock.
*
* This check is required for certain operations on folders.
*
* @param dbc the database context
* @param resource the resource to check the system locks for
*
* @return true
if the given resource contains a resource that has a system lock
*
* @throws CmsException if something goes wrong
*/
public boolean hasSystemLocks(CmsDbContext dbc, CmsResource resource) throws CmsException {
if (resource == null) {
return false;
}
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
if (lock.getSystemLock().isUnlocked()) {
// only system locks matter here
continue;
}
if (lock.getResourceName().startsWith(resource.getRootPath())) {
if (lock.getResourceName().startsWith(resource.getRootPath())) {
return true;
}
try {
resource = m_driverManager.readResource(dbc, lock.getResourceName(), CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
OpenCms.getMemoryMonitor().uncacheLock(lock.getResourceName());
continue;
}
CmsResource lockedResource;
try {
lockedResource = m_driverManager.readResource(dbc, lock.getResourceName(), CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
OpenCms.getMemoryMonitor().uncacheLock(lock.getResourceName());
continue;
}
if (lockedResource.getSiblingCount() > 1) {
Iterator itSiblings = internalReadSiblings(dbc, lockedResource).iterator();
while (itSiblings.hasNext()) {
CmsResource sibling = itSiblings.next();
CmsLock siblingLock = internalSiblingLock(lock, sibling.getRootPath());
if (siblingLock.getResourceName().startsWith(resource.getRootPath())) {
return true;
}
}
}
}
}
return false;
}
/**
* Moves a lock during the move resource operation.
*
* @param source the source root path
* @param destination the destination root path
*/
public void moveResource(String source, String destination) {
CmsLock lock = OpenCms.getMemoryMonitor().getCachedLock(source);
if (lock != null) {
OpenCms.getMemoryMonitor().uncacheLock(lock.getResourceName());
CmsLock newLock = new CmsLock(destination, lock.getUserId(), lock.getProject(), lock.getType());
lock = lock.getRelatedLock();
if ((lock != null) && !lock.isNullLock()) {
CmsLock relatedLock = new CmsLock(destination, lock.getUserId(), lock.getProject(), lock.getType());
newLock.setRelatedLock(relatedLock);
}
OpenCms.getMemoryMonitor().cacheLock(newLock);
}
}
/**
* Reads the latest saved locks from the database and installs them to
* this lock manager.
*
* @param dbc the current database context
*
* @throws CmsException if something goes wrong
*/
public void readLocks(CmsDbContext dbc) throws CmsException {
if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_3_SHELL_ACCESS) {
// read the locks only if the wizard is not enabled
Map lockCache = new HashMap();
List locks = m_driverManager.getProjectDriver(dbc).readLocks(dbc);
Iterator itLocks = locks.iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
internalLockResource(lock, lockCache);
}
OpenCms.getMemoryMonitor().flushLocks(lockCache);
m_runningInServlet = true;
}
}
/**
* Removes a resource after it has been deleted by the driver manager.
*
* @param dbc the current database context
* @param resourceName the root path of the deleted resource
* @throws CmsException if something goes wrong
*/
public void removeDeletedResource(CmsDbContext dbc, String resourceName) throws CmsException {
try {
m_driverManager.getVfsDriver(dbc).readResource(dbc, dbc.currentProject().getUuid(), resourceName, false);
throw new CmsLockException(
Messages.get().container(
Messages.ERR_REMOVING_UNDELETED_RESOURCE_1,
dbc.getRequestContext().removeSiteRoot(resourceName)));
} catch (CmsVfsResourceNotFoundException e) {
// ok, ignore
}
unlockResource(resourceName, true);
unlockResource(resourceName, false);
}
/**
* Removes all locks of a user.
*
* Edition and system locks are removed.
*
* @param userId the id of the user whose locks should be removed
*/
public void removeLocks(CmsUUID userId) {
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock currentLock = itLocks.next();
boolean editLock = currentLock.getEditionLock().getUserId().equals(userId);
boolean sysLock = currentLock.getSystemLock().getUserId().equals(userId);
if (editLock) {
unlockResource(currentLock.getResourceName(), false);
}
if (sysLock) {
unlockResource(currentLock.getResourceName(), true);
}
}
}
/**
* Removes a resource from the lock manager.
*
* The forceUnlock option should be used with caution.
* forceUnlock will remove the lock by ignoring any rules which may cause wrong lock states.
*
* @param dbc the current database context
* @param resource the resource
* @param forceUnlock true
, if a resource is forced to get unlocked (only edition locks),
* no matter by which user and in which project the resource is currently locked
* @param removeSystemLock true
, if you also want to remove system locks
*
* @return the previous {@link CmsLock} object of the resource,
* or {@link CmsLock#getNullLock()}
if the resource was unlocked
*
* @throws CmsException if something goes wrong
*/
public CmsLock removeResource(CmsDbContext dbc, CmsResource resource, boolean forceUnlock, boolean removeSystemLock)
throws CmsException {
String resourcename = resource.getRootPath();
CmsLock lock = getLock(dbc, resource).getEditionLock();
// check some abort conditions first
if (!lock.isNullLock()) {
// the resource is locked by another user or in other project
if (!forceUnlock && (!lock.isOwnedInProjectBy(dbc.currentUser(), dbc.currentProject()))) {
throw new CmsLockException(
Messages.get().container(Messages.ERR_RESOURCE_UNLOCK_1, dbc.removeSiteRoot(resourcename)));
}
// sub-resources of a locked folder can't be unlocked
if (!forceUnlock && lock.isInherited()) {
throw new CmsLockException(
Messages.get().container(Messages.ERR_UNLOCK_LOCK_INHERITED_1, dbc.removeSiteRoot(resourcename)));
}
}
// remove the lock and clean-up stuff
if (lock.isExclusive()) {
if (resource.isFolder() && !lock.getType().isShallow()) {
// in case of a folder, remove any exclusive locks on sub-resources that probably have
// been upgraded from an inherited lock when the user edited a resource
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
String lockedPath = (itLocks.next()).getResourceName();
if (lockedPath.startsWith(resourcename) && !lockedPath.equals(resourcename)) {
// remove the exclusive locked sub-resource
unlockResource(lockedPath, false);
}
}
}
if (removeSystemLock) {
unlockResource(resourcename, true);
}
unlockResource(resourcename, false);
return lock;
}
if (lock.getType().isSharedExclusive()) {
List locks = OpenCms.getMemoryMonitor().getAllCachedLockPaths();
// when a resource with a shared lock gets unlocked, fetch all siblings of the resource
// to the same content record to identify the exclusive locked sibling
List siblings = internalReadSiblings(dbc, resource);
for (int i = 0; i < siblings.size(); i++) {
CmsResource sibling = siblings.get(i);
if (locks.contains(sibling.getRootPath())) {
// remove the exclusive locked sibling
if (removeSystemLock) {
unlockResource(sibling.getRootPath(), true);
}
unlockResource(sibling.getRootPath(), false);
break; // it can only be one!
}
}
return lock;
}
// remove system locks only if explicit required
if (removeSystemLock && !getLock(dbc, resource).getSystemLock().isUnlocked()) {
return unlockResource(resourcename, true);
}
return lock;
}
/**
* Removes all resources locked in a project.
*
* @param projectId the ID of the project where the resources have been locked
* @param removeSystemLocks if true
, also system locks are removed
*/
public void removeResourcesInProject(CmsUUID projectId, boolean removeSystemLocks) {
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock currentLock = itLocks.next();
if (removeSystemLocks && currentLock.getSystemLock().getProjectId().equals(projectId)) {
unlockResource(currentLock.getResourceName(), true);
}
if (currentLock.getEditionLock().getProjectId().equals(projectId)) {
unlockResource(currentLock.getResourceName(), false);
}
}
}
/**
* Removes all exclusive temporary locks of a user.
*
* Only edition lock can be temporary, so no system locks are removed.
*
* @param userId the id of the user whose locks has to be removed
*/
public void removeTempLocks(CmsUUID userId) {
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock currentLock = itLocks.next();
if (currentLock.isTemporary() && currentLock.getUserId().equals(userId)) {
unlockResource(currentLock.getResourceName(), false);
}
}
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
// bring the list of locked resources into a human readable order first
List lockedResources = OpenCms.getMemoryMonitor().getAllCachedLocks();
Collections.sort(lockedResources);
// iterate all locks
Iterator itLocks = lockedResources.iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
buf.append(lock).append("\n");
}
return buf.toString();
}
/**
* Writes the locks that are currently stored in-memory to the database to allow restoring them in
* later startups.
*
* This overwrites the locks previously stored in the underlying database table.
*
* @param dbc the current database context
*
* @throws CmsException if something goes wrong
*/
public void writeLocks(CmsDbContext dbc) throws CmsException {
if (m_isDirty // only if something changed
&& m_runningInServlet // only if started in run level 4
&& OpenCms.getMemoryMonitor().requiresPersistency()) { // only if persistency is required
List locks = OpenCms.getMemoryMonitor().getAllCachedLocks();
m_driverManager.getProjectDriver(dbc).writeLocks(dbc, locks);
m_isDirty = false;
}
}
/**
* Checks if the given resource is lockable by the given user/project/lock type.
*
* @param dbc just to get the site path of the resource
* @param resource the resource to check lockability for
* @param user the user to check
* @param project the project to check
* @param type the lock type to check
* @param currentLock the resource current lock
*
* @throws CmsLockException if resource is not lockable
*/
private void checkLockable(
CmsDbContext dbc,
CmsResource resource,
CmsUser user,
CmsProject project,
CmsLockType type,
CmsLock currentLock)
throws CmsLockException {
if (!currentLock.isLockableBy(user)) {
// check type, owner and project for system locks
// this is required if publishing several siblings
if (currentLock.getSystemLock().isUnlocked()
|| (currentLock.getType() != type)
|| !currentLock.isOwnedInProjectBy(user, project)) {
// display the right message
CmsMessageContainer message = null;
if (currentLock.getSystemLock().isPublish()) {
message = Messages.get().container(
Messages.ERR_RESOURCE_LOCKED_FORPUBLISH_1,
dbc.getRequestContext().getSitePath(resource));
} else if (currentLock.getEditionLock().isInherited()) {
message = Messages.get().container(
Messages.ERR_RESOURCE_LOCKED_INHERITED_1,
dbc.getRequestContext().getSitePath(resource));
} else {
message = Messages.get().container(
Messages.ERR_RESOURCE_LOCKED_BYOTHERUSER_1,
dbc.getRequestContext().getSitePath(resource));
}
throw new CmsLockException(message);
}
}
}
/**
* Returns the direct lock of a resource.
*
* @param resourcename the name of the resource
*
* @return the direct lock of the resource or null
*/
private CmsLock getDirectLock(String resourcename) {
return OpenCms.getMemoryMonitor().getCachedLock(resourcename);
}
/**
* Returns the lock of a possible locked parent folder of a resource, system locks are ignored.
*
* @param resourceName the name of the resource
*
* @return the lock of a parent folder, or {@link CmsLock#getNullLock()} if no parent folders are locked by a non system lock
*/
private CmsLock getParentFolderLock(String resourceName) {
Iterator itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
while (itLocks.hasNext()) {
CmsLock lock = itLocks.next();
if (lock.getResourceName().endsWith("/")
&& !lock.getType().isShallow()
&& resourceName.startsWith(lock.getResourceName())
&& !resourceName.equals(lock.getResourceName())) {
// system locks does not get inherited
lock = lock.getEditionLock();
// check the lock
if (!lock.isUnlocked()) {
return lock;
}
}
}
return CmsLock.getNullLock();
}
/**
* Returns the inherited lock of a resource.
*
* @param resourcename the name of the resource
* @return the inherited lock or the null lock
*/
private CmsLock getParentLock(String resourcename) {
CmsLock parentFolderLock = getParentFolderLock(resourcename);
if (!parentFolderLock.isNullLock()) {
return new CmsLock(
resourcename,
parentFolderLock.getUserId(),
parentFolderLock.getProject(),
CmsLockType.INHERITED);
}
return CmsLock.getNullLock();
}
/**
* Returns the indirect lock of a resource depending on siblings lock state.
*
* @param siblings the list of siblings
* @param resourcename the name of the resource
*
* @return the indirect lock of the resource or the null lock
*/
private CmsLock getSiblingsLock(List siblings, String resourcename) {
for (int i = 0; i < siblings.size(); i++) {
CmsResource sibling = siblings.get(i);
CmsLock exclusiveLock = getDirectLock(sibling.getRootPath());
if (exclusiveLock != null) {
// a sibling is already locked
return internalSiblingLock(exclusiveLock, resourcename);
}
}
// no locked siblings found
return null;
}
/**
* Finally set the given lock.
*
* @param lock the lock to set
* @param locks during reading the locks from db we need to operate on an extra map
*
* @throws CmsLockException if the lock is not compatible with the current lock
*/
private void internalLockResource(CmsLock lock, Map locks) throws CmsLockException {
CmsLock currentLock = null;
if (locks == null) {
currentLock = OpenCms.getMemoryMonitor().getCachedLock(lock.getResourceName());
} else {
currentLock = locks.get(lock.getResourceName());
}
if (currentLock != null) {
if (currentLock.getSystemLock().equals(lock) || currentLock.getEditionLock().equals(lock)) {
return;
}
if (!currentLock.getSystemLock().isUnlocked() && lock.getSystemLock().isUnlocked()) {
lock.setRelatedLock(currentLock);
if (locks == null) {
OpenCms.getMemoryMonitor().cacheLock(lock);
} else {
locks.put(lock.getResourceName(), lock);
}
} else if (currentLock.getSystemLock().isUnlocked() && !lock.getSystemLock().isUnlocked()) {
currentLock.setRelatedLock(lock);
} else {
throw new CmsLockException(
Messages.get().container(Messages.ERR_LOCK_ILLEGAL_STATE_2, currentLock, lock));
}
} else {
if (locks == null) {
OpenCms.getMemoryMonitor().cacheLock(lock);
} else {
locks.put(lock.getResourceName(), lock);
}
}
}
/**
* Reads all siblings from a given resource.
*
* The result is a list of {@link CmsResource}
objects.
* It does NOT contain the resource itself, only the siblings of the resource.
*
* @param dbc the current database context
* @param resource the resource to find all siblings from
*
* @return a list of {@link CmsResource}
Objects that
* are siblings to the specified resource,
* excluding the specified resource itself
*
* @throws CmsException if something goes wrong
*/
private List internalReadSiblings(CmsDbContext dbc, CmsResource resource) throws CmsException {
// reading siblings using the DriverManager methods while the lock state is checked would
// result in an infinite loop, therefore we must access the VFS driver directly
List siblings = m_driverManager.getVfsDriver(
dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, true);
siblings.remove(resource);
return siblings;
}
/**
* Returns a shared lock for the given excclusive lock and sibling.
*
* @param exclusiveLock the exclusive lock to use (has to be set on a sibling of siblingName)
* @param siblingName the siblings name
*
* @return the shared lock
*/
private CmsLock internalSiblingLock(CmsLock exclusiveLock, String siblingName) {
CmsLock lock = null;
if (!exclusiveLock.getSystemLock().isUnlocked()) {
lock = new CmsLock(
siblingName,
exclusiveLock.getUserId(),
exclusiveLock.getProject(),
exclusiveLock.getSystemLock().getType());
}
if ((lock == null) || !exclusiveLock.getEditionLock().isNullLock()) {
CmsLockType type = CmsLockType.SHARED_EXCLUSIVE;
if (!getParentLock(siblingName).isNullLock()) {
type = CmsLockType.SHARED_INHERITED;
}
if (lock == null) {
lock = new CmsLock(siblingName, exclusiveLock.getUserId(), exclusiveLock.getProject(), type);
} else {
CmsLock editionLock = new CmsLock(
siblingName,
exclusiveLock.getUserId(),
exclusiveLock.getProject(),
type);
lock.setRelatedLock(editionLock);
}
}
return lock;
}
/**
* Sets the given lock to the resource.
*
* @param lock the lock to set
*
* @throws CmsLockException if the lock is not compatible with the current lock
*/
private void lockResource(CmsLock lock) throws CmsLockException {
m_isDirty = true;
internalLockResource(lock, null);
}
/**
* Unlocks the the resource with the given name.
*
* @param resourceName the name of the resource to unlock
* @param systemLocks true
if only system locks should be removed,
* and false
if only exclusive locks should be removed
*
* @return the removed lock object
*/
private CmsLock unlockResource(String resourceName, boolean systemLocks) {
m_isDirty = true;
// get the current lock
CmsLock lock = OpenCms.getMemoryMonitor().getCachedLock(resourceName);
if (lock == null) {
return CmsLock.getNullLock();
}
// check the lock type (system or user) to remove
if (systemLocks) {
if (!lock.getSystemLock().isUnlocked()) {
// if a system lock has to be removed
// user locks are removed too
OpenCms.getMemoryMonitor().uncacheLock(resourceName);
return lock;
} else {
// if it is a edition lock, do nothing
return CmsLock.getNullLock();
}
} else {
if (lock.getSystemLock().isUnlocked()) {
// if it is just an edition lock just remove it
OpenCms.getMemoryMonitor().uncacheLock(resourceName);
return lock;
} else {
// if it is a system lock check the edition lock
if (!lock.getEditionLock().isUnlocked()) {
// remove the edition lock
CmsLock tmp = lock.getEditionLock();
CmsLock sysLock = lock.getSystemLock();
sysLock.setRelatedLock(null);
if (!sysLock.equals(lock)) {
// replace the lock entry if needed
OpenCms.getMemoryMonitor().cacheLock(sysLock);
}
return tmp;
} else {
// if there is no edition lock, only a system lock, do nothing
return CmsLock.getNullLock();
}
}
}
}
}