1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.jcr;
25
26 import java.util.Collection;
27 import java.util.UUID;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30 import javax.jcr.Item;
31 import javax.jcr.Node;
32 import javax.jcr.RepositoryException;
33 import javax.jcr.Session;
34 import javax.jcr.lock.Lock;
35 import javax.jcr.lock.LockException;
36 import net.jcip.annotations.ThreadSafe;
37 import org.modeshape.common.i18n.I18n;
38 import org.modeshape.graph.ExecutionContext;
39 import org.modeshape.graph.Graph;
40 import org.modeshape.graph.Location;
41 import org.modeshape.graph.connector.LockFailedException;
42 import org.modeshape.graph.property.DateTime;
43 import org.modeshape.graph.property.DateTimeFactory;
44 import org.modeshape.graph.property.Path;
45 import org.modeshape.graph.property.PathFactory;
46 import org.modeshape.graph.property.PathNotFoundException;
47 import org.modeshape.graph.property.Property;
48 import org.modeshape.graph.property.PropertyFactory;
49 import org.modeshape.graph.property.ValueFactory;
50
51
52
53
54
55 @ThreadSafe
56 class WorkspaceLockManager {
57
58 private final ExecutionContext context;
59 private final Path locksPath;
60 private final RepositoryLockManager repositoryLockManager;
61 private final String workspaceName;
62 private final ConcurrentMap<UUID, ModeShapeLock> workspaceLocksByNodeUuid;
63
64 WorkspaceLockManager( ExecutionContext context,
65 RepositoryLockManager repositoryLockManager,
66 String workspaceName,
67 Path locksPath ) {
68 this.context = context;
69 this.repositoryLockManager = repositoryLockManager;
70 this.workspaceName = workspaceName;
71 this.locksPath = locksPath;
72
73 this.workspaceLocksByNodeUuid = new ConcurrentHashMap<UUID, ModeShapeLock>();
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 ModeShapeLock lock( JcrSession session,
92 Location nodeLocation,
93 boolean isDeep,
94 boolean isSessionScoped ) throws RepositoryException {
95 assert nodeLocation != null;
96
97 UUID lockUuid = UUID.randomUUID();
98 UUID nodeUuid = uuidFor(session, nodeLocation);
99
100 if (nodeUuid == null) {
101 throw new RepositoryException(JcrI18n.uuidRequiredForLock.text(nodeLocation));
102 }
103
104 ExecutionContext sessionContext = session.getExecutionContext();
105 String lockOwner = session.getUserID();
106
107 DateTimeFactory dateFactory = sessionContext.getValueFactories().getDateFactory();
108 DateTime expirationDate = dateFactory.create();
109 expirationDate = expirationDate.plusMillis(JcrEngine.LOCK_EXTENSION_INTERVAL_IN_MILLIS);
110
111 lockNodeInSystemGraph(sessionContext,
112 nodeUuid,
113 lockUuid,
114 workspaceName,
115 isSessionScoped,
116 isDeep,
117 session.sessionId(),
118 session.getUserID(),
119 expirationDate);
120
121 try {
122 lockNodeInRepository(session, nodeUuid, session.getUserID(), isDeep);
123 } catch (LockFailedException lfe) {
124
125 unlockNodeInSystemGraph(session.getExecutionContext(), nodeUuid);
126 throw new RepositoryException(lfe);
127 }
128
129 ModeShapeLock lock = lockNodeInternally(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
130
131 session.cache().refreshProperties(Location.create(nodeUuid));
132
133 return lock;
134 }
135
136 ModeShapeLock lockNodeInternally( String lockOwner,
137 UUID lockUuid,
138 UUID lockedNodeUuid,
139 boolean isDeep,
140 boolean isSessionScoped ) {
141 ModeShapeLock lock = createLock(lockOwner, lockUuid, lockedNodeUuid, isDeep, isSessionScoped);
142
143 workspaceLocksByNodeUuid.put(lockedNodeUuid, lock);
144
145 return lock;
146 }
147
148 ModeShapeLock createLock( org.modeshape.graph.Node lockNode ) {
149 return new ModeShapeLock(lockNode);
150 }
151
152
153 ModeShapeLock createLock( String lockOwner,
154 UUID lockUuid,
155 UUID nodeUuid,
156 boolean isDeep,
157 boolean isSessionScoped ) {
158 return new ModeShapeLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 void lockNodeInRepository( JcrSession session,
186 UUID nodeUuid,
187 String lockOwner,
188 boolean isDeep ) throws LockFailedException, RepositoryException {
189 PropertyFactory propFactory = session.getExecutionContext().getPropertyFactory();
190 Property lockOwnerProp = propFactory.create(JcrLexicon.LOCK_OWNER, lockOwner);
191 Property lockIsDeepProp = propFactory.create(JcrLexicon.LOCK_IS_DEEP, isDeep);
192
193
194 Graph.Batch workspaceBatch = repositoryLockManager.createWorkspaceGraph(workspaceName, session.getExecutionContext()).batch();
195 workspaceBatch.set(lockOwnerProp, lockIsDeepProp).on(nodeUuid);
196 if (isDeep) {
197 workspaceBatch.lock(nodeUuid).andItsDescendants().withDefaultTimeout();
198 } else {
199 workspaceBatch.lock(nodeUuid).only().withDefaultTimeout();
200 }
201 workspaceBatch.execute();
202
203 }
204
205 private final void lockNodeInSystemGraph( ExecutionContext sessionContext,
206 UUID nodeUuid,
207 UUID lockUuid,
208 String workspaceName,
209 boolean isSessionScoped,
210 boolean lockIsDeep,
211 String sessionId,
212 String lockOwner,
213 DateTime expirationDate ) {
214 PathFactory pathFactory = sessionContext.getValueFactories().getPathFactory();
215 PropertyFactory propFactory = sessionContext.getPropertyFactory();
216
217 Graph graph = repositoryLockManager.createSystemGraph(sessionContext);
218 graph.create(pathFactory.create(locksPath, pathFactory.createSegment(lockUuid.toString())),
219 propFactory.create(JcrLexicon.PRIMARY_TYPE, ModeShapeLexicon.LOCK),
220 propFactory.create(ModeShapeLexicon.WORKSPACE, workspaceName),
221 propFactory.create(ModeShapeLexicon.LOCKED_UUID, nodeUuid.toString()),
222 propFactory.create(ModeShapeLexicon.IS_SESSION_SCOPED, isSessionScoped),
223 propFactory.create(ModeShapeLexicon.LOCKING_SESSION, sessionId),
224 propFactory.create(ModeShapeLexicon.EXPIRATION_DATE, expirationDate),
225
226 propFactory.create(ModeShapeLexicon.IS_HELD_BY_SESSION, false),
227 propFactory.create(JcrLexicon.LOCK_OWNER, lockOwner),
228 propFactory.create(JcrLexicon.LOCK_IS_DEEP, lockIsDeep)).ifAbsent().and();
229
230 }
231
232
233
234
235
236
237
238 void unlock( ExecutionContext sessionExecutionContext,
239 ModeShapeLock lock ) {
240 try {
241
242 unlockNodeInSystemGraph(sessionExecutionContext, lock.getUuid());
243
244
245 unlockNodeInRepository(sessionExecutionContext, lock);
246
247 unlockNodeInternally(lock.nodeUuid);
248 } catch (PathNotFoundException pnfe) {
249
250
251
252
253 if (!lock.nodeUuid.equals(pnfe.getLocation().getUuid())) {
254
255 throw new IllegalStateException(pnfe);
256 }
257 workspaceLocksByNodeUuid.remove(lock.nodeUuid);
258 }
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273 void unlockNodeInRepository( ExecutionContext sessionExecutionContext,
274 ModeShapeLock lock ) {
275 Graph.Batch workspaceBatch = repositoryLockManager.createWorkspaceGraph(this.workspaceName, sessionExecutionContext).batch();
276
277 workspaceBatch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
278 workspaceBatch.unlock(lock.nodeUuid);
279
280 workspaceBatch.execute();
281 }
282
283 void unlockNodeInSystemGraph( ExecutionContext sessionExecutionContext,
284 UUID lockUuid ) {
285 PathFactory pathFactory = sessionExecutionContext.getValueFactories().getPathFactory();
286 Graph systemGraph = repositoryLockManager.createSystemGraph(sessionExecutionContext);
287 systemGraph.delete(pathFactory.create(locksPath, pathFactory.createSegment(lockUuid.toString())));
288
289 }
290
291 boolean unlockNodeInternally( UUID lockedNodeUuid ) {
292 return workspaceLocksByNodeUuid.remove(lockedNodeUuid) != null;
293 }
294
295
296
297
298
299
300
301
302
303
304 boolean isHeldBySession( JcrSession session,
305 String lockToken ) throws LockException {
306 assert lockToken != null;
307
308 ExecutionContext context = session.getExecutionContext();
309 ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
310 PathFactory pathFactory = context.getValueFactories().getPathFactory();
311
312 try {
313 org.modeshape.graph.Node lockNode = repositoryLockManager.createSystemGraph(context).getNodeAt(pathFactory.create(locksPath,
314 pathFactory.createSegment(lockToken)));
315
316 return booleanFactory.create(lockNode.getProperty(ModeShapeLexicon.IS_HELD_BY_SESSION).getFirstValue());
317 } catch (PathNotFoundException pnfe) {
318 I18n msg = JcrI18n.invalidLockToken;
319 throw new LockException(msg.text(lockToken));
320 }
321
322 }
323
324
325
326
327
328
329
330
331
332
333 void setHeldBySession( JcrSession session,
334 String lockToken,
335 boolean value ) {
336 assert lockToken != null;
337
338 ExecutionContext context = session.getExecutionContext();
339 PropertyFactory propFactory = context.getPropertyFactory();
340 PathFactory pathFactory = context.getValueFactories().getPathFactory();
341
342 repositoryLockManager.createSystemGraph(context).set(propFactory.create(ModeShapeLexicon.IS_HELD_BY_SESSION, value)).on(pathFactory.create(locksPath,
343 pathFactory.createSegment(lockToken)));
344 }
345
346
347
348
349
350
351
352
353
354 ModeShapeLock lockFor( String lockToken ) {
355 for (ModeShapeLock lock : workspaceLocksByNodeUuid.values()) {
356 if (lockToken.equals(lock.getLockToken())) {
357 return lock;
358 }
359 }
360
361 return null;
362 }
363
364
365
366
367
368
369
370
371 ModeShapeLock lockFor( JcrSession session,
372 Location nodeLocation ) {
373 UUID nodeUuid = uuidFor(session, nodeLocation);
374 return lockFor(nodeUuid);
375 }
376
377
378
379
380
381
382
383 ModeShapeLock lockFor( UUID nodeUuid ) {
384 if (nodeUuid == null) return null;
385 return workspaceLocksByNodeUuid.get(nodeUuid);
386 }
387
388
389
390
391
392
393
394
395
396
397 UUID uuidFor( JcrSession session,
398 Location location ) {
399 assert location != null;
400
401 if (location.getUuid() != null) return location.getUuid();
402
403 org.modeshape.graph.property.Property uuidProp = location.getIdProperty(JcrLexicon.UUID);
404 if (uuidProp == null) return null;
405
406 ExecutionContext context = session.getExecutionContext();
407 return context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
408 }
409
410
411
412
413
414
415 void cleanLocks( JcrSession session ) {
416 ExecutionContext context = session.getExecutionContext();
417 Collection<String> lockTokens = session.lockManager().lockTokens();
418 for (String lockToken : lockTokens) {
419 ModeShapeLock lock = lockFor(lockToken);
420 if (lock != null && lock.isSessionScoped()) {
421 unlock(context, lock);
422 }
423 }
424 }
425
426
427
428
429
430 @ThreadSafe
431 public class ModeShapeLock {
432 final UUID nodeUuid;
433 private final UUID lockUuid;
434 private final String lockOwner;
435 private final boolean deep;
436 private final boolean sessionScoped;
437
438 @SuppressWarnings( "synthetic-access" )
439 ModeShapeLock( org.modeshape.graph.Node lockNode ) {
440 ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
441 ValueFactory<UUID> uuidFactory = context.getValueFactories().getUuidFactory();
442 ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
443
444 assert lockNode.getLocation().getPath() != null;
445
446 String lockUuidAsString = lockNode.getLocation().getPath().getLastSegment().getName().getLocalName();
447 Property lockOwnerProperty = lockNode.getProperty(JcrLexicon.LOCK_OWNER);
448 Property nodeUuidProperty = lockNode.getProperty(ModeShapeLexicon.LOCKED_UUID);
449 Property lockIsDeepProperty = lockNode.getProperty(JcrLexicon.LOCK_IS_DEEP);
450 Property isSessionScopedProperty = lockNode.getProperty(ModeShapeLexicon.IS_SESSION_SCOPED);
451
452 assert lockUuidAsString != null;
453 assert lockOwnerProperty != null;
454 assert nodeUuidProperty != null;
455 assert lockIsDeepProperty != null;
456 assert isSessionScopedProperty != null;
457
458 this.lockOwner = stringFactory.create(lockOwnerProperty.getFirstValue());
459 this.lockUuid = UUID.fromString(lockUuidAsString);
460 this.nodeUuid = uuidFactory.create(nodeUuidProperty.getFirstValue());
461 this.deep = booleanFactory.create(lockIsDeepProperty.getFirstValue());
462 this.sessionScoped = booleanFactory.create(isSessionScopedProperty.getFirstValue());
463 }
464
465 ModeShapeLock( String lockOwner,
466 UUID lockUuid,
467 UUID nodeUuid,
468 boolean deep,
469 boolean sessionScoped ) {
470 super();
471 this.lockOwner = lockOwner;
472 this.lockUuid = lockUuid;
473 this.nodeUuid = nodeUuid;
474 this.deep = deep;
475 this.sessionScoped = sessionScoped;
476 }
477
478 @SuppressWarnings( "synthetic-access" )
479 public boolean isLive() {
480 return workspaceLocksByNodeUuid.containsKey(nodeUuid);
481 }
482
483 public UUID getUuid() {
484 return lockUuid;
485 }
486
487 public boolean isDeep() {
488 return deep;
489 }
490
491 public String getLockOwner() {
492 return lockOwner;
493 }
494
495 public boolean isSessionScoped() {
496 return sessionScoped;
497 }
498
499 public String getLockToken() {
500 return lockUuid.toString();
501 }
502
503 @SuppressWarnings( "synthetic-access" )
504 public Lock lockFor( SessionCache cache ) throws RepositoryException {
505 final AbstractJcrNode node = cache.findJcrNode(Location.create(nodeUuid));
506 final JcrSession session = cache.session();
507 return new Lock() {
508 public String getLockOwner() {
509 return lockOwner;
510 }
511
512 public String getLockToken() {
513 if (sessionScoped) return null;
514 String uuidString = lockUuid.toString();
515 return session.lockManager().lockTokens().contains(uuidString) ? uuidString : null;
516 }
517
518 public Node getNode() {
519 return node;
520 }
521
522 public boolean isDeep() {
523 return deep;
524 }
525
526 public boolean isLive() {
527 return workspaceLocksByNodeUuid.containsKey(nodeUuid);
528 }
529
530 public boolean isSessionScoped() {
531 return sessionScoped;
532 }
533
534 public void refresh() throws LockException {
535 String uuidString = lockUuid.toString();
536 if (!session.lockManager().lockTokens().contains(uuidString)) {
537 throw new LockException(JcrI18n.notLocked.text(node.location));
538 }
539 }
540
541 @Override
542 public long getSecondsRemaining() {
543 return isLockOwningSession() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
544 }
545
546 @Override
547 public boolean isLockOwningSession() {
548 String uuidString = lockUuid.toString();
549 return session.lockManager().lockTokens().contains(uuidString);
550 }
551
552 };
553 }
554 }
555 }