1 19 20 package org.apache.cayenne.access; 21 22 import java.io.Serializable ; 23 import java.util.ArrayList ; 24 import java.util.Collection ; 25 import java.util.Collections ; 26 import java.util.HashMap ; 27 import java.util.Iterator ; 28 import java.util.List ; 29 import java.util.Map ; 30 31 import org.apache.cayenne.DataObject; 32 import org.apache.cayenne.DataRow; 33 import org.apache.cayenne.ObjectContext; 34 import org.apache.cayenne.ObjectId; 35 import org.apache.cayenne.PersistenceState; 36 import org.apache.cayenne.Persistent; 37 import org.apache.cayenne.access.ObjectDiff.ArcOperation; 38 import org.apache.cayenne.access.event.SnapshotEvent; 39 import org.apache.cayenne.access.event.SnapshotEventListener; 40 import org.apache.cayenne.graph.GraphChangeHandler; 41 import org.apache.cayenne.graph.GraphDiff; 42 import org.apache.cayenne.graph.GraphManager; 43 import org.apache.cayenne.graph.NodeCreateOperation; 44 import org.apache.cayenne.graph.NodeDeleteOperation; 45 import org.apache.cayenne.graph.NodeDiff; 46 import org.apache.cayenne.map.DataMap; 47 import org.apache.cayenne.map.DbEntity; 48 import org.apache.cayenne.map.ObjEntity; 49 import org.apache.cayenne.map.Procedure; 50 import org.apache.cayenne.query.ObjectIdQuery; 51 import org.apache.cayenne.query.PrefetchTreeNode; 52 import org.apache.cayenne.query.QueryMetadata; 53 import org.apache.cayenne.query.RefreshQuery; 54 import org.apache.cayenne.query.SQLResultSetMapping; 55 import org.apache.cayenne.reflect.AttributeProperty; 56 import org.apache.cayenne.reflect.ClassDescriptor; 57 import org.apache.cayenne.reflect.PropertyVisitor; 58 import org.apache.cayenne.reflect.ToManyProperty; 59 import org.apache.cayenne.reflect.ToOneProperty; 60 import org.apache.commons.collections.map.AbstractReferenceMap; 61 import org.apache.commons.collections.map.ReferenceMap; 62 63 72 public class ObjectStore implements Serializable , SnapshotEventListener, GraphManager { 77 78 84 static Map createObjectMap() { 85 return new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); 86 } 87 88 protected Map objectMap; 89 protected Map changes; 90 91 int currentDiffId; 94 95 103 protected transient DataRowStore dataRowCache; 104 105 private boolean dataRowCacheSet; 107 108 111 protected DataContext context; 112 113 public ObjectStore() { 114 this(null); 115 } 116 117 public ObjectStore(DataRowStore dataRowCache) { 118 this(dataRowCache, null); 119 } 120 121 128 public ObjectStore(DataRowStore dataRowCache, Map objectMap) { 129 setDataRowCache(dataRowCache); 130 this.objectMap = objectMap != null ? objectMap : ObjectStore.createObjectMap(); 131 this.changes = new HashMap (); 132 } 133 134 139 synchronized ObjectDiff registerDiff(Object nodeId, NodeDiff diff) { 140 141 if (diff != null) { 142 diff.setDiffId(++currentDiffId); 143 } 144 145 ObjectDiff objectDiff = (ObjectDiff) changes.get(nodeId); 146 147 if (objectDiff == null) { 148 149 Persistent object = (Persistent) objectMap.get(nodeId); 150 if (object.getPersistenceState() == PersistenceState.COMMITTED) { 151 object.setPersistenceState(PersistenceState.MODIFIED); 152 153 if (object instanceof DataObject) { 156 157 DataObject dataObject = (DataObject) object; 158 DataRow snapshot = getCachedSnapshot((ObjectId) nodeId); 159 160 if (snapshot != null 161 && snapshot.getVersion() != dataObject.getSnapshotVersion()) { 162 DataContextDelegate delegate = context.nonNullDelegate(); 163 if (delegate.shouldMergeChanges(dataObject, snapshot)) { 164 ClassDescriptor descriptor = context 165 .getEntityResolver() 166 .getClassDescriptor( 167 ((ObjectId) nodeId).getEntityName()); 168 DataRowUtils.forceMergeWithSnapshot( 169 context, 170 descriptor, 171 dataObject, 172 snapshot); 173 dataObject.setSnapshotVersion(snapshot.getVersion()); 174 delegate.finishedMergeChanges(dataObject); 175 } 176 } 177 } 178 } 179 180 objectDiff = new ObjectDiff(object); 181 objectDiff.setDiffId(++currentDiffId); 182 changes.put(nodeId, objectDiff); 183 } 184 185 if (diff != null) { 186 objectDiff.addDiff(diff); 187 } 188 189 return objectDiff; 190 } 191 192 197 public int registeredObjectsCount() { 198 return objectMap.size(); 199 } 200 201 208 public int cachedQueriesCount() { 209 return context != null && context.queryCache != null 210 ? context.queryCache.size() 211 : 0; 212 } 213 214 217 public DataRowStore getDataRowCache() { 218 219 221 if (dataRowCache == null && context != null && dataRowCacheSet) { 225 synchronized (this) { 226 if (dataRowCache == null) { 227 DataDomain domain = context.getParentDataDomain(); 228 if (domain != null) { 229 setDataRowCache(domain.getSharedSnapshotCache()); 230 } 231 } 232 } 233 } 234 235 return dataRowCache; 236 } 237 238 242 public void setDataRowCache(DataRowStore dataRowCache) { 246 if (dataRowCache == this.dataRowCache) { 247 return; 248 } 249 250 if (this.dataRowCache != null && dataRowCache.getEventManager() != null) { 251 dataRowCache.getEventManager().removeListener( 252 this, 253 this.dataRowCache.getSnapshotEventSubject()); 254 } 255 256 this.dataRowCache = dataRowCache; 257 258 if (dataRowCache != null && dataRowCache.getEventManager() != null) { 259 dataRowCache.getEventManager().addNonBlockingListener( 263 this, 264 "snapshotsChanged", 265 SnapshotEvent.class, 266 dataRowCache.getSnapshotEventSubject(), 267 dataRowCache); 268 } 269 270 dataRowCacheSet = dataRowCache != null; 271 } 272 273 279 public synchronized void objectsInvalidated(Collection objects) { 280 if (context != null) { 281 context.invalidateObjects(objects); 282 } 283 } 284 285 292 public synchronized void objectsUnregistered(Collection objects) { 295 if (objects.isEmpty()) { 296 return; 297 } 298 299 Collection ids = new ArrayList (objects.size()); 300 301 Iterator it = objects.iterator(); 302 while (it.hasNext()) { 303 Persistent object = (Persistent) it.next(); 304 305 ObjectId id = object.getObjectId(); 306 307 objectMap.remove(id); 309 changes.remove(id); 310 ids.add(id); 311 312 object.setObjectContext(null); 313 object.setObjectId(null); 314 object.setPersistenceState(PersistenceState.TRANSIENT); 315 } 316 317 if (getDataRowCache() != null) { 321 getDataRowCache().processSnapshotChanges( 323 this, 324 Collections.EMPTY_MAP, 325 Collections.EMPTY_LIST, 326 ids, 327 Collections.EMPTY_LIST); 328 } 329 } 330 331 336 public synchronized void objectsRolledBack() { 337 Iterator it = getObjectIterator(); 338 339 while (it.hasNext()) { 341 Persistent object = (Persistent) it.next(); 342 int objectState = object.getPersistenceState(); 343 switch (objectState) { 344 case PersistenceState.NEW: 345 it.remove(); 346 347 object.setObjectContext(null); 348 object.setObjectId(null); 349 object.setPersistenceState(PersistenceState.TRANSIENT); 350 break; 351 case PersistenceState.DELETED: 352 case PersistenceState.MODIFIED: 356 object.setPersistenceState(PersistenceState.HOLLOW); 359 break; 360 default: 361 break; 363 } 364 } 365 366 this.changes = new HashMap (); 369 } 370 371 385 public void snapshotsUpdatedForObjects(List objects, List snapshots, boolean refresh) { 388 DataRowStore cache = getDataRowCache(); 389 if (cache != null) { 390 synchronized (this) { 391 cache.snapshotsUpdatedForObjects(objects, snapshots, refresh); 392 } 393 } 394 } 395 396 401 ObjectStoreGraphDiff getChanges() { 402 return new ObjectStoreGraphDiff(this); 403 } 404 405 410 Map getChangesByObjectId() { 411 return changes; 412 } 413 414 417 void postprocessAfterPhantomCommit() { 418 419 Iterator it = changes.keySet().iterator(); 420 while (it.hasNext()) { 421 ObjectId id = (ObjectId) it.next(); 422 423 Persistent object = (Persistent) objectMap.get(id); 424 425 object.setPersistenceState(PersistenceState.COMMITTED); 428 } 429 430 this.changes.clear(); 432 } 433 434 439 void postprocessAfterCommit(GraphDiff parentChanges) { 440 441 Iterator entries = objectMap.entrySet().iterator(); 442 443 while (entries.hasNext()) { 445 Map.Entry entry = (Map.Entry ) entries.next(); 446 447 Persistent object = (Persistent) entry.getValue(); 448 449 switch (object.getPersistenceState()) { 450 case PersistenceState.DELETED: 451 entries.remove(); 452 object.setObjectContext(null); 453 object.setPersistenceState(PersistenceState.TRANSIENT); 454 break; 455 case PersistenceState.NEW: 456 case PersistenceState.MODIFIED: 457 object.setPersistenceState(PersistenceState.COMMITTED); 458 break; 459 } 460 } 461 462 if (!parentChanges.isNoop()) { 464 parentChanges.apply(new GraphChangeHandler() { 465 466 public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) { 467 } 468 469 public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) { 470 } 471 472 public void nodeCreated(Object nodeId) { 473 } 474 475 public void nodeIdChanged(Object nodeId, Object newId) { 476 processIdChange(nodeId, newId); 477 } 478 479 public void nodePropertyChanged( 480 Object nodeId, 481 String property, 482 Object oldValue, 483 Object newValue) { 484 } 485 486 public void nodeRemoved(Object nodeId) { 487 } 488 }); 489 } 490 491 this.changes = new HashMap (); 494 } 495 496 504 public synchronized void startTrackingNewObjects() { 505 } 507 508 516 public synchronized void unregisterNewObjects() { 517 } 519 520 526 public DataRow getCachedSnapshot(ObjectId oid) { 527 528 if (context != null && context.getChannel() != null) { 529 ObjectIdQuery query = new CachedSnapshotQuery(oid); 530 List results = context.getChannel().onQuery(context, query).firstList(); 531 return results.isEmpty() ? null : (DataRow) results.get(0); 532 } 533 else { 534 return null; 535 } 536 } 537 538 546 public synchronized List getCachedQueryResult(String name) { 547 return context != null && context.queryCache != null ? context.queryCache 548 .get(new CacheQueryMetadata(name)) : null; 549 } 550 551 557 public synchronized void cacheQueryResult(String name, List results) { 558 if (context != null) { 559 context.getQueryCache().put(new CacheQueryMetadata(name), results); 560 } 561 } 562 563 571 public synchronized DataRow getSnapshot(ObjectId oid) { 572 573 if (context != null && context.getChannel() != null) { 574 ObjectIdQuery query = new ObjectIdQuery(oid, true, ObjectIdQuery.CACHE); 575 List results = context.getChannel().onQuery(context, query).firstList(); 576 return results.isEmpty() ? null : (DataRow) results.get(0); 577 } 578 else { 579 return null; 580 } 581 } 582 583 586 public synchronized Iterator getObjectIterator() { 587 return objectMap.values().iterator(); 588 } 589 590 597 public synchronized boolean hasChanges() { 598 return !changes.isEmpty(); 599 } 600 601 605 public synchronized List objectsInState(int state) { 606 List filteredObjects = new ArrayList (); 607 608 Iterator it = objectMap.values().iterator(); 609 while (it.hasNext()) { 610 Persistent nextObj = (Persistent) it.next(); 611 if (nextObj.getPersistenceState() == state) { 612 filteredObjects.add(nextObj); 613 } 614 } 615 616 return filteredObjects; 617 } 618 619 630 public void snapshotsChanged(SnapshotEvent event) { 631 if (event.getPostedBy() != this && event.getSource() == this.getDataRowCache()) { 633 processSnapshotEvent(event); 634 } 635 } 636 637 640 synchronized void processSnapshotEvent(SnapshotEvent event) { 641 642 Map modifiedDiffs = event.getModifiedDiffs(); 643 if (modifiedDiffs != null && !modifiedDiffs.isEmpty()) { 644 Iterator oids = modifiedDiffs.entrySet().iterator(); 645 646 while (oids.hasNext()) { 647 Map.Entry entry = (Map.Entry ) oids.next(); 648 processUpdatedSnapshot(entry.getKey(), (DataRow) entry.getValue()); 649 } 650 } 651 652 Collection deletedIDs = event.getDeletedIds(); 653 if (deletedIDs != null && !deletedIDs.isEmpty()) { 654 Iterator it = deletedIDs.iterator(); 655 while (it.hasNext()) { 656 processDeletedID(it.next()); 657 } 658 } 659 660 processInvalidatedIDs(event.getInvalidatedIds()); 661 processIndirectlyModifiedIDs(event.getIndirectlyModifiedIds()); 662 663 GraphDiff diff = new SnapshotEventDecorator(event); 667 668 ObjectContext originatingContext = (event.getPostedBy() instanceof ObjectContext) 669 ? (ObjectContext) event.getPostedBy() 670 : null; 671 context.fireDataChannelChanged(originatingContext, diff); 672 } 673 674 682 public void resolveHollow(Persistent object) { 683 context.prepareForAccess(object, null, false); 684 } 685 686 void processIdChange(Object nodeId, Object newId) { 687 Persistent object = (Persistent) objectMap.remove(nodeId); 688 689 if (object != null) { 690 object.setObjectId((ObjectId) newId); 691 objectMap.put(newId, object); 692 693 Object change = changes.remove(nodeId); 694 if (change != null) { 695 changes.put(newId, change); 696 } 697 } 698 } 699 700 705 void processDeletedID(Object nodeId) { 706 707 Persistent object = (Persistent) objectMap.get(nodeId); 710 711 if (object != null) { 712 713 DataObject dataObject = (object instanceof DataObject) 714 ? (DataObject) object 715 : null; 716 717 DataContextDelegate delegate; 718 719 switch (object.getPersistenceState()) { 720 case PersistenceState.COMMITTED: 721 case PersistenceState.HOLLOW: 722 case PersistenceState.DELETED: 723 724 delegate = context.nonNullDelegate(); 726 727 if (dataObject == null || delegate.shouldProcessDelete(dataObject)) { 728 objectMap.remove(nodeId); 729 changes.remove(nodeId); 730 731 object.setObjectContext(null); 734 735 if (dataObject != null) { 736 delegate.finishedProcessDelete(dataObject); 737 } 738 } 739 740 break; 741 742 case PersistenceState.MODIFIED: 743 744 delegate = context.nonNullDelegate(); 746 if (dataObject != null && delegate.shouldProcessDelete(dataObject)) { 747 object.setPersistenceState(PersistenceState.NEW); 748 changes.remove(nodeId); 749 registerNode(nodeId, object); 750 nodeCreated(nodeId); 751 if (dataObject != null) { 752 delegate.finishedProcessDelete(dataObject); 753 } 754 } 755 756 break; 757 } 758 } 759 } 760 761 764 void processInvalidatedIDs(Collection invalidatedIDs) { 765 if (invalidatedIDs != null && !invalidatedIDs.isEmpty()) { 766 Iterator it = invalidatedIDs.iterator(); 767 while (it.hasNext()) { 768 ObjectId oid = (ObjectId) it.next(); 769 DataObject object = (DataObject) getNode(oid); 770 771 if (object == null) { 772 continue; 773 } 774 775 777 switch (object.getPersistenceState()) { 778 case PersistenceState.COMMITTED: 779 object.setPersistenceState(PersistenceState.HOLLOW); 780 break; 781 case PersistenceState.MODIFIED: 782 DataContext context = (DataContext) object.getObjectContext(); 783 DataRow diff = getSnapshot(oid); 784 DataContextDelegate delegate = context.nonNullDelegate(); 786 if (delegate.shouldMergeChanges(object, diff)) { 787 ClassDescriptor descriptor = context 788 .getEntityResolver() 789 .getClassDescriptor(oid.getEntityName()); 790 DataRowUtils.forceMergeWithSnapshot( 791 context, 792 descriptor, 793 object, 794 diff); 795 delegate.finishedMergeChanges(object); 796 } 797 798 case PersistenceState.HOLLOW: 799 break; 801 802 case PersistenceState.DELETED: 803 break; 805 } 806 } 807 } 808 } 809 810 815 void processIndirectlyModifiedIDs(Collection indirectlyModifiedIDs) { 816 Iterator indirectlyModifiedIt = indirectlyModifiedIDs.iterator(); 817 while (indirectlyModifiedIt.hasNext()) { 818 ObjectId oid = (ObjectId) indirectlyModifiedIt.next(); 819 820 final DataObject object = (DataObject) objectMap.get(oid); 823 824 if (object == null 825 || object.getPersistenceState() != PersistenceState.COMMITTED) { 826 continue; 827 } 828 829 833 DataContextDelegate delegate = context.nonNullDelegate(); 834 835 if (delegate.shouldMergeChanges(object, null)) { 836 837 ClassDescriptor descriptor = context 838 .getEntityResolver() 839 .getClassDescriptor(oid.getEntityName()); 840 descriptor.visitProperties(new PropertyVisitor() { 841 842 public boolean visitToMany(ToManyProperty property) { 843 property.invalidate(object); 844 return true; 845 } 846 847 public boolean visitToOne(ToOneProperty property) { 848 if (property 849 .getRelationship() 850 .isSourceIndependentFromTargetChange()) { 851 property.invalidate(object); 852 } 853 return true; 854 } 855 856 public boolean visitAttribute(AttributeProperty property) { 857 return true; 858 } 859 }); 860 861 delegate.finishedProcessDelete(object); 862 } 863 } 864 } 865 866 871 void processUpdatedSnapshot(Object nodeId, DataRow diff) { 872 873 DataObject object = (DataObject) objectMap.get(nodeId); 876 877 if (object != null) { 879 880 int state = object.getPersistenceState(); 881 if (state != PersistenceState.HOLLOW) { 882 883 if (state == PersistenceState.COMMITTED) { 885 DataContextDelegate delegate = context.nonNullDelegate(); 887 if (delegate.shouldMergeChanges(object, diff)) { 888 ClassDescriptor descriptor = context 889 .getEntityResolver() 890 .getClassDescriptor(((ObjectId) nodeId).getEntityName()); 891 892 DataRow snapshot = getSnapshot(object.getObjectId()); 902 903 DataRowUtils.refreshObjectWithSnapshot( 904 descriptor, 905 object, 906 snapshot, 907 true); 908 delegate.finishedMergeChanges(object); 909 } 910 } 911 else if (state == PersistenceState.DELETED 913 || state == PersistenceState.MODIFIED) { 914 915 DataContextDelegate delegate = context.nonNullDelegate(); 917 if (delegate.shouldMergeChanges(object, diff)) { 918 ClassDescriptor descriptor = context 919 .getEntityResolver() 920 .getClassDescriptor(((ObjectId) nodeId).getEntityName()); 921 DataRowUtils.forceMergeWithSnapshot( 922 context, 923 descriptor, 924 object, 925 diff); 926 delegate.finishedMergeChanges(object); 927 } 928 } 929 } 930 } 931 } 932 933 936 public DataContext getContext() { 937 return context; 938 } 939 940 943 public void setContext(DataContext context) { 944 this.context = context; 945 } 946 947 950 955 public synchronized Object getNode(Object nodeId) { 956 return objectMap.get(nodeId); 957 } 958 959 final Object getNodeNoSync(Object nodeId) { 961 return objectMap.get(nodeId); 962 } 963 964 970 public synchronized Collection registeredNodes() { 971 return new ArrayList (objectMap.values()); 972 } 973 974 977 public synchronized void registerNode(Object nodeId, Object nodeObject) { 978 objectMap.put(nodeId, nodeObject); 979 } 980 981 984 public synchronized Object unregisterNode(Object nodeId) { 985 Object object = getNode(nodeId); 986 if (object != null) { 987 objectsUnregistered(Collections.singleton(object)); 988 } 989 990 return object; 991 } 992 993 998 public void nodeIdChanged(Object nodeId, Object newId) { 999 throw new UnsupportedOperationException ("nodeIdChanged"); 1000 } 1001 1002 1005 public void nodeCreated(Object nodeId) { 1006 registerDiff(nodeId, new NodeCreateOperation(nodeId)); 1007 } 1008 1009 1012 public void nodeRemoved(Object nodeId) { 1013 registerDiff(nodeId, new NodeDeleteOperation(nodeId)); 1014 } 1015 1016 1021 public void nodePropertyChanged( 1022 Object nodeId, 1023 String property, 1024 Object oldValue, 1025 Object newValue) { 1026 1027 registerDiff(nodeId, null); 1028 } 1029 1030 1033 public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) { 1034 registerDiff(nodeId, new ArcOperation( 1035 nodeId, 1036 targetNodeId, 1037 arcId.toString(), 1038 false)); 1039 } 1040 1041 1044 public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) { 1045 registerDiff(nodeId, new ArcOperation( 1046 nodeId, 1047 targetNodeId, 1048 arcId.toString(), 1049 true)); 1050 } 1051 1052 final class CachedSnapshotQuery extends ObjectIdQuery { 1055 1056 CachedSnapshotQuery(ObjectId oid) { 1057 super(oid, true, ObjectIdQuery.CACHE_NOREFRESH); 1058 } 1059 1060 void resetId(ObjectId oid) { 1061 this.objectId = oid; 1062 this.replacementQuery = null; 1063 } 1064 } 1065 1066 class SnapshotEventDecorator implements GraphDiff { 1067 1068 SnapshotEvent event; 1069 1070 SnapshotEventDecorator(SnapshotEvent event) { 1071 this.event = event; 1072 } 1073 1074 SnapshotEvent getEvent() { 1075 return event; 1076 } 1077 1078 public void apply(GraphChangeHandler handler) { 1079 throw new UnsupportedOperationException (); 1080 } 1081 1082 public boolean isNoop() { 1083 throw new UnsupportedOperationException (); 1084 } 1085 1086 public void undo(GraphChangeHandler handler) { 1087 throw new UnsupportedOperationException (); 1088 } 1089 } 1090 1091 final class CacheQueryMetadata implements QueryMetadata { 1092 1093 private String cacheKey; 1094 1095 CacheQueryMetadata(String cacheKey) { 1096 this.cacheKey = cacheKey; 1097 } 1098 1099 public String getCacheKey() { 1100 return cacheKey; 1101 } 1102 1103 public SQLResultSetMapping getResultSetMapping() { 1104 return null; 1105 } 1106 1107 public String [] getCacheGroups() { 1108 return null; 1109 } 1110 1111 public String getCachePolicy() { 1112 return null; 1113 } 1114 1115 public DataMap getDataMap() { 1116 return null; 1117 } 1118 1119 public DbEntity getDbEntity() { 1120 return null; 1121 } 1122 1123 public int getFetchLimit() { 1124 return 0; 1125 } 1126 1127 public int getFetchStartIndex() { 1128 return 0; 1129 } 1130 1131 public ObjEntity getObjEntity() { 1132 return null; 1133 } 1134 1135 public ClassDescriptor getClassDescriptor() { 1136 return null; 1137 } 1138 1139 public int getPageSize() { 1140 return 0; 1141 } 1142 1143 public PrefetchTreeNode getPrefetchTree() { 1144 return null; 1145 } 1146 1147 public Procedure getProcedure() { 1148 return null; 1149 } 1150 1151 public boolean isFetchingDataRows() { 1152 return false; 1153 } 1154 1155 public boolean isRefreshingObjects() { 1156 return false; 1157 } 1158 1159 public boolean isResolvingInherited() { 1160 return false; 1161 } 1162 } 1163} 1164 | Popular Tags |