1 19 20 package org.apache.cayenne.access; 21 22 import java.util.Collection ; 23 import java.util.Collections ; 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 import java.util.TreeMap ; 28 29 import org.apache.cayenne.CayenneRuntimeException; 30 import org.apache.cayenne.DataChannel; 31 import org.apache.cayenne.ObjectContext; 32 import org.apache.cayenne.QueryResponse; 33 import org.apache.cayenne.cache.MapQueryCacheFactory; 34 import org.apache.cayenne.cache.QueryCache; 35 import org.apache.cayenne.cache.QueryCacheFactory; 36 import org.apache.cayenne.event.EventManager; 37 import org.apache.cayenne.graph.CompoundDiff; 38 import org.apache.cayenne.graph.GraphDiff; 39 import org.apache.cayenne.map.AshwoodEntitySorter; 40 import org.apache.cayenne.map.DataMap; 41 import org.apache.cayenne.map.EntityResolver; 42 import org.apache.cayenne.map.EntitySorter; 43 import org.apache.cayenne.query.Query; 44 import org.apache.cayenne.query.QueryChain; 45 import org.apache.cayenne.util.Util; 46 import org.apache.commons.collections.Transformer; 47 import org.apache.commons.lang.builder.ToStringBuilder; 48 49 57 public class DataDomain implements QueryEngine, DataChannel { 58 59 public static final String SHARED_CACHE_ENABLED_PROPERTY = "cayenne.DataDomain.sharedCache"; 60 public static final boolean SHARED_CACHE_ENABLED_DEFAULT = true; 61 62 public static final String VALIDATING_OBJECTS_ON_COMMIT_PROPERTY = "cayenne.DataDomain.validatingObjectsOnCommit"; 63 public static final boolean VALIDATING_OBJECTS_ON_COMMIT_DEFAULT = true; 64 65 public static final String USING_EXTERNAL_TRANSACTIONS_PROPERTY = "cayenne.DataDomain.usingExternalTransactions"; 66 public static final boolean USING_EXTERNAL_TRANSACTIONS_DEFAULT = false; 67 68 73 public static final String DATA_CONTEXT_FACTORY_PROPERTY = "cayenne.DataDomain.dataContextFactory"; 74 75 80 public static final String QUERY_CACHE_FACTORY_PROPERTY = "cayenne.DataDomain.queryCacheFactory"; 81 82 83 protected Map nodes = Collections.synchronizedMap(new TreeMap ()); 84 protected Map nodesByDataMapName = Collections.synchronizedMap(new HashMap ()); 85 86 90 protected Map properties = Collections.synchronizedMap(new TreeMap ()); 91 92 protected EntityResolver entityResolver; 93 protected DataRowStore sharedSnapshotCache; 94 protected TransactionDelegate transactionDelegate; 95 protected DataContextFactory dataContextFactory; 96 protected QueryCacheFactory queryCacheFactory; 97 protected String name; 98 99 protected boolean sharedCacheEnabled; 101 protected boolean validatingObjectsOnCommit; 102 protected boolean usingExternalTransactions; 103 104 107 protected EventManager eventManager; 108 109 112 EntitySorter entitySorter; 113 114 117 protected QueryCache queryCache; 118 119 122 public DataDomain(String name) { 123 DataContextFaults.init(); 124 setName(name); 125 resetProperties(); 126 } 127 128 135 public DataDomain(String name, Map properties) { 136 DataContextFaults.init(); 137 setName(name); 138 initWithProperties(properties); 139 } 140 141 144 EntitySorter getEntitySorter() { 148 149 if (entitySorter == null) { 150 synchronized (this) { 151 if (entitySorter == null) { 152 153 if (nodes.size() == 1) { 156 entitySorter = ((DataNode) nodes.values().iterator().next()) 157 .getEntitySorter(); 158 } 159 else { 160 entitySorter = new AshwoodEntitySorter(getDataMaps()); 161 } 162 } 163 } 164 } 165 166 return entitySorter; 167 } 168 169 175 void setEntitySorter(EntitySorter entitySorter) { 176 this.entitySorter = entitySorter; 177 } 178 179 182 protected void resetProperties() { 183 if (properties != null) { 184 properties.clear(); 185 } 186 187 sharedCacheEnabled = SHARED_CACHE_ENABLED_DEFAULT; 188 validatingObjectsOnCommit = VALIDATING_OBJECTS_ON_COMMIT_DEFAULT; 189 usingExternalTransactions = USING_EXTERNAL_TRANSACTIONS_DEFAULT; 190 dataContextFactory = null; 191 } 192 193 198 public void initWithProperties(Map properties) { 199 Map localMap = new HashMap (); 201 if (properties != null) { 202 localMap.putAll(properties); 203 } 204 205 this.properties = localMap; 206 207 Object sharedCacheEnabled = localMap.get(SHARED_CACHE_ENABLED_PROPERTY); 208 Object validatingObjectsOnCommit = localMap 209 .get(VALIDATING_OBJECTS_ON_COMMIT_PROPERTY); 210 Object usingExternalTransactions = localMap 211 .get(USING_EXTERNAL_TRANSACTIONS_PROPERTY); 212 213 Object dataContextFactory = localMap.get(DATA_CONTEXT_FACTORY_PROPERTY); 214 Object queryCacheFactory = localMap.get(QUERY_CACHE_FACTORY_PROPERTY); 215 216 this.sharedCacheEnabled = (sharedCacheEnabled != null) 218 ? "true".equalsIgnoreCase(sharedCacheEnabled.toString()) 219 : SHARED_CACHE_ENABLED_DEFAULT; 220 this.validatingObjectsOnCommit = (validatingObjectsOnCommit != null) 221 ? "true".equalsIgnoreCase(validatingObjectsOnCommit.toString()) 222 : VALIDATING_OBJECTS_ON_COMMIT_DEFAULT; 223 this.usingExternalTransactions = (usingExternalTransactions != null) 224 ? "true".equalsIgnoreCase(usingExternalTransactions.toString()) 225 : USING_EXTERNAL_TRANSACTIONS_DEFAULT; 226 227 if (dataContextFactory != null 228 && !Util.isEmptyString(dataContextFactory.toString())) { 229 this.dataContextFactory = (DataContextFactory) createInstance( 230 dataContextFactory.toString(), 231 DataContextFactory.class); 232 } 233 else { 234 this.dataContextFactory = null; 235 } 236 237 if (queryCacheFactory != null 238 && !Util.isEmptyString(dataContextFactory.toString())) { 239 queryCacheFactory = (QueryCacheFactory) createInstance(queryCacheFactory 240 .toString(), QueryCacheFactory.class); 241 } 242 else { 243 queryCacheFactory = null; 244 } 245 } 246 247 private Object createInstance(String className, Class implementedInterface) { 248 Class aClass; 249 try { 250 aClass = Class.forName(className, true, Thread 251 .currentThread() 252 .getContextClassLoader()); 253 } 254 catch (Exception e) { 255 throw new CayenneRuntimeException("Error loading '" + className + "'", e); 256 } 257 258 if (!implementedInterface.isAssignableFrom(aClass)) { 259 throw new CayenneRuntimeException("Failed to load '" 260 + className 261 + "' - it is expected to implement " 262 + implementedInterface); 263 } 264 265 try { 266 return aClass.newInstance(); 267 } 268 catch (Exception e) { 269 throw new CayenneRuntimeException("Error instantiating " + className, e); 270 } 271 } 272 273 278 public EventManager getEventManager() { 279 return eventManager; 280 } 281 282 287 public void setEventManager(EventManager eventManager) { 288 this.eventManager = eventManager; 289 290 if (sharedSnapshotCache != null) { 291 sharedSnapshotCache.setEventManager(eventManager); 292 } 293 } 294 295 298 public String getName() { 299 return name; 300 } 301 302 305 public synchronized void setName(String name) { 306 this.name = name; 307 if (sharedSnapshotCache != null) { 308 this.sharedSnapshotCache.setName(name); 309 } 310 } 311 312 318 public boolean isSharedCacheEnabled() { 319 return sharedCacheEnabled; 320 } 321 322 public void setSharedCacheEnabled(boolean sharedCacheEnabled) { 323 this.sharedCacheEnabled = sharedCacheEnabled; 324 } 325 326 332 public boolean isValidatingObjectsOnCommit() { 333 return validatingObjectsOnCommit; 334 } 335 336 342 public void setValidatingObjectsOnCommit(boolean flag) { 343 this.validatingObjectsOnCommit = flag; 344 } 345 346 352 public boolean isUsingExternalTransactions() { 353 return usingExternalTransactions; 354 } 355 356 362 public void setUsingExternalTransactions(boolean flag) { 363 this.usingExternalTransactions = flag; 364 } 365 366 371 public Map getProperties() { 372 return properties; 373 } 374 375 380 public TransactionDelegate getTransactionDelegate() { 381 return transactionDelegate; 382 } 383 384 390 public void setTransactionDelegate(TransactionDelegate transactionDelegate) { 391 this.transactionDelegate = transactionDelegate; 392 } 393 394 398 public synchronized DataRowStore getSharedSnapshotCache() { 399 if (sharedSnapshotCache == null && sharedCacheEnabled) { 400 this.sharedSnapshotCache = new DataRowStore(name, properties, eventManager); 401 } 402 403 return sharedSnapshotCache; 404 } 405 406 413 synchronized DataRowStore nonNullSharedSnapshotCache() { 414 if (sharedSnapshotCache == null) { 415 this.sharedSnapshotCache = new DataRowStore(name, properties, eventManager); 416 } 417 418 return sharedSnapshotCache; 419 } 420 421 425 public synchronized void setSharedSnapshotCache(DataRowStore snapshotCache) { 426 if (this.sharedSnapshotCache != snapshotCache) { 427 if (this.sharedSnapshotCache != null) { 428 this.sharedSnapshotCache.shutdown(); 429 } 430 this.sharedSnapshotCache = snapshotCache; 431 432 if (snapshotCache != null) { 433 snapshotCache.setEventManager(getEventManager()); 434 snapshotCache.setName(getName()); 435 } 436 } 437 } 438 439 public DataContextFactory getDataContextFactory() { 440 return dataContextFactory; 441 } 442 443 public void setDataContextFactory(DataContextFactory dataContextFactory) { 444 this.dataContextFactory = dataContextFactory; 445 } 446 447 448 public void addMap(DataMap map) { 449 getEntityResolver().addDataMap(map); 450 entitySorter = null; 451 } 452 453 454 public DataMap getMap(String mapName) { 455 return getEntityResolver().getDataMap(mapName); 456 } 457 458 462 public synchronized void removeMap(String mapName) { 463 DataMap map = getMap(mapName); 464 if (map == null) { 465 return; 466 } 467 468 Iterator it = nodes.values().iterator(); 470 while (it.hasNext()) { 471 DataNode node = (DataNode) it.next(); 472 node.removeDataMap(mapName); 473 } 474 475 getEntityResolver().removeDataMap(map); 477 entitySorter = null; 478 479 reindexNodes(); 481 } 482 483 487 public synchronized void removeDataNode(String nodeName) { 488 DataNode removed = (DataNode) nodes.remove(nodeName); 489 if (removed != null) { 490 491 removed.setEntityResolver(null); 492 493 Iterator it = nodesByDataMapName.values().iterator(); 494 while (it.hasNext()) { 495 if (it.next() == removed) { 496 it.remove(); 497 } 498 } 499 } 500 } 501 502 505 public Collection getDataMaps() { 506 return getEntityResolver().getDataMaps(); 507 } 508 509 512 public Collection getDataNodes() { 513 return Collections.unmodifiableCollection(nodes.values()); 514 } 515 516 519 public void reset() { 520 synchronized (nodes) { 521 nodes.clear(); 522 nodesByDataMapName.clear(); 523 524 if (entityResolver != null) { 525 entityResolver.clearCache(); 526 entityResolver = null; 527 } 528 } 529 } 530 531 535 public void clearDataMaps() { 536 getEntityResolver().setDataMaps(Collections.EMPTY_LIST); 537 } 538 539 542 public synchronized void addNode(DataNode node) { 543 544 nodes.put(node.getName(), node); 546 node.setEntityResolver(this.getEntityResolver()); 547 548 Iterator nodeMaps = node.getDataMaps().iterator(); 550 while (nodeMaps.hasNext()) { 551 DataMap map = (DataMap) nodeMaps.next(); 552 this.addMap(map); 553 this.nodesByDataMapName.put(map.getName(), node); 554 } 555 556 entitySorter = null; 557 } 558 559 564 public DataContext createDataContext() { 565 return createDataContext(isSharedCacheEnabled()); 566 } 567 568 576 public DataContext createDataContext(boolean useSharedCache) { 577 DataRowStore snapshotCache = (useSharedCache) 580 ? nonNullSharedSnapshotCache() 581 : new DataRowStore(name, properties, eventManager); 582 583 DataContext context; 584 if (null == dataContextFactory) { 585 context = new DataContext((DataChannel) this, new ObjectStore(snapshotCache)); 586 } 587 else { 588 context = dataContextFactory.createDataContext(this, new ObjectStore( 589 snapshotCache)); 590 } 591 context.setValidatingObjectsOnCommit(isValidatingObjectsOnCommit()); 592 return context; 593 } 594 595 606 public Transaction createTransaction() { 607 return (isUsingExternalTransactions()) ? Transaction 608 .externalTransaction(getTransactionDelegate()) : Transaction 609 .internalTransaction(getTransactionDelegate()); 610 } 611 612 615 public DataNode getNode(String nodeName) { 616 return (DataNode) nodes.get(nodeName); 617 } 618 619 622 public synchronized void reindexNodes() { 623 nodesByDataMapName.clear(); 624 625 Iterator nodes = this.getDataNodes().iterator(); 626 while (nodes.hasNext()) { 627 DataNode node = (DataNode) nodes.next(); 628 Iterator nodeMaps = node.getDataMaps().iterator(); 629 while (nodeMaps.hasNext()) { 630 DataMap map = (DataMap) nodeMaps.next(); 631 addMap(map); 632 nodesByDataMapName.put(map.getName(), node); 633 } 634 } 635 } 636 637 642 public DataNode lookupDataNode(DataMap map) { 643 synchronized (nodesByDataMapName) { 644 DataNode node = (DataNode) nodesByDataMapName.get(map.getName()); 645 if (node == null) { 646 reindexNodes(); 647 return (DataNode) nodesByDataMapName.get(map.getName()); 648 } 649 else { 650 return node; 651 } 652 } 653 } 654 655 661 public void setEntityResolver(EntityResolver entityResolver) { 662 this.entityResolver = entityResolver; 663 } 664 665 private synchronized void createEntityResolver() { 667 if (entityResolver == null) { 668 entityResolver = new EntityResolver(); 671 } 672 } 673 674 677 public void shutdown() { 678 if (sharedSnapshotCache != null) { 679 this.sharedSnapshotCache.shutdown(); 680 } 681 682 Collection dataNodes = getDataNodes(); 683 for (Iterator i = dataNodes.iterator(); i.hasNext();) { 684 DataNode node = (DataNode) i.next(); 685 try { 686 node.shutdown(); 687 } 688 catch (Exception ex) { 689 } 690 } 691 } 692 693 696 public void performQueries(final Collection queries, final OperationObserver callback) { 697 698 runInTransaction(new Transformer() { 699 700 public Object transform(Object input) { 701 new DataDomainLegacyQueryAction( 702 DataDomain.this, 703 new QueryChain(queries), 704 callback).execute(); 705 return null; 706 } 707 }); 708 } 709 710 712 717 public QueryResponse onQuery(final ObjectContext context, final Query query) { 718 return new DataDomainQueryAction(context, DataDomain.this, query).execute(); 723 } 724 725 728 public EntityResolver getEntityResolver() { 729 if (entityResolver == null) { 730 createEntityResolver(); 731 } 732 733 return entityResolver; 734 } 735 736 741 public GraphDiff onSync( 742 final ObjectContext originatingContext, 743 final GraphDiff changes, 744 int syncType) { 745 746 switch (syncType) { 747 case DataChannel.ROLLBACK_CASCADE_SYNC: 748 return onSyncRollback(originatingContext); 749 case DataChannel.FLUSH_NOCASCADE_SYNC: 753 case DataChannel.FLUSH_CASCADE_SYNC: 754 return (GraphDiff) runInTransaction(new Transformer() { 755 756 public Object transform(Object input) { 757 return onSyncFlush(originatingContext, changes); 758 } 759 }); 760 default: 761 throw new CayenneRuntimeException("Invalid synchronization type: " 762 + syncType); 763 } 764 } 765 766 GraphDiff onSyncRollback(ObjectContext originatingContext) { 767 769 Transaction transaction = Transaction.getThreadTransaction(); 770 if (transaction != null) { 771 transaction.setRollbackOnly(); 772 } 773 774 return new CompoundDiff(); 775 } 776 777 GraphDiff onSyncFlush(ObjectContext originatingContext, GraphDiff childChanges) { 778 779 if (!(originatingContext instanceof DataContext)) { 780 throw new CayenneRuntimeException( 781 "No support for committing ObjectContexts that are not DataContexts yet. " 782 + "Unsupported context: " 783 + originatingContext); 784 } 785 786 return new DataDomainFlushAction(this).flush( 787 (DataContext) originatingContext, 788 childChanges); 789 } 790 791 796 Object runInTransaction(Transformer operation) { 799 800 if (Transaction.getThreadTransaction() != null) { 802 return operation.transform(null); 803 } 804 805 807 Transaction transaction = createTransaction(); 808 Transaction.bindThreadTransaction(transaction); 809 810 try { 811 Object result = operation.transform(null); 813 transaction.commit(); 814 return result; 815 } 816 catch (Exception ex) { 817 transaction.setRollbackOnly(); 818 819 if (ex instanceof CayenneRuntimeException) { 821 throw (CayenneRuntimeException) ex; 822 } 823 else { 824 throw new CayenneRuntimeException(ex); 825 } 826 } 827 finally { 828 Transaction.bindThreadTransaction(null); 829 if (transaction.getStatus() == Transaction.STATUS_MARKED_ROLLEDBACK) { 830 try { 831 transaction.rollback(); 832 } 833 catch (Exception rollbackEx) { 834 QueryLogger.logQueryError(rollbackEx); 838 } 839 } 840 } 841 } 842 843 public String toString() { 844 return new ToStringBuilder(this).append("name", name).toString(); 845 } 846 847 852 public QueryCacheFactory getQueryCacheFactory() { 853 return queryCacheFactory != null ? queryCacheFactory : new MapQueryCacheFactory(); 854 } 855 856 859 public void setQueryCacheFactory(QueryCacheFactory queryCacheFactory) { 860 this.queryCacheFactory = queryCacheFactory; 861 } 862 863 870 public QueryCache getQueryCache() { 871 872 if (queryCache == null) { 873 synchronized (this) { 874 if (queryCache == null) { 875 queryCache = getQueryCacheFactory().getQueryCache( 876 Collections.EMPTY_MAP); 877 } 878 } 879 } 880 881 return queryCache; 882 } 883 884 887 QueryCache getQueryCacheInternal() { 888 return queryCache; 889 } 890 } 891 | Popular Tags |