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.security.AccessControlException;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedList;
30 import java.util.Set;
31 import java.util.UUID;
32 import javax.jcr.AccessDeniedException;
33 import javax.jcr.InvalidItemStateException;
34 import javax.jcr.PathNotFoundException;
35 import javax.jcr.RepositoryException;
36 import javax.jcr.lock.Lock;
37 import javax.jcr.lock.LockException;
38 import javax.jcr.lock.LockManager;
39 import org.modeshape.common.util.CheckArg;
40 import org.modeshape.graph.session.GraphSession.Node;
41 import org.modeshape.jcr.SessionCache.JcrNodePayload;
42 import org.modeshape.jcr.SessionCache.JcrPropertyPayload;
43 import org.modeshape.jcr.WorkspaceLockManager.ModeShapeLock;
44
45
46
47
48
49 public class JcrLockManager implements LockManager {
50
51 private final JcrSession session;
52 private final WorkspaceLockManager lockManager;
53 private final Set<String> lockTokens;
54
55 JcrLockManager( JcrSession session,
56 WorkspaceLockManager lockManager ) {
57 this.session = session;
58 this.lockManager = lockManager;
59 lockTokens = new HashSet<String>();
60 }
61
62 @Override
63 public void addLockToken( String lockToken ) throws LockException {
64 CheckArg.isNotNull(lockToken, "lock token");
65
66
67 if (lockTokens.contains(lockToken)) {
68 return;
69 }
70
71 if (lockManager.isHeldBySession(session, lockToken)) {
72 throw new LockException(JcrI18n.lockTokenAlreadyHeld.text(lockToken));
73 }
74
75 lockManager.setHeldBySession(session, lockToken, true);
76 lockTokens.add(lockToken);
77 }
78
79 @Override
80 public Lock getLock( String absPath ) throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException {
81 AbstractJcrNode node = session.getNode(absPath);
82 return getLock(node);
83 }
84
85 Lock getLock( AbstractJcrNode node ) throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException {
86 WorkspaceLockManager.ModeShapeLock lock = lockFor(node);
87 if (lock != null) return lock.lockFor(node.cache);
88 throw new LockException(JcrI18n.notLocked.text(node.location()));
89 }
90
91 @Override
92 public String[] getLockTokens() {
93 Set<String> publicTokens = new HashSet<String>(lockTokens);
94
95 for (Iterator<String> iter = publicTokens.iterator(); iter.hasNext();) {
96 String token = iter.next();
97 WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(token);
98 if (lock.isSessionScoped()) iter.remove();
99 }
100
101 return publicTokens.toArray(new String[publicTokens.size()]);
102 }
103
104 Set<String> lockTokens() {
105 return this.lockTokens;
106 }
107
108 @Override
109 public boolean holdsLock( String absPath ) throws PathNotFoundException, RepositoryException {
110 AbstractJcrNode node = session.getNode(absPath);
111 return holdsLock(node);
112 }
113
114 boolean holdsLock( AbstractJcrNode node ) {
115 WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session, node.location());
116
117 return lock != null;
118
119 }
120
121 @Override
122 public boolean isLocked( String absPath ) throws PathNotFoundException, RepositoryException {
123 AbstractJcrNode node = session.getNode(absPath);
124 return isLocked(node);
125 }
126
127 boolean isLocked( AbstractJcrNode node ) throws PathNotFoundException, RepositoryException {
128 return lockFor(node) != null;
129 }
130
131 @Override
132 public Lock lock( String absPath,
133 boolean isDeep,
134 boolean isSessionScoped,
135 long timeoutHint,
136 String ownerInfo )
137 throws LockException, PathNotFoundException, AccessDeniedException, InvalidItemStateException, RepositoryException {
138 AbstractJcrNode node = session.getNode(absPath);
139 return lock(node, isDeep, isSessionScoped, timeoutHint, ownerInfo);
140 }
141
142 Lock lock( AbstractJcrNode node,
143 boolean isDeep,
144 boolean isSessionScoped,
145 long timeoutHint,
146 String ownerInfo )
147 throws LockException, PathNotFoundException, AccessDeniedException, InvalidItemStateException, RepositoryException {
148 if (!node.isLockable()) {
149 throw new LockException(JcrI18n.nodeNotLockable.text(node.getPath()));
150 }
151
152 if (node.isLocked()) {
153 throw new LockException(JcrI18n.alreadyLocked.text(node.location()));
154 }
155
156 if (node.isModified()) {
157 throw new InvalidItemStateException();
158 }
159
160 if (isDeep) {
161 LinkedList<Node<JcrNodePayload, JcrPropertyPayload>> nodesToVisit = new LinkedList<Node<JcrNodePayload, JcrPropertyPayload>>();
162 nodesToVisit.add(node.nodeInfo());
163
164 while (!nodesToVisit.isEmpty()) {
165 Node<JcrNodePayload, JcrPropertyPayload> graphNode = nodesToVisit.remove(nodesToVisit.size() - 1);
166 if (lockManager.lockFor(session, graphNode.getLocation()) != null) throw new LockException(
167 JcrI18n.parentAlreadyLocked.text(node.location,
168 graphNode.getLocation()));
169
170 for (Node<JcrNodePayload, JcrPropertyPayload> child : graphNode.getChildren()) {
171 nodesToVisit.add(child);
172 }
173 }
174 }
175
176 WorkspaceLockManager.ModeShapeLock lock = lockManager.lock(session, node.location(), isDeep, isSessionScoped);
177
178 addLockToken(lock.getLockToken());
179 return lock.lockFor(session.cache());
180
181 }
182
183 @Override
184 public void removeLockToken( String lockToken ) throws LockException {
185 CheckArg.isNotNull(lockToken, "lockToken");
186
187
188 if (!lockTokens.contains(lockToken)) {
189 throw new LockException(JcrI18n.invalidLockToken.text(lockToken));
190 }
191
192
193
194
195
196
197 ModeShapeLock lock = lockManager.lockFor(lockToken);
198 if (lock == null) {
199
200 lockTokens.remove(lockToken);
201 return;
202 }
203
204 if (lock.isSessionScoped()) {
205 throw new IllegalStateException(JcrI18n.cannotRemoveLockToken.text(lockToken));
206 }
207
208 lockManager.setHeldBySession(session, lockToken, false);
209 lockTokens.remove(lockToken);
210 }
211
212 @Override
213 public void unlock( String absPath )
214 throws PathNotFoundException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
215 AbstractJcrNode node = session.getNode(absPath);
216 unlock(node);
217 }
218
219 @SuppressWarnings( "unused" )
220 void unlock( AbstractJcrNode node )
221 throws PathNotFoundException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
222 WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session, node.location());
223
224 if (lock == null) {
225 throw new LockException(JcrI18n.notLocked.text(node.location()));
226 }
227
228 if (lockTokens.contains(lock.getLockToken())) {
229 lockManager.unlock(session.getExecutionContext(), lock);
230 removeLockToken(lock.getLockToken());
231 } else {
232 try {
233
234 session.checkPermission(session.cache().workspaceName(), null, ModeShapePermissions.UNLOCK_ANY);
235
236
237 lockManager.unlock(session.getExecutionContext(), lock);
238 } catch (AccessControlException iae) {
239 throw new LockException(JcrI18n.lockTokenNotHeld.text(node.location()));
240 }
241 }
242
243 }
244
245
246
247
248 final void cleanLocks() {
249 lockManager.cleanLocks(session);
250 }
251
252 final WorkspaceLockManager.ModeShapeLock lockFor( AbstractJcrNode node ) throws RepositoryException {
253
254 if (session == null || session.workspace() == null) return null;
255
256 WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session, node.location());
257 if (lock != null) return lock;
258
259 AbstractJcrNode parent = node;
260 while (!parent.isRoot()) {
261 parent = parent.getParent();
262
263 WorkspaceLockManager.ModeShapeLock parentLock = lockManager.lockFor(session, parent.location());
264 if (parentLock != null && parentLock.isLive()) {
265 return parentLock.isDeep() ? parentLock : null;
266 }
267 }
268 return null;
269 }
270
271 final WorkspaceLockManager.ModeShapeLock lockFor( UUID nodeUuid ) {
272
273 if (session == null || session.workspace() == null) return null;
274
275 return lockManager.lockFor(nodeUuid);
276 }
277
278 }