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.graph.request.processor;
25
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.Iterator;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Queue;
34 import net.jcip.annotations.Immutable;
35 import net.jcip.annotations.NotThreadSafe;
36 import org.modeshape.common.util.CheckArg;
37 import org.modeshape.graph.ExecutionContext;
38 import org.modeshape.graph.GraphI18n;
39 import org.modeshape.graph.Location;
40 import org.modeshape.graph.cache.CachePolicy;
41 import org.modeshape.graph.connector.LockFailedException;
42 import org.modeshape.graph.observe.Changes;
43 import org.modeshape.graph.observe.Observer;
44 import org.modeshape.graph.property.DateTime;
45 import org.modeshape.graph.property.Name;
46 import org.modeshape.graph.property.Path;
47 import org.modeshape.graph.property.Property;
48 import org.modeshape.graph.property.ReferentialIntegrityException;
49 import org.modeshape.graph.request.AccessQueryRequest;
50 import org.modeshape.graph.request.CacheableRequest;
51 import org.modeshape.graph.request.ChangeRequest;
52 import org.modeshape.graph.request.CloneBranchRequest;
53 import org.modeshape.graph.request.CloneWorkspaceRequest;
54 import org.modeshape.graph.request.CollectGarbageRequest;
55 import org.modeshape.graph.request.CompositeRequest;
56 import org.modeshape.graph.request.CopyBranchRequest;
57 import org.modeshape.graph.request.CreateNodeRequest;
58 import org.modeshape.graph.request.CreateWorkspaceRequest;
59 import org.modeshape.graph.request.DeleteBranchRequest;
60 import org.modeshape.graph.request.DeleteChildrenRequest;
61 import org.modeshape.graph.request.DestroyWorkspaceRequest;
62 import org.modeshape.graph.request.FullTextSearchRequest;
63 import org.modeshape.graph.request.GetWorkspacesRequest;
64 import org.modeshape.graph.request.InvalidRequestException;
65 import org.modeshape.graph.request.LockBranchRequest;
66 import org.modeshape.graph.request.MoveBranchRequest;
67 import org.modeshape.graph.request.ReadAllChildrenRequest;
68 import org.modeshape.graph.request.ReadAllPropertiesRequest;
69 import org.modeshape.graph.request.ReadBlockOfChildrenRequest;
70 import org.modeshape.graph.request.ReadBranchRequest;
71 import org.modeshape.graph.request.ReadNextBlockOfChildrenRequest;
72 import org.modeshape.graph.request.ReadNodeRequest;
73 import org.modeshape.graph.request.ReadPropertyRequest;
74 import org.modeshape.graph.request.RemovePropertyRequest;
75 import org.modeshape.graph.request.RenameNodeRequest;
76 import org.modeshape.graph.request.Request;
77 import org.modeshape.graph.request.SetPropertyRequest;
78 import org.modeshape.graph.request.UnlockBranchRequest;
79 import org.modeshape.graph.request.UnsupportedRequestException;
80 import org.modeshape.graph.request.UpdatePropertiesRequest;
81 import org.modeshape.graph.request.UpdateValuesRequest;
82 import org.modeshape.graph.request.VerifyNodeExistsRequest;
83 import org.modeshape.graph.request.VerifyWorkspaceRequest;
84
85
86
87
88
89
90 @NotThreadSafe
91 public abstract class RequestProcessor {
92
93 private final ExecutionContext context;
94 private final String sourceName;
95 private final DateTime nowInUtc;
96 private final CachePolicy defaultCachePolicy;
97 private List<ChangeRequest> changes;
98 private final Observer observer;
99
100 protected RequestProcessor( String sourceName,
101 ExecutionContext context,
102 Observer observer ) {
103 this(sourceName, context, observer, null, null);
104 }
105
106 protected RequestProcessor( String sourceName,
107 ExecutionContext context,
108 Observer observer,
109 DateTime now ) {
110 this(sourceName, context, observer, now, null);
111 }
112
113 protected RequestProcessor( String sourceName,
114 ExecutionContext context,
115 Observer observer,
116 DateTime now,
117 CachePolicy defaultCachePolicy ) {
118 CheckArg.isNotEmpty(sourceName, "sourceName");
119 CheckArg.isNotNull(context, "context");
120 this.context = context;
121 this.sourceName = sourceName;
122 this.nowInUtc = now != null ? now : context.getValueFactories().getDateFactory().createUtc();
123 this.defaultCachePolicy = defaultCachePolicy;
124 this.changes = observer != null ? new LinkedList<ChangeRequest>() : null;
125 this.observer = observer;
126 }
127
128
129
130
131
132
133 protected void recordChange( ChangeRequest request ) {
134 assert request != null;
135 assert !request.isCancelled();
136 assert !request.hasError();
137 if (changes != null) changes.add(request);
138 }
139
140
141
142
143
144
145 public final String getSourceName() {
146 return sourceName;
147 }
148
149
150
151
152
153
154 public final ExecutionContext getExecutionContext() {
155 return this.context;
156 }
157
158
159
160
161
162
163 public final DateTime getNowInUtc() {
164 return this.nowInUtc;
165 }
166
167
168
169
170 protected final CachePolicy getDefaultCachePolicy() {
171 return defaultCachePolicy;
172 }
173
174
175
176
177
178
179 protected void setCacheableInfo( CacheableRequest request ) {
180
181 if (request.getCachePolicy() == null && defaultCachePolicy != null) {
182 request.setCachePolicy(defaultCachePolicy);
183 }
184 request.setTimeLoaded(nowInUtc);
185 }
186
187
188
189
190
191
192
193 protected void setCacheableInfo( CacheableRequest request,
194 CachePolicy cachePolicy ) {
195 if (cachePolicy == null) cachePolicy = defaultCachePolicy;
196 if (cachePolicy != null) {
197 if (request.getCachePolicy() != null) {
198
199 if (request.getCachePolicy().getTimeToLive() > cachePolicy.getTimeToLive()) {
200 request.setCachePolicy(cachePolicy);
201 }
202 } else {
203
204 request.setCachePolicy(cachePolicy);
205 }
206 }
207 request.setTimeLoaded(nowInUtc);
208 }
209
210
211
212
213
214
215
216
217
218
219 public void process( Request request ) {
220 if (request == null) return;
221 try {
222 if (request.isCancelled()) return;
223
224 switch (request.getType()) {
225 case ACCESS_QUERY:
226 process((AccessQueryRequest)request);
227 break;
228 case COMPOSITE:
229 process((CompositeRequest)request);
230 break;
231 case CLONE_BRANCH:
232 process((CloneBranchRequest)request);
233 break;
234 case CLONE_WORKSPACE:
235 process((CloneWorkspaceRequest)request);
236 break;
237 case COLLECT_GARBAGE:
238 process((CollectGarbageRequest)request);
239 break;
240 case COPY_BRANCH:
241 process((CopyBranchRequest)request);
242 break;
243 case CREATE_NODE:
244 process((CreateNodeRequest)request);
245 break;
246 case CREATE_WORKSPACE:
247 process((CreateWorkspaceRequest)request);
248 break;
249 case DELETE_BRANCH:
250 process((DeleteBranchRequest)request);
251 break;
252 case DELETE_CHILDREN:
253 process((DeleteChildrenRequest)request);
254 break;
255 case DESTROY_WORKSPACE:
256 process((DestroyWorkspaceRequest)request);
257 break;
258 case FULL_TEXT_SEARCH:
259 process((FullTextSearchRequest)request);
260 break;
261 case GET_WORKSPACES:
262 process((GetWorkspacesRequest)request);
263 break;
264 case LAST:
265 break;
266 case LOCK_BRANCH:
267 process((LockBranchRequest)request);
268 break;
269 case MOVE_BRANCH:
270 process((MoveBranchRequest)request);
271 break;
272 case READ_ALL_CHILDREN:
273 process((ReadAllChildrenRequest)request);
274 break;
275 case READ_ALL_PROPERTIES:
276 process((ReadAllPropertiesRequest)request);
277 break;
278 case READ_BLOCK_OF_CHILDREN:
279 process((ReadBlockOfChildrenRequest)request);
280 break;
281 case READ_BRANCH:
282 process((ReadBranchRequest)request);
283 break;
284 case READ_NEXT_BLOCK_OF_CHILDREN:
285 process((ReadNextBlockOfChildrenRequest)request);
286 break;
287 case READ_NODE:
288 process((ReadNodeRequest)request);
289 break;
290 case READ_PROPERTY:
291 process((ReadPropertyRequest)request);
292 break;
293 case REMOVE_PROPERTY:
294 process((RemovePropertyRequest)request);
295 break;
296 case RENAME_NODE:
297 process((RenameNodeRequest)request);
298 break;
299 case SET_PROPERTY:
300 process((SetPropertyRequest)request);
301 break;
302 case UNLOCK_BRANCH:
303 process((UnlockBranchRequest)request);
304 break;
305 case UPDATE_PROPERTIES:
306 process((UpdatePropertiesRequest)request);
307 break;
308 case UPDATE_VALUES:
309 process((UpdateValuesRequest)request);
310 break;
311 case VERIFY_NODE_EXISTS:
312 process((VerifyNodeExistsRequest)request);
313 break;
314 case VERIFY_WORKSPACE:
315 process((VerifyWorkspaceRequest)request);
316 break;
317 default:
318 processUnknownRequest(request);
319 }
320 } catch (RuntimeException e) {
321 request.setError(e);
322 } finally {
323 completeRequest(request);
324 }
325 }
326
327 protected void completeRequest( Request request ) {
328 request.freeze();
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343 public void process( CompositeRequest request ) {
344 if (request == null) return;
345 boolean hasErrors = false;
346 boolean readonly = request.isReadOnly();
347
348 Iterator<Request> iter = request.iterator();
349 while (iter.hasNext()) {
350 Request embedded = iter.next();
351 assert embedded != null;
352 if (embedded.isCancelled()) return;
353 try {
354 process(embedded);
355 } catch (RuntimeException e) {
356 embedded.setError(e);
357 }
358 if (!hasErrors && embedded.hasError()) {
359 hasErrors = true;
360 if (!readonly && !embedded.isReadOnly()) {
361
362
363
364 assert embedded.getError() != null;
365 request.setError(embedded.getError());
366
367 while (iter.hasNext()) {
368 embedded = iter.next();
369
370 embedded.cancel();
371 embedded.freeze();
372 }
373 return;
374 }
375 }
376 }
377 if (hasErrors) {
378 request.checkForErrors();
379 }
380 }
381
382
383
384
385
386
387
388
389 protected void processUnknownRequest( Request request ) {
390 request.setError(new InvalidRequestException(GraphI18n.unsupportedRequestType.text(request.getClass().getName(), request)));
391 }
392
393
394
395
396
397
398
399
400
401 public abstract void process( VerifyWorkspaceRequest request );
402
403
404
405
406
407
408
409
410
411 public abstract void process( GetWorkspacesRequest request );
412
413
414
415
416
417
418
419
420
421 public abstract void process( CreateWorkspaceRequest request );
422
423
424
425
426
427
428
429
430
431 public abstract void process( CloneBranchRequest request );
432
433
434
435
436
437
438
439
440
441 public abstract void process( CloneWorkspaceRequest request );
442
443
444
445
446
447
448
449
450
451 public abstract void process( DestroyWorkspaceRequest request );
452
453
454
455
456
457
458
459
460
461 public abstract void process( CopyBranchRequest request );
462
463
464
465
466
467
468
469
470
471 public abstract void process( CreateNodeRequest request );
472
473
474
475
476
477
478
479
480
481
482
483 public abstract void process( DeleteBranchRequest request );
484
485
486
487
488
489
490
491
492
493
494
495 public void process( DeleteChildrenRequest request ) {
496 if (request == null) return;
497 if (request.isCancelled()) return;
498
499 ReadAllChildrenRequest readChildren = new ReadAllChildrenRequest(request.at(), request.inWorkspace());
500 process(readChildren);
501 if (readChildren.hasError()) {
502 request.setError(readChildren.getError());
503 return;
504 }
505 if (readChildren.isCancelled()) return;
506
507
508 for (Location child : readChildren) {
509 if (request.isCancelled()) return;
510 DeleteBranchRequest deleteChild = new DeleteBranchRequest(child, request.inWorkspace());
511 process(deleteChild);
512 request.addDeletedChild(child);
513 }
514
515
516 request.setActualLocationOfNode(readChildren.getActualLocationOfNode());
517 }
518
519
520
521
522
523
524
525
526
527 public abstract void process( MoveBranchRequest request );
528
529
530
531
532
533
534
535
536
537 public abstract void process( ReadAllChildrenRequest request );
538
539
540
541
542
543
544
545
546
547
548
549
550
551 public void process( ReadBlockOfChildrenRequest request ) {
552 if (request == null) return;
553
554 ReadAllChildrenRequest readAll = new ReadAllChildrenRequest(request.of(), request.inWorkspace());
555 process(readAll);
556 if (readAll.hasError()) {
557 request.setError(readAll.getError());
558 return;
559 }
560 List<Location> allChildren = readAll.getChildren();
561
562
563 if (allChildren.size() < request.startingAtIndex()) return;
564
565
566 int endIndex = Math.min(request.endingBefore(), allChildren.size());
567 for (int i = request.startingAtIndex(); i != endIndex; ++i) {
568 request.addChild(allChildren.get(i));
569 }
570
571 request.setActualLocationOfNode(readAll.getActualLocationOfNode());
572 setCacheableInfo(request);
573 }
574
575
576
577
578
579
580
581
582
583
584
585 public void process( ReadNextBlockOfChildrenRequest request ) {
586 if (request == null) return;
587
588
589 Location actualSiblingLocation = request.startingAfter();
590 Path path = actualSiblingLocation.getPath();
591 Path parentPath = null;
592 if (path != null) parentPath = path.getParent();
593 if (parentPath == null) {
594
595 VerifyNodeExistsRequest verifySibling = new VerifyNodeExistsRequest(request.startingAfter(), request.inWorkspace());
596 process(verifySibling);
597 actualSiblingLocation = verifySibling.getActualLocationOfNode();
598 parentPath = actualSiblingLocation.getPath().getParent();
599 }
600 assert parentPath != null;
601
602
603 ReadAllChildrenRequest readAll = new ReadAllChildrenRequest(Location.create(parentPath), request.inWorkspace());
604 process(readAll);
605 if (readAll.hasError()) {
606 request.setError(readAll.getError());
607 return;
608 }
609 List<Location> allChildren = readAll.getChildren();
610
611
612 boolean found = false;
613 int count = 0;
614 for (Location child : allChildren) {
615 if (count > request.count()) break;
616 if (!found) {
617
618 found = child.isSame(request.startingAfter());
619 } else {
620
621 ++count;
622 request.addChild(child);
623 }
624 }
625
626
627 request.setActualLocationOfStartingAfterNode(actualSiblingLocation);
628 setCacheableInfo(request);
629 }
630
631
632
633
634
635
636
637
638
639
640
641 public void process( ReadBranchRequest request ) {
642 if (request == null) return;
643
644 Queue<LocationWithDepth> locationsToRead = new LinkedList<LocationWithDepth>();
645 locationsToRead.add(new LocationWithDepth(request.at(), 1));
646 int maxDepthPerRead = Math.min(request.maximumDepth(), absoluteMaximumDepthForBranchReads());
647
648
649 boolean first = true;
650 while (locationsToRead.peek() != null) {
651 if (request.isCancelled()) return;
652 LocationWithDepth read = locationsToRead.poll();
653
654
655 if (read.depth > maxDepthPerRead) break;
656
657
658 ReadNodeRequest readNode = new ReadNodeRequest(read.location, request.inWorkspace());
659 process(readNode);
660 if (readNode.hasError()) {
661 request.setError(readNode.getError());
662 return;
663 }
664
665 Location actualLocation = readNode.getActualLocationOfNode();
666 if (first) {
667
668 request.setActualLocationOfNode(actualLocation);
669 }
670
671
672 request.setChildren(actualLocation, readNode.getChildren());
673 request.setProperties(actualLocation, readNode.getProperties());
674
675 if (includeChildrenInSubgraph(actualLocation, readNode.getPropertiesByName(), first)) {
676
677
678 for (Location child : readNode.getChildren()) {
679 locationsToRead.add(new LocationWithDepth(child, read.depth + 1));
680 }
681 }
682
683 if (first) first = false;
684 }
685 setCacheableInfo(request);
686 }
687
688
689
690
691
692
693
694
695
696 protected int absoluteMaximumDepthForBranchReads() {
697 return Integer.MAX_VALUE;
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711 protected boolean includeChildrenInSubgraph( Location location,
712 Map<Name, Property> properties,
713 boolean topOfSubgraph ) {
714 return true;
715 }
716
717
718
719
720
721
722
723
724
725 public abstract void process( ReadAllPropertiesRequest request );
726
727
728
729
730
731
732
733
734
735
736 public void process( ReadNodeRequest request ) {
737 if (request == null) return;
738
739 ReadAllPropertiesRequest readProperties = new ReadAllPropertiesRequest(request.at(), request.inWorkspace());
740 process(readProperties);
741 if (readProperties.hasError()) {
742 request.setError(readProperties.getError());
743 return;
744 }
745
746 request.setActualLocationOfNode(readProperties.getActualLocationOfNode());
747
748
749 ReadAllChildrenRequest readChildren = new ReadAllChildrenRequest(request.at(), request.inWorkspace());
750 process(readChildren);
751 if (readChildren.hasError()) {
752 request.setError(readChildren.getError());
753 return;
754 }
755 if (request.isCancelled()) return;
756
757 for (Property property : readProperties) {
758 request.addProperty(property);
759 }
760 for (Location child : readChildren) {
761 request.addChild(child);
762 }
763 setCacheableInfo(request);
764 }
765
766
767
768
769
770
771
772
773
774
775 public void process( ReadPropertyRequest request ) {
776 if (request == null) return;
777 ReadAllPropertiesRequest readNode = new ReadAllPropertiesRequest(request.on(), request.inWorkspace());
778 process(readNode);
779 if (readNode.hasError()) {
780 request.setError(readNode.getError());
781 return;
782 }
783 Property property = readNode.getPropertiesByName().get(request.named());
784 request.setProperty(property);
785
786 request.setActualLocationOfNode(readNode.getActualLocationOfNode());
787 setCacheableInfo(request);
788 }
789
790
791
792
793
794
795
796
797
798
799 public void process( VerifyNodeExistsRequest request ) {
800 if (request == null) return;
801 ReadAllPropertiesRequest readNode = new ReadAllPropertiesRequest(request.at(), request.inWorkspace());
802 process(readNode);
803 if (readNode.hasError()) {
804 request.setError(readNode.getError());
805 return;
806 }
807
808 request.setActualLocationOfNode(readNode.getActualLocationOfNode());
809 setCacheableInfo(request);
810 }
811
812
813
814
815
816
817
818
819
820
821 public void process( RemovePropertyRequest request ) {
822 if (request == null) return;
823 Map<Name, Property> properties = Collections.singletonMap(request.propertyName(), null);
824 UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.from(), request.inWorkspace(), properties);
825 process(update);
826 if (update.hasError()) {
827 request.setError(update.getError());
828 }
829
830 request.setActualLocationOfNode(update.getActualLocationOfNode());
831 }
832
833
834
835
836
837
838
839
840
841
842 public void process( SetPropertyRequest request ) {
843 if (request == null) return;
844 Property property = request.property();
845 Map<Name, Property> properties = Collections.singletonMap(property.getName(), property);
846 UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.on(), request.inWorkspace(), properties);
847 process(update);
848 if (update.hasError()) {
849 request.setError(update.getError());
850 } else {
851
852 request.setActualLocationOfNode(update.getActualLocationOfNode());
853 request.setNewProperty(update.isNewProperty(property.getName()));
854 }
855 }
856
857
858
859
860
861
862
863
864
865 public abstract void process( UpdatePropertiesRequest request );
866
867
868
869
870
871
872
873
874
875 public void process( UpdateValuesRequest request ) {
876 String workspaceName = request.inWorkspace();
877 Location on = request.on();
878 Name propertyName = request.property();
879
880
881 ReadPropertyRequest readProperty = new ReadPropertyRequest(on, workspaceName, propertyName);
882 process(readProperty);
883
884 if (readProperty.hasError()) {
885 request.setError(readProperty.getError());
886 return;
887 }
888
889 Property property = readProperty.getProperty();
890 List<Object> actualRemovedValues = new ArrayList<Object>(request.removedValues().size());
891 List<Object> newValues = property == null ? new LinkedList<Object>() : new LinkedList<Object>(
892 Arrays.asList(property.getValuesAsArray()));
893
894 for (Object removedValue : request.removedValues()) {
895 for (Iterator<Object> iter = newValues.iterator(); iter.hasNext();) {
896 if (iter.next().equals(removedValue)) {
897 iter.remove();
898 actualRemovedValues.add(removedValue);
899 break;
900 }
901 }
902 }
903
904 newValues.addAll(request.addedValues());
905 Property newProperty = getExecutionContext().getPropertyFactory().create(propertyName, newValues);
906
907
908 SetPropertyRequest setProperty = new SetPropertyRequest(on, workspaceName, newProperty);
909 process(setProperty);
910
911 if (setProperty.hasError()) {
912 request.setError(setProperty.getError());
913 } else {
914
915 request.setActualLocation(setProperty.getActualLocationOfNode(), request.addedValues(), actualRemovedValues);
916 request.setActualProperty(newProperty, setProperty.isNewProperty());
917 }
918
919 }
920
921
922
923
924
925
926
927
928
929
930
931
932 public void process( RenameNodeRequest request ) {
933 if (request == null) return;
934 Location from = request.at();
935 if (!from.hasPath()) {
936 throw new UnsupportedOperationException();
937 }
938 Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(from.getPath(), request.toName());
939 Location to = Location.create(newPath);
940 MoveBranchRequest move = new MoveBranchRequest(from, to, request.inWorkspace());
941 process(move);
942
943 request.setActualLocations(move.getActualLocationBefore(), move.getActualLocationAfter());
944 }
945
946
947
948
949
950
951
952
953
954
955
956
957
958 public void process( LockBranchRequest request ) {
959 Location actualLocation = request.at();
960 if (!actualLocation.hasPath()) {
961 VerifyNodeExistsRequest nodeExists = new VerifyNodeExistsRequest(request.at(), request.inWorkspace());
962
963 process(nodeExists);
964
965 if (nodeExists.hasError()) {
966 request.setError(nodeExists.getError());
967 return;
968 }
969
970 actualLocation = nodeExists.getActualLocationOfNode();
971 }
972
973 request.setActualLocation(actualLocation);
974 }
975
976
977
978
979
980
981
982
983
984
985 public void process( UnlockBranchRequest request ) {
986 Location actualLocation = request.at();
987 if (!actualLocation.hasPath()) {
988 VerifyNodeExistsRequest nodeExists = new VerifyNodeExistsRequest(request.at(), request.inWorkspace());
989
990 process(nodeExists);
991
992 if (nodeExists.hasError()) {
993 request.setError(nodeExists.getError());
994 return;
995 }
996
997 actualLocation = nodeExists.getActualLocationOfNode();
998 }
999
1000 request.setActualLocation(actualLocation);
1001 }
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013 public void process( AccessQueryRequest request ) {
1014 processUnknownRequest(request);
1015 }
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026 public void process( FullTextSearchRequest request ) {
1027 processUnknownRequest(request);
1028 }
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038 public void process( CollectGarbageRequest request ) {
1039
1040 }
1041
1042
1043
1044
1045 public void close() {
1046
1047 }
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062 public List<ChangeRequest> getChanges() {
1063 return changes;
1064 }
1065
1066
1067
1068
1069
1070 public void notifyObserverOfChanges() {
1071 if (observer == null) {
1072 if (changes != null) changes.clear();
1073 return;
1074 }
1075 if (this.changes.isEmpty()) return;
1076
1077 String userName = context.getSecurityContext() != null ? context.getSecurityContext().getUserName() : null;
1078 if (userName == null) userName = "";
1079 String contextId = context.getId();
1080 String processId = context.getProcessId();
1081 Map<String, String> userData = context.getData();
1082 Changes changes = new Changes(processId, contextId, userName, getSourceName(), getNowInUtc(), this.changes, userData);
1083 observer.notify(changes);
1084
1085 this.changes = null;
1086 }
1087
1088
1089
1090
1091
1092
1093 @Immutable
1094 protected class LocationWithDepth {
1095 protected final Location location;
1096 protected final int depth;
1097
1098 public LocationWithDepth( Location location,
1099 int depth ) {
1100 this.location = location;
1101 this.depth = depth;
1102 }
1103
1104 public Location getLocation() {
1105 return location;
1106 }
1107
1108 public int getDepth() {
1109 return depth;
1110 }
1111
1112 @Override
1113 public int hashCode() {
1114 return location.hashCode();
1115 }
1116
1117 @Override
1118 public String toString() {
1119 return location.toString() + " at depth " + depth;
1120 }
1121 }
1122
1123 }