View Javadoc

1   package org.modeshape.graph.connector.path;
2   
3   import java.util.Collections;
4   import java.util.HashMap;
5   import java.util.HashSet;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Set;
9   import org.modeshape.common.i18n.I18n;
10  import org.modeshape.common.util.CheckArg;
11  import org.modeshape.graph.ExecutionContext;
12  import org.modeshape.graph.GraphI18n;
13  import org.modeshape.graph.Location;
14  import org.modeshape.graph.NodeConflictBehavior;
15  import org.modeshape.graph.observe.Observer;
16  import org.modeshape.graph.property.Name;
17  import org.modeshape.graph.property.Path;
18  import org.modeshape.graph.property.PathFactory;
19  import org.modeshape.graph.property.PathNotFoundException;
20  import org.modeshape.graph.property.Property;
21  import org.modeshape.graph.property.Path.Segment;
22  import org.modeshape.graph.query.QueryResults;
23  import org.modeshape.graph.request.AccessQueryRequest;
24  import org.modeshape.graph.request.CloneBranchRequest;
25  import org.modeshape.graph.request.CloneWorkspaceRequest;
26  import org.modeshape.graph.request.CopyBranchRequest;
27  import org.modeshape.graph.request.CreateNodeRequest;
28  import org.modeshape.graph.request.CreateWorkspaceRequest;
29  import org.modeshape.graph.request.DeleteBranchRequest;
30  import org.modeshape.graph.request.DestroyWorkspaceRequest;
31  import org.modeshape.graph.request.FullTextSearchRequest;
32  import org.modeshape.graph.request.GetWorkspacesRequest;
33  import org.modeshape.graph.request.InvalidRequestException;
34  import org.modeshape.graph.request.InvalidWorkspaceException;
35  import org.modeshape.graph.request.LockBranchRequest;
36  import org.modeshape.graph.request.MoveBranchRequest;
37  import org.modeshape.graph.request.ReadAllChildrenRequest;
38  import org.modeshape.graph.request.ReadAllPropertiesRequest;
39  import org.modeshape.graph.request.ReadNodeRequest;
40  import org.modeshape.graph.request.Request;
41  import org.modeshape.graph.request.UnlockBranchRequest;
42  import org.modeshape.graph.request.UpdatePropertiesRequest;
43  import org.modeshape.graph.request.VerifyWorkspaceRequest;
44  import org.modeshape.graph.request.processor.RequestProcessor;
45  
46  /**
47   * The default implementation of the {@link RequestProcessor} for path repositories.
48   */
49  public class PathRequestProcessor extends RequestProcessor {
50  
51      private final PathFactory pathFactory;
52      private final PathRepository repository;
53      private final boolean updatesAllowed;
54      private final PathRepositoryTransaction txn;
55  
56      public PathRequestProcessor( ExecutionContext context,
57                                   PathRepository repository,
58                                   Observer observer,
59                                   boolean updatesAllowed,
60                                   PathRepositoryTransaction txn ) {
61          super(repository.getSourceName(), context, observer);
62          this.repository = repository;
63          pathFactory = context.getValueFactories().getPathFactory();
64          this.updatesAllowed = updatesAllowed;
65          this.txn = txn;
66      }
67  
68      protected boolean updatesAllowed( Request request ) {
69          if (!updatesAllowed) {
70              request.setError(new InvalidRequestException(GraphI18n.sourceIsReadOnly.text(getSourceName())));
71          }
72          return !request.hasError();
73      }
74  
75      @Override
76      public void process( VerifyWorkspaceRequest request ) {
77          PathWorkspace original = getWorkspace(request, request.workspaceName());
78          if (original != null) {
79              Path path = getExecutionContext().getValueFactories().getPathFactory().createRootPath();
80              request.setActualRootLocation(Location.create(path, repository.getRootNodeUuid()));
81              request.setActualWorkspaceName(original.getName());
82          }
83      }
84  
85      @Override
86      public void process( GetWorkspacesRequest request ) {
87          Set<String> names = repository.getWorkspaceNames();
88          request.setAvailableWorkspaceNames(new HashSet<String>(names));
89          setCacheableInfo(request);
90      }
91  
92      @Override
93      public void process( CreateWorkspaceRequest request ) {
94          // There's a separate flag to allow creating workspaces (which may not require modifying existing data)
95          // if (!updatesAllowed(request)) return;
96  
97          if (!repository.isWritable()) {
98              String msg = GraphI18n.sourceIsReadOnly.text(repository.getSourceName());
99              request.setError(new InvalidRequestException(msg));
100             return;
101         }
102 
103         WritablePathRepository writableRepo = (WritablePathRepository)repository;
104 
105         PathWorkspace workspace = writableRepo.createWorkspace(getExecutionContext(),
106                                                                request.desiredNameOfNewWorkspace(),
107                                                                request.conflictBehavior());
108         if (workspace == null) {
109             String msg = GraphI18n.workspaceAlreadyExistsInRepository.text(request.desiredNameOfNewWorkspace(),
110                                                                            repository.getSourceName());
111             request.setError(new InvalidWorkspaceException(msg));
112         } else {
113             request.setActualRootLocation(Location.create(pathFactory.createRootPath(), repository.getRootNodeUuid()));
114             request.setActualWorkspaceName(workspace.getName());
115             recordChange(request);
116         }
117     }
118 
119     @Override
120     public void process( CloneBranchRequest request ) {
121         if (!updatesAllowed(request)) return;
122 
123         PathWorkspace workspace = getWorkspace(request, request.fromWorkspace());
124         PathWorkspace intoWorkspace = getWorkspace(request, request.intoWorkspace());
125 
126         if (workspace == null || intoWorkspace == null) return;
127         PathNode node = getTargetNode(workspace, request, request.from());
128         if (node == null) return;
129 
130         if (!(intoWorkspace instanceof WritablePathWorkspace)) {
131             I18n msg = GraphI18n.workspaceIsReadOnly;
132             request.setError(new InvalidRequestException(msg.text(repository.getSourceName(), intoWorkspace.getName())));
133             return;
134         }
135 
136         WritablePathWorkspace newWorkspace = (WritablePathWorkspace)intoWorkspace;
137 
138         // Look up the new parent, which must exist ...
139         Path newParentPath = request.into().getPath();
140         PathNode newParent = newWorkspace.getNode(newParentPath);
141         Set<Location> removedExistingNodes = new HashSet<Location>();
142         Name desiredName = request.desiredName();
143         PathNode newNode = newWorkspace.copyNode(getExecutionContext(), node, workspace, newParent, desiredName, true);
144 
145         Location oldLocation = Location.create(node.getPath(), node.getUuid());
146         Location newLocation = Location.create(newNode.getPath(), newNode.getUuid());
147         request.setActualLocations(oldLocation, newLocation);
148         request.setRemovedNodes(Collections.unmodifiableSet(removedExistingNodes));
149         recordChange(request);
150     }
151 
152     @Override
153     public void process( CloneWorkspaceRequest request ) {
154         if (!updatesAllowed(request)) return;
155 
156         // Find the original workspace that we're cloning ...
157         final ExecutionContext context = getExecutionContext();
158         String targetWorkspaceName = request.desiredNameOfTargetWorkspace();
159         String nameOfWorkspaceToBeCloned = request.nameOfWorkspaceToBeCloned();
160         PathWorkspace original = repository.getWorkspace(nameOfWorkspaceToBeCloned);
161         PathWorkspace target = repository.getWorkspace(targetWorkspaceName);
162 
163         if (!repository.isWritable()) {
164             String msg = GraphI18n.sourceIsReadOnly.text(repository.getSourceName());
165             request.setError(new InvalidRequestException(msg));
166             return;
167         }
168 
169         WritablePathRepository writableRepo = (WritablePathRepository)repository;
170 
171         if (target != null) {
172             String msg = GraphI18n.workspaceAlreadyExistsInRepository.text(targetWorkspaceName, repository.getSourceName());
173             request.setError(new InvalidWorkspaceException(msg));
174             return;
175         }
176 
177         if (original == null) {
178             switch (request.cloneConflictBehavior()) {
179                 case DO_NOT_CLONE:
180                     String msg = GraphI18n.workspaceDoesNotExistInRepository.text(nameOfWorkspaceToBeCloned,
181                                                                                   repository.getSourceName());
182                     request.setError(new InvalidWorkspaceException(msg));
183                     return;
184                 case SKIP_CLONE:
185                     target = writableRepo.createWorkspace(context, targetWorkspaceName, request.targetConflictBehavior());
186                     assert target != null;
187 
188                     request.setActualRootLocation(Location.create(pathFactory.createRootPath(), writableRepo.getRootNodeUuid()));
189                     request.setActualWorkspaceName(target.getName());
190                     return;
191             }
192         }
193         assert original != null;
194         target = writableRepo.createWorkspace(context,
195                                               targetWorkspaceName,
196                                               request.targetConflictBehavior(),
197                                               nameOfWorkspaceToBeCloned);
198         assert target != null;
199 
200         request.setActualRootLocation(Location.create(pathFactory.createRootPath(), writableRepo.getRootNodeUuid()));
201         request.setActualWorkspaceName(target.getName());
202         recordChange(request);
203     }
204 
205     @Override
206     public void process( DestroyWorkspaceRequest request ) {
207         if (!updatesAllowed(request)) return;
208 
209         PathWorkspace workspace = repository.getWorkspace(request.workspaceName());
210         if (workspace != null) {
211             request.setActualRootLocation(Location.create(pathFactory.createRootPath(), repository.getRootNodeUuid()));
212             recordChange(request);
213         } else {
214             String msg = GraphI18n.workspaceDoesNotExistInRepository.text(request.workspaceName(), repository.getSourceName());
215             request.setError(new InvalidWorkspaceException(msg));
216         }
217     }
218 
219     @Override
220     public void process( CopyBranchRequest request ) {
221         if (!updatesAllowed(request)) return;
222 
223         PathWorkspace workspace = getWorkspace(request, request.fromWorkspace());
224         PathWorkspace intoWorkspace = getWorkspace(request, request.intoWorkspace());
225         if (workspace == null || intoWorkspace == null) return;
226         PathNode node = getTargetNode(workspace, request, request.from());
227         if (node == null) return;
228 
229         if (!(intoWorkspace instanceof WritablePathWorkspace)) {
230             I18n msg = GraphI18n.workspaceIsReadOnly;
231             request.setError(new InvalidRequestException(msg.text(repository.getSourceName(), intoWorkspace.getName())));
232             return;
233         }
234 
235         WritablePathWorkspace newWorkspace = (WritablePathWorkspace)intoWorkspace;
236 
237         // Look up the new parent, which must exist ...
238         Path newParentPath = request.into().getPath();
239         Name desiredName = request.desiredName();
240         PathNode newParent = newWorkspace.getNode(newParentPath);
241         PathNode newNode = newWorkspace.copyNode(getExecutionContext(), node, workspace, newParent, desiredName, true);
242         Location oldLocation = Location.create(node.getPath(), node.getUuid());
243         Location newLocation = Location.create(newNode.getPath(), newNode.getUuid());
244         request.setActualLocations(oldLocation, newLocation);
245         recordChange(request);
246     }
247 
248     @Override
249     public void process( CreateNodeRequest request ) {
250         if (!updatesAllowed(request)) return;
251 
252         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
253         if (workspace == null) return;
254         Path parent = request.under().getPath();
255         CheckArg.isNotNull(parent, "request.under().getPath()");
256         PathNode node = null;
257         // Look up the parent node, which must exist ...
258 
259         PathNode parentNode = workspace.getNode(parent);
260         if (parentNode == null) {
261             Path lowestExisting = workspace.getLowestExistingPath(parent);
262             request.setError(new PathNotFoundException(request.under(), lowestExisting, GraphI18n.nodeDoesNotExist.text(parent)));
263             return;
264         }
265 
266         if (!(workspace instanceof WritablePathWorkspace)) {
267             I18n msg = GraphI18n.workspaceIsReadOnly;
268             request.setError(new InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
269             return;
270         }
271 
272         WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
273 
274         // Make a list of the properties that we will store: all props except dna:uuid and jcr:uuid
275         Map<Name, Property> propsToStore = new HashMap<Name, Property>(request.properties().size());
276         for (Property property : request.properties()) {
277             if (property.size() > 0) propsToStore.put(property.getName(), property);
278         }
279 
280         NodeConflictBehavior conflictBehavior = request.conflictBehavior();
281         switch (conflictBehavior) {
282             case APPEND:
283                 node = newWorkspace.createNode(getExecutionContext(), parentNode, request.named(), propsToStore, conflictBehavior);
284                 break;
285             case DO_NOT_REPLACE:
286                 for (Segment childSegment : parentNode.getChildSegments()) {
287                     if (request.named().equals(childSegment.getName())) {
288                         Path childPath = pathFactory.create(parent, childSegment);
289                         node = newWorkspace.getNode(childPath);
290                         break;
291                     }
292                 }
293                 if (node == null) {
294                     node = newWorkspace.createNode(getExecutionContext(),
295                                                    parentNode,
296                                                    request.named(),
297                                                    propsToStore,
298                                                    conflictBehavior);
299                 }
300                 break;
301             case REPLACE:
302                 // See if the node already exists (this doesn't record an error on the request) ...
303                 node = workspace.getNode(pathFactory.create(parent, request.named()));
304                 if (node != null) {
305                     newWorkspace.removeNode(getExecutionContext(), node.getPath());
306                 }
307                 node = newWorkspace.createNode(getExecutionContext(), parentNode, request.named(), propsToStore, conflictBehavior);
308                 break;
309             case UPDATE:
310                 // See if the node already exists (this doesn't record an error on the request) ...
311                 node = newWorkspace.getNode(pathFactory.create(parent, request.named()));
312                 if (node == null) {
313                     node = newWorkspace.createNode(getExecutionContext(),
314                                                    parentNode,
315                                                    request.named(),
316                                                    propsToStore,
317                                                    conflictBehavior);
318                 } // otherwise, we found it and we're setting any properties below
319                 break;
320         }
321         assert node != null;
322 
323         Location actualLocation = Location.create(node.getPath(), node.getUuid());
324         request.setActualLocationOfNode(actualLocation);
325         recordChange(request);
326 
327     }
328 
329     @Override
330     public void process( DeleteBranchRequest request ) {
331         if (!updatesAllowed(request)) return;
332 
333         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
334         if (workspace == null) return;
335         PathNode node = getTargetNode(workspace, request, request.at());
336         if (node == null) return;
337 
338         if (!(workspace instanceof WritablePathWorkspace)) {
339             I18n msg = GraphI18n.workspaceIsReadOnly;
340             request.setError(new InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
341             return;
342         }
343 
344         WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
345         newWorkspace.removeNode(getExecutionContext(), node.getPath());
346 
347         request.setActualLocationOfNode(Location.create(node.getPath(), node.getUuid()));
348         recordChange(request);
349     }
350 
351     @Override
352     public void process( MoveBranchRequest request ) {
353         if (!updatesAllowed(request)) return;
354 
355         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
356         if (workspace == null) return;
357 
358         PathNode beforeNode = request.before() != null ? getTargetNode(workspace, request, request.before()) : null;
359         PathNode node = getTargetNode(workspace, request, request.from());
360         if (node == null) return;
361         if (request.hasError()) return; // if beforeNode could not be found
362         // Look up the new parent, which must exist ...
363         Path newParentPath;
364 
365         if (request.into() != null) {
366             newParentPath = request.into().getPath();
367         } else {
368             // into or before cannot both be null
369             assert beforeNode != null;
370             newParentPath = beforeNode.getPath().getParent();
371         }
372 
373         PathNode newParent = workspace.getNode(newParentPath);
374         if (newParent == null) {
375             Path lowestExisting = workspace.getLowestExistingPath(newParentPath);
376             request.setError(new PathNotFoundException(request.into(), lowestExisting,
377                                                        GraphI18n.nodeDoesNotExist.text(newParentPath)));
378             return;
379         }
380 
381         if (!(workspace instanceof WritablePathWorkspace)) {
382             I18n msg = GraphI18n.workspaceIsReadOnly;
383             request.setError(new InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
384             return;
385         }
386 
387         WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
388 
389         node = newWorkspace.moveNode(getExecutionContext(), node, request.desiredName(), newWorkspace, newParent, beforeNode);
390         assert node.getPath().getParent().equals(newParent.getPath());
391 
392         Location oldLocation = Location.create(request.from().getPath());
393         Location newLocation = Location.create(node.getPath(), node.getUuid());
394         request.setActualLocations(oldLocation, newLocation);
395         recordChange(request);
396 
397     }
398 
399     @Override
400     public void process( ReadNodeRequest request ) {
401         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
402         if (workspace == null) return;
403 
404         PathNode node = getTargetNode(workspace, request, request.at());
405         if (node == null) {
406             request.setError(new PathNotFoundException(request.at(), workspace.getLowestExistingPath(request.at().getPath())));
407             return;
408         }
409 
410         // Get the names of the children ...
411         for (Path.Segment childSegment : node.getChildSegments()) {
412             request.addChild(Location.create(pathFactory.create(node.getPath(), childSegment)));
413         }
414 
415         // Get the properties of the node ...
416         request.addProperties(node.getProperties().values());
417 
418         request.setActualLocationOfNode(Location.create(node.getPath(), node.getUuid()));
419         setCacheableInfo(request);
420     }
421 
422     @Override
423     public void process( ReadAllChildrenRequest request ) {
424         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
425         if (workspace == null) return;
426 
427         PathNode node = getTargetNode(workspace, request, request.of());
428         if (node == null) {
429             request.setError(new PathNotFoundException(request.of(), workspace.getLowestExistingPath(request.of().getPath())));
430             return;
431         }
432 
433         List<Path.Segment> childSegments = node.getChildSegments();
434         for (Path.Segment childSegment : childSegments) {
435             request.addChild(Location.create(pathFactory.create(node.getPath(), childSegment)));
436         }
437         request.setActualLocationOfNode(Location.create(node.getPath(), node.getUuid()));
438         setCacheableInfo(request);
439     }
440 
441     @Override
442     public void process( ReadAllPropertiesRequest request ) {
443         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
444         if (workspace == null) return;
445 
446         PathNode node = getTargetNode(workspace, request, request.at());
447         if (node == null) {
448             request.setError(new PathNotFoundException(request.at(), workspace.getLowestExistingPath(request.at().getPath())));
449             return;
450         }
451 
452         // Get the properties of the node ...
453         request.addProperties(node.getProperties().values());
454         request.setActualLocationOfNode(Location.create(node.getPath(), node.getUuid()));
455         setCacheableInfo(request);
456     }
457 
458     @Override
459     public void process( AccessQueryRequest request ) {
460         PathWorkspace workspace = getWorkspace(request, request.workspace());
461         if (workspace == null) return;
462         final ExecutionContext context = getExecutionContext();
463         QueryResults results = workspace.query(context, request);
464         if (results != null) {
465             request.setResults(results.getTuples(), results.getStatistics());
466         } else {
467             super.processUnknownRequest(request);
468         }
469     }
470 
471     @Override
472     public void process( FullTextSearchRequest request ) {
473         PathWorkspace workspace = getWorkspace(request, request.workspace());
474         if (workspace == null) return;
475         final ExecutionContext context = getExecutionContext();
476         QueryResults results = workspace.search(context, request.expression());
477         if (results != null) {
478             request.setResults(results.getColumns(), results.getTuples(), results.getStatistics());
479         } else {
480             super.processUnknownRequest(request);
481         }
482     }
483 
484     @Override
485     public void process( UpdatePropertiesRequest request ) {
486         if (!updatesAllowed(request)) return;
487 
488         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
489         PathNode node = getTargetNode(workspace, request, request.on());
490         if (node == null) return;
491 
492         if (!(workspace instanceof WritablePathWorkspace)) {
493             I18n msg = GraphI18n.workspaceIsReadOnly;
494             request.setError(new InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
495             return;
496         }
497 
498         WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
499 
500         // Now set (or remove) the properties to the supplied node ...
501         newWorkspace.setProperties(getExecutionContext(), node.getPath(), request.properties());
502 
503         request.setActualLocationOfNode(Location.create(node.getPath(), node.getUuid()));
504         recordChange(request);
505     }
506 
507     @Override
508     public void process( LockBranchRequest request ) {
509         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
510         PathNode node = getTargetNode(workspace, request, request.at());
511         if (node == null) return;
512 
513         workspace.lockNode(node, request.lockScope(), request.lockTimeoutInMillis());
514 
515         request.setActualLocation(Location.create(node.getPath(), node.getUuid()));
516         recordChange(request);
517     }
518 
519     @Override
520     public void process( UnlockBranchRequest request ) {
521         PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
522         PathNode node = getTargetNode(workspace, request, request.at());
523         if (node == null) return;
524 
525         workspace.unlockNode(node);
526 
527         request.setActualLocation(Location.create(node.getPath(), node.getUuid()));
528         recordChange(request);
529     }
530 
531     protected PathWorkspace getWorkspace( Request request,
532                                           String workspaceName ) {
533         // Get the workspace for this request ...
534         PathWorkspace workspace = repository.getWorkspace(workspaceName);
535         if (workspace == null) {
536             String msg = GraphI18n.workspaceDoesNotExistInRepository.text(workspaceName, repository.getSourceName());
537             request.setError(new InvalidWorkspaceException(msg));
538         }
539         return workspace;
540     }
541 
542     protected PathNode getTargetNode( PathWorkspace workspace,
543                                       Request request,
544                                       Location location ) {
545         if (workspace == null) return null;
546         PathNode node = null;
547 
548         if (location.getUuid() != null) {
549             if (repository.getRootNodeUuid().equals(location.getUuid())) {
550                 PathFactory pathFactory = new ExecutionContext().getValueFactories().getPathFactory();
551                 return workspace.getNode(pathFactory.createRootPath());
552             }
553         }
554 
555         if (!location.hasPath()) {
556             I18n msg = GraphI18n.pathConnectorRequestsMustHavePath;
557             request.setError(new IllegalArgumentException(msg.text()));
558             return null;
559         }
560 
561         // Look up the node with the supplied path ...
562         Path path = location.getPath();
563         if (path != null) {
564             node = workspace.getNode(path);
565         }
566 
567         if (node == null && request != null) {
568             if (path == null) {
569                 // Missing path, and could not find by UUID ...
570                 request.setError(new PathNotFoundException(location, pathFactory.createRootPath(),
571                                                            GraphI18n.nodeDoesNotExist.text(path)));
572                 return null;
573             }
574             // Could not find the node given the supplied path, so find the lowest path that does exist ...
575             Path lowestExisting = workspace.getLowestExistingPath(path);
576             request.setError(new PathNotFoundException(location, lowestExisting, GraphI18n.nodeDoesNotExist.text(path)));
577         }
578         return node;
579     }
580 
581     /**
582      * Returns the transaction associated with this request processor. This transaction must eventually either be
583      * {@link PathRepositoryTransaction#commit() committed} or {@link PathRepositoryTransaction#rollback() rolled back}.
584      * 
585      * @return the transaction associated with this request processor; never null
586      * @see PathRepositoryTransaction#commit()
587      * @see PathRepositoryTransaction#rollback()
588      */
589     public PathRepositoryTransaction getTransaction() {
590         return this.txn;
591     }
592 }