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