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.io.IOException;
27 import java.io.InputStream;
28 import java.security.AccessControlException;
29 import java.util.HashSet;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.UUID;
33 import javax.jcr.AccessDeniedException;
34 import javax.jcr.InvalidItemStateException;
35 import javax.jcr.InvalidSerializedDataException;
36 import javax.jcr.ItemExistsException;
37 import javax.jcr.ItemNotFoundException;
38 import javax.jcr.NoSuchWorkspaceException;
39 import javax.jcr.PathNotFoundException;
40 import javax.jcr.RepositoryException;
41 import javax.jcr.Session;
42 import javax.jcr.Workspace;
43 import javax.jcr.lock.Lock;
44 import javax.jcr.lock.LockException;
45 import javax.jcr.nodetype.ConstraintViolationException;
46 import javax.jcr.nodetype.NodeTypeManager;
47 import javax.jcr.observation.ObservationManager;
48 import javax.jcr.query.QueryManager;
49 import javax.jcr.version.Version;
50 import javax.jcr.version.VersionException;
51 import net.jcip.annotations.NotThreadSafe;
52 import org.modeshape.common.util.CheckArg;
53 import org.modeshape.graph.ExecutionContext;
54 import org.modeshape.graph.Graph;
55 import org.modeshape.graph.Location;
56 import org.modeshape.graph.Subgraph;
57 import org.modeshape.graph.SubgraphNode;
58 import org.modeshape.graph.connector.RepositoryConnectionFactory;
59 import org.modeshape.graph.connector.RepositorySource;
60 import org.modeshape.graph.connector.RepositorySourceException;
61 import org.modeshape.graph.connector.UuidAlreadyExistsException;
62 import org.modeshape.graph.property.Name;
63 import org.modeshape.graph.property.NamespaceRegistry;
64 import org.modeshape.graph.property.Path;
65 import org.modeshape.graph.property.PathFactory;
66 import org.modeshape.graph.property.Property;
67 import org.modeshape.graph.property.ValueFormatException;
68 import org.modeshape.graph.property.basic.LocalNamespaceRegistry;
69 import org.modeshape.graph.request.InvalidWorkspaceException;
70 import org.modeshape.graph.request.ReadBranchRequest;
71 import org.modeshape.graph.session.GraphSession;
72 import org.modeshape.graph.session.GraphSession.Node;
73 import org.modeshape.jcr.JcrContentHandler.EnclosingSAXException;
74 import org.modeshape.jcr.JcrContentHandler.SaveMode;
75 import org.modeshape.jcr.SessionCache.JcrNodePayload;
76 import org.modeshape.jcr.SessionCache.JcrPropertyPayload;
77 import org.modeshape.jcr.WorkspaceLockManager.ModeShapeLock;
78 import org.xml.sax.ContentHandler;
79 import org.xml.sax.InputSource;
80 import org.xml.sax.SAXException;
81 import org.xml.sax.SAXParseException;
82 import org.xml.sax.XMLReader;
83 import org.xml.sax.helpers.XMLReaderFactory;
84
85
86
87
88 @NotThreadSafe
89 class JcrWorkspace implements Workspace {
90
91
92
93
94
95
96 private final String name;
97
98
99
100
101 private final ExecutionContext context;
102
103
104
105
106
107
108 private final JcrRepository repository;
109
110
111
112
113
114
115
116 private final Graph graph;
117
118
119
120
121
122
123 private final JcrNamespaceRegistry workspaceRegistry;
124
125
126
127
128 private final JcrNodeTypeManager nodeTypeManager;
129
130
131
132
133 private final JcrVersionManager versionManager;
134
135
136
137
138 private final JcrQueryManager queryManager;
139
140
141
142
143 private final JcrObservationManager observationManager;
144
145 private final WorkspaceLockManager lockManager;
146
147
148
149
150 private final JcrSession session;
151
152 JcrWorkspace( JcrRepository repository,
153 String workspaceName,
154 ExecutionContext context,
155 Map<String, Object> sessionAttributes ) {
156
157 assert workspaceName != null;
158 assert context != null;
159 assert context.getSecurityContext() != null;
160 assert repository != null;
161 this.name = workspaceName;
162 this.repository = repository;
163 this.lockManager = repository.getLockManager(workspaceName);
164
165
166 NamespaceRegistry globalRegistry = context.getNamespaceRegistry();
167 LocalNamespaceRegistry localRegistry = new LocalNamespaceRegistry(globalRegistry);
168 this.context = context.with(localRegistry);
169
170
171 this.graph = this.repository.createWorkspaceGraph(this.name, this.context);
172
173
174 this.session = new JcrSession(this.repository, this, this.context, globalRegistry, sessionAttributes);
175
176
177 this.nodeTypeManager = new JcrNodeTypeManager(session, this.repository.getRepositoryTypeManager());
178 this.versionManager = new JcrVersionManager(this.session);
179 this.queryManager = new JcrQueryManager(this.session);
180 this.observationManager = new JcrObservationManager(this.session, this.repository.getRepositoryObservable());
181
182
183
184
185
186
187
188 this.workspaceRegistry = new JcrNamespaceRegistry(this.repository.getPersistentRegistry(), this.session);
189
190 }
191
192 final Graph graph() {
193 return this.graph;
194 }
195
196 final String getSourceName() {
197 return this.repository.getRepositorySourceName();
198 }
199
200 final JcrNodeTypeManager nodeTypeManager() {
201 return this.nodeTypeManager;
202 }
203
204 final ExecutionContext context() {
205 return this.context;
206 }
207
208 final WorkspaceLockManager lockManager() {
209 return this.lockManager;
210 }
211
212 final JcrObservationManager observationManager() {
213 return this.observationManager;
214 }
215
216 final JcrQueryManager queryManager() {
217 return this.queryManager;
218 }
219
220
221
222
223 public final String getName() {
224 return name;
225 }
226
227
228
229
230 public final Session getSession() {
231 return this.session;
232 }
233
234
235
236
237
238
239 public final javax.jcr.NamespaceRegistry getNamespaceRegistry() {
240 return workspaceRegistry;
241 }
242
243
244
245
246 public String[] getAccessibleWorkspaceNames() throws RepositoryException {
247 try {
248 Set<String> workspaceNamesFromGraph = graph.getWorkspaces();
249 Set<String> workspaceNames = new HashSet<String>(workspaceNamesFromGraph.size());
250
251 for (String workspaceName : workspaceNamesFromGraph) {
252 try {
253 session.checkPermission(workspaceName, null, ModeShapePermissions.READ);
254 workspaceNames.add(workspaceName);
255 } catch (AccessControlException ace) {
256
257 }
258 }
259
260 return workspaceNames.toArray(new String[workspaceNames.size()]);
261 } catch (RepositorySourceException e) {
262 throw new RepositoryException(JcrI18n.errorObtainingWorkspaceNames.text(getSourceName(), e.getMessage()), e);
263 }
264 }
265
266
267
268
269 public final NodeTypeManager getNodeTypeManager() {
270 return nodeTypeManager;
271 }
272
273
274
275
276
277
278 public final ObservationManager getObservationManager() {
279 return this.observationManager;
280 }
281
282
283
284
285 public final QueryManager getQueryManager() {
286 return queryManager;
287 }
288
289 final JcrVersionManager versionManager() {
290 return versionManager;
291 }
292
293
294
295
296
297
298 public void clone( String srcWorkspace,
299 String srcAbsPath,
300 String destAbsPath,
301 boolean removeExisting )
302 throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException,
303 LockException, RepositoryException {
304 CheckArg.isNotNull(srcWorkspace, "source workspace name");
305 CheckArg.isNotNull(srcAbsPath, "source path");
306 CheckArg.isNotNull(destAbsPath, "destination path");
307
308 if (!graph.getWorkspaces().contains(srcWorkspace)) {
309 throw new NoSuchWorkspaceException(JcrI18n.workspaceNameIsInvalid.text(graph.getSourceName(), this.name));
310 }
311
312
313 PathFactory factory = context.getValueFactories().getPathFactory();
314 Path srcPath = null;
315 Path destPath = null;
316 try {
317 srcPath = factory.create(srcAbsPath);
318 } catch (ValueFormatException e) {
319 throw new PathNotFoundException(JcrI18n.invalidPathParameter.text(srcAbsPath, "srcAbsPath"), e);
320 }
321 try {
322 destPath = factory.create(destAbsPath);
323 } catch (ValueFormatException e) {
324 throw new PathNotFoundException(JcrI18n.invalidPathParameter.text(destAbsPath, "destAbsPath"), e);
325 }
326
327
328 if (destAbsPath.endsWith("]")) {
329 throw new RepositoryException(JcrI18n.pathCannotHaveSameNameSiblingIndex.text(destAbsPath));
330 }
331
332 try {
333
334
335 Name newNodeName = destPath.getLastSegment().getName();
336 SessionCache cache = this.session.cache();
337
338
339
340
341 org.modeshape.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace, context).getNodeAt(srcPath);
342 Property uuidProp = sourceNode.getProperty(ModeShapeLexicon.UUID);
343
344 if (uuidProp != null) {
345 UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
346
347 ModeShapeLock sourceLock = lockManager().lockFor(session, Location.create(sourceUuid));
348 if (sourceLock != null && sourceLock.getLockToken() == null) {
349 throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath));
350 }
351 }
352
353 AbstractJcrNode parentNode = cache.findJcrNode(Location.create(destPath.getParent()));
354
355 if (parentNode.isLocked()) {
356 Lock newParentLock = parentNode.getLock();
357 if (newParentLock != null && newParentLock.getLockToken() == null) {
358 throw new LockException(destAbsPath);
359 }
360 }
361
362 if (!parentNode.isCheckedOut()) {
363 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(parentNode.getPath()));
364 }
365
366 Node<JcrNodePayload, JcrPropertyPayload> parent = cache.findNode(null, destPath.getParent());
367 cache.findBestNodeDefinition(parent, newNodeName, parent.getPayload().getPrimaryTypeName());
368
369 if (removeExisting) {
370
371
372
373
374 Set<UUID> uuidsInCloneBranch = getUuidsInBranch(srcPath, srcWorkspace);
375 if (!uuidsInCloneBranch.isEmpty()) {
376
377
378 GraphSession<JcrNodePayload, JcrPropertyPayload> graphSession = cache.graphSession();
379 Node<JcrNodePayload, JcrPropertyPayload> node = null;
380 for (UUID uuid : uuidsInCloneBranch) {
381 Location location = Location.create(uuid);
382 try {
383 node = graphSession.findNodeWith(location);
384 } catch (org.modeshape.graph.property.PathNotFoundException e) {
385
386 continue;
387 }
388
389 NodeDefinitionId childDefnId = node.getPayload().getDefinitionId();
390 JcrNodeType nodeType = nodeTypeManager().getNodeType(childDefnId.getNodeTypeName());
391 JcrNodeDefinition childDefn = nodeType.childNodeDefinition(childDefnId);
392 if (childDefn.isMandatory()) {
393
394 String path = node.getPath().getString(context.getNamespaceRegistry());
395 throw new ConstraintViolationException(JcrI18n.cannotRemoveNodeFromClone.text(path, uuid));
396 }
397
398 if (node.isChanged(true)) {
399
400 String path = node.getPath().getString(context.getNamespaceRegistry());
401 throw new RepositoryException(JcrI18n.cannotRemoveNodeFromCloneDueToChangesInSession.text(path, uuid));
402 }
403 }
404 }
405 }
406
407
408 cache.graphSession().immediateClone(srcPath, srcWorkspace, destPath, removeExisting, false);
409 } catch (ItemNotFoundException e) {
410
411 throw new PathNotFoundException(e.getLocalizedMessage(), e);
412 } catch (org.modeshape.graph.property.PathNotFoundException e) {
413 throw new PathNotFoundException(e.getLocalizedMessage(), e);
414 } catch (UuidAlreadyExistsException e) {
415 throw new ItemExistsException(e.getLocalizedMessage(), e);
416 } catch (InvalidWorkspaceException e) {
417 throw new NoSuchWorkspaceException(e.getLocalizedMessage(), e);
418 } catch (RepositorySourceException e) {
419 throw new RepositoryException(e.getLocalizedMessage(), e);
420 } catch (AccessControlException ace) {
421 throw new AccessDeniedException(ace);
422 }
423 }
424
425 protected Set<UUID> getUuidsInBranch( Path sourcePath,
426 String workspace ) {
427 String existingWorkspace = graph.getCurrentWorkspaceName();
428 try {
429 graph.useWorkspace(workspace);
430 Subgraph subgraph = graph.getSubgraphOfDepth(ReadBranchRequest.NO_MAXIMUM_DEPTH).at(sourcePath);
431
432 Set<UUID> uuids = new HashSet<UUID>();
433 for (SubgraphNode nodeInSubgraph : subgraph) {
434 UUID uuid = nodeInSubgraph.getLocation().getUuid();
435 if (uuid != null) uuids.add(uuid);
436 }
437 return uuids;
438 } finally {
439 graph.useWorkspace(existingWorkspace);
440 }
441 }
442
443
444
445
446
447
448 public void copy( String srcAbsPath,
449 String destAbsPath )
450 throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException,
451 LockException, RepositoryException {
452 this.copy(this.name, srcAbsPath, destAbsPath);
453 }
454
455
456
457
458 public void copy( String srcWorkspace,
459 String srcAbsPath,
460 String destAbsPath )
461 throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException,
462 LockException, RepositoryException {
463 CheckArg.isNotNull(srcWorkspace, "source workspace name");
464 CheckArg.isNotNull(srcAbsPath, "source path");
465 CheckArg.isNotNull(destAbsPath, "destination path");
466
467 if (!graph.getWorkspaces().contains(srcWorkspace)) {
468 throw new NoSuchWorkspaceException(JcrI18n.workspaceNameIsInvalid.text(graph.getSourceName(), this.name));
469 }
470
471
472 PathFactory factory = context.getValueFactories().getPathFactory();
473 Path srcPath = null;
474 Path destPath = null;
475 try {
476 srcPath = factory.create(srcAbsPath);
477 } catch (ValueFormatException e) {
478 throw new PathNotFoundException(JcrI18n.invalidPathParameter.text(srcAbsPath, "srcAbsPath"), e);
479 }
480 try {
481 destPath = factory.create(destAbsPath);
482 } catch (ValueFormatException e) {
483 throw new PathNotFoundException(JcrI18n.invalidPathParameter.text(destAbsPath, "destAbsPath"), e);
484 }
485
486
487 if (destAbsPath.endsWith("]")) {
488 throw new RepositoryException(JcrI18n.pathCannotHaveSameNameSiblingIndex.text(destAbsPath));
489 }
490
491 try {
492
493
494 Name newNodeName = destPath.getLastSegment().getName();
495 SessionCache cache = this.session.cache();
496
497
498
499
500 org.modeshape.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace, context).getNodeAt(srcPath);
501 Property uuidProp = sourceNode.getProperty(ModeShapeLexicon.UUID);
502
503 if (uuidProp != null) {
504 UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
505
506 ModeShapeLock sourceLock = lockManager().lockFor(session, Location.create(sourceUuid));
507 if (sourceLock != null && sourceLock.getLockToken() == null) {
508 throw new LockException(srcAbsPath);
509 }
510 }
511
512 AbstractJcrNode parentNode = cache.findJcrNode(Location.create(destPath.getParent()));
513
514 if (parentNode.isLocked()) {
515 Lock newParentLock = parentNode.getLock();
516 if (newParentLock != null && newParentLock.getLockToken() == null) {
517 throw new LockException(destAbsPath);
518 }
519 }
520
521 if (!parentNode.isCheckedOut()) {
522 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(parentNode.getPath()));
523 }
524
525 Node<JcrNodePayload, JcrPropertyPayload> parent = cache.findNode(null, destPath.getParent());
526 cache.findBestNodeDefinition(parent, newNodeName, parent.getPayload().getPrimaryTypeName());
527
528
529
530 cache.graphSession().immediateCopy(srcPath, srcWorkspace, destPath);
531 } catch (ItemNotFoundException e) {
532
533 throw new PathNotFoundException(e.getLocalizedMessage(), e);
534 } catch (org.modeshape.graph.property.PathNotFoundException e) {
535 throw new PathNotFoundException(e.getLocalizedMessage(), e);
536 } catch (UuidAlreadyExistsException e) {
537 throw new ItemExistsException(e.getLocalizedMessage(), e);
538 } catch (InvalidWorkspaceException e) {
539 throw new NoSuchWorkspaceException(e.getLocalizedMessage(), e);
540 } catch (RepositorySourceException e) {
541 throw new RepositoryException(e.getLocalizedMessage(), e);
542 } catch (AccessControlException ace) {
543 throw new AccessDeniedException(ace);
544 }
545 }
546
547
548
549
550
551
552 public ContentHandler getImportContentHandler( String parentAbsPath,
553 int uuidBehavior )
554 throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, AccessDeniedException,
555 RepositoryException {
556
557 CheckArg.isNotNull(parentAbsPath, "parentAbsPath");
558
559 Path parentPath = this.context.getValueFactories().getPathFactory().create(parentAbsPath);
560
561 return new JcrContentHandler(this.session, parentPath, uuidBehavior, SaveMode.WORKSPACE);
562 }
563
564
565
566
567
568
569 public void importXML( String parentAbsPath,
570 InputStream in,
571 int uuidBehavior )
572 throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException,
573 InvalidSerializedDataException, LockException, AccessDeniedException, RepositoryException {
574
575 CheckArg.isNotNull(parentAbsPath, "parentAbsPath");
576 CheckArg.isNotNull(in, "in");
577
578 try {
579 XMLReader parser = XMLReaderFactory.createXMLReader();
580 parser.setContentHandler(getImportContentHandler(parentAbsPath, uuidBehavior));
581 parser.parse(new InputSource(in));
582 } catch (EnclosingSAXException ese) {
583 Exception cause = ese.getException();
584 if (cause instanceof ItemExistsException) {
585 throw (ItemExistsException)cause;
586 } else if (cause instanceof ConstraintViolationException) {
587 throw (ConstraintViolationException)cause;
588 }
589 throw new RepositoryException(cause);
590 } catch (SAXParseException se) {
591 throw new InvalidSerializedDataException(se);
592 } catch (SAXException se) {
593 throw new RepositoryException(se);
594 }
595
596 }
597
598
599
600
601
602
603 public void move( String srcAbsPath,
604 String destAbsPath ) throws PathNotFoundException, RepositoryException {
605 CheckArg.isNotEmpty(srcAbsPath, "srcAbsPath");
606 CheckArg.isNotEmpty(destAbsPath, "destAbsPath");
607
608
609 PathFactory factory = context.getValueFactories().getPathFactory();
610 Path srcPath = null;
611 Path destPath = null;
612 try {
613 srcPath = factory.create(srcAbsPath);
614 } catch (ValueFormatException e) {
615 throw new PathNotFoundException(JcrI18n.invalidPathParameter.text(srcAbsPath, "srcAbsPath"), e);
616 }
617 try {
618 destPath = factory.create(destAbsPath);
619 } catch (ValueFormatException e) {
620 throw new PathNotFoundException(JcrI18n.invalidPathParameter.text(destAbsPath, "destAbsPath"), e);
621 }
622
623
624 if (destAbsPath.endsWith("]")) {
625 throw new RepositoryException(JcrI18n.pathCannotHaveSameNameSiblingIndex.text(destAbsPath));
626 }
627
628 try {
629
630
631 Name newNodeName = destPath.getLastSegment().getName();
632 SessionCache cache = this.session.cache();
633 Node<JcrNodePayload, JcrPropertyPayload> newParent = cache.findNode(null, destPath.getParent());
634 cache.findBestNodeDefinition(newParent, newNodeName, newParent.getPayload().getPrimaryTypeName());
635
636 AbstractJcrNode sourceNode = cache.findJcrNode(Location.create(srcPath));
637
638 if (sourceNode.isLocked()) {
639 Lock sourceLock = sourceNode.getLock();
640 if (sourceLock != null && sourceLock.getLockToken() == null) {
641 throw new LockException(srcAbsPath);
642 }
643 }
644
645 AbstractJcrNode parentNode = cache.findJcrNode(Location.create(destPath.getParent()));
646
647 if (parentNode.isLocked()) {
648 Lock newParentLock = parentNode.getLock();
649 if (newParentLock != null && newParentLock.getLockToken() == null) {
650 throw new LockException(destAbsPath);
651 }
652 }
653
654 if (!sourceNode.isCheckedOut()) {
655 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(sourceNode.getPath()));
656 }
657
658 if (!parentNode.isCheckedOut()) {
659 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(parentNode.getPath()));
660 }
661
662
663 cache.graphSession().immediateMove(srcPath, destPath);
664 } catch (AccessControlException ace) {
665 throw new AccessDeniedException(ace);
666 } catch (ItemNotFoundException infe) {
667 throw new PathNotFoundException(infe);
668 } catch (org.modeshape.graph.property.PathNotFoundException pnfe) {
669 throw new PathNotFoundException(pnfe);
670 }
671 }
672
673
674
675
676
677
678 public void restore( Version[] versions,
679 boolean removeExisting ) throws RepositoryException {
680 if (session.hasPendingChanges()) {
681 throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text());
682 }
683
684 versionManager().restore(versions, removeExisting);
685 }
686
687 }