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.observe;
25
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.TreeMap;
35 import net.jcip.annotations.Immutable;
36 import net.jcip.annotations.NotThreadSafe;
37 import net.jcip.annotations.ThreadSafe;
38 import org.modeshape.common.util.HashCode;
39 import org.modeshape.graph.Location;
40 import org.modeshape.graph.property.Name;
41 import org.modeshape.graph.property.Path;
42 import org.modeshape.graph.property.Property;
43 import org.modeshape.graph.request.ChangeRequest;
44 import org.modeshape.graph.request.CloneBranchRequest;
45 import org.modeshape.graph.request.CloneWorkspaceRequest;
46 import org.modeshape.graph.request.CopyBranchRequest;
47 import org.modeshape.graph.request.CreateNodeRequest;
48 import org.modeshape.graph.request.CreateWorkspaceRequest;
49 import org.modeshape.graph.request.DeleteBranchRequest;
50 import org.modeshape.graph.request.DeleteChildrenRequest;
51 import org.modeshape.graph.request.DestroyWorkspaceRequest;
52 import org.modeshape.graph.request.LockBranchRequest;
53 import org.modeshape.graph.request.MoveBranchRequest;
54 import org.modeshape.graph.request.RemovePropertyRequest;
55 import org.modeshape.graph.request.RenameNodeRequest;
56 import org.modeshape.graph.request.SetPropertyRequest;
57 import org.modeshape.graph.request.UnlockBranchRequest;
58 import org.modeshape.graph.request.UpdatePropertiesRequest;
59 import org.modeshape.graph.request.UpdateValuesRequest;
60
61
62
63
64
65
66 @ThreadSafe
67 public abstract class NetChangeObserver extends ChangeObserver {
68
69 public enum ChangeType {
70 NODE_ADDED,
71 NODE_REMOVED,
72 PROPERTY_ADDED,
73 PROPERTY_REMOVED,
74 PROPERTY_CHANGED,
75 NODE_LOCKED,
76 NODE_UNLOCKED;
77 }
78
79 protected NetChangeObserver() {
80 }
81
82
83
84
85
86
87 private void deleteLocationDetails( String workspace,
88 Location location,
89 Map<String, Map<Location, NetChangeDetails>> workspaceLocationMap ) {
90 Map<Location, NetChangeDetails> detailsByLocation = workspaceLocationMap.get(workspace);
91 assert (detailsByLocation != null);
92 detailsByLocation.remove(location);
93 }
94
95
96
97
98
99
100
101 private NetChangeDetails findDetailsByLocation( String workspace,
102 Location location,
103 Map<String, Map<Location, NetChangeDetails>> workspaceLocationMap ) {
104 Map<Location, NetChangeDetails> detailsByLocation = workspaceLocationMap.get(workspace);
105 NetChangeDetails details = null;
106
107 if (detailsByLocation == null) {
108 detailsByLocation = new TreeMap<Location, NetChangeDetails>();
109 workspaceLocationMap.put(workspace, detailsByLocation);
110 details = new NetChangeDetails();
111 detailsByLocation.put(location, details);
112 } else {
113 details = detailsByLocation.get(location);
114
115 if (details == null) {
116 details = new NetChangeDetails();
117 detailsByLocation.put(location, details);
118 }
119 }
120
121 return details;
122 }
123
124
125
126
127
128
129 @Override
130 public void notify( Changes changes ) {
131 Map<String, Map<Location, NetChangeDetails>> detailsByLocationByWorkspace = new HashMap<String, Map<Location, NetChangeDetails>>();
132
133 for (ChangeRequest change : changes.getChangeRequests()) {
134 Location location = change.changedLocation();
135 assert (location.getPath() != null);
136
137
138 String workspace = change.changedWorkspace();
139 NetChangeDetails details = findDetailsByLocation(workspace, location, detailsByLocationByWorkspace);
140
141
142 if (change instanceof CreateNodeRequest) {
143 CreateNodeRequest create = (CreateNodeRequest)change;
144 details.addEventType(ChangeType.NODE_ADDED);
145 for (Property property : create) {
146 details.addProperty(property);
147 }
148 } else if (change instanceof UpdatePropertiesRequest) {
149 UpdatePropertiesRequest update = (UpdatePropertiesRequest)change;
150 for (Map.Entry<Name, Property> entry : update.properties().entrySet()) {
151 Name propName = entry.getKey();
152 Property property = entry.getValue();
153
154 if (property != null) {
155 if (update.isNewProperty(propName)) {
156 details.addProperty(property);
157 } else {
158 details.changeProperty(property);
159 }
160 } else {
161 details.removeProperty(propName);
162 }
163 }
164 } else if (change instanceof SetPropertyRequest) {
165 SetPropertyRequest set = (SetPropertyRequest)change;
166
167 if (set.isNewProperty()) {
168 details.addProperty(set.property());
169 } else {
170 details.changeProperty(set.property());
171 }
172 } else if (change instanceof RemovePropertyRequest) {
173 RemovePropertyRequest remove = (RemovePropertyRequest)change;
174 details.removeProperty(remove.propertyName());
175 } else if (change instanceof DeleteBranchRequest) {
176
177 if (details.getEventTypes().contains(ChangeType.NODE_ADDED)) {
178 deleteLocationDetails(workspace, location, detailsByLocationByWorkspace);
179 } else {
180 details.addEventType(ChangeType.NODE_REMOVED);
181 }
182 } else if (change instanceof DeleteChildrenRequest) {
183 DeleteChildrenRequest delete = (DeleteChildrenRequest)change;
184 for (Location deletedChild : delete.getActualChildrenDeleted()) {
185 NetChangeDetails childDetails = findDetailsByLocation(workspace, deletedChild, detailsByLocationByWorkspace);
186
187 if (childDetails.getEventTypes().contains(ChangeType.NODE_ADDED)) {
188 deleteLocationDetails(workspace, deletedChild, detailsByLocationByWorkspace);
189 } else {
190 childDetails.addEventType(ChangeType.NODE_REMOVED);
191 }
192 }
193 } else if (change instanceof LockBranchRequest) {
194 details.setLockAction(LockAction.LOCKED);
195 } else if (change instanceof UnlockBranchRequest) {
196 details.setLockAction(LockAction.UNLOCKED);
197 } else if (change instanceof CopyBranchRequest) {
198 details.addEventType(ChangeType.NODE_ADDED);
199 } else if (change instanceof MoveBranchRequest) {
200
201
202 Location original = ((MoveBranchRequest)change).getActualLocationBefore();
203 NetChangeDetails originalDetails = findDetailsByLocation(workspace, original, detailsByLocationByWorkspace);
204 originalDetails.addEventType(ChangeType.NODE_REMOVED);
205
206
207 details.addEventType(ChangeType.NODE_ADDED);
208 } else if (change instanceof CloneBranchRequest) {
209 CloneBranchRequest cloneRequest = (CloneBranchRequest)change;
210
211
212 for (Location removed : cloneRequest.getRemovedNodes()) {
213 NetChangeDetails removedDetails = findDetailsByLocation(workspace, removed, detailsByLocationByWorkspace);
214 removedDetails.addEventType(ChangeType.NODE_REMOVED);
215 }
216
217
218 details.addEventType(ChangeType.NODE_ADDED);
219 } else if (change instanceof RenameNodeRequest) {
220
221 Location original = ((RenameNodeRequest)change).getActualLocationBefore();
222 NetChangeDetails originalDetails = findDetailsByLocation(workspace, original, detailsByLocationByWorkspace);
223 originalDetails.addEventType(ChangeType.NODE_REMOVED);
224
225
226 details.addEventType(ChangeType.NODE_ADDED);
227 } else if (change instanceof UpdateValuesRequest) {
228 UpdateValuesRequest updateValuesRequest = (UpdateValuesRequest)change;
229
230 if (!updateValuesRequest.addedValues().isEmpty() || !updateValuesRequest.removedValues().isEmpty()) {
231 assert (updateValuesRequest.getActualProperty() != null);
232
233 if (updateValuesRequest.isNewProperty()) {
234 details.addEventType(ChangeType.PROPERTY_ADDED);
235 details.addProperty(updateValuesRequest.getActualProperty());
236 } else {
237 details.addEventType(ChangeType.PROPERTY_CHANGED);
238 details.changeProperty(updateValuesRequest.getActualProperty());
239 }
240 } else if (details.getEventTypes().isEmpty()) {
241
242 deleteLocationDetails(workspace, location, detailsByLocationByWorkspace);
243 }
244 } else if (change instanceof CreateWorkspaceRequest) {
245 details.addEventType(ChangeType.NODE_ADDED);
246 } else if (change instanceof DestroyWorkspaceRequest) {
247 details.addEventType(ChangeType.NODE_REMOVED);
248 } else if (change instanceof CloneWorkspaceRequest) {
249 details.addEventType(ChangeType.NODE_ADDED);
250 }
251 }
252
253
254 List<NetChange> netChanges = new LinkedList<NetChange>();
255 for (Map.Entry<String, Map<Location, NetChangeDetails>> byWorkspaceEntry : detailsByLocationByWorkspace.entrySet()) {
256 String workspaceName = byWorkspaceEntry.getKey();
257
258 for (Map.Entry<Location, NetChangeDetails> entry : byWorkspaceEntry.getValue().entrySet()) {
259 Location location = entry.getKey();
260 NetChangeDetails details = entry.getValue();
261 netChanges.add(new NetChange(workspaceName, location, details.getEventTypes(), details.getAddedProperties(),
262 details.getModifiedProperties(), details.getRemovedProperties()));
263 }
264 }
265
266 notify(new NetChanges(changes, netChanges));
267 }
268
269
270
271
272
273
274 protected abstract void notify( NetChanges netChanges );
275
276
277
278
279
280 @Immutable
281 public static final class NetChanges extends Changes {
282 private static final long serialVersionUID = 1L;
283
284 private final List<NetChange> netChanges;
285
286 public NetChanges( Changes changes,
287 List<NetChange> netChanges ) {
288 super(changes);
289 assert netChanges != null;
290 assert !netChanges.isEmpty();
291 this.netChanges = Collections.unmodifiableList(netChanges);
292 }
293
294
295
296
297
298
299 public List<NetChange> getNetChanges() {
300 return this.netChanges;
301 }
302
303
304
305
306
307
308 @Override
309 public String toString() {
310 if (processId.length() != 0) {
311 return getTimestamp() + " @" + getUserName() + " [" + getSourceName() + "] - " + netChanges.size() + " changes";
312 }
313 return getTimestamp() + " @" + getUserName() + " #" + getProcessId() + " [" + getSourceName() + "] - "
314 + netChanges.size() + " changes";
315 }
316 }
317
318
319
320
321 @Immutable
322 public static final class NetChange {
323
324 private final String workspaceName;
325 private final Location location;
326 private final EnumSet<ChangeType> eventTypes;
327 private final Set<Property> addedProperties;
328 private final Set<Property> modifiedProperties;
329 private final Set<Property> addedOrModifiedProperties;
330 private final Set<Name> removedProperties;
331 private final int hc;
332
333 public NetChange( String workspaceName,
334 Location location,
335 EnumSet<ChangeType> eventTypes,
336 Set<Property> addedProperties,
337 Set<Property> modifiedProperties,
338 Set<Name> removedProperties ) {
339 assert workspaceName != null;
340 assert location != null;
341 this.workspaceName = workspaceName;
342 this.location = location;
343 this.hc = HashCode.compute(this.workspaceName, this.location);
344 this.eventTypes = eventTypes;
345 Set<Property> addedOrModified = null;
346 if (addedProperties == null) {
347 addedProperties = Collections.emptySet();
348 addedOrModified = modifiedProperties;
349 } else {
350 addedOrModified = addedProperties;
351 }
352 if (modifiedProperties == null) {
353 if (addedOrModified == null) addedOrModified = Collections.emptySet();
354 modifiedProperties = Collections.emptySet();
355 } else {
356 if (addedOrModified == null) {
357 addedOrModified = modifiedProperties;
358 } else {
359 addedOrModified = new HashSet<Property>(modifiedProperties);
360 addedOrModified.addAll(addedProperties);
361 }
362 }
363 if (removedProperties == null) removedProperties = Collections.emptySet();
364 this.addedProperties = Collections.unmodifiableSet(addedProperties);
365 this.modifiedProperties = Collections.unmodifiableSet(modifiedProperties);
366 this.removedProperties = Collections.unmodifiableSet(removedProperties);
367 this.addedOrModifiedProperties = Collections.unmodifiableSet(addedOrModified);
368 }
369
370
371
372
373 public Location getLocation() {
374 return this.location;
375 }
376
377
378
379
380 public Path getPath() {
381 return this.location.getPath();
382 }
383
384
385
386
387 public String getRepositoryWorkspaceName() {
388 return this.workspaceName;
389 }
390
391
392
393
394 public Set<Property> getAddedProperties() {
395 return this.addedProperties;
396 }
397
398
399
400
401 public Set<Property> getModifiedProperties() {
402 return this.modifiedProperties;
403 }
404
405
406
407
408
409
410 public Set<Property> getAddedOrModifiedProperties() {
411 return this.addedOrModifiedProperties;
412 }
413
414
415
416
417 public Set<Name> getRemovedProperties() {
418 return this.removedProperties;
419 }
420
421
422
423
424 @Override
425 public int hashCode() {
426 return this.hc;
427 }
428
429
430
431
432
433
434
435 public boolean includesAllOf( ChangeType... jcrEventTypes ) {
436 for (ChangeType jcrEventType : jcrEventTypes) {
437 if (!this.eventTypes.contains(jcrEventType)) return false;
438 }
439 return true;
440 }
441
442
443
444
445
446
447
448 public boolean includes( ChangeType... jcrEventTypes ) {
449 for (ChangeType jcrEventType : jcrEventTypes) {
450 if (this.eventTypes.contains(jcrEventType)) return true;
451 }
452 return false;
453 }
454
455 public boolean isSameNode( NetChange that ) {
456 if (that == this) return true;
457 if (this.hc != that.hc) return false;
458 if (!this.workspaceName.equals(that.workspaceName)) return false;
459 if (!this.location.isSame(that.location)) return false;
460 return true;
461 }
462
463
464
465
466
467
468
469 public boolean isPropertyModified( String property ) {
470 return this.modifiedProperties.contains(property);
471 }
472
473
474
475
476
477
478
479 public boolean isPropertyRemoved( String property ) {
480 return this.removedProperties.contains(property);
481 }
482
483
484
485
486 @Override
487 public boolean equals( Object obj ) {
488 if (obj == this) return true;
489 if (obj instanceof NetChange) {
490 NetChange that = (NetChange)obj;
491 if (!this.isSameNode(that)) return false;
492 if (this.eventTypes != that.eventTypes) return false;
493 return true;
494 }
495 return false;
496 }
497
498
499
500
501 @Override
502 public String toString() {
503 return this.workspaceName + "=>" + this.location;
504 }
505 }
506
507 private enum LockAction {
508 LOCKED,
509 UNLOCKED;
510 }
511
512
513
514
515 @NotThreadSafe
516 private static class NetChangeDetails {
517
518 private final Set<Property> modifiedProperties = new HashSet<Property>();
519 private final Set<Property> addedProperties = new HashSet<Property>();
520 private final Set<Name> removedProperties = new HashSet<Name>();
521 private EnumSet<ChangeType> eventTypes = EnumSet.noneOf(ChangeType.class);
522
523 protected NetChangeDetails() {
524 }
525
526 public void setLockAction( LockAction lockAction ) {
527 switch (lockAction) {
528 case LOCKED:
529
530 eventTypes.add(ChangeType.NODE_LOCKED);
531 eventTypes.remove(ChangeType.NODE_UNLOCKED);
532 break;
533 case UNLOCKED:
534 if (!eventTypes.remove(ChangeType.NODE_LOCKED)) {
535
536 eventTypes.add(ChangeType.NODE_UNLOCKED);
537 }
538 break;
539 }
540 }
541
542 public void addEventType( ChangeType eventType ) {
543 this.eventTypes.add(eventType);
544 }
545
546 public void addProperty( Property property ) {
547 this.addedProperties.add(property);
548 this.eventTypes.add(ChangeType.PROPERTY_ADDED);
549 }
550
551 public void changeProperty( Property property ) {
552
553 if (!this.addedProperties.contains(property)) {
554 this.modifiedProperties.add(property);
555 this.eventTypes.add(ChangeType.PROPERTY_CHANGED);
556 }
557 }
558
559 public void removeProperty( Name propertyName ) {
560
561 boolean handled = false;
562
563 for (Property property : this.addedProperties) {
564 if (property.getName().equals(propertyName)) {
565 handled = true;
566 this.addedProperties.remove(property);
567
568
569 if (this.addedProperties.isEmpty()) {
570 this.eventTypes.remove(ChangeType.PROPERTY_ADDED);
571 }
572
573 break;
574 }
575 }
576
577 if (!handled) {
578
579 for (Property property : this.modifiedProperties) {
580 if (property.getName().equals(propertyName)) {
581 this.modifiedProperties.remove(property);
582
583
584 if (this.modifiedProperties.isEmpty()) {
585 this.eventTypes.remove(ChangeType.PROPERTY_CHANGED);
586 }
587
588 break;
589 }
590 }
591
592
593 this.removedProperties.add(propertyName);
594 this.eventTypes.add(ChangeType.PROPERTY_REMOVED);
595 }
596 }
597
598
599
600
601 public EnumSet<ChangeType> getEventTypes() {
602 return this.eventTypes;
603 }
604
605
606
607
608 public Set<Property> getAddedProperties() {
609 return this.addedProperties;
610 }
611
612
613
614
615 public Set<Property> getModifiedProperties() {
616 return this.modifiedProperties;
617 }
618
619
620
621
622 public Set<Name> getRemovedProperties() {
623 return this.removedProperties;
624 }
625 }
626 }