001: /*
002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/lock/CmsLockManager.java,v $
003: * Date : $Date: 2008-02-27 12:05:46 $
004: * Version: $Revision: 1.48 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: */
031:
032: package org.opencms.lock;
033:
034: import org.opencms.db.CmsDbContext;
035: import org.opencms.db.CmsDriverManager;
036: import org.opencms.file.CmsProject;
037: import org.opencms.file.CmsResource;
038: import org.opencms.file.CmsResourceFilter;
039: import org.opencms.file.CmsUser;
040: import org.opencms.file.CmsVfsResourceNotFoundException;
041: import org.opencms.i18n.CmsMessageContainer;
042: import org.opencms.main.CmsException;
043: import org.opencms.main.OpenCms;
044: import org.opencms.util.CmsUUID;
045:
046: import java.util.ArrayList;
047: import java.util.Collections;
048: import java.util.HashMap;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Map;
052:
053: /**
054: * The CmsLockManager is used by the Cms application to detect
055: * the lock state of a resource.<p>
056: *
057: * The lock state depends on the path of the resource, and probably
058: * locked parent folders. The result of a query to the lock manager
059: * are instances of CmsLock objects.<p>
060: *
061: * @author Michael Emmerich
062: * @author Thomas Weckert
063: * @author Andreas Zahner
064: * @author Michael Moossen
065: *
066: * @version $Revision: 1.48 $
067: *
068: * @since 6.0.0
069: *
070: * @see org.opencms.file.CmsObject#getLock(CmsResource)
071: * @see org.opencms.lock.CmsLock
072: */
073: public final class CmsLockManager {
074:
075: /** The driver manager instance. */
076: private CmsDriverManager m_driverManager;
077:
078: /** The flag to indicate if the locks should be written to the db. */
079: private boolean m_isDirty = false;
080:
081: /** The flag to indicate if the lock manager has been started in run level 4. */
082: private boolean m_runningInServlet = false;
083:
084: /**
085: * Default constructor, creates a new lock manager.<p>
086: *
087: * @param driverManager the driver manager instance
088: */
089: public CmsLockManager(CmsDriverManager driverManager) {
090:
091: m_driverManager = driverManager;
092: }
093:
094: /**
095: * Adds a resource to the lock manager.<p>
096: *
097: * @param dbc the current database context
098: * @param resource the resource
099: * @param user the user who locked the resource
100: * @param project the project where the resource is locked
101: * @param type the lock type
102: *
103: * @throws CmsLockException if the resource is locked
104: * @throws CmsException if something goes wrong
105: */
106: public void addResource(CmsDbContext dbc, CmsResource resource,
107: CmsUser user, CmsProject project, CmsLockType type)
108: throws CmsLockException, CmsException {
109:
110: // check the type
111: if (!type.isSystem() && !type.isExclusive()) {
112: // invalid type
113: throw new CmsLockException(Messages.get().container(
114: Messages.ERR_INVALID_LOCK_TYPE_1, type.toString()));
115: }
116:
117: // get the current lock
118: CmsLock currentLock = getLock(dbc, resource);
119:
120: // check lockability
121: checkLockable(dbc, resource, user, project, type, currentLock);
122:
123: boolean needNewLock = true;
124: // prevent shared locks get compromised
125: if ((type.isExclusive())
126: && !(type.isTemporary() && currentLock.isInherited())) {
127: if (!currentLock.getEditionLock().isUnlocked()) {
128: needNewLock = false;
129: }
130: }
131:
132: CmsLock newLock = CmsLock.getNullLock();
133: if (needNewLock) {
134: // lock the resource
135: newLock = new CmsLock(resource.getRootPath(), user.getId(),
136: project, type);
137: lockResource(newLock);
138: }
139:
140: // handle collisions with exclusive locked sub-resources in case of a folder
141: if (resource.isFolder() && newLock.getSystemLock().isUnlocked()) {
142: String resourceName = resource.getRootPath();
143: Iterator itLocks = OpenCms.getMemoryMonitor()
144: .getAllCachedLocks().iterator();
145: while (itLocks.hasNext()) {
146: CmsLock lock = (CmsLock) itLocks.next();
147: String lockedPath = lock.getResourceName();
148: if (lockedPath.startsWith(resourceName)
149: && !lockedPath.equals(resourceName)) {
150: unlockResource(lockedPath, false);
151: }
152: }
153: }
154: }
155:
156: /**
157: * Counts the exclusive locked resources in a project.<p>
158: *
159: * @param project the project
160: *
161: * @return the number of exclusive locked resources in the specified project
162: */
163: public int countExclusiveLocksInProject(CmsProject project) {
164:
165: int count = 0;
166: Iterator itLocks = OpenCms.getMemoryMonitor()
167: .getAllCachedLocks().iterator();
168: while (itLocks.hasNext()) {
169: CmsLock lock = (CmsLock) itLocks.next();
170: if (lock.getEditionLock().isInProject(project)) {
171: count++;
172: }
173: }
174: return count;
175: }
176:
177: /**
178: * Returns the lock state of the given resource.<p>
179: *
180: * In case no lock is set, the <code>null lock</code> which can be obtained
181: * by {@link CmsLock#getNullLock()} is returned.<p>
182: *
183: * @param dbc the current database context
184: * @param resource the resource
185: *
186: * @return the lock state of the given resource
187:
188: * @throws CmsException if something goes wrong
189: */
190: public CmsLock getLock(CmsDbContext dbc, CmsResource resource)
191: throws CmsException {
192:
193: return getLock(dbc, resource, true);
194: }
195:
196: /**
197: * Returns the lock state of the given resource.<p>
198: *
199: * In case no lock is set, the <code>null lock</code> which can be obtained
200: * by {@link CmsLock#getNullLock()} is returned.<p>
201: *
202: * @param dbc the current database context
203: * @param resource the resource
204: * @param includeSiblings if siblings (shared locks) should be included in the search
205: *
206: * @return the lock state of the given resource
207:
208: * @throws CmsException if something goes wrong
209: */
210: public CmsLock getLock(CmsDbContext dbc, CmsResource resource,
211: boolean includeSiblings) throws CmsException {
212:
213: // resources are never locked in the online project
214: // and non-existent resources are never locked
215: if ((resource == null)
216: || (dbc.currentProject().isOnlineProject())) {
217: return CmsLock.getNullLock();
218: }
219:
220: // check exclusive direct locks first
221: CmsLock lock = getDirectLock(resource.getRootPath());
222: if ((lock == null) && includeSiblings) {
223: // check if siblings are exclusively locked
224: List siblings = internalReadSiblings(dbc, resource);
225: lock = getSiblingsLock(siblings, resource.getRootPath());
226: }
227: if (lock == null) {
228: // if there is no parent lock, this will be the null lock as well
229: lock = getParentLock(resource.getRootPath());
230: }
231: if (!lock.getSystemLock().isUnlocked()) {
232: lock = lock.getSystemLock();
233: } else {
234: lock = lock.getEditionLock();
235: }
236: return lock;
237: }
238:
239: /**
240: * Returns all exclusive locked resources matching the given resource name and filter.<p>
241: *
242: * @param dbc the database context
243: * @param resourceName the resource name
244: * @param filter the lock filter
245: *
246: * @return a list of root paths
247: *
248: * @throws CmsException if something goes wrong
249: */
250: public List getLocks(CmsDbContext dbc, String resourceName,
251: CmsLockFilter filter) throws CmsException {
252:
253: List locks = new ArrayList();
254: Iterator itLocks = OpenCms.getMemoryMonitor()
255: .getAllCachedLocks().iterator();
256: while (itLocks.hasNext()) {
257: CmsLock lock = (CmsLock) itLocks.next();
258: if (filter.isSharedExclusive()) {
259: CmsResource resource;
260: try {
261: resource = m_driverManager.readResource(dbc, lock
262: .getResourceName(), CmsResourceFilter.ALL);
263: } catch (CmsVfsResourceNotFoundException e) {
264: OpenCms.getMemoryMonitor().uncacheLock(
265: lock.getResourceName());
266: continue;
267: }
268: if (resource.getSiblingCount() > 1) {
269: Iterator itSiblings = internalReadSiblings(dbc,
270: resource).iterator();
271: while (itSiblings.hasNext()) {
272: CmsResource sibling = (CmsResource) itSiblings
273: .next();
274: CmsLock siblingLock = internalSiblingLock(lock,
275: sibling.getRootPath());
276: if (filter.match(resourceName, siblingLock)) {
277: locks.add(siblingLock);
278: }
279: }
280: }
281: }
282: if (filter.match(resourceName, lock)) {
283: locks.add(lock);
284: }
285: }
286: return locks;
287: }
288:
289: /**
290: * Returns <code>true</code> if the given resource contains a resource that has a system lock.<p>
291: *
292: * This check is required for certain operations on folders.<p>
293: *
294: * @param dbc the database context
295: * @param resource the resource to check the system locks for
296: *
297: * @return <code>true</code> if the given resource contains a resource that has a system lock
298: *
299: * @throws CmsException if something goes wrong
300: */
301: public boolean hasSystemLocks(CmsDbContext dbc, CmsResource resource)
302: throws CmsException {
303:
304: if (resource == null) {
305: return false;
306: }
307: Iterator itLocks = OpenCms.getMemoryMonitor()
308: .getAllCachedLocks().iterator();
309: while (itLocks.hasNext()) {
310: CmsLock lock = (CmsLock) itLocks.next();
311: if (lock.getSystemLock().isUnlocked()) {
312: // only system locks matter here
313: continue;
314: }
315: if (lock.getResourceName().startsWith(
316: resource.getRootPath())) {
317: if (lock.getResourceName().startsWith(
318: resource.getRootPath())) {
319: return true;
320: }
321: try {
322: resource = m_driverManager.readResource(dbc, lock
323: .getResourceName(), CmsResourceFilter.ALL);
324: } catch (CmsVfsResourceNotFoundException e) {
325: OpenCms.getMemoryMonitor().uncacheLock(
326: lock.getResourceName());
327: continue;
328: }
329: CmsResource lockedResource;
330: try {
331: lockedResource = m_driverManager.readResource(dbc,
332: lock.getResourceName(),
333: CmsResourceFilter.ALL);
334: } catch (CmsVfsResourceNotFoundException e) {
335: OpenCms.getMemoryMonitor().uncacheLock(
336: lock.getResourceName());
337: continue;
338: }
339: if (lockedResource.getSiblingCount() > 1) {
340: Iterator itSiblings = internalReadSiblings(dbc,
341: lockedResource).iterator();
342: while (itSiblings.hasNext()) {
343: CmsResource sibling = (CmsResource) itSiblings
344: .next();
345: CmsLock siblingLock = internalSiblingLock(lock,
346: sibling.getRootPath());
347: if (siblingLock.getResourceName().startsWith(
348: resource.getRootPath())) {
349: return true;
350: }
351: }
352: }
353: }
354: }
355: return false;
356: }
357:
358: /**
359: * Moves a lock during the move resource operation.<p>
360: *
361: * @param source the source root path
362: * @param destination the destination root path
363: */
364: public void moveResource(String source, String destination) {
365:
366: CmsLock lock = OpenCms.getMemoryMonitor().getCachedLock(source);
367: if (lock != null) {
368: OpenCms.getMemoryMonitor().uncacheLock(
369: lock.getResourceName());
370: CmsLock newLock = new CmsLock(destination,
371: lock.getUserId(), lock.getProject(), lock.getType());
372: lock = lock.getRelatedLock();
373: if ((lock != null) && !lock.isNullLock()) {
374: CmsLock relatedLock = new CmsLock(destination, lock
375: .getUserId(), lock.getProject(), lock.getType());
376: newLock.setRelatedLock(relatedLock);
377: }
378: OpenCms.getMemoryMonitor().cacheLock(newLock);
379: }
380: }
381:
382: /**
383: * Reads the latest saved locks from the database and installs them to
384: * this lock manager.<p>
385: *
386: * @param dbc the current database context
387: *
388: * @throws CmsException if something goes wrong
389: */
390: public void readLocks(CmsDbContext dbc) throws CmsException {
391:
392: if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_3_SHELL_ACCESS) {
393: // read the locks only if the wizard is not enabled
394: Map lockCache = new HashMap();
395: List locks = m_driverManager.getProjectDriver().readLocks(
396: dbc);
397: Iterator itLocks = locks.iterator();
398: while (itLocks.hasNext()) {
399: CmsLock lock = (CmsLock) itLocks.next();
400: internalLockResource(lock, lockCache);
401: }
402: OpenCms.getMemoryMonitor().flushLocks(lockCache);
403: m_runningInServlet = true;
404: }
405: }
406:
407: /**
408: * Removes a resource after it has been deleted by the driver manager.<p>
409: *
410: * @param dbc the current database context
411: * @param resourceName the root path of the deleted resource
412: * @throws CmsException if something goes wrong
413: */
414: public void removeDeletedResource(CmsDbContext dbc,
415: String resourceName) throws CmsException {
416:
417: try {
418: m_driverManager.getVfsDriver()
419: .readResource(dbc, dbc.currentProject().getUuid(),
420: resourceName, false);
421: throw new CmsLockException(Messages.get().container(
422: Messages.ERR_REMOVING_UNDELETED_RESOURCE_1,
423: dbc.getRequestContext()
424: .removeSiteRoot(resourceName)));
425: } catch (CmsVfsResourceNotFoundException e) {
426: // ok, ignore
427: }
428: unlockResource(resourceName, true);
429: unlockResource(resourceName, false);
430: }
431:
432: /**
433: * Removes all locks of a user.<p>
434: *
435: * Edition and system locks are removed.<p>
436: *
437: * @param userId the id of the user whose locks should be removed
438: */
439: public void removeLocks(CmsUUID userId) {
440:
441: Iterator itLocks = OpenCms.getMemoryMonitor()
442: .getAllCachedLocks().iterator();
443: while (itLocks.hasNext()) {
444: CmsLock currentLock = (CmsLock) itLocks.next();
445: boolean editLock = currentLock.getEditionLock().getUserId()
446: .equals(userId);
447: boolean sysLock = currentLock.getSystemLock().getUserId()
448: .equals(userId);
449: if (editLock) {
450: unlockResource(currentLock.getResourceName(), false);
451: }
452: if (sysLock) {
453: unlockResource(currentLock.getResourceName(), true);
454: }
455: }
456: }
457:
458: /**
459: * Removes a resource from the lock manager.<p>
460: *
461: * The forceUnlock option should be used with caution.<br>
462: * forceUnlock will remove the lock by ignoring any rules which may cause wrong lock states.<p>
463: *
464: * @param dbc the current database context
465: * @param resource the resource
466: * @param forceUnlock <code>true</code>, if a resource is forced to get unlocked (only edition locks),
467: * no matter by which user and in which project the resource is currently locked
468: * @param removeSystemLock <code>true</code>, if you also want to remove system locks
469: *
470: * @return the previous {@link CmsLock} object of the resource,
471: * or <code>{@link CmsLock#getNullLock()}</code> if the resource was unlocked
472: *
473: * @throws CmsException if something goes wrong
474: */
475: public CmsLock removeResource(CmsDbContext dbc,
476: CmsResource resource, boolean forceUnlock,
477: boolean removeSystemLock) throws CmsException {
478:
479: String resourcename = resource.getRootPath();
480: CmsLock lock = getLock(dbc, resource).getEditionLock();
481:
482: // check some abort conditions first
483: if (!lock.isNullLock()) {
484: // the resource is locked by another user or in other project
485: if (!forceUnlock
486: && (!lock.isOwnedInProjectBy(dbc.currentUser(), dbc
487: .currentProject()))) {
488: throw new CmsLockException(Messages.get().container(
489: Messages.ERR_RESOURCE_UNLOCK_1,
490: dbc.removeSiteRoot(resourcename)));
491: }
492:
493: // sub-resources of a locked folder can't be unlocked
494: if (!forceUnlock && lock.isInherited()) {
495: throw new CmsLockException(Messages.get().container(
496: Messages.ERR_UNLOCK_LOCK_INHERITED_1,
497: dbc.removeSiteRoot(resourcename)));
498: }
499: }
500:
501: // remove the lock and clean-up stuff
502: if (lock.isExclusive()) {
503: if (resource.isFolder()) {
504: // in case of a folder, remove any exclusive locks on sub-resources that probably have
505: // been upgraded from an inherited lock when the user edited a resource
506: Iterator itLocks = OpenCms.getMemoryMonitor()
507: .getAllCachedLocks().iterator();
508: while (itLocks.hasNext()) {
509: String lockedPath = ((CmsLock) itLocks.next())
510: .getResourceName();
511: if (lockedPath.startsWith(resourcename)
512: && !lockedPath.equals(resourcename)) {
513: // remove the exclusive locked sub-resource
514: unlockResource(lockedPath, false);
515: }
516: }
517: }
518: if (removeSystemLock) {
519: unlockResource(resourcename, true);
520: }
521: unlockResource(resourcename, false);
522: return lock;
523: }
524:
525: if (lock.getType().isSharedExclusive()) {
526: List locks = OpenCms.getMemoryMonitor()
527: .getAllCachedLockPaths();
528: // when a resource with a shared lock gets unlocked, fetch all siblings of the resource
529: // to the same content record to identify the exclusive locked sibling
530: List siblings = internalReadSiblings(dbc, resource);
531: for (int i = 0; i < siblings.size(); i++) {
532: CmsResource sibling = (CmsResource) siblings.get(i);
533: if (locks.contains(sibling.getRootPath())) {
534: // remove the exclusive locked sibling
535: if (removeSystemLock) {
536: unlockResource(sibling.getRootPath(), true);
537: }
538: unlockResource(sibling.getRootPath(), false);
539: break; // it can only be one!
540: }
541: }
542: return lock;
543: }
544:
545: // remove system locks only if explicit required
546: if (removeSystemLock
547: && !getLock(dbc, resource).getSystemLock().isUnlocked()) {
548: return unlockResource(resourcename, true);
549: }
550: return lock;
551: }
552:
553: /**
554: * Removes all resources locked in a project.<p>
555: *
556: * @param projectId the ID of the project where the resources have been locked
557: * @param removeSystemLocks if <code>true</code>, also system locks are removed
558: */
559: public void removeResourcesInProject(CmsUUID projectId,
560: boolean removeSystemLocks) {
561:
562: Iterator itLocks = OpenCms.getMemoryMonitor()
563: .getAllCachedLocks().iterator();
564: while (itLocks.hasNext()) {
565: CmsLock currentLock = (CmsLock) itLocks.next();
566: if (removeSystemLocks
567: && currentLock.getSystemLock().getProjectId()
568: .equals(projectId)) {
569: unlockResource(currentLock.getResourceName(), true);
570: }
571: if (currentLock.getEditionLock().getProjectId().equals(
572: projectId)) {
573: unlockResource(currentLock.getResourceName(), false);
574: }
575: }
576: }
577:
578: /**
579: * Removes all exclusive temporary locks of a user.<p>
580: *
581: * Only edition lock can be temporary, so no system locks are removed.<p>
582: *
583: * @param userId the id of the user whose locks has to be removed
584: */
585: public void removeTempLocks(CmsUUID userId) {
586:
587: Iterator itLocks = OpenCms.getMemoryMonitor()
588: .getAllCachedLocks().iterator();
589: while (itLocks.hasNext()) {
590: CmsLock currentLock = (CmsLock) itLocks.next();
591: if (currentLock.isTemporary()
592: && currentLock.getUserId().equals(userId)) {
593: unlockResource(currentLock.getResourceName(), false);
594: }
595: }
596: }
597:
598: /**
599: * @see java.lang.Object#toString()
600: */
601: public String toString() {
602:
603: StringBuffer buf = new StringBuffer();
604:
605: // bring the list of locked resources into a human readable order first
606: List lockedResources = OpenCms.getMemoryMonitor()
607: .getAllCachedLocks();
608: Collections.sort(lockedResources);
609:
610: // iterate all locks
611: Iterator itLocks = lockedResources.iterator();
612: while (itLocks.hasNext()) {
613: CmsLock lock = (CmsLock) itLocks.next();
614: buf.append(lock).append("\n");
615: }
616: return buf.toString();
617: }
618:
619: /**
620: * Writes the locks that are currently stored in-memory to the database to allow restoring them in
621: * later startups.<p>
622: *
623: * This overwrites the locks previously stored in the underlying database table.<p>
624: *
625: * @param dbc the current database context
626: *
627: * @throws CmsException if something goes wrong
628: */
629: public void writeLocks(CmsDbContext dbc) throws CmsException {
630:
631: if (m_isDirty // only if something changed
632: && m_runningInServlet // only if started in run level 4
633: && OpenCms.getMemoryMonitor().requiresPersistency()) { // only if persistency is required
634:
635: List locks = OpenCms.getMemoryMonitor().getAllCachedLocks();
636: m_driverManager.getProjectDriver().writeLocks(dbc, locks);
637: m_isDirty = false;
638: }
639: }
640:
641: /**
642: * Checks if the given resource is lockable by the given user/project/lock type.<p>
643: *
644: * @param dbc just to get the site path of the resource
645: * @param resource the resource to check lockability for
646: * @param user the user to check
647: * @param project the project to check
648: * @param type the lock type to check
649: * @param currentLock the resource current lock
650: *
651: * @throws CmsLockException if resource is not lockable
652: */
653: private void checkLockable(CmsDbContext dbc, CmsResource resource,
654: CmsUser user, CmsProject project, CmsLockType type,
655: CmsLock currentLock) throws CmsLockException {
656:
657: if (!currentLock.isLockableBy(user)) {
658: // check type, owner and project for system locks
659: // this is required if publishing several siblings
660: if (currentLock.getSystemLock().isUnlocked()
661: || (currentLock.getType() != type)
662: || !currentLock.isOwnedInProjectBy(user, project)) {
663: // display the right message
664: CmsMessageContainer message = null;
665: if (currentLock.getSystemLock().isPublish()) {
666: message = Messages.get().container(
667: Messages.ERR_RESOURCE_LOCKED_FORPUBLISH_1,
668: dbc.getRequestContext().getSitePath(
669: resource));
670: } else if (currentLock.getEditionLock().isInherited()) {
671: message = Messages.get().container(
672: Messages.ERR_RESOURCE_LOCKED_INHERITED_1,
673: dbc.getRequestContext().getSitePath(
674: resource));
675: } else {
676: message = Messages.get().container(
677: Messages.ERR_RESOURCE_LOCKED_BYOTHERUSER_1,
678: dbc.getRequestContext().getSitePath(
679: resource));
680: }
681: throw new CmsLockException(message);
682: }
683: }
684: }
685:
686: /**
687: * Returns the direct lock of a resource.<p>
688: *
689: * @param resourcename the name of the resource
690: *
691: * @return the direct lock of the resource or <code>null</code>
692: */
693: private CmsLock getDirectLock(String resourcename) {
694:
695: return OpenCms.getMemoryMonitor().getCachedLock(resourcename);
696: }
697:
698: /**
699: * Returns the lock of a possible locked parent folder of a resource, system locks are ignored.<p>
700: *
701: * @param resourceName the name of the resource
702: *
703: * @return the lock of a parent folder, or {@link CmsLock#getNullLock()} if no parent folders are locked by a non system lock
704: */
705: private CmsLock getParentFolderLock(String resourceName) {
706:
707: Iterator itLocks = OpenCms.getMemoryMonitor()
708: .getAllCachedLocks().iterator();
709: while (itLocks.hasNext()) {
710: CmsLock lock = (CmsLock) itLocks.next();
711: if (lock.getResourceName().endsWith("/")
712: && resourceName.startsWith(lock.getResourceName())
713: && !resourceName.equals(lock.getResourceName())) {
714: // system locks does not get inherited
715: lock = lock.getEditionLock();
716: // check the lock
717: if (!lock.isUnlocked()) {
718: return lock;
719: }
720: }
721: }
722: return CmsLock.getNullLock();
723: }
724:
725: /**
726: * Returns the inherited lock of a resource.<p>
727: *
728: * @param resourcename the name of the resource
729: * @return the inherited lock or the null lock
730: */
731: private CmsLock getParentLock(String resourcename) {
732:
733: CmsLock parentFolderLock = getParentFolderLock(resourcename);
734: if (!parentFolderLock.isNullLock()) {
735: return new CmsLock(resourcename, parentFolderLock
736: .getUserId(), parentFolderLock.getProject(),
737: CmsLockType.INHERITED);
738: }
739: return CmsLock.getNullLock();
740: }
741:
742: /**
743: * Returns the indirect lock of a resource depending on siblings lock state.<p>
744: *
745: * @param siblings the list of siblings
746: * @param resourcename the name of the resource
747: *
748: * @return the indirect lock of the resource or the null lock
749: */
750: private CmsLock getSiblingsLock(List siblings, String resourcename) {
751:
752: for (int i = 0; i < siblings.size(); i++) {
753: CmsResource sibling = (CmsResource) siblings.get(i);
754: CmsLock exclusiveLock = getDirectLock(sibling.getRootPath());
755: if (exclusiveLock != null) {
756: // a sibling is already locked
757: return internalSiblingLock(exclusiveLock, resourcename);
758: }
759: }
760: // no locked siblings found
761: return null;
762:
763: }
764:
765: /**
766: * Finally set the given lock.<p>
767: *
768: * @param lock the lock to set
769: * @param locks during reading the locks from db we need to operate on an extra map
770: *
771: * @throws CmsLockException if the lock is not compatible with the current lock
772: */
773: private void internalLockResource(CmsLock lock, Map locks)
774: throws CmsLockException {
775:
776: CmsLock currentLock = null;
777: if (locks == null) {
778: currentLock = OpenCms.getMemoryMonitor().getCachedLock(
779: lock.getResourceName());
780: } else {
781: currentLock = (CmsLock) locks.get(lock.getResourceName());
782: }
783: if (currentLock != null) {
784: if (currentLock.getSystemLock().equals(lock)
785: || currentLock.getEditionLock().equals(lock)) {
786: return;
787: }
788: if (!currentLock.getSystemLock().isUnlocked()
789: && lock.getSystemLock().isUnlocked()) {
790: lock.setRelatedLock(currentLock);
791: if (locks == null) {
792: OpenCms.getMemoryMonitor().cacheLock(lock);
793: } else {
794: locks.put(lock.getResourceName(), lock);
795: }
796: } else if (currentLock.getSystemLock().isUnlocked()
797: && !lock.getSystemLock().isUnlocked()) {
798: currentLock.setRelatedLock(lock);
799: } else {
800: throw new CmsLockException(Messages.get().container(
801: Messages.ERR_LOCK_ILLEGAL_STATE_2, currentLock,
802: lock));
803: }
804: } else {
805: if (locks == null) {
806: OpenCms.getMemoryMonitor().cacheLock(lock);
807: } else {
808: locks.put(lock.getResourceName(), lock);
809: }
810: }
811: }
812:
813: /**
814: * Reads all siblings from a given resource.<p>
815: *
816: * The result is a list of <code>{@link CmsResource}</code> objects.
817: * It does NOT contain the resource itself, only the siblings of the resource.<p>
818: *
819: * @param dbc the current database context
820: * @param resource the resource to find all siblings from
821: *
822: * @return a list of <code>{@link CmsResource}</code> Objects that
823: * are siblings to the specified resource,
824: * excluding the specified resource itself
825: *
826: * @throws CmsException if something goes wrong
827: */
828: private List internalReadSiblings(CmsDbContext dbc,
829: CmsResource resource) throws CmsException {
830:
831: // reading siblings using the DriverManager methods while the lock state is checked would
832: // result in an infinite loop, therefore we must access the VFS driver directly
833: List siblings = m_driverManager.getVfsDriver().readSiblings(
834: dbc, dbc.currentProject().getUuid(), resource, true);
835: siblings.remove(resource);
836: return siblings;
837: }
838:
839: /**
840: * Returns a shared lock for the given excclusive lock and sibling.<p>
841: *
842: * @param exclusiveLock the exclusive lock to use (has to be set on a sibling of siblingName)
843: * @param siblingName the siblings name
844: *
845: * @return the shared lock
846: */
847: private CmsLock internalSiblingLock(CmsLock exclusiveLock,
848: String siblingName) {
849:
850: CmsLock lock = null;
851: if (!exclusiveLock.getSystemLock().isUnlocked()) {
852: lock = new CmsLock(siblingName, exclusiveLock.getUserId(),
853: exclusiveLock.getProject(), exclusiveLock
854: .getSystemLock().getType());
855: }
856: if ((lock == null)
857: || !exclusiveLock.getEditionLock().isNullLock()) {
858: CmsLockType type = CmsLockType.SHARED_EXCLUSIVE;
859: if (!getParentLock(siblingName).isNullLock()) {
860: type = CmsLockType.SHARED_INHERITED;
861: }
862: if (lock == null) {
863: lock = new CmsLock(siblingName, exclusiveLock
864: .getUserId(), exclusiveLock.getProject(), type);
865: } else {
866: CmsLock editionLock = new CmsLock(siblingName,
867: exclusiveLock.getUserId(), exclusiveLock
868: .getProject(), type);
869: lock.setRelatedLock(editionLock);
870: }
871: }
872: return lock;
873: }
874:
875: /**
876: * Sets the given lock to the resource.<p>
877: *
878: * @param lock the lock to set
879: *
880: * @throws CmsLockException if the lock is not compatible with the current lock
881: */
882: private void lockResource(CmsLock lock) throws CmsLockException {
883:
884: m_isDirty = true;
885: internalLockResource(lock, null);
886: }
887:
888: /**
889: * Unlocks the the resource with the given name.<p>
890: *
891: * @param resourceName the name of the resource to unlock
892: * @param systemLocks <code>true</code> if only system locks should be removed,
893: * and <code>false</code> if only exclusive locks should be removed
894: *
895: * @return the removed lock object
896: */
897: private CmsLock unlockResource(String resourceName,
898: boolean systemLocks) {
899:
900: m_isDirty = true;
901:
902: // get the current lock
903: CmsLock lock = OpenCms.getMemoryMonitor().getCachedLock(
904: resourceName);
905: if (lock == null) {
906: return CmsLock.getNullLock();
907: }
908:
909: // check the lock type (system or user) to remove
910: if (systemLocks) {
911: if (!lock.getSystemLock().isUnlocked()) {
912: // if a system lock has to be removed
913: // user locks are removed too
914: OpenCms.getMemoryMonitor().uncacheLock(resourceName);
915: return lock;
916: } else {
917: // if it is a edition lock, do nothing
918: return CmsLock.getNullLock();
919: }
920: } else {
921: if (lock.getSystemLock().isUnlocked()) {
922: // if it is just an edition lock just remove it
923: OpenCms.getMemoryMonitor().uncacheLock(resourceName);
924: return lock;
925: } else {
926: // if it is a system lock check the edition lock
927: if (!lock.getEditionLock().isUnlocked()) {
928: // remove the edition lock
929: CmsLock tmp = lock.getEditionLock();
930: CmsLock sysLock = lock.getSystemLock();
931: sysLock.setRelatedLock(null);
932: if (!sysLock.equals(lock)) {
933: // replace the lock entry if needed
934: OpenCms.getMemoryMonitor().cacheLock(sysLock);
935: }
936: return tmp;
937: } else {
938: // if there is no edition lock, only a system lock, do nothing
939: return CmsLock.getNullLock();
940: }
941: }
942: }
943: }
944: }
|