1 package org.modeshape.jcr;
2
3 import java.util.HashSet;
4 import java.util.Set;
5 import java.util.UUID;
6 import java.util.concurrent.ConcurrentHashMap;
7 import java.util.concurrent.ConcurrentMap;
8 import net.jcip.annotations.ThreadSafe;
9 import org.modeshape.common.util.Logger;
10 import org.modeshape.graph.ExecutionContext;
11 import org.modeshape.graph.Graph;
12 import org.modeshape.graph.Location;
13 import org.modeshape.graph.Node;
14 import org.modeshape.graph.Subgraph;
15 import org.modeshape.graph.observe.Changes;
16 import org.modeshape.graph.property.DateTime;
17 import org.modeshape.graph.property.DateTimeFactory;
18 import org.modeshape.graph.property.Path;
19 import org.modeshape.graph.property.PathFactory;
20 import org.modeshape.graph.property.PathNotFoundException;
21 import org.modeshape.graph.property.Property;
22 import org.modeshape.graph.property.ValueFactory;
23 import org.modeshape.graph.property.Path.Segment;
24 import org.modeshape.graph.request.ChangeRequest;
25 import org.modeshape.graph.request.CreateNodeRequest;
26
27 @ThreadSafe
28 class RepositoryLockManager implements JcrSystemObserver {
29
30 private static final Logger LOGGER = Logger.getLogger(RepositoryLockManager.class);
31
32 private final JcrRepository repository;
33 private final ConcurrentMap<String, WorkspaceLockManager> lockManagers;
34 private final Path locksPath;
35
36 RepositoryLockManager( JcrRepository repository ) {
37 this.repository = repository;
38 ExecutionContext executionContext = repository.getExecutionContext();
39 PathFactory pathFactory = executionContext.getValueFactories().getPathFactory();
40
41 this.lockManagers = new ConcurrentHashMap<String, WorkspaceLockManager>();
42 this.locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, ModeShapeLexicon.LOCKS);
43
44 if (locksPath != null) {
45
46 Property locksPrimaryType = executionContext.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE,
47 ModeShapeLexicon.LOCKS);
48
49 repository.createSystemGraph(executionContext).create(locksPath, locksPrimaryType).ifAbsent().and();
50 }
51
52 }
53
54
55
56
57
58
59
60
61 WorkspaceLockManager getLockManager( String workspaceName ) {
62 WorkspaceLockManager lockManager = lockManagers.get(workspaceName);
63 if (lockManager != null) return lockManager;
64
65 ExecutionContext executionContext = repository.getExecutionContext();
66 lockManager = new WorkspaceLockManager(executionContext, this, workspaceName, locksPath);
67 WorkspaceLockManager newLockManager = lockManagers.putIfAbsent(workspaceName, lockManager);
68
69 if (newLockManager != null) return newLockManager;
70 return lockManager;
71 }
72
73 JcrGraph createSystemGraph( ExecutionContext context ) {
74 return repository.createSystemGraph(context);
75 }
76
77 JcrGraph createWorkspaceGraph( String workspaceName,
78 ExecutionContext workspaceContext ) {
79 return repository.createWorkspaceGraph(workspaceName, workspaceContext);
80 }
81
82
83
84
85
86 void cleanUpLocks() {
87 if (LOGGER.isTraceEnabled()) {
88 LOGGER.trace(JcrI18n.cleaningUpLocks.text());
89 }
90
91 Set<JcrSession> activeSessions = repository.activeSessions();
92 Set<String> activeSessionIds = new HashSet<String>(activeSessions.size());
93
94 for (JcrSession activeSession : activeSessions) {
95 activeSessionIds.add(activeSession.sessionId());
96 }
97
98 ExecutionContext executionContext = repository.getExecutionContext();
99 Graph systemGraph = createSystemGraph(executionContext);
100 PathFactory pathFactory = executionContext.getValueFactories().getPathFactory();
101 ValueFactory<Boolean> booleanFactory = executionContext.getValueFactories().getBooleanFactory();
102 ValueFactory<String> stringFactory = executionContext.getValueFactories().getStringFactory();
103
104 DateTimeFactory dateFactory = executionContext.getValueFactories().getDateFactory();
105 DateTime now = dateFactory.create();
106 DateTime newExpirationDate = now.plusMillis(JcrEngine.LOCK_EXTENSION_INTERVAL_IN_MILLIS);
107
108 Path locksPath = pathFactory.createAbsolutePath(JcrLexicon.SYSTEM, ModeShapeLexicon.LOCKS);
109
110 Subgraph locksGraph = null;
111 try {
112 locksGraph = systemGraph.getSubgraphOfDepth(2).at(locksPath);
113 } catch (PathNotFoundException pnfe) {
114
115 return;
116 }
117
118 for (Location lockLocation : locksGraph.getRoot().getChildren()) {
119 Node lockNode = locksGraph.getNode(lockLocation);
120
121 Boolean isSessionScoped = booleanFactory.create(lockNode.getProperty(ModeShapeLexicon.IS_SESSION_SCOPED).getFirstValue());
122
123 if (!isSessionScoped) continue;
124 String lockingSession = stringFactory.create(lockNode.getProperty(ModeShapeLexicon.LOCKING_SESSION).getFirstValue());
125
126
127 if (activeSessionIds.contains(lockingSession)) {
128 systemGraph.set(ModeShapeLexicon.EXPIRATION_DATE).on(lockLocation).to(newExpirationDate);
129 } else {
130 DateTime expirationDate = dateFactory.create(lockNode.getProperty(ModeShapeLexicon.EXPIRATION_DATE).getFirstValue());
131
132 if (expirationDate.isBefore(now)) {
133 String workspaceName = stringFactory.create(lockNode.getProperty(ModeShapeLexicon.WORKSPACE).getFirstValue());
134 WorkspaceLockManager lockManager = lockManagers.get(workspaceName);
135 lockManager.unlock(executionContext, lockManager.createLock(lockNode));
136 }
137 }
138 }
139
140 if (LOGGER.isTraceEnabled()) {
141 LOGGER.trace(JcrI18n.cleanedUpLocks.text());
142 }
143 }
144
145
146
147
148
149
150 @Override
151 public Path getObservedPath() {
152 return locksPath;
153 }
154
155 @Override
156 public void notify( Changes changes ) {
157 for (ChangeRequest change : changes.getChangeRequests()) {
158 assert change.changedLocation().hasPath();
159
160 Path changedPath = change.changedLocation().getPath();
161 if (changedPath.equals(locksPath)) {
162
163 continue;
164 }
165 assert locksPath.isAncestorOf(changedPath);
166
167 Segment rawUuid = changedPath.getLastSegment();
168 UUID lockedNodeUuid = UUID.fromString(string(rawUuid));
169 assert lockedNodeUuid != null;
170
171 switch (change.getType()) {
172 case CREATE_NODE:
173 CreateNodeRequest create = (CreateNodeRequest)change;
174
175 Property workspaceNameProp = null;
176 Property lockOwnerProp = null;
177 Property lockUuidProp = null;
178 Property isDeepProp = null;
179 Property isSessionScopedProp = null;
180
181 for (Property prop : create.properties()) {
182 if (JcrLexicon.LOCK_OWNER.equals(prop.getName())) {
183 lockOwnerProp = prop;
184 } else if (JcrLexicon.LOCK_IS_DEEP.equals(prop.getName())) {
185 isDeepProp = prop;
186 } else if (ModeShapeLexicon.IS_HELD_BY_SESSION.equals(prop.getName())) {
187 isSessionScopedProp = prop;
188 } else if (JcrLexicon.UUID.equals(prop.getName())) {
189 isSessionScopedProp = prop;
190 } else if (ModeShapeLexicon.WORKSPACE_NAME.equals(prop.getName())) {
191 workspaceNameProp = prop;
192 }
193 }
194
195 String lockOwner = firstString(lockOwnerProp);
196 String workspaceName = firstString(workspaceNameProp);
197 UUID lockUuid = firstUuid(lockUuidProp);
198 boolean isDeep = firstBoolean(isDeepProp);
199 boolean isSessionScoped = firstBoolean(isSessionScopedProp);
200
201 WorkspaceLockManager workspaceManager = getLockManager(workspaceName);
202 assert workspaceManager != null;
203
204 workspaceManager.lockNodeInternally(lockOwner, lockUuid, lockedNodeUuid, isDeep, isSessionScoped);
205
206 break;
207 case DELETE_BRANCH:
208
209
210 boolean success = false;
211 for (WorkspaceLockManager workspaceLockManager : lockManagers.values()) {
212 if (workspaceLockManager.lockFor(lockedNodeUuid) != null) {
213 success |= workspaceLockManager.unlockNodeInternally(lockedNodeUuid);
214 break;
215 }
216 }
217
218 assert success : "No internal lock existed for node " + lockedNodeUuid.toString();
219
220 break;
221 default:
222 assert false : "Unexpected change request: " + change;
223 }
224
225 }
226 }
227
228 private final String string( Segment rawString ) {
229 ExecutionContext context = repository.getExecutionContext();
230 return context.getValueFactories().getStringFactory().create(rawString);
231 }
232
233 private final String firstString( Property property ) {
234 if (property == null) return null;
235 Object firstValue = property.getFirstValue();
236
237 ExecutionContext context = repository.getExecutionContext();
238 return context.getValueFactories().getStringFactory().create(firstValue);
239 }
240
241 private final UUID firstUuid( Property property ) {
242 if (property == null) return null;
243 Object firstValue = property.getFirstValue();
244
245 ExecutionContext context = repository.getExecutionContext();
246 return context.getValueFactories().getUuidFactory().create(firstValue);
247 }
248
249 private final boolean firstBoolean( Property property ) {
250 if (property == null) return false;
251 Object firstValue = property.getFirstValue();
252
253 ExecutionContext context = repository.getExecutionContext();
254 return context.getValueFactories().getBooleanFactory().create(firstValue);
255 }
256 }