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.lang.ref.SoftReference;
27 import java.security.AccessControlException;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.UUID;
39 import javax.jcr.AccessDeniedException;
40 import javax.jcr.InvalidItemStateException;
41 import javax.jcr.Item;
42 import javax.jcr.ItemExistsException;
43 import javax.jcr.ItemNotFoundException;
44 import javax.jcr.NoSuchWorkspaceException;
45 import javax.jcr.PathNotFoundException;
46 import javax.jcr.PropertyType;
47 import javax.jcr.RepositoryException;
48 import javax.jcr.Session;
49 import javax.jcr.Value;
50 import javax.jcr.nodetype.ConstraintViolationException;
51 import javax.jcr.nodetype.NoSuchNodeTypeException;
52 import javax.jcr.nodetype.NodeType;
53 import javax.jcr.nodetype.PropertyDefinition;
54 import javax.jcr.version.VersionException;
55 import net.jcip.annotations.Immutable;
56 import net.jcip.annotations.ThreadSafe;
57 import org.modeshape.common.i18n.I18n;
58 import org.modeshape.common.util.Logger;
59 import org.modeshape.graph.ExecutionContext;
60 import org.modeshape.graph.Graph;
61 import org.modeshape.graph.Location;
62 import org.modeshape.graph.connector.RepositorySourceException;
63 import org.modeshape.graph.property.Binary;
64 import org.modeshape.graph.property.BinaryFactory;
65 import org.modeshape.graph.property.DateTime;
66 import org.modeshape.graph.property.Name;
67 import org.modeshape.graph.property.NameFactory;
68 import org.modeshape.graph.property.NamespaceRegistry;
69 import org.modeshape.graph.property.Path;
70 import org.modeshape.graph.property.PathFactory;
71 import org.modeshape.graph.property.Property;
72 import org.modeshape.graph.property.PropertyFactory;
73 import org.modeshape.graph.property.ValueFactories;
74 import org.modeshape.graph.property.ValueFactory;
75 import org.modeshape.graph.property.ValueFormatException;
76 import org.modeshape.graph.request.InvalidWorkspaceException;
77 import org.modeshape.graph.session.GraphSession;
78 import org.modeshape.graph.session.InvalidStateException;
79 import org.modeshape.graph.session.ValidationException;
80 import org.modeshape.graph.session.GraphSession.Node;
81 import org.modeshape.graph.session.GraphSession.NodeId;
82 import org.modeshape.graph.session.GraphSession.PropertyInfo;
83 import org.modeshape.graph.session.GraphSession.Status;
84 import org.modeshape.jcr.JcrRepository.Option;
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 @ThreadSafe
123 class SessionCache {
124
125
126
127
128
129 protected static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
130
131 protected static final Set<Name> EMPTY_NAMES = Collections.emptySet();
132
133 private final JcrSession session;
134 private final String workspaceName;
135 protected final ExecutionContext context;
136 protected final ValueFactories factories;
137 protected final PathFactory pathFactory;
138 protected final NameFactory nameFactory;
139 protected final ValueFactory<String> stringFactory;
140 protected final NamespaceRegistry namespaces;
141 protected final PropertyFactory propertyFactory;
142 private final Graph store;
143 protected final Name defaultPrimaryTypeName;
144 protected final Property defaultPrimaryTypeProperty;
145 protected final Path rootPath;
146 protected final Name residualName;
147
148 private final GraphSession<JcrNodePayload, JcrPropertyPayload> graphSession;
149
150 public SessionCache( JcrSession session ) {
151 this(session, session.workspace().getName(), session.getExecutionContext(), session.nodeTypeManager(), session.graph());
152 }
153
154 public SessionCache( JcrSession session,
155 String workspaceName,
156 ExecutionContext context,
157 JcrNodeTypeManager nodeTypes,
158 Graph store ) {
159 assert session != null;
160 assert workspaceName != null;
161 assert context != null;
162 assert store != null;
163 this.session = session;
164 this.workspaceName = workspaceName;
165 this.store = store;
166 this.context = context;
167 this.factories = context.getValueFactories();
168 this.pathFactory = this.factories.getPathFactory();
169 this.nameFactory = this.factories.getNameFactory();
170 this.stringFactory = context.getValueFactories().getStringFactory();
171 this.namespaces = context.getNamespaceRegistry();
172 this.propertyFactory = context.getPropertyFactory();
173 this.defaultPrimaryTypeName = JcrNtLexicon.UNSTRUCTURED;
174 this.defaultPrimaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, this.defaultPrimaryTypeName);
175 this.rootPath = pathFactory.createRootPath();
176 this.residualName = nameFactory.create(JcrNodeType.RESIDUAL_ITEM_NAME);
177
178
179 this.graphSession = new GraphSession<JcrNodePayload, JcrPropertyPayload>(this.store, this.workspaceName,
180 new JcrNodeOperations(), new JcrAuthorizer());
181
182 try {
183 int depth = Integer.parseInt(session.repository().getOptions().get(Option.READ_DEPTH));
184 if (depth > 0) this.graphSession.setDepthForLoadingNodes(depth);
185 } catch (RuntimeException e) {
186 }
187 }
188
189 final GraphSession<JcrNodePayload, JcrPropertyPayload> graphSession() {
190 return graphSession;
191 }
192
193 JcrSession session() {
194 return session;
195 }
196
197 String workspaceName() {
198 return workspaceName;
199 }
200
201 String sourceName() {
202 return store.getSourceName();
203 }
204
205 ExecutionContext context() {
206 return context;
207 }
208
209 ValueFactories factories() {
210 return factories;
211 }
212
213 PathFactory pathFactory() {
214 return pathFactory;
215 }
216
217 NameFactory nameFactory() {
218 return nameFactory;
219 }
220
221 ValueFactory<String> stringFactory() {
222 return factories.getStringFactory();
223 }
224
225 JcrNodeTypeManager nodeTypes() {
226 return session.nodeTypeManager();
227 }
228
229 final String readable( Name name ) {
230 return name.getString(namespaces);
231 }
232
233 final String readable( Path.Segment segment ) {
234 return segment.getString(namespaces);
235 }
236
237 final String readable( Path path ) {
238 return path.getString(namespaces);
239 }
240
241 final String readable( Location location ) {
242 return location.getString(namespaces);
243 }
244
245 final String readable( Iterable<Name> names ) {
246 StringBuilder sb = new StringBuilder();
247 sb.append('[');
248 boolean first = true;
249 for (Name name : names) {
250 if (first) {
251 first = false;
252 } else {
253 sb.append(", ");
254 }
255 sb.append(name.getString(namespaces));
256 }
257 sb.append(']');
258 return sb.toString();
259 }
260
261
262
263
264
265
266 boolean hasPendingChanges() {
267 return graphSession.hasPendingChanges();
268 }
269
270
271
272
273
274
275
276
277
278 public void refresh( boolean keepChanges ) {
279 graphSession.refresh(keepChanges);
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294 public void refresh( NodeId nodeId,
295 Path absolutePath,
296 boolean keepChanges ) throws InvalidItemStateException, RepositoryException {
297 assert nodeId != null;
298 try {
299 Node<JcrNodePayload, JcrPropertyPayload> node = graphSession.findNodeWith(nodeId, absolutePath);
300 graphSession.refresh(node, keepChanges);
301 } catch (InvalidStateException e) {
302 throw new InvalidItemStateException(e.getLocalizedMessage());
303 } catch (org.modeshape.graph.property.PathNotFoundException e) {
304 throw new InvalidItemStateException(e.getLocalizedMessage());
305 } catch (RepositorySourceException e) {
306 throw new RepositoryException(e.getLocalizedMessage());
307 }
308 }
309
310
311
312
313
314
315
316
317 public void refreshProperties( Location location ) throws InvalidItemStateException, RepositoryException {
318 assert location != null;
319 try {
320 Node<JcrNodePayload, JcrPropertyPayload> node = graphSession.findNodeWith(location);
321
322 graphSession.refreshProperties(node);
323 } catch (InvalidStateException e) {
324 throw new InvalidItemStateException(e.getLocalizedMessage());
325 } catch (org.modeshape.graph.property.PathNotFoundException e) {
326 throw new InvalidItemStateException(e.getLocalizedMessage());
327 } catch (RepositorySourceException e) {
328 throw new RepositoryException(e.getLocalizedMessage());
329 }
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 protected JcrNodeDefinition findBestNodeDefinition( Node<JcrNodePayload, JcrPropertyPayload> parent,
346 Name newNodeName,
347 Name newNodePrimaryTypeName )
348 throws ItemExistsException, ConstraintViolationException, RepositoryException {
349 assert parent != null;
350 assert newNodeName != null;
351
352 Name primaryTypeName = parent.getPayload().getPrimaryTypeName();
353 List<Name> mixinTypeNames = parent.getPayload().getMixinTypeNames();
354
355
356 int snsCount = parent.getChildrenCount(newNodeName) + 1;
357 JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
358 mixinTypeNames,
359 newNodeName,
360 newNodePrimaryTypeName,
361 snsCount,
362 true);
363 if (definition == null) {
364 if (snsCount > 1) {
365 definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
366 mixinTypeNames,
367 newNodeName,
368 newNodePrimaryTypeName,
369 1,
370 true);
371
372 if (definition != null) {
373 throw new ItemExistsException(JcrI18n.noSnsDefinition.text(readable(newNodeName),
374 readable(parent.getPath()),
375 readable(primaryTypeName),
376 readable(mixinTypeNames)));
377 }
378 }
379
380 throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
381 readable(newNodeName),
382 readable(parent.getPath()),
383 readable(primaryTypeName),
384 readable(mixinTypeNames)));
385 }
386
387 return definition;
388 }
389
390
391
392
393
394
395
396
397
398
399 public void save() throws ItemNotFoundException, AccessDeniedException, ConstraintViolationException, RepositoryException {
400 try {
401 graphSession.save();
402 } catch (ValidationException e) {
403 throw new ConstraintViolationException(e.getLocalizedMessage(), e);
404 } catch (InvalidStateException e) {
405 throw new InvalidItemStateException(e.getLocalizedMessage(), e);
406 } catch (RepositorySourceException e) {
407 throw new RepositoryException(e.getLocalizedMessage(), e);
408 } catch (AccessControlException e) {
409 throw new AccessDeniedException(e.getMessage(), e);
410 }
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425 public void save( NodeId nodeId,
426 Path absolutePath )
427 throws ItemNotFoundException, AccessDeniedException, ConstraintViolationException, RepositoryException {
428 assert nodeId != null;
429 try {
430 Node<JcrNodePayload, JcrPropertyPayload> node = graphSession.findNodeWith(nodeId, absolutePath);
431 assert node != null;
432 graphSession.save(node);
433 } catch (ValidationException e) {
434 throw new ConstraintViolationException(e.getLocalizedMessage(), e);
435 } catch (InvalidStateException e) {
436 throw new InvalidItemStateException(e.getLocalizedMessage(), e);
437 } catch (RepositorySourceException e) {
438 throw new RepositoryException(e.getLocalizedMessage(), e);
439 } catch (AccessControlException e) {
440 throw new AccessDeniedException(e.getMessage(), e);
441 }
442 }
443
444
445
446
447
448
449
450
451
452
453
454
455 public Node<JcrNodePayload, JcrPropertyPayload> findNode( NodeId id,
456 Path absolutePath )
457 throws ItemNotFoundException, AccessDeniedException, RepositoryException {
458 try {
459 return graphSession.findNodeWith(id, absolutePath);
460 } catch (org.modeshape.graph.property.PathNotFoundException e) {
461 throw new ItemNotFoundException(e.getMessage(), e);
462 } catch (RepositorySourceException e) {
463 throw new RepositoryException(e.getMessage(), e);
464 } catch (AccessControlException e) {
465 throw new AccessDeniedException(e.getMessage(), e);
466 }
467 }
468
469
470
471
472
473
474
475
476
477
478
479 public Node<JcrNodePayload, JcrPropertyPayload> findNodeWith( Location location )
480 throws ItemNotFoundException, AccessDeniedException, RepositoryException {
481 try {
482 return graphSession.findNodeWith(location);
483 } catch (org.modeshape.graph.property.PathNotFoundException e) {
484 throw new ItemNotFoundException(e.getMessage(), e);
485 } catch (RepositorySourceException e) {
486 throw new RepositoryException(e.getMessage(), e);
487 } catch (AccessControlException e) {
488 throw new AccessDeniedException(e.getMessage(), e);
489 }
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506 public Node<JcrNodePayload, JcrPropertyPayload> findNode( NodeId from,
507 Path fromAbsolutePath,
508 Path relativePath )
509 throws PathNotFoundException, ItemNotFoundException, AccessDeniedException, RepositoryException {
510
511 Node<JcrNodePayload, JcrPropertyPayload> referenceNode = findNode(from, fromAbsolutePath);
512 try {
513 return graphSession.findNodeRelativeTo(referenceNode, relativePath);
514 } catch (org.modeshape.graph.property.PathNotFoundException e) {
515 throw new PathNotFoundException(e.getMessage(), e);
516 } catch (RepositorySourceException e) {
517 throw new RepositoryException(e.getMessage(), e);
518 } catch (AccessControlException e) {
519 throw new AccessDeniedException(e.getMessage(), e);
520 }
521 }
522
523
524
525
526
527
528
529 public JcrRootNode findJcrRootNode() throws RepositoryException {
530 return (JcrRootNode)graphSession.getRoot().getPayload().getJcrNode();
531 }
532
533
534
535
536
537
538
539
540
541
542
543 public AbstractJcrNode findJcrNode( Location location )
544 throws ItemNotFoundException, AccessDeniedException, RepositoryException {
545 try {
546 return graphSession.findNodeWith(location).getPayload().getJcrNode();
547 } catch (org.modeshape.graph.property.PathNotFoundException e) {
548 throw new ItemNotFoundException(e.getMessage(), e);
549 } catch (RepositorySourceException e) {
550 throw new RepositoryException(e.getMessage(), e);
551 } catch (AccessControlException e) {
552 throw new AccessDeniedException(e.getMessage(), e);
553 }
554 }
555
556
557
558
559
560
561
562
563
564
565
566
567 public AbstractJcrNode findJcrNode( NodeId id,
568 Path absolutePath )
569 throws ItemNotFoundException, AccessDeniedException, RepositoryException {
570 return findNode(id, absolutePath).getPayload().getJcrNode();
571 }
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588 public AbstractJcrNode findJcrNode( NodeId from,
589 Path fromAbsolutePath,
590 Path relativePath )
591 throws ItemNotFoundException, PathNotFoundException, InvalidItemStateException, AccessDeniedException,
592 RepositoryException {
593
594 Node<JcrNodePayload, JcrPropertyPayload> referenceNode = findNode(from, fromAbsolutePath);
595 try {
596
597 return graphSession.findNodeRelativeTo(referenceNode, relativePath).getPayload().getJcrNode();
598 } catch (org.modeshape.graph.property.PathNotFoundException e) {
599 throw new PathNotFoundException(e.getMessage(), e);
600 } catch (RepositorySourceException e) {
601 throw new RepositoryException(e.getMessage(), e);
602 } catch (AccessControlException e) {
603 throw new AccessDeniedException(e.getMessage(), e);
604 }
605 }
606
607
608
609
610
611
612
613
614
615
616
617
618 public AbstractJcrProperty findJcrProperty( NodeId id,
619 Path absolutePath,
620 Name propertyName )
621 throws PathNotFoundException, AccessDeniedException, RepositoryException {
622
623 Node<JcrNodePayload, JcrPropertyPayload> node = findNode(id, absolutePath);
624 PropertyInfo<JcrPropertyPayload> propertyInfo = node.getProperty(propertyName);
625 if (propertyInfo != null) {
626 if (propertyName.equals(JcrLexicon.UUID) && !isReferenceable(node)) return null;
627 return propertyInfo.getPayload().getJcrProperty();
628 }
629 return null;
630 }
631
632
633
634
635
636
637
638
639
640
641
642 public Collection<AbstractJcrProperty> findJcrPropertiesFor( NodeId id,
643 Path absolutePath )
644 throws PathNotFoundException, AccessDeniedException, RepositoryException {
645 try {
646 Node<JcrNodePayload, JcrPropertyPayload> node = graphSession.findNodeWith(id, absolutePath);
647 Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(node.getPropertyCount());
648 for (org.modeshape.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> property : node.getProperties()) {
649 Name propertyName = property.getName();
650 if (propertyName.equals(JcrLexicon.UUID) && !isReferenceable(node)) continue;
651 if (!propertyName.getNamespaceUri().equals(ModeShapeIntLexicon.Namespace.URI)) {
652 AbstractJcrProperty prop = property.getPayload().getJcrProperty();
653 if (prop != null) result.add(prop);
654 }
655 }
656 return result;
657 } catch (org.modeshape.graph.property.PathNotFoundException e) {
658 throw new PathNotFoundException(e.getMessage(), e);
659 } catch (RepositorySourceException e) {
660 throw new RepositoryException(e.getMessage(), e);
661 } catch (AccessControlException e) {
662 throw new AccessDeniedException(e.getMessage(), e);
663 }
664 }
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 public AbstractJcrItem findJcrItem( NodeId from,
683 Path fromAbsolutePath,
684 Path relativePath )
685 throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
686 if (from == null && fromAbsolutePath == null) {
687 from = graphSession.getRoot().getNodeId();
688 }
689
690 if (relativePath.size() == 0) {
691 return findJcrNode(from, fromAbsolutePath);
692 }
693 if (relativePath.size() == 1) {
694 Path.Segment segment = relativePath.getLastSegment();
695 if (segment.isSelfReference()) return findJcrNode(from, fromAbsolutePath);
696 if (segment.isParentReference()) {
697 return findJcrNode(from, fromAbsolutePath, relativePath);
698 }
699 }
700
701
702 Path.Segment lastSegment = relativePath.getLastSegment();
703 if (lastSegment.getIndex() > 1) {
704
705 return findJcrNode(from, fromAbsolutePath);
706 }
707
708 Node<JcrNodePayload, JcrPropertyPayload> fromNode = null;
709 Node<JcrNodePayload, JcrPropertyPayload> parent = null;
710 try {
711 fromNode = graphSession.findNodeWith(from, fromAbsolutePath);
712 if (from == null) from = fromNode.getNodeId();
713 assert from != null;
714 if (relativePath.size() == 1) {
715
716 parent = fromNode;
717 } else {
718
719 parent = graphSession.findNodeRelativeTo(fromNode, relativePath.getParent());
720 }
721 } catch (org.modeshape.graph.property.PathNotFoundException e) {
722 throw new ItemNotFoundException(e.getMessage(), e);
723 } catch (RepositorySourceException e) {
724 throw new RepositoryException(e.getMessage(), e);
725 } catch (AccessControlException e) {
726 throw new AccessDeniedException(e.getMessage(), e);
727 }
728
729
730
731
732
733 if (parent.hasChild(lastSegment)) {
734
735 Node<JcrNodePayload, JcrPropertyPayload> child = parent.getChild(lastSegment);
736 return child.getPayload().getJcrNode();
737 }
738
739
740 org.modeshape.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> propertyInfo = parent.getProperty(lastSegment.getName());
741 if (propertyInfo != null) {
742 return propertyInfo.getPayload().getJcrProperty();
743 }
744
745
746 String msg = null;
747 if (from.equals(graphSession.getRoot().getNodeId())) {
748
749 Path absolutePath = rootPath.resolve(relativePath);
750 msg = JcrI18n.itemNotFoundAtPath.text(readable(absolutePath), workspaceName);
751 } else {
752
753 Path referenceNodePath = fromNode.getPath();
754 msg = JcrI18n.itemNotFoundAtPathRelativeToReferenceNode.text(readable(relativePath),
755 readable(referenceNodePath),
756 workspaceName);
757 }
758 throw new ItemNotFoundException(msg);
759 }
760
761
762
763
764
765
766
767
768
769
770
771 public final boolean isNodeType( Node<JcrNodePayload, JcrPropertyPayload> node,
772 Name nodeType ) throws RepositoryException {
773 Name primaryTypeName = node.getPayload().getPrimaryTypeName();
774 JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
775 if (primaryType.isNodeType(nodeType)) {
776 return true;
777 }
778 JcrNodeTypeManager nodeTypes = session().nodeTypeManager();
779 for (Name mixinTypeName : node.getPayload().getMixinTypeNames()) {
780 JcrNodeType mixinType = nodeTypes.getNodeType(mixinTypeName);
781 if (mixinType != null && mixinType.isNodeType(nodeType)) {
782 return true;
783 }
784 }
785 return false;
786 }
787
788 public boolean isReferenceable( Node<JcrNodePayload, JcrPropertyPayload> node ) throws RepositoryException {
789 return isNodeType(node, JcrMixLexicon.REFERENCEABLE);
790 }
791
792 public boolean isVersionable( Node<JcrNodePayload, JcrPropertyPayload> node ) throws RepositoryException {
793 return isNodeType(node, JcrMixLexicon.VERSIONABLE);
794 }
795
796
797
798
799
800
801
802
803
804
805
806 public NodeEditor getEditorFor( Node<JcrNodePayload, JcrPropertyPayload> node ) {
807 return new NodeEditor(node);
808 }
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825 public NodeEditor getEditorFor( NodeId id,
826 Path absolutePath )
827 throws ItemNotFoundException, AccessDeniedException, InvalidItemStateException, RepositoryException {
828 Node<JcrNodePayload, JcrPropertyPayload> node = this.graphSession.findNodeWith(id, absolutePath);
829 return new NodeEditor(node);
830 }
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851 Path getPathForCorrespondingNode( String workspaceName,
852 UUID uuid,
853 Path relativePath )
854 throws NoSuchWorkspaceException, AccessDeniedException, ItemNotFoundException, RepositoryException {
855 assert workspaceName != null;
856 assert uuid != null || relativePath != null;
857
858 try {
859 try {
860 store.useWorkspace(workspaceName);
861 } catch (InvalidWorkspaceException iwe) {
862 throw new NoSuchWorkspaceException(JcrI18n.workspaceNameIsInvalid.text(store.getSourceName(), workspaceName));
863 }
864 org.modeshape.graph.Node node;
865 if (uuid != null) {
866 node = store.getNodeAt(uuid);
867
868 if (relativePath != null) {
869 Path nodePath = node.getLocation().getPath();
870 Path absolutePath = relativePath.resolveAgainst(nodePath);
871 node = store.getNodeAt(absolutePath);
872 }
873
874 } else {
875 Path absolutePath = pathFactory.createAbsolutePath(relativePath.getSegmentsList());
876 node = store.getNodeAt(absolutePath);
877 }
878 assert node != null;
879
880 Path path = node.getLocation().getPath();
881 try {
882 this.session().checkPermission(workspaceName, path, "read");
883 } catch (AccessControlException ace) {
884 throw new AccessDeniedException(ace);
885 }
886 return path;
887 } catch (org.modeshape.graph.property.PathNotFoundException pnfe) {
888 throw new ItemNotFoundException(pnfe);
889 } finally {
890 store.useWorkspace(this.workspaceName);
891 }
892 }
893
894
895
896
897 public final class NodeEditor {
898 private final Node<JcrNodePayload, JcrPropertyPayload> node;
899
900 protected NodeEditor( Node<JcrNodePayload, JcrPropertyPayload> node ) {
901 this.node = node;
902 }
903
904
905
906
907
908
909
910
911
912
913
914 private void checkCardinalityOfExistingProperty( Name propertyName,
915 boolean isMultiple )
916 throws javax.jcr.ValueFormatException, RepositoryException {
917
918 PropertyInfo<JcrPropertyPayload> propInfo = this.node.getProperty(propertyName);
919 if (propInfo != null && propInfo.isMultiValued() != isMultiple) {
920 String workspaceName = SessionCache.this.workspaceName();
921 String propName = readable(propertyName);
922 String path = readable(node.getPath());
923 if (isMultiple) {
924 I18n msg = JcrI18n.unableToSetSingleValuedPropertyUsingMultipleValues;
925 throw new javax.jcr.ValueFormatException(msg.text(propName, path, workspaceName));
926 }
927 I18n msg = JcrI18n.unableToSetMultiValuedPropertyUsingSingleValue;
928 throw new javax.jcr.ValueFormatException(msg.text(propName, path, workspaceName));
929 }
930
931 }
932
933
934
935
936
937
938 boolean isCheckedOut() throws RepositoryException {
939 for (Node<JcrNodePayload, JcrPropertyPayload> curr = node; curr.getParent() != null; curr = curr.getParent()) {
940 if (isNodeType(curr, JcrMixLexicon.VERSIONABLE)) {
941 PropertyInfo<JcrPropertyPayload> prop = curr.getProperty(JcrLexicon.IS_CHECKED_OUT);
942
943
944 return prop == null || prop.getPayload().getJcrProperty().getBoolean();
945 }
946 }
947
948 return true;
949 }
950
951
952
953
954
955
956
957
958
959
960
961
962
963 public AbstractJcrProperty setProperty( Name name,
964 JcrValue value )
965 throws AccessDeniedException, ConstraintViolationException, RepositoryException {
966 return setProperty(name, value, true);
967 }
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984 public AbstractJcrProperty setProperty( Name name,
985 JcrValue value,
986 boolean skipProtected )
987 throws AccessDeniedException, ConstraintViolationException, VersionException, RepositoryException {
988 assert name != null;
989 assert value != null;
990
991
992
993
994
995 if (!isCheckedOut() && skipProtected) {
996 String path = node.getLocation().getPath().getString(context().getNamespaceRegistry());
997 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(path));
998 }
999
1000 checkCardinalityOfExistingProperty(name, false);
1001
1002 JcrPropertyDefinition definition = null;
1003
1004
1005 PropertyInfo<JcrPropertyPayload> existing = node.getProperty(name);
1006 if (existing != null) {
1007
1008
1009
1010 definition = nodeTypes().getPropertyDefinition(existing.getPayload().getPropertyDefinitionId());
1011
1012 if (definition != null) {
1013
1014 if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != value.getType()) {
1015
1016
1017 definition = null;
1018 } else {
1019
1020 if (!definition.satisfiesConstraints(value)) definition = null;
1021 }
1022 }
1023 }
1024 JcrNodePayload payload = node.getPayload();
1025 if (definition == null) {
1026
1027 definition = nodeTypes().findPropertyDefinition(payload.getPrimaryTypeName(),
1028 payload.getMixinTypeNames(),
1029 name,
1030 value,
1031 true,
1032 skipProtected);
1033
1034
1035
1036
1037 boolean referencePropMissedConstraints = definition != null
1038 && definition.getRequiredType() == PropertyType.REFERENCE
1039 && !definition.canCastToTypeAndSatisfyConstraints(value);
1040 if (definition == null || referencePropMissedConstraints) {
1041 throw new ConstraintViolationException(JcrI18n.noDefinition.text("property",
1042 readable(name),
1043 readable(node.getPath()),
1044 readable(payload.getPrimaryTypeName()),
1045 readable(payload.getMixinTypeNames())));
1046 }
1047 } else {
1048
1049 if (skipProtected && definition.isProtected()) throw new ConstraintViolationException(
1050 JcrI18n.noDefinition.text("property",
1051 readable(name),
1052 readable(node.getPath()),
1053 readable(payload.getPrimaryTypeName()),
1054 readable(payload.getMixinTypeNames())));
1055 }
1056
1057 Object objValue = value.value();
1058 int propertyType = definition.getRequiredType();
1059 if (propertyType == PropertyType.UNDEFINED || propertyType == value.getType()) {
1060
1061 propertyType = value.getType();
1062 } else {
1063
1064 org.modeshape.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
1065 ValueFactory<?> factory = factories().getValueFactory(dnaPropertyType);
1066 objValue = factory.create(objValue);
1067 }
1068 Property dnaProp = propertyFactory.create(name, objValue);
1069
1070 try {
1071
1072 AbstractJcrProperty jcrProp = null;
1073 if (existing != null) {
1074 jcrProp = existing.getPayload().getJcrProperty();
1075 } else {
1076 AbstractJcrNode jcrNode = payload.getJcrNode();
1077 if (definition.isMultiple()) {
1078 jcrProp = new JcrMultiValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
1079 } else {
1080 jcrProp = new JcrSingleValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
1081 }
1082 }
1083 assert jcrProp != null;
1084 JcrPropertyPayload propPayload = new JcrPropertyPayload(definition.getId(), propertyType, jcrProp);
1085 node.setProperty(dnaProp, definition.isMultiple(), propPayload);
1086 return jcrProp;
1087 } catch (ValidationException e) {
1088 throw new ConstraintViolationException(e.getMessage(), e);
1089 } catch (RepositorySourceException e) {
1090 throw new RepositoryException(e.getMessage(), e);
1091 } catch (AccessControlException e) {
1092 throw new AccessDeniedException(e.getMessage(), e);
1093 }
1094 }
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114 public AbstractJcrProperty setProperty( Name name,
1115 Value[] values,
1116 int valueType )
1117 throws AccessDeniedException, ConstraintViolationException, RepositoryException, javax.jcr.ValueFormatException {
1118 return setProperty(name, values, valueType, true);
1119 }
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139 public AbstractJcrProperty setProperty( Name name,
1140 Value[] values,
1141 int valueType,
1142 boolean skipProtected )
1143 throws AccessDeniedException, ConstraintViolationException, RepositoryException, javax.jcr.ValueFormatException,
1144 VersionException {
1145 assert name != null;
1146 assert values != null;
1147
1148
1149
1150
1151
1152 if (!isCheckedOut() && skipProtected) {
1153 String path = node.getLocation().getPath().getString(context().getNamespaceRegistry());
1154 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(path));
1155 }
1156
1157 checkCardinalityOfExistingProperty(name, true);
1158
1159 int len = values.length;
1160 Value[] newValues = null;
1161 if (len == 0) {
1162 newValues = JcrMultiValueProperty.EMPTY_VALUES;
1163 } else {
1164 List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
1165 int expectedType = -1;
1166 for (int i = 0; i != len; ++i) {
1167 Value value = values[i];
1168
1169 if (value == null) continue;
1170 if (expectedType == -1) {
1171 expectedType = value.getType();
1172 } else if (value.getType() != expectedType) {
1173
1174 StringBuilder sb = new StringBuilder();
1175 sb.append('[');
1176 for (int j = 0; j != values.length; ++j) {
1177 if (j != 0) sb.append(",");
1178 sb.append(values[j].toString());
1179 }
1180 sb.append(']');
1181 String propType = PropertyType.nameFromValue(expectedType);
1182 I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
1183 String path = readable(node.getPath());
1184 String workspaceName = SessionCache.this.workspaceName();
1185 throw new javax.jcr.ValueFormatException(msg.text(readable(name), values, propType, path, workspaceName));
1186 }
1187 if (value.getType() != valueType && valueType != PropertyType.UNDEFINED) {
1188 value = ((JcrValue)value).asType(valueType);
1189 }
1190 valuesWithDesiredType.add(value);
1191 }
1192 if (valuesWithDesiredType.isEmpty()) {
1193 newValues = JcrMultiValueProperty.EMPTY_VALUES;
1194 } else {
1195 newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
1196 }
1197 }
1198
1199 int numValues = newValues.length;
1200 JcrPropertyDefinition definition = null;
1201
1202
1203 PropertyInfo<JcrPropertyPayload> existing = node.getProperty(name);
1204 if (existing != null) {
1205
1206
1207 definition = nodeTypes().getPropertyDefinition(existing.getPayload().getPropertyDefinitionId());
1208
1209 if (definition != null) {
1210
1211 if (numValues == 0) {
1212
1213 } else {
1214
1215 int type = newValues[0].getType();
1216 if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != type) {
1217
1218
1219 definition = null;
1220 } else {
1221
1222 if (!definition.satisfiesConstraints(newValues)) definition = null;
1223 }
1224 }
1225 }
1226 }
1227 JcrNodePayload payload = node.getPayload();
1228 if (definition == null) {
1229
1230 definition = nodeTypes().findPropertyDefinition(payload.getPrimaryTypeName(),
1231 payload.getMixinTypeNames(),
1232 name,
1233 newValues,
1234 skipProtected);
1235
1236
1237
1238
1239 boolean referencePropMissedConstraints = definition != null
1240 && definition.getRequiredType() == PropertyType.REFERENCE
1241 && !definition.canCastToTypeAndSatisfyConstraints(newValues);
1242 if (definition == null || referencePropMissedConstraints) {
1243 throw new ConstraintViolationException(JcrI18n.noDefinition.text("property",
1244 readable(name),
1245 readable(node.getPath()),
1246 readable(payload.getPrimaryTypeName()),
1247 readable(payload.getMixinTypeNames())));
1248 }
1249 } else {
1250
1251 if (skipProtected && definition.isProtected()) throw new ConstraintViolationException(
1252 JcrI18n.noDefinition.text("property",
1253 readable(name),
1254 readable(node.getPath()),
1255 readable(payload.getPrimaryTypeName()),
1256 readable(payload.getMixinTypeNames())));
1257 }
1258
1259
1260 int type = numValues != 0 ? newValues[0].getType() : definition.getRequiredType();
1261 Object[] objValues = new Object[numValues];
1262 int propertyType = definition.getRequiredType();
1263 if (propertyType == PropertyType.UNDEFINED || propertyType == type) {
1264
1265 propertyType = type;
1266 for (int i = 0; i != numValues; ++i) {
1267 objValues[i] = ((JcrValue)newValues[i]).value();
1268 }
1269 } else {
1270
1271 assert propertyType != type;
1272 org.modeshape.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
1273 ValueFactory<?> factory = factories().getValueFactory(dnaPropertyType);
1274 for (int i = 0; i != numValues; ++i) {
1275 objValues[i] = factory.create(((JcrValue)newValues[i]).value());
1276 }
1277 }
1278 Property dnaProp = propertyFactory.create(name, objValues);
1279
1280 try {
1281
1282 AbstractJcrProperty jcrProp = null;
1283 if (existing != null) {
1284 jcrProp = existing.getPayload().getJcrProperty();
1285 } else {
1286 AbstractJcrNode jcrNode = payload.getJcrNode();
1287 if (definition.isMultiple()) {
1288 jcrProp = new JcrMultiValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
1289 } else {
1290 jcrProp = new JcrSingleValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
1291 }
1292 }
1293 assert jcrProp != null;
1294 JcrPropertyPayload propPayload = new JcrPropertyPayload(definition.getId(), propertyType, jcrProp);
1295 node.setProperty(dnaProp, definition.isMultiple(), propPayload);
1296 return jcrProp;
1297 } catch (ValidationException e) {
1298 throw new ConstraintViolationException(e.getMessage(), e);
1299 } catch (RepositorySourceException e) {
1300 throw new RepositoryException(e.getMessage(), e);
1301 } catch (AccessControlException e) {
1302 throw new AccessDeniedException(e.getMessage(), e);
1303 }
1304 }
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314 public boolean removeProperty( Name name ) throws AccessDeniedException, RepositoryException {
1315 try {
1316 return node.removeProperty(name) != null;
1317 } catch (ValidationException e) {
1318 throw new ConstraintViolationException(e.getMessage(), e);
1319 } catch (RepositorySourceException e) {
1320 throw new RepositoryException(e.getMessage(), e);
1321 } catch (AccessControlException e) {
1322 throw new AccessDeniedException(e.getMessage(), e);
1323 }
1324 }
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336 public void orderChildBefore( Path.Segment childToBeMoved,
1337 Path.Segment before ) throws AccessDeniedException, RepositoryException {
1338 try {
1339 node.orderChildBefore(childToBeMoved, before);
1340 } catch (ValidationException e) {
1341 throw new ConstraintViolationException(e.getMessage(), e);
1342 } catch (RepositorySourceException e) {
1343 throw new RepositoryException(e.getMessage(), e);
1344 } catch (AccessControlException e) {
1345 throw new AccessDeniedException(e.getMessage(), e);
1346 }
1347 }
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361 public Node<JcrNodePayload, JcrPropertyPayload> moveToBeChild( AbstractJcrNode child,
1362 Name newNodeName )
1363 throws ItemNotFoundException, InvalidItemStateException, ConstraintViolationException, RepositoryException {
1364
1365
1366 Node<JcrNodePayload, JcrPropertyPayload> existingChild = findNode(child.nodeId, child.location.getPath());
1367 if (existingChild.equals(node) || node.isAtOrBelow(existingChild)) {
1368 String pathOfChild = readable(existingChild.getPath());
1369 String thisPath = readable(node.getPath());
1370 String msg = JcrI18n.unableToMoveNodeToBeChildOfDecendent.text(pathOfChild, thisPath, workspaceName());
1371 throw new RepositoryException(msg);
1372 }
1373
1374
1375 JcrNodeDefinition defn = findBestNodeDefinition(node, newNodeName, child.getPrimaryTypeName());
1376
1377 try {
1378
1379 existingChild.moveTo(node, newNodeName);
1380
1381 NodeDefinitionId existingChildDefinitionId = existingChild.getPayload().getDefinitionId();
1382 if (!defn.getId().equals(existingChildDefinitionId)) {
1383
1384 NodeEditor newChildEditor = getEditorFor(existingChild);
1385 try {
1386 JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, defn.getId()
1387 .getString());
1388 newChildEditor.setProperty(ModeShapeIntLexicon.NODE_DEFINITON, value);
1389 } catch (ConstraintViolationException e) {
1390
1391
1392
1393
1394 existingChild.setPayload(existingChild.getPayload().with(defn.getId()));
1395
1396
1397 newChildEditor.removeProperty(ModeShapeIntLexicon.NODE_DEFINITON);
1398 }
1399 }
1400
1401 return existingChild;
1402 } catch (ValidationException e) {
1403 throw new ConstraintViolationException(e.getMessage(), e);
1404 } catch (RepositorySourceException e) {
1405 throw new RepositoryException(e.getMessage(), e);
1406 } catch (AccessControlException e) {
1407 throw new AccessDeniedException(e.getMessage(), e);
1408 }
1409 }
1410
1411 public void addMixin( JcrNodeType mixinCandidateType ) throws javax.jcr.ValueFormatException, RepositoryException {
1412 try {
1413 PropertyInfo<JcrPropertyPayload> existingMixinProperty = node.getProperty(JcrLexicon.MIXIN_TYPES);
1414
1415
1416 Value[] existingMixinValues;
1417 if (existingMixinProperty != null) {
1418 existingMixinValues = existingMixinProperty.getPayload().getJcrProperty().getValues();
1419 } else {
1420 existingMixinValues = new Value[0];
1421 }
1422
1423 Value[] newMixinValues = new Value[existingMixinValues.length + 1];
1424 System.arraycopy(existingMixinValues, 0, newMixinValues, 0, existingMixinValues.length);
1425 newMixinValues[newMixinValues.length - 1] = new JcrValue(factories(), SessionCache.this, PropertyType.NAME,
1426 mixinCandidateType.getInternalName());
1427
1428 setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, PropertyType.NAME, false);
1429
1430
1431
1432
1433 autoCreateItemsFor(mixinCandidateType);
1434
1435 if (mixinCandidateType.isNodeType(JcrMixLexicon.REFERENCEABLE)) {
1436
1437 UUID uuid = node.getLocation().getUuid();
1438 if (uuid == null) uuid = (UUID)node.getLocation().getIdProperty(JcrLexicon.UUID).getFirstValue();
1439 if (uuid == null) uuid = UUID.randomUUID();
1440 JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, uuid);
1441 setProperty(JcrLexicon.UUID, value, false);
1442 }
1443 } catch (RepositorySourceException e) {
1444 throw new RepositoryException(e.getMessage(), e);
1445 } catch (AccessControlException e) {
1446 throw new AccessDeniedException(e.getMessage(), e);
1447 }
1448 }
1449
1450 private void autoCreateItemsFor( JcrNodeType nodeType )
1451 throws InvalidItemStateException, ConstraintViolationException, AccessDeniedException, RepositoryException {
1452
1453 for (JcrPropertyDefinition propertyDefinition : nodeType.allPropertyDefinitions()) {
1454 if (propertyDefinition.isAutoCreated() && !propertyDefinition.isProtected()) {
1455 PropertyInfo<JcrPropertyPayload> autoCreatedProp = node.getProperty(propertyDefinition.getInternalName());
1456 if (autoCreatedProp == null) {
1457
1458 if (propertyDefinition.getDefaultValues() != null) {
1459 if (propertyDefinition.isMultiple()) {
1460 setProperty(propertyDefinition.getInternalName(),
1461 propertyDefinition.getDefaultValues(),
1462 propertyDefinition.getRequiredType());
1463 } else {
1464 assert propertyDefinition.getDefaultValues().length == 1;
1465 setProperty(propertyDefinition.getInternalName(),
1466 (JcrValue)propertyDefinition.getDefaultValues()[0]);
1467 }
1468 }
1469
1470 }
1471 }
1472 }
1473
1474 for (JcrNodeDefinition nodeDefinition : nodeType.allChildNodeDefinitions()) {
1475 if (nodeDefinition.isAutoCreated() && !nodeDefinition.isProtected()) {
1476 Name nodeName = nodeDefinition.getInternalName();
1477 if (node.getChildrenCount(nodeName) == 0) {
1478 assert nodeDefinition.getDefaultPrimaryType() != null;
1479 createChild(nodeName, null, ((JcrNodeType)nodeDefinition.getDefaultPrimaryType()).getInternalName());
1480 }
1481 }
1482 }
1483 }
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498 public JcrNode createChild( Name name,
1499 UUID desiredUuid,
1500 Name primaryTypeName )
1501 throws InvalidItemStateException, ConstraintViolationException, AccessDeniedException, RepositoryException {
1502
1503 if (desiredUuid == null) desiredUuid = UUID.randomUUID();
1504 try {
1505
1506
1507 int numSns = node.getChildrenCount(name) + 1;
1508 JcrNodePayload payload = node.getPayload();
1509 JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(payload.getPrimaryTypeName(),
1510 payload.getMixinTypeNames(),
1511 name,
1512 primaryTypeName,
1513 numSns,
1514 true);
1515
1516 if (definition == null) {
1517
1518
1519 definition = nodeTypes().findChildNodeDefinition(payload.getPrimaryTypeName(),
1520 payload.getMixinTypeNames(),
1521 name,
1522 primaryTypeName,
1523 numSns - 1,
1524 true);
1525 if (definition != null) {
1526
1527 Path pathForChild = pathFactory.create(node.getPath(), name, numSns);
1528 String msg = JcrI18n.noSnsDefinitionForNode.text(pathForChild, workspaceName());
1529 throw new ItemExistsException(msg);
1530 }
1531
1532 Path pathForChild = pathFactory.create(node.getPath(), name, numSns);
1533 String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(pathForChild,
1534 workspaceName(),
1535 sourceName());
1536
1537 throw new ConstraintViolationException(msg);
1538 }
1539
1540
1541 JcrNodeType primaryType = null;
1542 if (primaryTypeName != null) {
1543 primaryType = nodeTypes().getNodeType(primaryTypeName);
1544 if (primaryType == null) {
1545 Path pathForChild = pathFactory.create(node.getPath(), name, numSns);
1546 I18n msg = JcrI18n.unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
1547 throw new NoSuchNodeTypeException(msg.text(primaryTypeName, pathForChild, workspaceName()));
1548 }
1549
1550 if (primaryType.isMixin()) {
1551 I18n msg = JcrI18n.cannotUseMixinTypeAsPrimaryType;
1552 throw new ConstraintViolationException(msg.text(primaryType.getName()));
1553 }
1554
1555 if (primaryType.isAbstract()) {
1556 I18n msg = JcrI18n.primaryTypeCannotBeAbstract;
1557 throw new ConstraintViolationException(msg.text(primaryType.getName()));
1558 }
1559
1560 } else {
1561 primaryType = (JcrNodeType)definition.getDefaultPrimaryType();
1562 if (primaryType == null) {
1563
1564 Path pathForChild = pathFactory.create(node.getPath(), name, numSns);
1565 I18n msg = JcrI18n.unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
1566 String nodeTypeName = definition.getDeclaringNodeType().getName();
1567 throw new NoSuchNodeTypeException(msg.text(definition.getName(),
1568 nodeTypeName,
1569 pathForChild,
1570 workspaceName()));
1571 }
1572 }
1573 primaryTypeName = primaryType.getInternalName();
1574
1575
1576
1577
1578
1579
1580 Property primaryTypeProp = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
1581 Property nodeDefinitionProp = propertyFactory.create(ModeShapeIntLexicon.NODE_DEFINITON, definition.getId()
1582 .getString());
1583
1584
1585 Node<JcrNodePayload, JcrPropertyPayload> result = null;
1586 boolean isReferenceable = primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE);
1587 Property uuidProperty = null;
1588 if (desiredUuid != null || isReferenceable) {
1589 if (desiredUuid == null) {
1590 desiredUuid = UUID.randomUUID();
1591 }
1592 uuidProperty = propertyFactory.create(JcrLexicon.UUID, desiredUuid);
1593 }
1594 if (uuidProperty != null) {
1595 result = node.createChild(name, Collections.singleton(uuidProperty), primaryTypeProp, nodeDefinitionProp);
1596 } else {
1597 result = node.createChild(name, primaryTypeProp, nodeDefinitionProp);
1598 }
1599
1600 JcrNode jcrNode = (JcrNode)result.getPayload().getJcrNode();
1601
1602
1603
1604 JcrValue now = jcrNode.valueFrom(Calendar.getInstance());
1605 JcrValue by = jcrNode.valueFrom(session().getUserID());
1606 boolean isCreatedType = primaryType.isNodeType(JcrMixLexicon.CREATED);
1607 boolean isHierarchyNode = primaryType.isNodeType(JcrNtLexicon.HIERARCHY_NODE);
1608 if (isHierarchyNode || isCreatedType) {
1609 NodeEditor editor = jcrNode.editor();
1610 if (isHierarchyNode) {
1611 editor.setProperty(JcrLexicon.CREATED, now, false);
1612 }
1613 if (isCreatedType) {
1614 editor.setProperty(JcrLexicon.CREATED, now, false);
1615 editor.setProperty(JcrLexicon.CREATED_BY, by, false);
1616 }
1617 }
1618
1619
1620 jcrNode.editor().autoCreateItemsFor(primaryType);
1621
1622
1623 return jcrNode;
1624 } catch (ValidationException e) {
1625 throw new ConstraintViolationException(e.getMessage(), e);
1626 } catch (RepositorySourceException e) {
1627 throw new RepositoryException(e.getMessage(), e);
1628 } catch (AccessControlException e) {
1629 throw new AccessDeniedException(e.getMessage(), e);
1630 }
1631 }
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642 public boolean destroyChild( Node<JcrNodePayload, JcrPropertyPayload> child )
1643 throws AccessDeniedException, RepositoryException {
1644 if (!child.getParent().equals(node)) return false;
1645 try {
1646 child.destroy();
1647 } catch (AccessControlException e) {
1648 throw new AccessDeniedException(e.getMessage(), e);
1649 }
1650 return true;
1651 }
1652
1653
1654
1655
1656
1657
1658
1659
1660 public boolean destroy() throws AccessDeniedException, RepositoryException {
1661 try {
1662 node.destroy();
1663 } catch (AccessControlException e) {
1664 throw new AccessDeniedException(e.getMessage(), e);
1665 }
1666 return true;
1667 }
1668 }
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684 protected JcrPropertyDefinition findBestPropertyDefintion( Name primaryTypeNameOfParent,
1685 List<Name> mixinTypeNamesOfParent,
1686 Property dnaProperty,
1687 int propertyType,
1688 boolean isSingle,
1689 boolean skipProtected ) {
1690 JcrPropertyDefinition definition = null;
1691 if (propertyType == PropertyType.UNDEFINED) {
1692 propertyType = PropertyTypeUtil.jcrPropertyTypeFor(dnaProperty);
1693 }
1694
1695
1696 if (isSingle) {
1697
1698 Object value = dnaProperty.getFirstValue();
1699 Value jcrValue = new JcrValue(factories(), SessionCache.this, propertyType, value);
1700 definition = nodeTypes().findPropertyDefinition(primaryTypeNameOfParent,
1701 mixinTypeNamesOfParent,
1702 dnaProperty.getName(),
1703 jcrValue,
1704 true,
1705 skipProtected);
1706 } else {
1707
1708 Value[] jcrValues = new Value[dnaProperty.size()];
1709 int index = 0;
1710 for (Object value : dnaProperty) {
1711 jcrValues[index++] = new JcrValue(factories(), SessionCache.this, propertyType, value);
1712 }
1713 definition = nodeTypes().findPropertyDefinition(primaryTypeNameOfParent,
1714 mixinTypeNamesOfParent,
1715 dnaProperty.getName(),
1716 jcrValues,
1717 skipProtected);
1718 }
1719
1720 if (definition != null) return definition;
1721
1722
1723 return null;
1724 }
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145 protected final void updateSingleMultipleProperty( Node<JcrNodePayload, JcrPropertyPayload> node,
2146 Name singleMultiPropertyName,
2147 boolean add ) {
2148 PropertyInfo<JcrPropertyPayload> existing = node.getProperty(ModeShapeIntLexicon.MULTI_VALUED_PROPERTIES);
2149 Set<Name> singleMultiPropertyNames = null;
2150 if (existing != null) {
2151 singleMultiPropertyNames = new HashSet<Name>();
2152
2153 for (Object value : existing.getProperty()) {
2154 singleMultiPropertyNames.add(nameFactory().create(value));
2155 }
2156 if (add) singleMultiPropertyNames.add(singleMultiPropertyName);
2157 else singleMultiPropertyNames.remove(singleMultiPropertyName);
2158 } else {
2159 if (add) {
2160 singleMultiPropertyNames = Collections.singleton(singleMultiPropertyName);
2161 } else {
2162
2163 return;
2164 }
2165 }
2166
2167 if (singleMultiPropertyNames.isEmpty()) {
2168
2169 assert existing != null;
2170 node.removeProperty(existing.getName());
2171 return;
2172 }
2173 PropertyInfo<JcrPropertyPayload> property = createSingleMultipleProperty(node.getPayload(),
2174 existing,
2175 singleMultiPropertyNames);
2176 node.setProperty(property.getProperty(), property.isMultiValued(), property.getPayload());
2177 }
2178
2179 protected PropertyInfo<JcrPropertyPayload> createSingleMultipleProperty( JcrNodePayload nodePayload,
2180 PropertyInfo<JcrPropertyPayload> existing,
2181 Set<Name> singleMultiPropertyNames ) {
2182
2183 int number = singleMultiPropertyNames.size();
2184
2185 String[] names = new String[number];
2186 JcrValue[] values = new JcrValue[number];
2187 if (number == 1) {
2188 String str = singleMultiPropertyNames.iterator().next().getString(namespaces);
2189 names[0] = str;
2190 values[0] = new JcrValue(factories(), this, PropertyType.STRING, str);
2191 } else {
2192 int index = 0;
2193 for (Name name : singleMultiPropertyNames) {
2194 String str = name.getString(namespaces);
2195 names[index] = str;
2196 values[index] = new JcrValue(factories(), this, PropertyType.STRING, str);
2197 ++index;
2198 }
2199 }
2200 JcrPropertyDefinition definition = nodeTypes().findPropertyDefinition(nodePayload.getPrimaryTypeName(),
2201 nodePayload.getMixinTypeNames(),
2202 ModeShapeIntLexicon.MULTI_VALUED_PROPERTIES,
2203 values,
2204 false);
2205 Property dnaProp = propertyFactory.create(ModeShapeIntLexicon.MULTI_VALUED_PROPERTIES,
2206 singleMultiPropertyNames.iterator());
2207 return createPropertyInfo(nodePayload, dnaProp, definition, PropertyType.STRING, existing);
2208 }
2209
2210 protected final PropertyInfo<JcrPropertyPayload> createPropertyInfo( JcrNodePayload nodePayload,
2211 Property dnaProp,
2212 JcrPropertyDefinition definition,
2213 int propertyType,
2214 PropertyInfo<JcrPropertyPayload> existing ) {
2215
2216 AbstractJcrProperty jcrProp = null;
2217 if (existing != null && existing.getPayload() != null) {
2218 jcrProp = existing.getPayload().getJcrProperty();
2219 } else {
2220 AbstractJcrNode jcrNode = nodePayload.getJcrNode();
2221 if (definition.isMultiple()) {
2222 jcrProp = new JcrMultiValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
2223 } else {
2224 jcrProp = new JcrSingleValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
2225 }
2226 }
2227 assert jcrProp != null;
2228 JcrPropertyPayload propPayload = new JcrPropertyPayload(definition.getId(), propertyType, jcrProp);
2229 Status status = existing != null ? Status.CHANGED : Status.NEW;
2230 return new GraphSession.PropertyInfo<JcrPropertyPayload>(dnaProp, definition.isMultiple(), status, propPayload);
2231 }
2232
2233 @Immutable
2234 final class JcrNodeOperations extends GraphSession.NodeOperations<JcrNodePayload, JcrPropertyPayload> {
2235 private final Logger LOGGER = Logger.getLogger(JcrNodeOperations.class);
2236 private final String user = SessionCache.this.session().getUserID();
2237
2238 private Map<Name, PropertyInfo<JcrPropertyPayload>> buildProperties( org.modeshape.graph.Node persistentNode,
2239 Node<JcrNodePayload, JcrPropertyPayload> node,
2240 JcrNodePayload nodePayload,
2241 boolean referenceable ) {
2242
2243 AbstractJcrNode jcrNode = nodePayload.getJcrNode();
2244 Name primaryTypeName = nodePayload.getPrimaryTypeName();
2245 List<Name> mixinTypeNames = nodePayload.getMixinTypeNames();
2246
2247 Location location = persistentNode.getLocation();
2248 Map<Name, Property> graphProperties = persistentNode.getPropertiesByName();
2249
2250 if (!graphProperties.containsKey(JcrLexicon.PRIMARY_TYPE)) {
2251 Property primaryTypeProperty;
2252
2253 if (location.getPath().isRoot()) {
2254 primaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
2255 } else {
2256 primaryTypeProperty = defaultPrimaryTypeProperty;
2257 }
2258
2259 graphProperties = new HashMap<Name, Property>(graphProperties);
2260 graphProperties.put(primaryTypeProperty.getName(), primaryTypeProperty);
2261 }
2262
2263
2264 Set<Name> multiValuedPropertyNames = EMPTY_NAMES;
2265 Set<Name> newSingleMultiPropertyNames = null;
2266 Property multiValuedPropNamesProp = graphProperties.get(ModeShapeIntLexicon.MULTI_VALUED_PROPERTIES);
2267 if (multiValuedPropNamesProp != null && !multiValuedPropNamesProp.isEmpty()) {
2268 multiValuedPropertyNames = getSingleMultiPropertyNames(multiValuedPropNamesProp, location);
2269 }
2270
2271
2272 Map<Name, GraphSession.PropertyInfo<JcrPropertyPayload>> props = new HashMap<Name, GraphSession.PropertyInfo<JcrPropertyPayload>>();
2273 for (Property dnaProp : graphProperties.values()) {
2274 Name name = dnaProp.getName();
2275
2276
2277
2278
2279
2280 if (ModeShapeLexicon.UUID.equals(name)) continue;
2281
2282
2283 boolean isSingle = dnaProp.isSingle();
2284
2285 if (isSingle && multiValuedPropertyNames.contains(name)) isSingle = false;
2286
2287
2288 int propertyType = PropertyTypeUtil.jcrPropertyTypeFor(dnaProp);
2289 PropertyDefinition propertyDefinition = findBestPropertyDefintion(primaryTypeName,
2290 mixinTypeNames,
2291 dnaProp,
2292 propertyType,
2293 isSingle,
2294 false);
2295
2296
2297 if (propertyDefinition == null && INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
2298
2299 NodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
2300 for (PropertyDefinition anyDefinition : unstructured.getDeclaredPropertyDefinitions()) {
2301 if (anyDefinition.isMultiple()) {
2302 propertyDefinition = anyDefinition;
2303 break;
2304 }
2305 }
2306 }
2307 if (propertyDefinition == null) {
2308
2309 continue;
2310 }
2311
2312
2313 boolean isMultiple = propertyDefinition.isMultiple();
2314 if (!isMultiple && dnaProp.isEmpty()) {
2315
2316 continue;
2317 }
2318
2319
2320 if (isMultiple && isSingle) {
2321 if (newSingleMultiPropertyNames == null) newSingleMultiPropertyNames = new HashSet<Name>();
2322 newSingleMultiPropertyNames.add(name);
2323 }
2324
2325
2326 int definitionType = propertyDefinition.getRequiredType();
2327 if (definitionType != PropertyType.UNDEFINED) {
2328 propertyType = definitionType;
2329 }
2330
2331
2332 JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
2333 AbstractJcrProperty jcrProp = null;
2334 if (isMultiple) {
2335 jcrProp = new JcrMultiValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
2336 } else {
2337 jcrProp = new JcrSingleValueProperty(SessionCache.this, jcrNode, dnaProp.getName());
2338 }
2339 JcrPropertyPayload payload = new JcrPropertyPayload(defn.getId(), propertyType, jcrProp);
2340 PropertyInfo<JcrPropertyPayload> propInfo = new PropertyInfo<JcrPropertyPayload>(dnaProp, defn.isMultiple(),
2341 Status.UNCHANGED, payload);
2342 props.put(name, propInfo);
2343 }
2344
2345
2346 if (referenceable) {
2347 UUID uuid = location.getUuid();
2348 org.modeshape.graph.property.Property uuidProperty = null;
2349 if (uuid != null) {
2350
2351 uuidProperty = location.getIdProperty(JcrLexicon.UUID);
2352 if (uuidProperty == null) {
2353 uuidProperty = propertyFactory.create(JcrLexicon.UUID, uuid);
2354 }
2355 } else {
2356 uuidProperty = location.getIdProperty(JcrLexicon.UUID);
2357
2358 if (uuidProperty != null) {
2359 uuid = factories().getUuidFactory().create(uuidProperty.getFirstValue());
2360 } else {
2361 uuidProperty = graphProperties.get(ModeShapeLexicon.UUID);
2362 uuid = factories().getUuidFactory().create(uuidProperty.getFirstValue());
2363
2364 uuidProperty = null;
2365 }
2366 }
2367
2368 if (uuid != null && uuidProperty == null) uuidProperty = propertyFactory.create(JcrLexicon.UUID, uuid);
2369
2370
2371 JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, uuid);
2372 JcrPropertyDefinition propDefn = nodeTypes().findPropertyDefinition(primaryTypeName,
2373 mixinTypeNames,
2374 JcrLexicon.UUID,
2375 value,
2376 false,
2377 false);
2378 PropertyInfo<JcrPropertyPayload> propInfo = createPropertyInfo(nodePayload,
2379 uuidProperty,
2380 propDefn,
2381 PropertyType.STRING,
2382 null);
2383 props.put(JcrLexicon.UUID, propInfo);
2384 } else {
2385
2386 props.remove(JcrLexicon.UUID);
2387 }
2388
2389 props.remove(ModeShapeLexicon.UUID);
2390
2391
2392 if (newSingleMultiPropertyNames != null) {
2393 PropertyInfo<JcrPropertyPayload> info = createSingleMultipleProperty(nodePayload,
2394 null,
2395 newSingleMultiPropertyNames);
2396 props.put(info.getName(), info);
2397 }
2398
2399 return props;
2400 }
2401
2402
2403
2404
2405
2406
2407
2408 @Override
2409 public void materializeProperties( org.modeshape.graph.Node persistentNode,
2410 Node<JcrNodePayload, JcrPropertyPayload> node ) {
2411
2412 JcrNodePayload nodePayload = node.getPayload();
2413 boolean referenceable = false;
2414
2415 try {
2416 referenceable = isReferenceable(node);
2417 } catch (RepositoryException re) {
2418 throw new IllegalStateException(re);
2419 }
2420
2421 Map<Name, PropertyInfo<JcrPropertyPayload>> props = buildProperties(persistentNode, node, nodePayload, referenceable);
2422
2423 node.loadedWith(props);
2424
2425 }
2426
2427
2428
2429
2430
2431
2432
2433 @Override
2434 public void materialize( org.modeshape.graph.Node persistentNode,
2435 Node<JcrNodePayload, JcrPropertyPayload> node ) {
2436
2437 Location location = node.getLocation();
2438
2439
2440 Map<Name, Property> graphProperties = persistentNode.getPropertiesByName();
2441 final boolean isRoot = location.getPath().isRoot();
2442 Name primaryTypeName = null;
2443 org.modeshape.graph.property.Property primaryTypeProperty = graphProperties.get(JcrLexicon.PRIMARY_TYPE);
2444 if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
2445 try {
2446 primaryTypeName = factories.getNameFactory().create(primaryTypeProperty.getFirstValue());
2447 } catch (ValueFormatException e) {
2448
2449 }
2450 }
2451 if (primaryTypeName == null) {
2452
2453 if (isRoot) {
2454 primaryTypeName = ModeShapeLexicon.ROOT;
2455 primaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
2456 } else {
2457 primaryTypeName = defaultPrimaryTypeName;
2458 primaryTypeProperty = defaultPrimaryTypeProperty;
2459 }
2460 }
2461 assert primaryTypeProperty != null;
2462 assert primaryTypeProperty.isEmpty() == false;
2463
2464
2465 JcrNodeDefinition definition = null;
2466 org.modeshape.graph.property.Property nodeDefnProperty = graphProperties.get(ModeShapeIntLexicon.NODE_DEFINITON);
2467 if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
2468 String nodeDefinitionString = stringFactory.create(nodeDefnProperty.getFirstValue());
2469 NodeDefinitionId id = NodeDefinitionId.fromString(nodeDefinitionString, nameFactory);
2470 definition = nodeTypes().getNodeDefinition(id);
2471 }
2472
2473 if (definition == null) {
2474 if (isRoot) {
2475 try {
2476 definition = nodeTypes().getRootNodeDefinition();
2477 } catch (RepositoryException e) {
2478
2479 throw new ValidationException(e.getMessage(), e);
2480 }
2481 } else {
2482 Name childName = node.getName();
2483 Node<JcrNodePayload, JcrPropertyPayload> parent = node.getParent();
2484 JcrNodePayload parentInfo = parent.getPayload();
2485 int numExistingChildrenWithSameName = parent.getChildrenCount(childName);
2486
2487
2488
2489 --numExistingChildrenWithSameName;
2490 definition = nodeTypes().findChildNodeDefinition(parentInfo.getPrimaryTypeName(),
2491 parentInfo.getMixinTypeNames(),
2492 childName,
2493 primaryTypeName,
2494 numExistingChildrenWithSameName,
2495 false);
2496 }
2497 }
2498 if (definition == null) {
2499 String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(readable(node.getPath()),
2500 workspaceName(),
2501 sourceName());
2502 throw new ValidationException(msg);
2503 }
2504
2505
2506
2507
2508 boolean referenceable = false;
2509
2510
2511 JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
2512 if (primaryType == null) {
2513 Path path = location.getPath();
2514 String msg = JcrI18n.missingNodeTypeForExistingNode.text(readable(primaryTypeName),
2515 readable(path),
2516 workspaceName(),
2517 sourceName());
2518 throw new ValidationException(msg);
2519 }
2520 if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) referenceable = true;
2521
2522
2523 Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
2524 List<Name> mixinTypeNames = null;
2525 if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
2526 for (Object mixinTypeValue : mixinTypesProperty) {
2527 Name mixinTypeName = nameFactory.create(mixinTypeValue);
2528 if (mixinTypeNames == null) mixinTypeNames = new LinkedList<Name>();
2529 mixinTypeNames.add(mixinTypeName);
2530 JcrNodeType mixinType = nodeTypes().getNodeType(mixinTypeName);
2531 if (mixinType == null) continue;
2532 if (!referenceable && mixinType.isNodeType(JcrMixLexicon.REFERENCEABLE)) referenceable = true;
2533 }
2534 }
2535
2536
2537 JcrNodePayload nodePayload = new JcrNodePayload(SessionCache.this, node, primaryTypeName, mixinTypeNames,
2538 definition.getId());
2539
2540 Map<Name, PropertyInfo<JcrPropertyPayload>> props = buildProperties(persistentNode, node, nodePayload, referenceable);
2541
2542
2543 node.loadedWith(persistentNode.getChildren(), props, persistentNode.getExpirationTime());
2544 node.setPayload(nodePayload);
2545 }
2546
2547
2548
2549
2550
2551
2552
2553 @Override
2554 public void postSetProperty( Node<JcrNodePayload, JcrPropertyPayload> node,
2555 Name propertyName,
2556 PropertyInfo<JcrPropertyPayload> oldProperty ) {
2557 super.postSetProperty(node, propertyName, oldProperty);
2558
2559 if (propertyName.equals(ModeShapeIntLexicon.MULTI_VALUED_PROPERTIES)) return;
2560 if (propertyName.equals(JcrLexicon.MIXIN_TYPES)) {
2561
2562 Set<Name> mixinTypeNames = new HashSet<Name>();
2563 NameFactory nameFactory = context().getValueFactories().getNameFactory();
2564 for (Object value : node.getProperty(propertyName).getProperty()) {
2565 mixinTypeNames.add(nameFactory.create(value));
2566 }
2567 node.setPayload(node.getPayload().with(new ArrayList<Name>(mixinTypeNames)));
2568 }
2569
2570
2571
2572 PropertyInfo<JcrPropertyPayload> changedProperty = node.getProperty(propertyName);
2573 if (changedProperty.isMultiValued()) {
2574
2575 if (changedProperty.getProperty().isSingle()) {
2576
2577 updateSingleMultipleProperty(node, propertyName, true);
2578 } else {
2579
2580 updateSingleMultipleProperty(node, propertyName, false);
2581 }
2582 }
2583 }
2584
2585
2586
2587
2588
2589
2590
2591 @Override
2592 public void preSave( org.modeshape.graph.session.GraphSession.Node<JcrNodePayload, JcrPropertyPayload> node,
2593 DateTime saveTime ) throws ValidationException {
2594 JcrNodePayload payload = node.getPayload();
2595
2596 Name primaryTypeName = payload.getPrimaryTypeName();
2597 List<Name> mixinTypeNames = payload.getMixinTypeNames();
2598 Set<JcrNodeDefinition> satisfiedChildNodes = new HashSet<JcrNodeDefinition>();
2599 Set<JcrPropertyDefinition> satisfiedProperties = new HashSet<JcrPropertyDefinition>();
2600
2601
2602 boolean referenceable = false;
2603 try {
2604 referenceable = isReferenceable(node);
2605 } catch (RepositoryException e) {
2606 throw new ValidationException(e.getLocalizedMessage());
2607 }
2608 for (org.modeshape.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> property : node.getProperties()) {
2609 if (property.getName().equals(JcrLexicon.UUID) && !referenceable) continue;
2610 JcrPropertyPayload propPayload = property.getPayload();
2611 JcrPropertyDefinition definition = findBestPropertyDefintion(primaryTypeName,
2612 mixinTypeNames,
2613 property.getProperty(),
2614 propPayload.getPropertyType(),
2615 property.getProperty().isSingle(),
2616 false);
2617 if (definition == null) {
2618 throw new ValidationException(JcrI18n.noDefinition.text("property",
2619 readable(property.getName()),
2620 readable(node.getPath()),
2621 readable(primaryTypeName),
2622 readable(mixinTypeNames)));
2623 }
2624
2625 satisfiedProperties.add(definition);
2626 }
2627
2628 for (org.modeshape.graph.session.GraphSession.Node<JcrNodePayload, JcrPropertyPayload> child : node.getChildren()) {
2629 int snsCount = node.getChildrenCount(child.getName());
2630 JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
2631 mixinTypeNames,
2632 child.getName(),
2633 child.getPayload().getPrimaryTypeName(),
2634 snsCount,
2635 false);
2636 if (definition == null) {
2637 throw new ValidationException(JcrI18n.noDefinition.text("child node",
2638 readable(child.getName()),
2639 readable(node.getPath()),
2640 readable(primaryTypeName),
2641 readable(mixinTypeNames)));
2642 }
2643 satisfiedChildNodes.add(definition);
2644 }
2645
2646 JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
2647 boolean isLastModifiedType = primaryType.isNodeType(JcrMixLexicon.LAST_MODIFIED);
2648 boolean isCreatedType = primaryType.isNodeType(JcrMixLexicon.CREATED);
2649 boolean isETag = primaryType.isNodeType(JcrMixLexicon.ETAG);
2650 for (JcrPropertyDefinition definition : primaryType.getPropertyDefinitions()) {
2651 if (definition.isMandatory() && !definition.isProtected() && !satisfiedProperties.contains(definition)) {
2652 throw new ValidationException(JcrI18n.noDefinition.text("property",
2653 definition.getName(),
2654 readable(node.getPath()),
2655 readable(primaryTypeName),
2656 readable(mixinTypeNames)));
2657 }
2658 }
2659 for (JcrNodeDefinition definition : primaryType.getChildNodeDefinitions()) {
2660 if (definition.isMandatory() && !definition.isProtected() && !satisfiedChildNodes.contains(definition)) {
2661 throw new ValidationException(JcrI18n.noDefinition.text("child node",
2662 definition.getName(),
2663 readable(node.getPath()),
2664 readable(primaryTypeName),
2665 readable(mixinTypeNames)));
2666 }
2667 }
2668
2669 if (mixinTypeNames != null) {
2670 for (Name mixinTypeName : mixinTypeNames) {
2671 JcrNodeType mixinType = nodeTypes().getNodeType(mixinTypeName);
2672 isLastModifiedType = isLastModifiedType || mixinType.isNodeType(JcrMixLexicon.LAST_MODIFIED);
2673 isCreatedType = isCreatedType || mixinType.isNodeType(JcrMixLexicon.CREATED);
2674 isETag = isETag || mixinType.isNodeType(JcrMixLexicon.ETAG);
2675 for (JcrPropertyDefinition definition : mixinType.getPropertyDefinitions()) {
2676 if (definition.isMandatory() && !definition.isProtected() && !satisfiedProperties.contains(definition)) {
2677 throw new ValidationException(JcrI18n.noDefinition.text("child node",
2678 definition.getName(),
2679 readable(node.getPath()),
2680 readable(primaryTypeName),
2681 readable(mixinTypeNames)));
2682 }
2683 }
2684 for (JcrNodeDefinition definition : mixinType.getChildNodeDefinitions()) {
2685 if (definition.isMandatory() && !definition.isProtected() && !satisfiedChildNodes.contains(definition)) {
2686 throw new ValidationException(JcrI18n.noDefinition.text("child node",
2687 definition.getName(),
2688 readable(node.getPath()),
2689 readable(primaryTypeName),
2690 readable(mixinTypeNames)));
2691 }
2692 }
2693
2694 }
2695 }
2696
2697
2698 if (isETag) {
2699
2700
2701
2702 List<Name> binaryPropertyNames = new ArrayList<Name>();
2703 for (org.modeshape.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> property : node.getProperties()) {
2704 if (property.getProperty().size() == 0) continue;
2705 if (property.getPayload().getPropertyType() != PropertyType.BINARY) continue;
2706 binaryPropertyNames.add(property.getName());
2707 }
2708 StringBuilder sb = new StringBuilder();
2709 if (!binaryPropertyNames.isEmpty()) {
2710 Collections.sort(binaryPropertyNames);
2711 BinaryFactory binaryFactory = context().getValueFactories().getBinaryFactory();
2712 for (Name name : binaryPropertyNames) {
2713 org.modeshape.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> property = node.getProperty(name);
2714 for (Object value : property.getProperty()) {
2715 Binary binary = binaryFactory.create(value);
2716 String hash = new String(binary.getHash());
2717
2718 sb.append(hash);
2719 }
2720 }
2721 }
2722 String etagValue = sb.toString();
2723 setProperty(node, primaryTypeName, mixinTypeNames, false, JcrLexicon.ETAG, PropertyType.STRING, etagValue);
2724 }
2725
2726
2727
2728
2729 if (isCreatedType) {
2730 setPropertyIfAbsent(node, primaryTypeName, mixinTypeNames, false, JcrLexicon.CREATED, PropertyType.DATE, saveTime);
2731 setPropertyIfAbsent(node,
2732 primaryTypeName,
2733 mixinTypeNames,
2734 false,
2735 JcrLexicon.CREATED_BY,
2736 PropertyType.STRING,
2737 user);
2738 }
2739
2740
2741 if (isLastModifiedType) {
2742
2743 setPropertyIfAbsent(node,
2744 primaryTypeName,
2745 mixinTypeNames,
2746 false,
2747 JcrLexicon.LAST_MODIFIED,
2748 PropertyType.DATE,
2749 saveTime);
2750 setPropertyIfAbsent(node,
2751 primaryTypeName,
2752 mixinTypeNames,
2753 false,
2754 JcrLexicon.LAST_MODIFIED_BY,
2755 PropertyType.STRING,
2756 user);
2757 }
2758 }
2759
2760 protected void setPropertyIfAbsent( org.modeshape.graph.session.GraphSession.Node<JcrNodePayload, JcrPropertyPayload> node,
2761 Name primaryTypeName,
2762 List<Name> mixinTypeNames,
2763 boolean skipProtected,
2764 Name propertyName,
2765 int propertyType,
2766 Object value ) {
2767 if (node.getProperty(propertyName) != null) return;
2768 setProperty(node, primaryTypeName, mixinTypeNames, skipProtected, propertyName, propertyType, value);
2769 }
2770
2771 protected void setProperty( org.modeshape.graph.session.GraphSession.Node<JcrNodePayload, JcrPropertyPayload> node,
2772 Name primaryTypeName,
2773 List<Name> mixinTypeNames,
2774 boolean skipProtected,
2775 Name propertyName,
2776 int propertyType,
2777 Object value ) {
2778 Property graphProp = propertyFactory.create(propertyName, value);
2779 JcrPropertyDefinition propDefn = findBestPropertyDefintion(primaryTypeName,
2780 mixinTypeNames,
2781 graphProp,
2782 propertyType,
2783 true,
2784 skipProtected);
2785 AbstractJcrNode jcrNode = node.getPayload().getJcrNode();
2786 AbstractJcrProperty jcrProp = new JcrSingleValueProperty(SessionCache.this, jcrNode, propertyName);
2787 JcrPropertyPayload propPayload = new JcrPropertyPayload(propDefn.getId(), propertyType, jcrProp);
2788 node.setProperty(graphProp, false, propPayload);
2789 }
2790
2791 @Override
2792 public void compute( Graph.Batch batch,
2793 Node<JcrNodePayload, JcrPropertyPayload> node ) {
2794 try {
2795 JcrWorkspace workspace = session().workspace();
2796
2797
2798 if (workspace != null) {
2799 workspace.versionManager().initializeVersionHistoryFor(batch, node, null, false);
2800 }
2801 } catch (RepositoryException re) {
2802 throw new IllegalStateException(re);
2803 }
2804 }
2805
2806
2807
2808
2809
2810
2811
2812 @Override
2813 public void postCreateChild( Node<JcrNodePayload, JcrPropertyPayload> parent,
2814 Node<JcrNodePayload, JcrPropertyPayload> child,
2815 Map<Name, PropertyInfo<JcrPropertyPayload>> properties ) throws ValidationException {
2816 super.postCreateChild(parent, child, properties);
2817
2818
2819
2820 PropertyInfo<JcrPropertyPayload> primaryTypeInfo = properties.get(JcrLexicon.PRIMARY_TYPE);
2821 PropertyInfo<JcrPropertyPayload> nodeDefnInfo = properties.get(ModeShapeIntLexicon.NODE_DEFINITON);
2822 Name primaryTypeName = nameFactory().create(primaryTypeInfo.getProperty().getFirstValue());
2823 String nodeDefnIdStr = stringFactory().create(nodeDefnInfo.getProperty().getFirstValue());
2824 NodeDefinitionId nodeDefnId = NodeDefinitionId.fromString(nodeDefnIdStr, nameFactory);
2825
2826
2827 JcrNodePayload nodePayload = new JcrNodePayload(SessionCache.this, child, primaryTypeName, null, nodeDefnId);
2828 child.setPayload(nodePayload);
2829
2830
2831 JcrNodeType ntBase = nodeTypes().getNodeType(JcrNtLexicon.BASE);
2832 assert ntBase != null;
2833 primaryTypeInfo = createPropertyInfo(child.getPayload(),
2834 primaryTypeInfo.getProperty(),
2835 ntBase.allPropertyDefinitions(JcrLexicon.PRIMARY_TYPE).iterator().next(),
2836 PropertyType.NAME,
2837 primaryTypeInfo);
2838 properties.put(primaryTypeInfo.getName(), primaryTypeInfo);
2839 nodeDefnInfo = createPropertyInfo(child.getPayload(),
2840 nodeDefnInfo.getProperty(),
2841 ntBase.allPropertyDefinitions(ModeShapeIntLexicon.NODE_DEFINITON).iterator().next(),
2842 PropertyType.STRING,
2843 nodeDefnInfo);
2844 properties.put(nodeDefnInfo.getName(), nodeDefnInfo);
2845
2846
2847 PropertyInfo<JcrPropertyPayload> uuidInfo = properties.get(JcrLexicon.UUID);
2848 if (uuidInfo != null) {
2849 JcrNodeType mixRef = nodeTypes().getNodeType(JcrMixLexicon.REFERENCEABLE);
2850 assert mixRef != null;
2851 uuidInfo = createPropertyInfo(child.getPayload(),
2852 uuidInfo.getProperty(),
2853 mixRef.allPropertyDefinitions(JcrLexicon.UUID).iterator().next(),
2854 PropertyType.STRING,
2855 uuidInfo);
2856 properties.put(uuidInfo.getName(), uuidInfo);
2857 }
2858 }
2859
2860 protected final Set<Name> getSingleMultiPropertyNames( Property dnaProperty,
2861 Location location ) {
2862 Set<Name> multiValuedPropertyNames = new HashSet<Name>();
2863 for (Object value : dnaProperty) {
2864 try {
2865 multiValuedPropertyNames.add(nameFactory.create(value));
2866 } catch (ValueFormatException e) {
2867 String msg = "{0} value \"{1}\" on {2} in \"{3}\" workspace is not a valid name and is being ignored";
2868 LOGGER.trace(e,
2869 msg,
2870 readable(ModeShapeIntLexicon.MULTI_VALUED_PROPERTIES),
2871 value,
2872 readable(location),
2873 workspaceName());
2874 }
2875 }
2876 return multiValuedPropertyNames;
2877 }
2878 }
2879
2880 @Immutable
2881 final class JcrAuthorizer implements GraphSession.Authorizer {
2882
2883
2884
2885
2886
2887
2888 public void checkPermissions( Path path,
2889 Action action ) throws AccessControlException {
2890 String jcrAction = null;
2891 switch (action) {
2892 case ADD_NODE:
2893 jcrAction = ModeShapePermissions.ADD_NODE;
2894 break;
2895 case READ:
2896 jcrAction = ModeShapePermissions.READ;
2897 break;
2898 case REMOVE:
2899 jcrAction = ModeShapePermissions.REMOVE;
2900 break;
2901 case SET_PROPERTY:
2902 jcrAction = ModeShapePermissions.SET_PROPERTY;
2903 break;
2904 }
2905 session().checkPermission(path, jcrAction);
2906 }
2907 }
2908
2909 @Immutable
2910 final static class JcrPropertyPayload {
2911 private final PropertyDefinitionId propertyDefinitionId;
2912 private final int jcrPropertyType;
2913 private final AbstractJcrProperty jcrProperty;
2914
2915 JcrPropertyPayload( PropertyDefinitionId propertyDefinitionId,
2916 int jcrPropertyType,
2917 AbstractJcrProperty jcrProperty ) {
2918 assert jcrProperty != null;
2919 this.propertyDefinitionId = propertyDefinitionId;
2920 this.jcrPropertyType = jcrPropertyType;
2921 this.jcrProperty = jcrProperty;
2922 }
2923
2924
2925
2926
2927 public AbstractJcrProperty getJcrProperty() {
2928 return jcrProperty;
2929 }
2930
2931
2932
2933
2934 public int getPropertyType() {
2935 return jcrPropertyType;
2936 }
2937
2938
2939
2940
2941 public PropertyDefinitionId getPropertyDefinitionId() {
2942 return propertyDefinitionId;
2943 }
2944
2945 public JcrPropertyPayload with( PropertyDefinitionId propertyDefinitionId ) {
2946 return new JcrPropertyPayload(propertyDefinitionId, jcrPropertyType, jcrProperty);
2947 }
2948
2949 public JcrPropertyPayload with( int jcrPropertyType ) {
2950 return new JcrPropertyPayload(propertyDefinitionId, jcrPropertyType, jcrProperty);
2951 }
2952
2953 public JcrPropertyPayload with( AbstractJcrProperty jcrProperty ) {
2954 return new JcrPropertyPayload(propertyDefinitionId, jcrPropertyType, jcrProperty);
2955 }
2956 }
2957
2958 @Immutable
2959 final static class JcrNodePayload {
2960 private final SessionCache cache;
2961 private final Node<JcrNodePayload, JcrPropertyPayload> owner;
2962 private final Name primaryTypeName;
2963 private final List<Name> mixinTypeNames;
2964 private final NodeDefinitionId nodeDefinitionId;
2965 private SoftReference<AbstractJcrNode> jcrNode;
2966
2967 JcrNodePayload( SessionCache cache,
2968 Node<JcrNodePayload, JcrPropertyPayload> owner,
2969 Name primaryTypeName,
2970 List<Name> mixinTypeNames,
2971 NodeDefinitionId nodeDefinitionId ) {
2972 assert owner != null;
2973 assert cache != null;
2974 this.cache = cache;
2975 this.owner = owner;
2976 this.primaryTypeName = primaryTypeName;
2977 this.mixinTypeNames = mixinTypeNames;
2978 this.nodeDefinitionId = nodeDefinitionId;
2979 this.jcrNode = new SoftReference<AbstractJcrNode>(null);
2980 }
2981
2982 JcrNodePayload( SessionCache cache,
2983 Node<JcrNodePayload, JcrPropertyPayload> owner,
2984 Name primaryTypeName,
2985 List<Name> mixinTypeNames,
2986 NodeDefinitionId nodeDefinitionId,
2987 SoftReference<AbstractJcrNode> jcrNode ) {
2988 assert jcrNode != null;
2989 assert owner != null;
2990 assert cache != null;
2991 this.cache = cache;
2992 this.owner = owner;
2993 this.primaryTypeName = primaryTypeName;
2994 this.mixinTypeNames = mixinTypeNames;
2995 this.nodeDefinitionId = nodeDefinitionId;
2996 this.jcrNode = jcrNode;
2997 }
2998
2999
3000
3001
3002 public Name getPrimaryTypeName() {
3003 return this.primaryTypeName;
3004 }
3005
3006
3007
3008
3009
3010
3011 public List<Name> getMixinTypeNames() {
3012 return this.mixinTypeNames != null ? this.mixinTypeNames : Collections.<Name>emptyList();
3013 }
3014
3015
3016
3017
3018 public NodeDefinitionId getDefinitionId() {
3019 return this.nodeDefinitionId;
3020 }
3021
3022
3023
3024
3025
3026
3027 public AbstractJcrNode getJcrNode() {
3028 AbstractJcrNode node = jcrNode.get();
3029 if (node == null) {
3030 if (owner.isRoot()) {
3031 node = new JcrRootNode(cache, owner.getNodeId(), owner.getLocation());
3032 } else {
3033 node = new JcrNode(cache, owner.getNodeId(), owner.getLocation());
3034 }
3035 jcrNode = new SoftReference<AbstractJcrNode>(node);
3036 }
3037
3038 if (JcrNtLexicon.VERSION.equals(primaryTypeName)) {
3039 return new JcrVersionNode(jcrNode.get());
3040 }
3041 if (JcrNtLexicon.VERSION_HISTORY.equals(primaryTypeName)) {
3042 return new JcrVersionHistoryNode(jcrNode.get());
3043 }
3044
3045 return jcrNode.get();
3046 }
3047
3048 public JcrNodePayload with( Name primaryTypeName ) {
3049 return new JcrNodePayload(cache, owner, primaryTypeName, mixinTypeNames, nodeDefinitionId, jcrNode);
3050 }
3051
3052 public JcrNodePayload with( List<Name> mixinTypeNames ) {
3053 return new JcrNodePayload(cache, owner, primaryTypeName, mixinTypeNames, nodeDefinitionId, jcrNode);
3054 }
3055
3056 public JcrNodePayload with( NodeDefinitionId nodeDefinitionId ) {
3057 return new JcrNodePayload(cache, owner, primaryTypeName, mixinTypeNames, nodeDefinitionId, jcrNode);
3058 }
3059
3060 public JcrNodePayload with( AbstractJcrNode jcrNode ) {
3061 return new JcrNodePayload(cache, owner, primaryTypeName, mixinTypeNames, nodeDefinitionId,
3062 new SoftReference<AbstractJcrNode>(jcrNode));
3063 }
3064 }
3065
3066 }