View Javadoc

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       * Returns the lock manager for the named workspace (if one already exists) or creates a new lock manager and returns it. This
56       * method is thread-safe.
57       * 
58       * @param workspaceName the name of the workspace for which the lock manager should be returned
59       * @return the lock manager for the workspace; never null
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       * Iterates through the list of session-scoped locks in this repository, deleting any session-scoped locks that were created
84       * by a session that is no longer active.
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             // It's possible for this to run before the dna:locks child node gets added to the /jcr:system node.
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             // Extend locks held by active sessions
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                 // Destroy expired locks (if it was still held by an active session, it would have been extended by now)
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      * {@inheritDoc}
147      * 
148      * @see org.modeshape.jcr.JcrSystemObserver#getObservedPath()
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                 // nothing to do with the "/jcr:system/mode:locks" node ...
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 }