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.io.OutputStream;
29 import java.math.BigDecimal;
30 import java.security.AccessControlException;
31 import java.util.ArrayList;
32 import java.util.Calendar;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.UUID;
40 import javax.jcr.Credentials;
41 import javax.jcr.InvalidSerializedDataException;
42 import javax.jcr.Item;
43 import javax.jcr.ItemExistsException;
44 import javax.jcr.ItemNotFoundException;
45 import javax.jcr.NamespaceException;
46 import javax.jcr.Node;
47 import javax.jcr.NodeIterator;
48 import javax.jcr.PathNotFoundException;
49 import javax.jcr.PropertyType;
50 import javax.jcr.ReferentialIntegrityException;
51 import javax.jcr.Repository;
52 import javax.jcr.RepositoryException;
53 import javax.jcr.Session;
54 import javax.jcr.SimpleCredentials;
55 import javax.jcr.UnsupportedRepositoryOperationException;
56 import javax.jcr.Value;
57 import javax.jcr.ValueFactory;
58 import javax.jcr.ValueFormatException;
59 import javax.jcr.Workspace;
60 import javax.jcr.lock.LockException;
61 import javax.jcr.nodetype.ConstraintViolationException;
62 import javax.jcr.query.Query;
63 import javax.jcr.query.QueryResult;
64 import javax.jcr.query.Row;
65 import javax.jcr.query.RowIterator;
66 import javax.jcr.retention.RetentionManager;
67 import javax.jcr.security.AccessControlManager;
68 import javax.jcr.version.VersionException;
69 import net.jcip.annotations.Immutable;
70 import net.jcip.annotations.NotThreadSafe;
71 import org.modeshape.common.util.CheckArg;
72 import org.modeshape.graph.ExecutionContext;
73 import org.modeshape.graph.Graph;
74 import org.modeshape.graph.GraphI18n;
75 import org.modeshape.graph.Location;
76 import org.modeshape.graph.SecurityContext;
77 import org.modeshape.graph.property.Binary;
78 import org.modeshape.graph.property.DateTime;
79 import org.modeshape.graph.property.NamespaceRegistry;
80 import org.modeshape.graph.property.Path;
81 import org.modeshape.graph.property.PathFactory;
82 import org.modeshape.graph.property.ValueFactories;
83 import org.modeshape.graph.property.Path.Segment;
84 import org.modeshape.graph.query.QueryBuilder;
85 import org.modeshape.graph.query.model.QueryCommand;
86 import org.modeshape.graph.query.model.TypeSystem;
87 import org.modeshape.graph.request.InvalidWorkspaceException;
88 import org.modeshape.graph.session.GraphSession;
89 import org.modeshape.jcr.JcrContentHandler.EnclosingSAXException;
90 import org.modeshape.jcr.JcrContentHandler.SaveMode;
91 import org.modeshape.jcr.JcrNamespaceRegistry.Behavior;
92 import org.modeshape.jcr.JcrRepository.Option;
93 import org.modeshape.jcr.SessionCache.JcrPropertyPayload;
94 import org.xml.sax.ContentHandler;
95 import org.xml.sax.InputSource;
96 import org.xml.sax.SAXException;
97 import org.xml.sax.SAXParseException;
98 import org.xml.sax.XMLReader;
99 import org.xml.sax.helpers.XMLReaderFactory;
100
101
102
103
104 @NotThreadSafe
105 class JcrSession implements Session {
106
107 private static final String[] NO_ATTRIBUTES_NAMES = new String[] {};
108
109
110
111
112 private final JcrRepository repository;
113
114
115
116
117 private final JcrWorkspace workspace;
118
119
120
121
122
123 private final JcrNamespaceRegistry sessionRegistry;
124
125
126
127
128 private ExecutionContext executionContext;
129
130
131
132
133 private final Map<String, Object> sessionAttributes;
134
135
136
137
138 private final JcrGraph graph;
139
140 private final SessionCache cache;
141
142
143
144
145 private final Path rootPath;
146
147 private boolean isLive;
148
149 private final boolean performReferentialIntegrityChecks;
150
151
152
153 private Set<Location> removedNodes = null;
154
155
156
157 private Set<String> removedReferenceableNodeUuids = null;
158
159 JcrSession( JcrRepository repository,
160 JcrWorkspace workspace,
161 ExecutionContext sessionContext,
162 NamespaceRegistry globalNamespaceRegistry,
163 Map<String, Object> sessionAttributes ) {
164 assert repository != null;
165 assert workspace != null;
166 assert sessionAttributes != null;
167 assert sessionContext != null;
168 this.repository = repository;
169 this.sessionAttributes = sessionAttributes;
170 this.workspace = workspace;
171
172
173 this.executionContext = sessionContext;
174 NamespaceRegistry local = sessionContext.getNamespaceRegistry();
175 this.sessionRegistry = new JcrNamespaceRegistry(Behavior.SESSION, local, globalNamespaceRegistry, this);
176 this.rootPath = this.executionContext.getValueFactories().getPathFactory().createRootPath();
177
178
179 this.graph = workspace.graph();
180
181 this.cache = new SessionCache(this);
182 this.isLive = true;
183
184 this.performReferentialIntegrityChecks = Boolean.valueOf(repository.getOptions()
185 .get(Option.PERFORM_REFERENTIAL_INTEGRITY_CHECKS))
186 .booleanValue();
187
188 assert this.sessionAttributes != null;
189 assert this.workspace != null;
190 assert this.repository != null;
191 assert this.executionContext != null;
192 assert this.sessionRegistry != null;
193 assert this.graph != null;
194 assert this.executionContext.getSecurityContext() != null;
195 }
196
197
198 final SessionCache cache() {
199 return this.cache;
200 }
201
202
203
204
205
206
207 public boolean isLive() {
208 return isLive;
209 }
210
211
212
213
214
215
216 final void checkLive() throws RepositoryException {
217 if (!isLive()) {
218 throw new RepositoryException(JcrI18n.sessionIsNotActive.text(sessionId()));
219 }
220 }
221
222 ExecutionContext getExecutionContext() {
223 return this.executionContext;
224 }
225
226 void setSessionData( String key,
227 String value ) {
228
229 this.executionContext = this.executionContext.with(key, value);
230 this.graph.setContext(this.executionContext);
231 }
232
233 String sessionId() {
234 return this.executionContext.getId();
235 }
236
237 JcrLockManager lockManager() {
238 return workspace.lockManager();
239 }
240
241 JcrNodeTypeManager nodeTypeManager() {
242 return this.workspace.nodeTypeManager();
243 }
244
245 NamespaceRegistry namespaces() {
246 return this.executionContext.getNamespaceRegistry();
247 }
248
249 void signalNamespaceChanges( boolean global ) {
250 nodeTypeManager().signalNamespaceChanges();
251 if (global) repository.getRepositoryTypeManager().signalNamespaceChanges();
252 }
253
254 JcrWorkspace workspace() {
255 return this.workspace;
256 }
257
258 JcrRepository repository() {
259 return this.repository;
260 }
261
262 Graph.Batch createBatch() {
263 return graph.batch();
264 }
265
266 Graph graph() {
267 return graph;
268 }
269
270 String sourceName() {
271 return this.repository.getRepositorySourceName();
272 }
273
274 Path pathFor( String path,
275 String parameterName ) throws RepositoryException {
276 try {
277 return this.executionContext.getValueFactories().getPathFactory().create(path);
278
279 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
280 throw new RepositoryException(JcrI18n.invalidPathParameter.text(path, parameterName), vfe);
281 }
282 }
283
284
285
286
287
288
289 public Workspace getWorkspace() {
290 return this.workspace;
291 }
292
293
294
295
296
297
298 public Repository getRepository() {
299 return this.repository;
300 }
301
302
303
304
305
306
307
308 public Object getAttribute( String name ) {
309 return sessionAttributes.get(name);
310 }
311
312
313
314
315
316
317
318 public String[] getAttributeNames() {
319 Set<String> names = sessionAttributes.keySet();
320 if (names.isEmpty()) return NO_ATTRIBUTES_NAMES;
321 return names.toArray(new String[names.size()]);
322 }
323
324
325
326
327 Map<String, Object> sessionAttributes() {
328 return new HashMap<String, Object>(sessionAttributes);
329 }
330
331
332
333
334
335
336 public String getNamespacePrefix( String uri ) throws RepositoryException {
337 return sessionRegistry.getPrefix(uri);
338 }
339
340
341
342
343
344
345 public String[] getNamespacePrefixes() throws RepositoryException {
346 return sessionRegistry.getPrefixes();
347 }
348
349
350
351
352
353
354 public String getNamespaceURI( String prefix ) throws RepositoryException {
355 return sessionRegistry.getURI(prefix);
356 }
357
358
359
360
361
362
363 public void setNamespacePrefix( String newPrefix,
364 String existingUri ) throws NamespaceException, RepositoryException {
365 sessionRegistry.registerNamespace(newPrefix, existingUri);
366 }
367
368
369
370
371
372
373 public void addLockToken( String lt ) {
374 CheckArg.isNotNull(lt, "lock token");
375
376 try {
377 lockManager().addLockToken(lt);
378 } catch (LockException le) {
379
380 }
381 }
382
383
384
385
386
387
388
389
390
391 final boolean hasRole( String roleName,
392 String workspaceName ) {
393 SecurityContext context = getExecutionContext().getSecurityContext();
394 if (context.hasRole(roleName)) return true;
395 roleName = roleName + "." + this.repository.getRepositorySourceName();
396 if (context.hasRole(roleName)) return true;
397 roleName = roleName + "." + workspaceName;
398 return context.hasRole(roleName);
399 }
400
401
402
403
404
405
406
407 public void checkPermission( String path,
408 String actions ) {
409 CheckArg.isNotEmpty(path, "path");
410
411 this.checkPermission(executionContext.getValueFactories().getPathFactory().create(path), actions);
412 }
413
414
415
416
417
418
419
420
421
422
423
424 void checkPermission( Path path,
425 String actions ) {
426 checkPermission(this.workspace().getName(), path, actions);
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440 void checkPermission( String workspaceName,
441 Path path,
442 String actions ) {
443 if (hasPermission(workspaceName, path, actions)) return;
444
445 String pathAsString = path != null ? path.getString(this.namespaces()) : "<unknown>";
446 throw new AccessControlException(JcrI18n.permissionDenied.text(pathAsString, actions));
447 }
448
449
450
451
452
453
454
455
456
457
458
459 public boolean hasPermission( String path,
460 String actions ) {
461 CheckArg.isNotEmpty(path, "path");
462
463 return hasPermission(this.workspace().getName(),
464 executionContext.getValueFactories().getPathFactory().create(path),
465 actions);
466 }
467
468 private boolean hasPermission( String workspaceName,
469 Path path,
470 String actions ) {
471 CheckArg.isNotEmpty(actions, "actions");
472
473 boolean hasPermission = true;
474 for (String action : actions.split(",")) {
475 if (ModeShapePermissions.READ.equals(action)) {
476 hasPermission &= hasRole(ModeShapeRoles.READONLY, workspaceName)
477 || hasRole(ModeShapeRoles.READWRITE, workspaceName)
478 || hasRole(ModeShapeRoles.ADMIN, workspaceName);
479 } else if (ModeShapePermissions.REGISTER_NAMESPACE.equals(action)
480 || ModeShapePermissions.REGISTER_TYPE.equals(action) || ModeShapePermissions.UNLOCK_ANY.equals(action)
481 || ModeShapePermissions.CREATE_WORKSPACE.equals(action)
482 || ModeShapePermissions.DELETE_WORKSPACE.equals(action)) {
483 hasPermission &= hasRole(ModeShapeRoles.ADMIN, workspaceName);
484 } else {
485 hasPermission &= hasRole(ModeShapeRoles.ADMIN, workspaceName) || hasRole(ModeShapeRoles.READWRITE, workspaceName);
486 }
487 }
488 return hasPermission;
489 }
490
491
492
493
494
495
496
497
498
499
500
501
502
503 public boolean hasCapability( String methodName,
504 Object target,
505 Object[] arguments ) throws IllegalArgumentException, RepositoryException {
506 CheckArg.isNotEmpty(methodName, "methodName");
507 CheckArg.isNotNull(target, "target");
508
509 if (target instanceof AbstractJcrNode) {
510 AbstractJcrNode node = (AbstractJcrNode)target;
511 if ("addNode".equals(methodName)) {
512 CheckArg.hasSizeOfAtLeast(arguments, 1, "arguments");
513 CheckArg.hasSizeOfAtMost(arguments, 2, "arguments");
514 CheckArg.isInstanceOf(arguments[0], String.class, "arguments[0]");
515
516 String relPath = (String)arguments[0];
517 String primaryNodeTypeName = null;
518 if (arguments.length > 1) {
519 CheckArg.isInstanceOf(arguments[1], String.class, "arguments[1]");
520 primaryNodeTypeName = (String)arguments[1];
521 }
522 return node.canAddNode(relPath, primaryNodeTypeName);
523 }
524 }
525 return true;
526 }
527
528
529
530
531
532
533 public void exportDocumentView( String absPath,
534 ContentHandler contentHandler,
535 boolean skipBinary,
536 boolean noRecurse ) throws RepositoryException, SAXException {
537 CheckArg.isNotNull(absPath, "absPath");
538 CheckArg.isNotNull(contentHandler, "contentHandler");
539 Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
540 Node exportRootNode = getNode(exportRootPath);
541 AbstractJcrExporter exporter = new JcrDocumentViewExporter(this);
542 exporter.exportView(exportRootNode, contentHandler, skipBinary, noRecurse);
543 }
544
545
546
547
548
549
550 public void exportDocumentView( String absPath,
551 OutputStream out,
552 boolean skipBinary,
553 boolean noRecurse ) throws RepositoryException {
554 CheckArg.isNotNull(absPath, "absPath");
555 CheckArg.isNotNull(out, "out");
556 Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
557 Node exportRootNode = getNode(exportRootPath);
558 AbstractJcrExporter exporter = new JcrDocumentViewExporter(this);
559 exporter.exportView(exportRootNode, out, skipBinary, noRecurse);
560 }
561
562
563
564
565
566
567 public void exportSystemView( String absPath,
568 ContentHandler contentHandler,
569 boolean skipBinary,
570 boolean noRecurse ) throws RepositoryException, SAXException {
571 CheckArg.isNotNull(absPath, "absPath");
572 CheckArg.isNotNull(contentHandler, "contentHandler");
573 Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
574 Node exportRootNode = getNode(exportRootPath);
575 AbstractJcrExporter exporter = new JcrSystemViewExporter(this);
576 exporter.exportView(exportRootNode, contentHandler, skipBinary, noRecurse);
577 }
578
579
580
581
582
583
584 public void exportSystemView( String absPath,
585 OutputStream out,
586 boolean skipBinary,
587 boolean noRecurse ) throws RepositoryException {
588 CheckArg.isNotNull(absPath, "absPath");
589 CheckArg.isNotNull(out, "out");
590 Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
591 Node exportRootNode = getNode(exportRootPath);
592 AbstractJcrExporter exporter = new JcrSystemViewExporter(this);
593 exporter.exportView(exportRootNode, out, skipBinary, noRecurse);
594 }
595
596
597
598
599
600
601 public ContentHandler getImportContentHandler( String parentAbsPath,
602 int uuidBehavior ) throws PathNotFoundException, RepositoryException {
603 Path parentPath = this.executionContext.getValueFactories().getPathFactory().create(parentAbsPath);
604 return new JcrContentHandler(this, parentPath, uuidBehavior, SaveMode.SESSION);
605 }
606
607
608
609
610
611
612
613 public Item getItem( String absolutePath ) throws RepositoryException {
614 CheckArg.isNotEmpty(absolutePath, "absolutePath");
615
616 Path path = executionContext.getValueFactories().getPathFactory().create(absolutePath);
617 if (path.isRoot()) {
618 return getRootNode();
619 }
620
621 if (path.isIdentifier() || path.getLastSegment().hasIndex()) {
622 return getNode(path);
623 }
624
625 try {
626 return cache.findJcrItem(null, rootPath, path.relativeTo(rootPath));
627 } catch (ItemNotFoundException e) {
628 throw new PathNotFoundException(e.getMessage(), e);
629 }
630 }
631
632
633
634
635
636
637
638
639
640 public AbstractJcrNode getNode( String absolutePath ) throws PathNotFoundException, RepositoryException {
641 CheckArg.isNotEmpty(absolutePath, "absolutePath");
642
643 Path path = executionContext.getValueFactories().getPathFactory().create(absolutePath);
644 if (path.isRoot()) {
645 return getRootNode();
646 }
647 return getNode(path);
648 }
649
650
651
652
653
654
655
656
657
658
659 public boolean nodeExists( String absolutePath ) throws PathNotFoundException, RepositoryException {
660 CheckArg.isNotEmpty(absolutePath, "absolutePath");
661
662 Path path = executionContext.getValueFactories().getPathFactory().create(absolutePath);
663 if (path.isRoot()) {
664 return true;
665 }
666 try {
667 cache.findJcrNode(null, path);
668 return true;
669 } catch (ItemNotFoundException e) {
670 return false;
671 }
672 }
673
674
675
676
677
678
679
680
681
682 public AbstractJcrProperty getProperty( String absolutePath ) throws PathNotFoundException, RepositoryException {
683 CheckArg.isNotEmpty(absolutePath, "absolutePath");
684
685 Path path = pathFor(absolutePath, "absolutePath");
686 if (path.isRoot()) {
687 throw new PathNotFoundException(JcrI18n.rootNodeIsNotProperty.text());
688 }
689 if (path.isIdentifier()) {
690 throw new PathNotFoundException(JcrI18n.identifierPathNeverReferencesProperty.text());
691 }
692
693 Segment lastSegment = path.getLastSegment();
694 if (lastSegment.hasIndex()) {
695 throw new RepositoryException(JcrI18n.pathCannotHaveSameNameSiblingIndex.text(absolutePath));
696 }
697
698
699 AbstractJcrNode parentNode = getNode(path.getParent());
700 AbstractJcrProperty property = parentNode.getProperty(lastSegment.getName());
701
702 if (property == null) {
703 throw new PathNotFoundException(GraphI18n.pathNotFoundExceptionLowestExistingLocationFound.text(absolutePath,
704 parentNode.getPath()));
705 }
706 return property;
707 }
708
709
710
711
712
713
714
715
716
717 public boolean propertyExists( String absolutePath ) throws RepositoryException {
718 CheckArg.isNotEmpty(absolutePath, "absolutePath");
719
720 Path path = pathFor(absolutePath, "absolutePath");
721 if (path.isRoot() || path.isIdentifier()) {
722 return false;
723 }
724
725 Segment lastSegment = path.getLastSegment();
726 if (lastSegment.hasIndex()) {
727 throw new RepositoryException(JcrI18n.pathCannotHaveSameNameSiblingIndex.text(absolutePath));
728 }
729
730 try {
731
732 AbstractJcrNode parentNode = getNode(path.getParent());
733 return parentNode.hasProperty(lastSegment.getName());
734 } catch (PathNotFoundException pnfe) {
735 return false;
736 }
737 }
738
739 public void removeItem( String absolutePath ) throws RepositoryException {
740 Item item = getItem(absolutePath);
741 item.remove();
742 }
743
744
745
746
747
748
749 public String[] getLockTokens() {
750 return lockManager().getLockTokens();
751 }
752
753
754
755
756
757
758
759
760
761 AbstractJcrNode getNode( Path path ) throws RepositoryException, PathNotFoundException {
762 if (path.isRoot()) return cache.findJcrRootNode();
763 try {
764 if (path.isIdentifier()) {
765
766 try {
767 UUID uuid = executionContext.getValueFactories().getUuidFactory().create(path);
768 return cache.findJcrNode(Location.create(uuid));
769 } catch (org.modeshape.graph.property.ValueFormatException e) {
770
771 String pathStr = executionContext.getValueFactories().getStringFactory().create(path);
772 throw new PathNotFoundException(JcrI18n.identifierPathContainedUnsupportedIdentifierFormat.text(pathStr));
773 }
774 }
775 return cache.findJcrNode(null, path);
776 } catch (ItemNotFoundException e) {
777 throw new PathNotFoundException(e.getMessage());
778 }
779 }
780
781
782
783
784
785
786 public AbstractJcrNode getNodeByUUID( String uuid ) throws ItemNotFoundException, RepositoryException {
787 return cache.findJcrNode(Location.create(UUID.fromString(uuid)));
788 }
789
790
791
792
793
794
795 @Override
796 public AbstractJcrNode getNodeByIdentifier( String id ) throws ItemNotFoundException, RepositoryException {
797
798 try {
799 return cache.findJcrNode(Location.create(UUID.fromString(id)));
800 } catch (IllegalArgumentException e) {
801 try {
802
803 PathFactory pathFactory = executionContext.getValueFactories().getPathFactory();
804 Path path = pathFactory.create(id);
805 return getNode(path);
806 } catch (org.modeshape.graph.property.ValueFormatException e2) {
807
808 throw new RepositoryException(JcrI18n.identifierPathContainedUnsupportedIdentifierFormat.text(id));
809 }
810 }
811 }
812
813
814
815
816
817
818 public AbstractJcrNode getRootNode() throws RepositoryException {
819 return cache.findJcrRootNode();
820 }
821
822
823
824
825
826
827
828 public String getUserID() {
829 return executionContext.getSecurityContext().getUserName();
830 }
831
832
833
834
835
836
837 public ValueFactory getValueFactory() {
838 final ValueFactories valueFactories = executionContext.getValueFactories();
839 final SessionCache sessionCache = this.cache;
840
841 return new ValueFactory() {
842
843 @Override
844 public Value createValue( String value,
845 int propertyType ) throws ValueFormatException {
846 return new JcrValue(valueFactories, sessionCache, propertyType, convertValueToType(value, propertyType));
847 }
848
849 @Override
850 public Value createValue( Node value ) throws RepositoryException {
851 if (!value.isNodeType(JcrMixLexicon.REFERENCEABLE.getString(JcrSession.this.namespaces()))) {
852 throw new RepositoryException(JcrI18n.nodeNotReferenceable.text());
853 }
854 String uuid = valueFactories.getStringFactory().create(value.getIdentifier());
855 return new JcrValue(valueFactories, sessionCache, PropertyType.REFERENCE, uuid);
856 }
857
858 @Override
859 public Value createValue( Node value,
860 boolean weak ) throws RepositoryException {
861 if (!value.isNodeType(JcrMixLexicon.REFERENCEABLE.getString(JcrSession.this.namespaces()))) {
862 throw new RepositoryException(JcrI18n.nodeNotReferenceable.text());
863 }
864 String uuid = valueFactories.getStringFactory().create(value.getIdentifier());
865 return new JcrValue(valueFactories, sessionCache, PropertyType.WEAKREFERENCE, uuid);
866 }
867
868 @Override
869 public Value createValue( javax.jcr.Binary value ) {
870 return new JcrValue(valueFactories, sessionCache, PropertyType.BINARY, value);
871 }
872
873 @Override
874 public Value createValue( InputStream value ) {
875 Binary binary = valueFactories.getBinaryFactory().create(value);
876 return new JcrValue(valueFactories, sessionCache, PropertyType.BINARY, binary);
877 }
878
879 @Override
880 public javax.jcr.Binary createBinary( InputStream value ) {
881 Binary binary = valueFactories.getBinaryFactory().create(value);
882 return new JcrBinary(binary);
883 }
884
885 @Override
886 public Value createValue( Calendar value ) {
887 DateTime dateTime = valueFactories.getDateFactory().create(value);
888 return new JcrValue(valueFactories, sessionCache, PropertyType.DATE, dateTime);
889 }
890
891 @Override
892 public Value createValue( boolean value ) {
893 return new JcrValue(valueFactories, sessionCache, PropertyType.BOOLEAN, value);
894 }
895
896 @Override
897 public Value createValue( double value ) {
898 return new JcrValue(valueFactories, sessionCache, PropertyType.DOUBLE, value);
899 }
900
901 @Override
902 public Value createValue( long value ) {
903 return new JcrValue(valueFactories, sessionCache, PropertyType.LONG, value);
904 }
905
906 @Override
907 public Value createValue( String value ) {
908 return new JcrValue(valueFactories, sessionCache, PropertyType.STRING, value);
909 }
910
911 @Override
912 public Value createValue( BigDecimal value ) {
913 return new JcrValue(valueFactories, sessionCache, PropertyType.DECIMAL, value);
914 }
915
916 Object convertValueToType( Object value,
917 int toType ) throws ValueFormatException {
918 switch (toType) {
919 case PropertyType.BOOLEAN:
920 try {
921 return valueFactories.getBooleanFactory().create(value);
922 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
923 throw new ValueFormatException(vfe);
924 }
925
926 case PropertyType.DATE:
927 try {
928 return valueFactories.getDateFactory().create(value);
929 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
930 throw new ValueFormatException(vfe);
931 }
932
933 case PropertyType.NAME:
934 try {
935 return valueFactories.getNameFactory().create(value);
936 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
937 throw new ValueFormatException(vfe);
938 }
939
940 case PropertyType.PATH:
941 try {
942 return valueFactories.getPathFactory().create(value);
943 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
944 throw new ValueFormatException(vfe);
945 }
946
947 case PropertyType.REFERENCE:
948 case PropertyType.WEAKREFERENCE:
949 try {
950 return valueFactories.getReferenceFactory().create(value);
951 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
952 throw new ValueFormatException(vfe);
953 }
954 case PropertyType.DOUBLE:
955 try {
956 return valueFactories.getDoubleFactory().create(value);
957 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
958 throw new ValueFormatException(vfe);
959 }
960 case PropertyType.LONG:
961 try {
962 return valueFactories.getLongFactory().create(value);
963 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
964 throw new ValueFormatException(vfe);
965 }
966 case PropertyType.DECIMAL:
967 try {
968 return valueFactories.getDecimalFactory().create(value);
969 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
970 throw new ValueFormatException(vfe);
971 }
972 case PropertyType.URI:
973 try {
974 return valueFactories.getUriFactory().create(value);
975 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
976 throw new ValueFormatException(vfe);
977 }
978
979
980 case PropertyType.BINARY:
981 try {
982 return valueFactories.getBinaryFactory().create(value);
983 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
984 throw new ValueFormatException(vfe);
985 }
986 case PropertyType.STRING:
987 try {
988 return valueFactories.getStringFactory().create(value);
989 } catch (org.modeshape.graph.property.ValueFormatException vfe) {
990 throw new ValueFormatException(vfe);
991 }
992 case PropertyType.UNDEFINED:
993 return value;
994
995 default:
996 assert false : "Unexpected JCR property type " + toType;
997
998 throw new IllegalStateException("Invalid property type " + toType);
999 }
1000 }
1001
1002 };
1003 }
1004
1005
1006
1007
1008
1009
1010 public boolean hasPendingChanges() {
1011 return cache.hasPendingChanges();
1012 }
1013
1014
1015
1016
1017
1018
1019 public Session impersonate( Credentials credentials ) throws RepositoryException {
1020 return repository.login(credentials, this.workspace.getName());
1021 }
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031 JcrSession with( String workspaceName ) throws RepositoryException {
1032 return repository.sessionForContext(executionContext, workspaceName, sessionAttributes);
1033 }
1034
1035
1036
1037
1038
1039
1040 public void importXML( String parentAbsPath,
1041 InputStream in,
1042 int uuidBehavior ) throws IOException, InvalidSerializedDataException, RepositoryException {
1043
1044 try {
1045 XMLReader parser = XMLReaderFactory.createXMLReader();
1046
1047 parser.setContentHandler(getImportContentHandler(parentAbsPath, uuidBehavior));
1048 parser.parse(new InputSource(in));
1049 } catch (EnclosingSAXException ese) {
1050 Exception cause = ese.getException();
1051 if (cause instanceof ItemExistsException) {
1052 throw (ItemExistsException)cause;
1053 } else if (cause instanceof ConstraintViolationException) {
1054 throw (ConstraintViolationException)cause;
1055 } else if (cause instanceof VersionException) {
1056 throw (VersionException)cause;
1057 }
1058 throw new RepositoryException(cause);
1059 } catch (SAXParseException se) {
1060 throw new InvalidSerializedDataException(se);
1061 } catch (SAXException se) {
1062 throw new RepositoryException(se);
1063 }
1064 }
1065
1066
1067
1068
1069
1070
1071
1072 public boolean itemExists( String absolutePath ) throws RepositoryException {
1073 try {
1074 return (getItem(absolutePath) != null);
1075 } catch (PathNotFoundException error) {
1076 return false;
1077 }
1078 }
1079
1080
1081
1082
1083
1084
1085 public void logout() {
1086 if (!isLive()) {
1087 return;
1088 }
1089
1090 isLive = false;
1091 this.workspace().observationManager().removeAllEventListeners();
1092 this.lockManager().cleanLocks();
1093 this.repository.sessionLoggedOut(this);
1094 this.executionContext.getSecurityContext().logout();
1095 }
1096
1097
1098
1099
1100
1101
1102 public void move( String srcAbsPath,
1103 String destAbsPath ) throws ItemExistsException, RepositoryException {
1104 CheckArg.isNotNull(srcAbsPath, "srcAbsPath");
1105 CheckArg.isNotNull(destAbsPath, "destAbsPath");
1106
1107 PathFactory pathFactory = executionContext.getValueFactories().getPathFactory();
1108 Path destPath = pathFactory.create(destAbsPath);
1109
1110
1111 if (destAbsPath.endsWith("]")) {
1112 throw new RepositoryException(JcrI18n.pathCannotHaveSameNameSiblingIndex.text(destAbsPath));
1113 }
1114
1115 Path.Segment newNodeName = null;
1116 AbstractJcrNode sourceNode = getNode(pathFactory.create(srcAbsPath));
1117 AbstractJcrNode newParentNode = null;
1118 if (destPath.isIdentifier()) {
1119 AbstractJcrNode existingDestNode = getNode(destPath);
1120 newParentNode = existingDestNode.getParent();
1121 newNodeName = existingDestNode.segment();
1122 } else {
1123 newParentNode = getNode(destPath.getParent());
1124 newNodeName = destPath.getSegment(destPath.size() - 1);
1125 }
1126
1127 if (sourceNode.isLocked() && !sourceNode.getLock().isLockOwningSession()) {
1128 javax.jcr.lock.Lock sourceLock = sourceNode.getLock();
1129 if (sourceLock != null && sourceLock.getLockToken() == null) {
1130 throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath));
1131 }
1132 }
1133
1134 if (newParentNode.isLocked() && !newParentNode.getLock().isLockOwningSession()) {
1135 javax.jcr.lock.Lock newParentLock = newParentNode.getLock();
1136 if (newParentLock != null && newParentLock.getLockToken() == null) {
1137 throw new LockException(JcrI18n.lockTokenNotHeld.text(destAbsPath));
1138 }
1139 }
1140
1141 if (!sourceNode.getParent().isCheckedOut()) {
1142 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(sourceNode.getPath()));
1143 }
1144
1145 if (!newParentNode.isCheckedOut()) {
1146 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(newParentNode.getPath()));
1147 }
1148
1149 newParentNode.editor().moveToBeChild(sourceNode, newNodeName.getName());
1150 }
1151
1152
1153
1154
1155
1156
1157 public void refresh( boolean keepChanges ) {
1158 this.cache.refresh(keepChanges);
1159 }
1160
1161
1162
1163
1164
1165
1166 public void removeLockToken( String lockToken ) {
1167 CheckArg.isNotNull(lockToken, "lock token");
1168
1169 try {
1170 lockManager().removeLockToken(lockToken);
1171 } catch (LockException le) {
1172
1173 }
1174 }
1175
1176 void recordRemoval( Location location ) throws RepositoryException {
1177 if (!performReferentialIntegrityChecks) {
1178 return;
1179 }
1180 if (removedNodes == null) {
1181 removedNodes = new HashSet<Location>();
1182 removedReferenceableNodeUuids = new HashSet<String>();
1183 }
1184
1185
1186 Path path = location.getPath();
1187 org.modeshape.graph.property.ValueFactory<String> stringFactory = executionContext.getValueFactories().getStringFactory();
1188 String pathStr = stringFactory.create(path);
1189 int sns = path.getLastSegment().getIndex();
1190 if (sns == Path.DEFAULT_INDEX) pathStr = pathStr + "[1]";
1191
1192 TypeSystem typeSystem = executionContext.getValueFactories().getTypeSystem();
1193 QueryBuilder builder = new QueryBuilder(typeSystem);
1194 QueryCommand query = builder.select("jcr:uuid")
1195 .from("mix:referenceable AS referenceable")
1196 .where()
1197 .path("referenceable")
1198 .isLike(pathStr + "%")
1199 .end()
1200 .query();
1201 JcrQueryManager queryManager = workspace().queryManager();
1202 Query jcrQuery = queryManager.createQuery(query);
1203 QueryResult result = jcrQuery.execute();
1204 RowIterator rows = result.getRows();
1205 while (rows.hasNext()) {
1206 Row row = rows.nextRow();
1207 String uuid = row.getValue("jcr:uuid").getString();
1208 if (uuid != null) removedReferenceableNodeUuids.add(uuid);
1209 }
1210
1211
1212 Set<Location> extras = null;
1213 for (Location alreadyDeleted : removedNodes) {
1214 Path alreadyDeletedPath = alreadyDeleted.getPath();
1215 if (alreadyDeletedPath.isAtOrAbove(path)) {
1216
1217 return;
1218 }
1219 if (alreadyDeletedPath.isDecendantOf(path)) {
1220
1221 if (extras == null) {
1222 extras = new HashSet<Location>();
1223 }
1224 extras.add(alreadyDeleted);
1225 }
1226 }
1227
1228 removedNodes.add(location);
1229 if (extras != null) {
1230
1231 removedNodes.removeAll(extras);
1232 }
1233 }
1234
1235 boolean wasRemovedInSession( Location location ) {
1236 if (removedNodes == null) return false;
1237 if (removedNodes.contains(location)) return true;
1238 Path path = location.getPath();
1239 for (Location removed : removedNodes) {
1240 if (removed.getPath().isAtOrAbove(path)) return true;
1241 }
1242 return false;
1243 }
1244
1245 boolean wasRemovedInSession( UUID uuid ) {
1246 if (removedReferenceableNodeUuids == null) return false;
1247 return removedReferenceableNodeUuids.contains(uuid);
1248
1249 }
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 void checkReferentialIntegrityOfChanges( AbstractJcrNode subgraphRoot )
1261 throws ReferentialIntegrityException, RepositoryException {
1262 if (removedNodes == null) return;
1263 if (removedReferenceableNodeUuids.isEmpty()) return;
1264
1265 if (removedNodes.size() == 1 && removedNodes.iterator().next().getPath().isRoot()) {
1266
1267 return;
1268 }
1269
1270 String subgraphPath = null;
1271 if (subgraphRoot != null) {
1272 subgraphPath = subgraphRoot.getPath();
1273 if (subgraphRoot.getIndex() == Path.DEFAULT_INDEX) subgraphPath = subgraphPath + "[1]";
1274 }
1275
1276
1277
1278 int maxBatchSize = 100;
1279 List<Object> someUuidsInBranch = new ArrayList<Object>(maxBatchSize);
1280 Iterator<String> uuidIter = removedReferenceableNodeUuids.iterator();
1281 while (uuidIter.hasNext()) {
1282
1283 while (uuidIter.hasNext() && someUuidsInBranch.size() <= maxBatchSize) {
1284 String uuid = uuidIter.next();
1285 someUuidsInBranch.add(uuid);
1286 }
1287 assert !someUuidsInBranch.isEmpty();
1288
1289 TypeSystem typeSystem = executionContext.getValueFactories().getTypeSystem();
1290 QueryBuilder builder = new QueryBuilder(typeSystem);
1291 QueryCommand query = null;
1292 if (subgraphPath != null) {
1293 query = builder.select("jcr:primaryType")
1294 .fromAllNodesAs("allNodes")
1295 .where()
1296 .referenceValue("allNodes")
1297 .isIn(someUuidsInBranch)
1298 .and()
1299 .path("allNodes")
1300 .isLike(subgraphPath + "%")
1301 .end()
1302 .query();
1303 } else {
1304 query = builder.select("jcr:primaryType")
1305 .fromAllNodesAs("allNodes")
1306 .where()
1307 .referenceValue("allNodes")
1308 .isIn(someUuidsInBranch)
1309 .end()
1310 .query();
1311 }
1312 Query jcrQuery = workspace().queryManager().createQuery(query);
1313
1314 QueryResult result = jcrQuery.execute();
1315 NodeIterator referencingNodes = result.getNodes();
1316 while (referencingNodes.hasNext()) {
1317
1318 throw new ReferentialIntegrityException();
1319 }
1320 someUuidsInBranch.clear();
1321 }
1322 }
1323
1324
1325
1326
1327
1328
1329 public void save() throws RepositoryException {
1330 checkReferentialIntegrityOfChanges(null);
1331 removedNodes = null;
1332 cache.save();
1333 }
1334
1335
1336
1337
1338
1339
1340
1341 public void reindexContent() {
1342 repository().queryManager().reindexContent(workspace());
1343 }
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353 public void reindexContent( String path,
1354 int depth ) {
1355 repository().queryManager().reindexContent(workspace(), path, depth);
1356 }
1357
1358
1359
1360
1361
1362
1363
1364 public Snapshot getSnapshot() {
1365 return new Snapshot(cache.graphSession().getRoot().getSnapshot(false));
1366 }
1367
1368
1369
1370
1371
1372
1373 @Override
1374 public String toString() {
1375 return getSnapshot().toString();
1376 }
1377
1378
1379
1380
1381
1382
1383 @Override
1384 public AccessControlManager getAccessControlManager() throws UnsupportedRepositoryOperationException, RepositoryException {
1385 throw new UnsupportedRepositoryOperationException();
1386 }
1387
1388
1389
1390
1391
1392
1393 @Override
1394 public RetentionManager getRetentionManager() throws UnsupportedRepositoryOperationException, RepositoryException {
1395 throw new UnsupportedRepositoryOperationException();
1396 }
1397
1398 @Immutable
1399 public class Snapshot {
1400 private final GraphSession.StructureSnapshot<JcrPropertyPayload> rootSnapshot;
1401
1402 protected Snapshot( GraphSession.StructureSnapshot<JcrPropertyPayload> snapshot ) {
1403 this.rootSnapshot = snapshot;
1404 }
1405
1406
1407
1408
1409
1410
1411 @Override
1412 public String toString() {
1413 return rootSnapshot.toString();
1414 }
1415 }
1416 }