KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > lock > CmsLockManager


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/lock/CmsLockManager.java,v $
3  * Date : $Date: 2006/09/21 09:34:47 $
4  * Version: $Revision: 1.38 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.lock;
33
34 import org.opencms.db.CmsDbContext;
35 import org.opencms.db.CmsDriverManager;
36 import org.opencms.file.CmsProject;
37 import org.opencms.file.CmsResource;
38 import org.opencms.file.CmsVfsResourceNotFoundException;
39 import org.opencms.main.CmsException;
40 import org.opencms.util.CmsUUID;
41
42 import java.util.ArrayList JavaDoc;
43 import java.util.Collections JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.Iterator JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.Map JavaDoc;
48
49 /**
50  * The CmsLockManager is used by the Cms application to detect
51  * the lock state of a resource.<p>
52  *
53  * The lock state depends on the path of the resource, and probably
54  * locked parent folders. The result of a query to the lock manager
55  * are instances of CmsLock objects.<p>
56  *
57  * @author Michael Emmerich
58  * @author Thomas Weckert
59  * @author Andreas Zahner
60  *
61  * @version $Revision: 1.38 $
62  *
63  * @since 6.0.0
64  *
65  * @see org.opencms.file.CmsObject#getLock(CmsResource)
66  * @see org.opencms.lock.CmsLock
67  */

68 public final class CmsLockManager {
69
70     /** The shared lock manager instance. */
71     private static CmsLockManager sharedInstance;
72
73     /** A map holding the exclusive CmsLocks. */
74     private Map JavaDoc m_exclusiveLocks;
75
76     /**
77      * Default constructor.<p>
78      */

79     private CmsLockManager() {
80
81         super();
82         m_exclusiveLocks = Collections.synchronizedMap(new HashMap JavaDoc());
83     }
84     
85     /**
86      * Returns the shared instance of the lock manager.<p>
87      *
88      * @return the shared instance of the lock manager
89      */

90     public static CmsLockManager getInstance() {
91
92         if (sharedInstance == null) {
93             // initialize the shared instance
94
sharedInstance = new CmsLockManager();
95         }
96         return sharedInstance;
97     }
98
99     /**
100      * Adds a resource to the lock manager.<p>
101      *
102      * @param driverManager the driver manager
103      * @param dbc the current database context
104      * @param resource the resource
105      * @param userId the ID of the user who locked the resource
106      * @param projectId the ID of the project where the resource is locked
107      * @param mode flag indicating the mode (temporary or common) of a lock
108      *
109      * @throws CmsLockException if the resource is locked
110      * @throws CmsException if somethong goes wrong
111      */

112     public void addResource(
113         CmsDriverManager driverManager,
114         CmsDbContext dbc,
115         CmsResource resource,
116         CmsUUID userId,
117         int projectId,
118         int mode) throws CmsLockException, CmsException {
119
120         CmsLock lock = getLock(driverManager, dbc, resource);
121         String JavaDoc resourceName = resource.getRootPath();
122
123         if (!lock.isNullLock() && !lock.getUserId().equals(dbc.currentUser().getId())) {
124             throw new CmsLockException(Messages.get().container(
125                 Messages.ERR_RESOURCE_LOCKED_1,
126                 dbc.getRequestContext().getSitePath(resource)));
127         }
128
129         if (lock.isNullLock()) {
130             // create a new exclusive lock unless the resource has already a shared lock due to a
131
// exclusive locked sibling
132
CmsLock newLock = new CmsLock(resourceName, userId, projectId, CmsLock.TYPE_EXCLUSIVE, mode);
133             m_exclusiveLocks.put(resourceName, newLock);
134         }
135
136         // handle collisions with exclusive locked sub-resources in case of a folder
137
if (CmsResource.isFolder(resourceName)) {
138             Iterator JavaDoc i = m_exclusiveLocks.keySet().iterator();
139             String JavaDoc lockedPath = null;
140
141             while (i.hasNext()) {
142                 lockedPath = (String JavaDoc)i.next();
143
144                 if (lockedPath.startsWith(resourceName) && !lockedPath.equals(resourceName)) {
145                     i.remove();
146                 }
147             }
148         }
149     }
150
151     /**
152      * Counts the exclusive locked resources inside a folder.<p>
153      *
154      * @param foldername the folder
155      *
156      * @return the number of exclusive locked resources in the specified folder
157      */

158     public int countExclusiveLocksInFolder(String JavaDoc foldername) {
159
160         Iterator JavaDoc i = m_exclusiveLocks.values().iterator();
161         CmsLock lock = null;
162         int count = 0;
163
164         while (i.hasNext()) {
165             lock = (CmsLock)i.next();
166             if (lock.getResourceName().startsWith(foldername)) {
167                 count++;
168             }
169         }
170
171         return count;
172     }
173
174     /**
175      * Counts the exclusive locked resources in a project.<p>
176      *
177      * @param project the project
178      *
179      * @return the number of exclusive locked resources in the specified project
180      */

181     public int countExclusiveLocksInProject(CmsProject project) {
182
183         Iterator JavaDoc i = m_exclusiveLocks.values().iterator();
184         CmsLock lock = null;
185         int count = 0;
186
187         while (i.hasNext()) {
188             lock = (CmsLock)i.next();
189             if (lock.getProjectId() == project.getId()) {
190                 count++;
191             }
192         }
193
194         return count;
195     }
196
197     /**
198      * Returns the lock for a resource.<p>
199      *
200      * @param driverManager the driver manager
201      * @param dbc the current database context
202      * @param resource the resource
203      *
204      * @return the CmsLock if the specified resource is locked, or the shared Null lock if the resource is not locked
205      * @throws CmsException if something goes wrong
206      */

207     public CmsLock getLock(CmsDriverManager driverManager, CmsDbContext dbc, CmsResource resource) throws CmsException {
208
209         String JavaDoc resourcename = resource.getRootPath();
210
211         // check some abort conditions first
212

213         if (dbc.currentProject().getId() == CmsProject.ONLINE_PROJECT_ID) {
214             // resources are never locked in the online project
215
return CmsLock.getNullLock();
216         }
217
218         if (resource == null) {
219             // non-existent resources are never locked
220
return CmsLock.getNullLock();
221         }
222
223         // try to find an exclusive lock
224

225         if (m_exclusiveLocks.containsKey(resourcename)) {
226             // the resource is exclusive locked
227
return (CmsLock)m_exclusiveLocks.get(resourcename);
228         }
229
230         // calculate the lock state
231

232         // fetch all siblings of the resource to the same content record
233
List JavaDoc siblings = internalReadSiblings(driverManager, dbc, resource);
234
235         CmsLock siblingLock;
236         CmsResource sibling;
237
238         CmsLock parentFolderLock = getParentFolderLock(resourcename);
239         if (parentFolderLock == null) {
240             // all parent folders are unlocked
241

242             for (int i = 0; i < siblings.size(); i++) {
243                 sibling = (CmsResource)siblings.get(i);
244                 siblingLock = (CmsLock)m_exclusiveLocks.get(sibling.getRootPath());
245
246                 if (siblingLock != null) {
247                     // a sibling is already exclusive locked
248
return new CmsLock(
249                         resourcename,
250                         siblingLock.getUserId(),
251                         siblingLock.getProjectId(),
252                         CmsLock.TYPE_SHARED_EXCLUSIVE);
253                 }
254             }
255
256             // no locked siblings found
257
return CmsLock.getNullLock();
258         } else {
259             // a parent folder is locked
260

261             for (int i = 0; i < siblings.size(); i++) {
262                 sibling = (CmsResource)siblings.get(i);
263
264                 if (m_exclusiveLocks.containsKey(sibling.getRootPath())) {
265                     // a sibling is already exclusive locked
266
return new CmsLock(
267                         resourcename,
268                         parentFolderLock.getUserId(),
269                         parentFolderLock.getProjectId(),
270                         CmsLock.TYPE_SHARED_INHERITED);
271                 }
272             }
273
274             // no locked siblings found
275
return new CmsLock(
276                 resourcename,
277                 parentFolderLock.getUserId(),
278                 parentFolderLock.getProjectId(),
279                 CmsLock.TYPE_INHERITED);
280         }
281     }
282
283     /**
284      * Proves if a resource is locked.<p>
285      *
286      * Use {@link org.opencms.lock.CmsLockManager#getLock(CmsDriverManager, CmsDbContext, CmsResource)}
287      * to obtain a CmsLock object for the specified resource to get further information
288      * about how the resource is locked.<p>
289      *
290      * @param driverManager the driver manager
291      * @param dbc the current database context
292      * @param resource the resource
293      *
294      * @return true, if and only if the resource is currently locked
295      * @throws CmsException if something goes wrong
296      */

297     public boolean isLocked(CmsDriverManager driverManager, CmsDbContext dbc, CmsResource resource) throws CmsException {
298
299         CmsLock lock = getLock(driverManager, dbc, resource);
300         return !lock.isNullLock();
301     }
302
303     /**
304      * Removes a resource after it has been deleted by the driver manager.<p>
305      *
306      * @param driverManager the driver manager
307      * @param dbc the current database context
308      * @param resourceName the root path of the deleted resource
309      * @throws CmsException if something goes wrong
310      */

311     public void removeDeletedResource(CmsDriverManager driverManager, CmsDbContext dbc, String JavaDoc resourceName)
312     throws CmsException {
313
314         boolean resourceExists;
315         try {
316             driverManager.getVfsDriver().readResource(dbc, dbc.currentProject().getId(), resourceName, false);
317             resourceExists = true;
318         } catch (CmsVfsResourceNotFoundException e) {
319             resourceExists = false;
320         }
321
322         if (resourceExists) {
323             throw new CmsLockException(Messages.get().container(
324                 Messages.ERR_REMOVING_UNDELETED_RESOURCE_1,
325                 dbc.getRequestContext().removeSiteRoot(resourceName)));
326         }
327
328         m_exclusiveLocks.remove(resourceName);
329     }
330
331     /**
332      * Removes a resource from the lock manager.<p>
333      *
334      * The forceUnlock option should be used with caution. forceUnlock will remove the lock
335      * by ignoring any rules which may cause wrong lock states.<p>
336      *
337      * @param driverManager the driver manager
338      * @param dbc the current database context
339      * @param resource the resource
340      * @param forceUnlock true, if a resource is forced to get unlocked, no matter by which user and in which project the resource is currently locked
341      *
342      * @return the previous CmsLock object of the resource, or null if the resource was unlocked
343      *
344      * @throws CmsException if something goes wrong
345      */

346     public CmsLock removeResource(
347         CmsDriverManager driverManager,
348         CmsDbContext dbc,
349         CmsResource resource,
350         boolean forceUnlock) throws CmsException {
351
352         String JavaDoc resourcename = resource.getRootPath();
353         CmsLock lock = getLock(driverManager, dbc, resource);
354         CmsResource sibling = null;
355
356         // check some abort conditions first
357

358         if (lock.isNullLock()) {
359             // the resource isn't locked
360
return null;
361         }
362
363         if (!forceUnlock
364             && (!lock.getUserId().equals(dbc.currentUser().getId()) || lock.getProjectId() != dbc.currentProject().getId())) {
365             // the resource is locked by another user
366
throw new CmsLockException(Messages.get().container(
367                 Messages.ERR_RESOURCE_UNLOCK_1,
368                 dbc.removeSiteRoot(resourcename)));
369         }
370
371         if (!forceUnlock
372             && (lock.getType() == CmsLock.TYPE_INHERITED || lock.getType() == CmsLock.TYPE_SHARED_INHERITED || (getParentFolderLock(resourcename) != null))) {
373             // sub-resources of a locked folder can't be unlocked
374
throw new CmsLockException(Messages.get().container(
375                 Messages.ERR_UNLOCK_LOCK_INHERITED_1,
376                 dbc.removeSiteRoot(resourcename)));
377         }
378
379         // remove the lock and clean-up stuff
380
if (lock.getType() == CmsLock.TYPE_EXCLUSIVE) {
381             if (resource.isFolder()) {
382                 // in case of a folder, remove any exclusive locks on sub-resources that probably have
383
// been upgraded from an inherited lock when the user edited a resource
384
Iterator JavaDoc i = m_exclusiveLocks.keySet().iterator();
385                 String JavaDoc lockedPath = null;
386
387                 while (i.hasNext()) {
388                     lockedPath = (String JavaDoc)i.next();
389                     if (lockedPath.startsWith(resourcename) && !lockedPath.equals(resourcename)) {
390                         // remove the exclusive locked sub-resource
391
i.remove();
392                     }
393                 }
394             }
395
396             return (CmsLock)m_exclusiveLocks.remove(resourcename);
397         }
398
399         if (lock.getType() == CmsLock.TYPE_SHARED_EXCLUSIVE) {
400             // when a resource with a shared lock gets unlocked, fetch all siblings of the resource
401
// to the same content record to identify the exclusive locked sibling
402
List JavaDoc siblings = internalReadSiblings(driverManager, dbc, resource);
403
404             for (int i = 0; i < siblings.size(); i++) {
405                 sibling = (CmsResource)siblings.get(i);
406
407                 if (m_exclusiveLocks.containsKey(sibling.getRootPath())) {
408                     // remove the exclusive locked sibling
409
m_exclusiveLocks.remove(sibling.getRootPath());
410                     break;
411                 }
412             }
413
414             return lock;
415         }
416
417         return lock;
418     }
419
420     /**
421      * Removes all resources that are exclusively locked in a project.<p>
422      *
423      * @param projectId the ID of the project where the resources have been locked
424      */

425     public void removeResourcesInProject(int projectId) {
426
427         Iterator JavaDoc i = m_exclusiveLocks.keySet().iterator();
428         CmsLock currentLock = null;
429
430         while (i.hasNext()) {
431             currentLock = (CmsLock)m_exclusiveLocks.get(i.next());
432
433             if (currentLock.getProjectId() == projectId) {
434                 // iterators are fail-fast!
435
i.remove();
436             }
437         }
438     }
439
440     /**
441      * Removes all exclusive temporary locks of a user.<p>
442      *
443      * @param userId the ID of the user whose locks are removed
444      */

445     public void removeTempLocks(CmsUUID userId) {
446
447         Iterator JavaDoc i = m_exclusiveLocks.keySet().iterator();
448         CmsLock currentLock = null;
449
450         while (i.hasNext()) {
451             currentLock = (CmsLock)m_exclusiveLocks.get(i.next());
452
453             if (currentLock.getUserId().equals(userId) && currentLock.getMode() == CmsLock.TEMPORARY) {
454                 // iterators are fail-fast!
455
i.remove();
456             }
457         }
458     }
459
460     /**
461      * Returns the number of exclusive locked resources.<p>
462      *
463      * @return the number of exclusive locked resources
464      */

465     public int size() {
466
467         return m_exclusiveLocks.size();
468     }
469
470     /**
471      * @see java.lang.Object#toString()
472      */

473     public String JavaDoc toString() {
474
475         // bring the list of locked resources into a human readable order first
476
List JavaDoc lockedResources = new ArrayList JavaDoc(m_exclusiveLocks.keySet());
477         Collections.sort(lockedResources);
478
479         Iterator JavaDoc i = lockedResources.iterator();
480         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
481         String JavaDoc lockedPath = null;
482         CmsLock currentLock = null;
483
484         while (i.hasNext()) {
485             lockedPath = (String JavaDoc)i.next();
486             currentLock = (CmsLock)m_exclusiveLocks.get(lockedPath);
487             buf.append(currentLock.toString()).append("\n");
488         }
489
490         return buf.toString();
491     }
492
493     /**
494      * @see java.lang.Object#finalize()
495      */

496     protected void finalize() throws Throwable JavaDoc {
497
498         try {
499             if (m_exclusiveLocks != null) {
500                 m_exclusiveLocks.clear();
501
502                 m_exclusiveLocks = null;
503                 sharedInstance = null;
504             }
505         } catch (Throwable JavaDoc t) {
506             // ignore
507
}
508         super.finalize();
509     }
510
511     /**
512      * Returns the lock of a possible locked parent folder of a resource.<p>
513      *
514      * @param resourcename the name of the resource
515      * @return the lock of a parent folder, or null if no parent folders are locked
516      */

517     private CmsLock getParentFolderLock(String JavaDoc resourcename) {
518
519         String JavaDoc lockedPath = null;
520         List JavaDoc keys = new ArrayList JavaDoc(m_exclusiveLocks.keySet());
521
522         for (int i = 0; i < keys.size(); i++) {
523             lockedPath = (String JavaDoc)keys.get(i);
524
525             if (resourcename.startsWith(lockedPath) && !resourcename.equals(lockedPath) && lockedPath.endsWith("/")) {
526                 return (CmsLock)m_exclusiveLocks.get(lockedPath);
527             }
528
529         }
530
531         return null;
532     }
533
534     /**
535      * Reads all siblings from a given resource.<p>
536      *
537      * The result is a list of <code>{@link CmsResource}</code> objects.
538      * It does NOT contain the resource itself, only the siblings of the resource.<p>
539      *
540      * @param driverManager the driver manager
541      * @param dbc the current database context
542      * @param resource the resource to find all siblings from
543      *
544      * @return a list of <code>{@link CmsResource}</code> Objects that
545      * are siblings to the specified resource,
546      * excluding the specified resource itself
547      *
548      * @throws CmsException if something goes wrong
549      */

550     private List JavaDoc internalReadSiblings(CmsDriverManager driverManager, CmsDbContext dbc, CmsResource resource)
551     throws CmsException {
552
553         // reading siblings using the DriverManager methods while the lock state is checked would
554
// inevitably result in an infinite loop...
555

556         List JavaDoc siblings = driverManager.getVfsDriver().readSiblings(dbc, dbc.currentProject(), resource, true);
557         siblings.remove(resource);
558
559         return siblings;
560     }
561 }
Popular Tags