KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > DataDomain


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.access;
21
22 import java.util.Collection JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.TreeMap JavaDoc;
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 /**
50  * DataDomain performs query routing functions in Cayenne. DataDomain creates single data
51  * source abstraction hiding multiple physical data sources from the user. When a child
52  * DataContext sends a query to the DataDomain, it is transparently routed to an
53  * appropriate DataNode.
54  *
55  * @author Andrus Adamchik
56  */

57 public class DataDomain implements QueryEngine, DataChannel {
58
59     public static final String JavaDoc SHARED_CACHE_ENABLED_PROPERTY = "cayenne.DataDomain.sharedCache";
60     public static final boolean SHARED_CACHE_ENABLED_DEFAULT = true;
61
62     public static final String JavaDoc 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 JavaDoc USING_EXTERNAL_TRANSACTIONS_PROPERTY = "cayenne.DataDomain.usingExternalTransactions";
66     public static final boolean USING_EXTERNAL_TRANSACTIONS_DEFAULT = false;
67
68     /**
69      * Defines a property name for storing an optional DataContextFactory.
70      *
71      * @since 1.2
72      */

73     public static final String JavaDoc DATA_CONTEXT_FACTORY_PROPERTY = "cayenne.DataDomain.dataContextFactory";
74
75     /**
76      * Defaines a property name for storing optional {@link QueryCacheFactory}.
77      *
78      * @since 3.0
79      */

80     public static final String JavaDoc QUERY_CACHE_FACTORY_PROPERTY = "cayenne.DataDomain.queryCacheFactory";
81
82     /** Stores mapping of data nodes to DataNode name keys. */
83     protected Map JavaDoc nodes = Collections.synchronizedMap(new TreeMap JavaDoc());
84     protected Map JavaDoc nodesByDataMapName = Collections.synchronizedMap(new HashMap JavaDoc());
85
86     /**
87      * Properties configured for DataDomain. These include properties of the DataRowStore
88      * and remote notifications.
89      */

90     protected Map JavaDoc properties = Collections.synchronizedMap(new TreeMap JavaDoc());
91
92     protected EntityResolver entityResolver;
93     protected DataRowStore sharedSnapshotCache;
94     protected TransactionDelegate transactionDelegate;
95     protected DataContextFactory dataContextFactory;
96     protected QueryCacheFactory queryCacheFactory;
97     protected String JavaDoc name;
98
99     // these are initializable from properties...
100
protected boolean sharedCacheEnabled;
101     protected boolean validatingObjectsOnCommit;
102     protected boolean usingExternalTransactions;
103
104     /**
105      * @since 1.2
106      */

107     protected EventManager eventManager;
108
109     /**
110      * @since 1.2
111      */

112     EntitySorter entitySorter;
113
114     /**
115      * @since 3.0
116      */

117     protected QueryCache queryCache;
118
119     /**
120      * Creates a DataDomain and assigns it a name.
121      */

122     public DataDomain(String JavaDoc name) {
123         DataContextFaults.init();
124         setName(name);
125         resetProperties();
126     }
127
128     /**
129      * Creates new DataDomain.
130      *
131      * @param name DataDomain name. Domain can be located using its name in the
132      * Configuration object.
133      * @param properties A Map containing domain configuration properties.
134      */

135     public DataDomain(String JavaDoc name, Map JavaDoc properties) {
136         DataContextFaults.init();
137         setName(name);
138         initWithProperties(properties);
139     }
140
141     /**
142      * @since 1.2
143      */

144     // TODO: andrus, 4/12/2006 - after 1.2 API freeze is over, replace DataNode
145
// EntitySorter with this one ... maybe even make it a part of server-side
146
// EntityResolver?
147
EntitySorter getEntitySorter() {
148
149         if (entitySorter == null) {
150             synchronized (this) {
151                 if (entitySorter == null) {
152
153                     // backwards compatibility mode... only possible in a single-node case
154
// see TODO above
155
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     /**
170      * Exists as a backdoor to override domain sorter until the sorter API is moved from
171      * DataNode.
172      *
173      * @since 1.2
174      */

175     void setEntitySorter(EntitySorter entitySorter) {
176         this.entitySorter = entitySorter;
177     }
178
179     /**
180      * @since 1.1
181      */

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     /**
194      * Reinitializes domain state with a new set of properties.
195      *
196      * @since 1.1
197      */

198     public void initWithProperties(Map JavaDoc properties) {
199         // create map with predictable modification and synchronization behavior
200
Map JavaDoc localMap = new HashMap JavaDoc();
201         if (properties != null) {
202             localMap.putAll(properties);
203         }
204
205         this.properties = localMap;
206
207         Object JavaDoc sharedCacheEnabled = localMap.get(SHARED_CACHE_ENABLED_PROPERTY);
208         Object JavaDoc validatingObjectsOnCommit = localMap
209                 .get(VALIDATING_OBJECTS_ON_COMMIT_PROPERTY);
210         Object JavaDoc usingExternalTransactions = localMap
211                 .get(USING_EXTERNAL_TRANSACTIONS_PROPERTY);
212
213         Object JavaDoc dataContextFactory = localMap.get(DATA_CONTEXT_FACTORY_PROPERTY);
214         Object JavaDoc queryCacheFactory = localMap.get(QUERY_CACHE_FACTORY_PROPERTY);
215
216         // init ivars from properties
217
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 JavaDoc createInstance(String JavaDoc className, Class JavaDoc implementedInterface) {
248         Class JavaDoc aClass;
249         try {
250             aClass = Class.forName(className, true, Thread
251                     .currentThread()
252                     .getContextClassLoader());
253         }
254         catch (Exception JavaDoc 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 JavaDoc e) {
269             throw new CayenneRuntimeException("Error instantiating " + className, e);
270         }
271     }
272
273     /**
274      * Returns EventManager used by this DataDomain.
275      *
276      * @since 1.2
277      */

278     public EventManager getEventManager() {
279         return eventManager;
280     }
281
282     /**
283      * Sets EventManager used by this DataDomain.
284      *
285      * @since 1.2
286      */

287     public void setEventManager(EventManager eventManager) {
288         this.eventManager = eventManager;
289
290         if (sharedSnapshotCache != null) {
291             sharedSnapshotCache.setEventManager(eventManager);
292         }
293     }
294
295     /**
296      * Returns "name" property value.
297      */

298     public String JavaDoc getName() {
299         return name;
300     }
301
302     /**
303      * Sets "name" property to a new value.
304      */

305     public synchronized void setName(String JavaDoc name) {
306         this.name = name;
307         if (sharedSnapshotCache != null) {
308             this.sharedSnapshotCache.setName(name);
309         }
310     }
311
312     /**
313      * Returns <code>true</code> if DataContexts produced by this DataDomain are using
314      * shared DataRowStore. Returns <code>false</code> if each DataContext would work
315      * with its own DataRowStore. Note that this setting can be overwritten per
316      * DataContext. See {@link #createDataContext(boolean)}.
317      */

318     public boolean isSharedCacheEnabled() {
319         return sharedCacheEnabled;
320     }
321
322     public void setSharedCacheEnabled(boolean sharedCacheEnabled) {
323         this.sharedCacheEnabled = sharedCacheEnabled;
324     }
325
326     /**
327      * Returns whether child DataContexts default behavior is to perform object validation
328      * before commit is executed.
329      *
330      * @since 1.1
331      */

332     public boolean isValidatingObjectsOnCommit() {
333         return validatingObjectsOnCommit;
334     }
335
336     /**
337      * Sets the property defining whether child DataContexts should perform object
338      * validation before commit is executed.
339      *
340      * @since 1.1
341      */

342     public void setValidatingObjectsOnCommit(boolean flag) {
343         this.validatingObjectsOnCommit = flag;
344     }
345
346     /**
347      * Returns whether this DataDomain should internally commit all transactions, or let
348      * container do that.
349      *
350      * @since 1.1
351      */

352     public boolean isUsingExternalTransactions() {
353         return usingExternalTransactions;
354     }
355
356     /**
357      * Sets a property defining whether this DataDomain should internally commit all
358      * transactions, or let container do that.
359      *
360      * @since 1.1
361      */

362     public void setUsingExternalTransactions(boolean flag) {
363         this.usingExternalTransactions = flag;
364     }
365
366     /**
367      * @since 1.1
368      * @return a Map of properties for this DataDomain. There is no guarantees of specific
369      * synchronization behavior of this map.
370      */

371     public Map JavaDoc getProperties() {
372         return properties;
373     }
374
375     /**
376      * @since 1.1
377      * @return TransactionDelegate associated with this DataDomain, or null if no delegate
378      * exist.
379      */

380     public TransactionDelegate getTransactionDelegate() {
381         return transactionDelegate;
382     }
383
384     /**
385      * Initializes TransactionDelegate used by all DataContexts associated with this
386      * DataDomain.
387      *
388      * @since 1.1
389      */

390     public void setTransactionDelegate(TransactionDelegate transactionDelegate) {
391         this.transactionDelegate = transactionDelegate;
392     }
393
394     /**
395      * Returns snapshots cache for this DataDomain, lazily initializing it on the first
396      * call if 'sharedCacheEnabled' flag is true.
397      */

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     /**
407      * Returns a guaranteed non-null shared snapshot cache regardless of the
408      * 'sharedCacheEnabled' flag setting. This allows to build DataContexts that do not
409      * follow the default policy.
410      *
411      * @since 3.0
412      */

413     synchronized DataRowStore nonNullSharedSnapshotCache() {
414         if (sharedSnapshotCache == null) {
415             this.sharedSnapshotCache = new DataRowStore(name, properties, eventManager);
416         }
417
418         return sharedSnapshotCache;
419     }
420
421     /**
422      * Shuts down the previous cache instance, sets cache to the new DataSowStore instance
423      * and updates two properties of the new DataSowStore: name and eventManager.
424      */

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     /** Registers new DataMap with this domain. */
448     public void addMap(DataMap map) {
449         getEntityResolver().addDataMap(map);
450         entitySorter = null;
451     }
452
453     /** Returns DataMap matching <code>name</code> parameter. */
454     public DataMap getMap(String JavaDoc mapName) {
455         return getEntityResolver().getDataMap(mapName);
456     }
457
458     /**
459      * Removes named DataMap from this DataDomain and any underlying DataNodes that
460      * include it.
461      */

462     public synchronized void removeMap(String JavaDoc mapName) {
463         DataMap map = getMap(mapName);
464         if (map == null) {
465             return;
466         }
467
468         // remove from data nodes
469
Iterator JavaDoc it = nodes.values().iterator();
470         while (it.hasNext()) {
471             DataNode node = (DataNode) it.next();
472             node.removeDataMap(mapName);
473         }
474
475         // remove from EntityResolver
476
getEntityResolver().removeDataMap(map);
477         entitySorter = null;
478
479         // reindex nodes to remove references on removed map entities
480
reindexNodes();
481     }
482
483     /**
484      * Removes a DataNode from DataDomain. Any maps previously associated with this node
485      * within domain will still be kept around, however they wan't be mapped to any node.
486      */

487     public synchronized void removeDataNode(String JavaDoc nodeName) {
488         DataNode removed = (DataNode) nodes.remove(nodeName);
489         if (removed != null) {
490
491             removed.setEntityResolver(null);
492
493             Iterator JavaDoc it = nodesByDataMapName.values().iterator();
494             while (it.hasNext()) {
495                 if (it.next() == removed) {
496                     it.remove();
497                 }
498             }
499         }
500     }
501
502     /**
503      * Returns a collection of registered DataMaps.
504      */

505     public Collection JavaDoc getDataMaps() {
506         return getEntityResolver().getDataMaps();
507     }
508
509     /**
510      * Returns an unmodifiable collection of DataNodes associated with this domain.
511      */

512     public Collection JavaDoc getDataNodes() {
513         return Collections.unmodifiableCollection(nodes.values());
514     }
515
516     /**
517      * Closes all data nodes, removes them from the list of available nodes.
518      */

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     /**
532      * Clears the list of internal DataMaps. In most cases it is wise to call "reset"
533      * before doing that.
534      */

535     public void clearDataMaps() {
536         getEntityResolver().setDataMaps(Collections.EMPTY_LIST);
537     }
538
539     /**
540      * Adds new DataNode.
541      */

542     public synchronized void addNode(DataNode node) {
543
544         // add node to name->node map
545
nodes.put(node.getName(), node);
546         node.setEntityResolver(this.getEntityResolver());
547
548         // add node to "ent name->node" map
549
Iterator JavaDoc 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     /**
560      * Creates and returns a new DataContext. If this DataDomain is configured to use
561      * shared cache, returned DataContext will use shared cache as well. Otherwise a new
562      * instance of DataRowStore will be used as its local cache.
563      */

564     public DataContext createDataContext() {
565         return createDataContext(isSharedCacheEnabled());
566     }
567
568     /**
569      * Creates a new DataContext.
570      *
571      * @param useSharedCache determines whether resulting DataContext should use shared
572      * vs. local cache. This setting overrides default behavior configured for
573      * this DataDomain via {@link #SHARED_CACHE_ENABLED_PROPERTY}.
574      * @since 1.1
575      */

576     public DataContext createDataContext(boolean useSharedCache) {
577         // for new dataRowStores use the same name for all stores
578
// it makes it easier to track the event subject
579
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     /**
596      * Creates and returns a new inactive transaction. Returned transaction is bound to
597      * the current execution thread.
598      * <p>
599      * If there is a TransactionDelegate, adds the delegate to the newly created
600      * Transaction. Behavior of the returned Transaction depends on
601      * "usingInternalTransactions" property setting.
602      * </p>
603      *
604      * @since 1.1
605      */

606     public Transaction createTransaction() {
607         return (isUsingExternalTransactions()) ? Transaction
608                 .externalTransaction(getTransactionDelegate()) : Transaction
609                 .internalTransaction(getTransactionDelegate());
610     }
611
612     /**
613      * Returns registered DataNode whose name matches <code>name</code> parameter.
614      */

615     public DataNode getNode(String JavaDoc nodeName) {
616         return (DataNode) nodes.get(nodeName);
617     }
618
619     /**
620      * Updates internal index of DataNodes stored by the entity name.
621      */

622     public synchronized void reindexNodes() {
623         nodesByDataMapName.clear();
624
625         Iterator JavaDoc nodes = this.getDataNodes().iterator();
626         while (nodes.hasNext()) {
627             DataNode node = (DataNode) nodes.next();
628             Iterator JavaDoc 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     /**
638      * Returns a DataNode that should handle queries for all entities in a DataMap.
639      *
640      * @since 1.1
641      */

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     /**
656      * Sets EntityResolver. If not set explicitly, DataDomain creates a default
657      * EntityResolver internally on demand.
658      *
659      * @since 1.1
660      */

661     public void setEntityResolver(EntityResolver entityResolver) {
662         this.entityResolver = entityResolver;
663     }
664
665     // creates default entity resolver if there is none set yet
666
private synchronized void createEntityResolver() {
667         if (entityResolver == null) {
668             // entity resolver will be self-indexing as we add all our maps
669
// to it as they are added to the DataDomain
670
entityResolver = new EntityResolver();
671         }
672     }
673
674     /**
675      * Shutdowns all owned data nodes. Invokes DataNode.shutdown().
676      */

677     public void shutdown() {
678         if (sharedSnapshotCache != null) {
679             this.sharedSnapshotCache.shutdown();
680         }
681
682         Collection JavaDoc dataNodes = getDataNodes();
683         for (Iterator JavaDoc i = dataNodes.iterator(); i.hasNext();) {
684             DataNode node = (DataNode) i.next();
685             try {
686                 node.shutdown();
687             }
688             catch (Exception JavaDoc ex) {
689             }
690         }
691     }
692
693     /**
694      * Routes queries to appropriate DataNodes for execution.
695      */

696     public void performQueries(final Collection JavaDoc queries, final OperationObserver callback) {
697
698         runInTransaction(new Transformer() {
699
700             public Object JavaDoc transform(Object JavaDoc input) {
701                 new DataDomainLegacyQueryAction(
702                         DataDomain.this,
703                         new QueryChain(queries),
704                         callback).execute();
705                 return null;
706             }
707         });
708     }
709
710     // ****** DataChannel methods:
711

712     /**
713      * Runs query returning generic QueryResponse.
714      *
715      * @since 1.2
716      */

717     public QueryResponse onQuery(final ObjectContext context, final Query query) {
718         // transaction note:
719
// we don't wrap this code in transaction to reduce transaction scope to
720
// just the DB operation for better performance ... query action will start a
721
// transaction itself when and if needed
722
return new DataDomainQueryAction(context, DataDomain.this, query).execute();
723     }
724
725     /**
726      * Returns an EntityResolver that stores mapping information for this domain.
727      */

728     public EntityResolver getEntityResolver() {
729         if (entityResolver == null) {
730             createEntityResolver();
731         }
732
733         return entityResolver;
734     }
735
736     /**
737      * Only handles commit-type synchronization, ignoring any other type.
738      *
739      * @since 1.2
740      */

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                 // "cascade" and "no_cascade" are the same from the DataDomain
750
// perspective,
751
// including transaction handling logic
752
case DataChannel.FLUSH_NOCASCADE_SYNC:
753             case DataChannel.FLUSH_CASCADE_SYNC:
754                 return (GraphDiff) runInTransaction(new Transformer() {
755
756                     public Object JavaDoc transform(Object JavaDoc 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         // if there is a transaction in progress, roll it back
768

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     /**
792      * Executes Transformer.transform() method in a transaction. Transaction policy is to
793      * check for the thread transaction, and use it if one exists. If it doesn't, a new
794      * transaction is created, with a scope limited to this method.
795      */

796     // WARNING: (andrus) if we ever decide to make this method protected or public, we
797
// need to change the signature to avoid API dependency on commons-collections
798
Object JavaDoc runInTransaction(Transformer operation) {
799
800         // user or container-managed or nested transaction
801
if (Transaction.getThreadTransaction() != null) {
802             return operation.transform(null);
803         }
804
805         // Cayenne-managed transaction
806

807         Transaction transaction = createTransaction();
808         Transaction.bindThreadTransaction(transaction);
809
810         try {
811             // implicit begin..
812
Object JavaDoc result = operation.transform(null);
813             transaction.commit();
814             return result;
815         }
816         catch (Exception JavaDoc ex) {
817             transaction.setRollbackOnly();
818
819             // must rethrow
820
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 JavaDoc rollbackEx) {
834                     // although we don't expect an exception here, print the stack, as
835
// there have been some Cayenne bugs already (CAY-557) that were
836
// masked by this 'catch' clause.
837
QueryLogger.logQueryError(rollbackEx);
838                 }
839             }
840         }
841     }
842
843     public String JavaDoc toString() {
844         return new ToStringBuilder(this).append("name", name).toString();
845     }
846
847     /**
848      * Returns a non-null {@link QueryCacheFactory}.
849      *
850      * @since 3.0
851      */

852     public QueryCacheFactory getQueryCacheFactory() {
853         return queryCacheFactory != null ? queryCacheFactory : new MapQueryCacheFactory();
854     }
855
856     /**
857      * @since 3.0
858      */

859     public void setQueryCacheFactory(QueryCacheFactory queryCacheFactory) {
860         this.queryCacheFactory = queryCacheFactory;
861     }
862
863     /**
864      * Returns shared {@link QueryCache} used by this DataDomain, creating it on the fly
865      * if needed. Uses factory obtained via {@link #getQueryCacheFactory()} to initialize
866      * the cache for the first time.
867      *
868      * @since 3.0
869      */

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     /**
885      * @since 3.0
886      */

887     QueryCache getQueryCacheInternal() {
888         return queryCache;
889     }
890 }
891
Popular Tags