This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new 885335b1b cleanup, including some deprecation
885335b1b is described below

commit 885335b1bdde795c2bf5cc3bd9a17e9e6a2e4062
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sat May 30 17:06:54 2026 -0400

    cleanup, including some deprecation
---
 .../java/org/apache/cayenne/ObjectContext.java     |    7 +-
 .../org/apache/cayenne/access/DataContext.java     |   64 +-
 .../cayenne/access/DataContextDeleteAction.java    |   50 +-
 .../cayenne/access/DataContextMergeHandler.java    |   12 +-
 .../cayenne/access/DataContextQueryAction.java     |   16 +-
 .../cayenne/access/DataContextSnapshotBuilder.java |    7 +-
 .../java/org/apache/cayenne/access/DataDomain.java | 1517 ++++++++++----------
 .../access/DataDomainLegacyQueryAction.java        |    2 -
 .../java/org/apache/cayenne/access/DataNode.java   |   14 +-
 .../org/apache/cayenne/access/QueryEngine.java     |   12 +-
 .../main/java/org/apache/cayenne/query/Query.java  |    6 +-
 11 files changed, 846 insertions(+), 861 deletions(-)

diff --git a/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java 
b/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java
index 4166b06f1..cc9d45c6d 100644
--- a/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java
+++ b/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java
@@ -171,9 +171,12 @@ public interface ObjectContext extends DataChannel, 
Serializable {
     void rollbackChangesLocally();
 
     /**
-     * Executes a selecting query, returning a list of persistent objects or
-     * data rows.
+     * Executes a selecting query, returning a list of persistent objects or 
data rows.
      */
+    // TODO: this will need to be deprecated at some point. The reason we 
can't do that yet is that
+    //  EJBQLQuery, SQLTemplate, RefreshQuery, RelationshipQuery, 
ObjectIdQuery do not implement "Select".
+    //  Those queries should eventually either go away (EJBQLQuery, 
ObjectIdQuery, SQLTemplate) or made implement Select
+    //  (RelationshipQuery, RefreshQuery)
     List performQuery(Query query);
 
     /**
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
index f52368a28..8cf567906 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -19,18 +19,6 @@
 
 package org.apache.cayenne.access;
 
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.DataRow;
@@ -55,7 +43,14 @@ import org.apache.cayenne.graph.GraphEvent;
 import org.apache.cayenne.graph.GraphManager;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.query.*;
+import org.apache.cayenne.query.IteratedQueryDecorator;
+import org.apache.cayenne.query.MappedExec;
+import org.apache.cayenne.query.MappedSelect;
+import org.apache.cayenne.query.ObjectIdQuery;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.RefreshQuery;
+import org.apache.cayenne.query.Select;
 import org.apache.cayenne.reflect.ClassDescriptor;
 import org.apache.cayenne.reflect.PropertyDescriptor;
 import org.apache.cayenne.runtime.CayenneRuntime;
@@ -65,6 +60,18 @@ import org.apache.cayenne.util.GenericResponse;
 import org.apache.cayenne.util.ObjectContextGraphAction;
 import org.apache.cayenne.util.Util;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * The most common implementation of {@link ObjectContext}. DataContext is an
  * isolated container of an object graph, in a sense that any uncommitted
@@ -78,16 +85,18 @@ public class DataContext implements ObjectContext {
      *
      * @since 3.0
      */
-    protected static final ThreadLocal<ObjectContext> threadObjectContext = 
new ThreadLocal<ObjectContext>();
+    @Deprecated(since = "5.0", forRemoval = true)
+    protected static final ThreadLocal<ObjectContext> threadObjectContext = 
new ThreadLocal<>();
 
     /**
      * Returns the ObjectContext bound to the current thread.
      *
      * @since 3.0
      * @return the ObjectContext associated with caller thread.
-     * @throws IllegalStateException
-     *             if there is no ObjectContext bound to the current thread.
+     * @throws IllegalStateException if there is no ObjectContext bound to the 
current thread.
+     * @deprecated if you are using thread context, you can create your own 
ThreadLocal
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static ObjectContext getThreadObjectContext() throws 
IllegalStateException {
         ObjectContext context = threadObjectContext.get();
         if (context == null) {
@@ -104,7 +113,9 @@ public class DataContext implements ObjectContext {
      * unbind currently bound ObjectContext.
      *
      * @since 3.0
+     * @deprecated if you are using thread context, you can create your own 
ThreadLocal
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static void bindThreadObjectContext(ObjectContext context) {
         threadObjectContext.set(context);
     }
@@ -133,11 +144,6 @@ public class DataContext implements ObjectContext {
     protected transient QueryCache queryCache;
     protected transient EntityResolver entityResolver;
 
-    /**
-     * @deprecated since 4.0 used in a method that itself should be deprecated,
-     *             so this is a temp code
-     */
-    @Deprecated
     protected transient TransactionFactory transactionFactory;
 
     protected transient DataContextMergeHandler mergeHandler;
@@ -313,7 +319,7 @@ public class DataContext implements ObjectContext {
 
         List<?> response = channel.onQuery(this, new 
DataDomainQuery()).firstList();
 
-        if (response != null && response.size() > 0 && response.get(0) 
instanceof DataDomain dataDomain) {
+        if (response != null && !response.isEmpty() && response.getFirst() 
instanceof DataDomain dataDomain) {
             return dataDomain;
         }
 
@@ -563,7 +569,7 @@ public class DataContext implements ObjectContext {
         if (object.getPersistenceState() == PersistenceState.HOLLOW) {
             ObjectId oid = object.getObjectId();
             List<?> objects = performQuery(new ObjectIdQuery(oid, false, 
ObjectIdQuery.CACHE));
-            if (objects.size() == 0) {
+            if (objects.isEmpty()) {
                 throw new FaultFailureException(
                         "Error resolving fault, no matching row exists in the 
database for ObjectId: " + oid);
             } else if (objects.size() > 1) {
@@ -630,7 +636,7 @@ public class DataContext implements ObjectContext {
 
         ClassDescriptor descriptor = 
getEntityResolver().getClassDescriptor(entity.getName());
         List<T> list = objectsFromDataRows(descriptor, 
Collections.singletonList(dataRow));
-        return list.get(0);
+        return list.getFirst();
     }
 
     /**
@@ -645,7 +651,7 @@ public class DataContext implements ObjectContext {
         ClassDescriptor descriptor = 
getEntityResolver().getClassDescriptor(entityName);
         List<?> list = objectsFromDataRows(descriptor, 
Collections.singletonList(dataRow));
 
-        return (Persistent) list.get(0);
+        return (Persistent) list.getFirst();
     }
 
     /**
@@ -900,13 +906,13 @@ public class DataContext implements ObjectContext {
     public <T> T selectOne(Select<T> query) {
         List<T> objects = select(query);
 
-        if (objects.size() == 0) {
+        if (objects.isEmpty()) {
             return null;
         } else if (objects.size() > 1) {
             throw new CayenneRuntimeException("Expected zero or one object, 
instead query matched: %d", objects.size());
         }
 
-        return objects.get(0);
+        return objects.getFirst();
     }
 
     /**
@@ -916,7 +922,7 @@ public class DataContext implements ObjectContext {
     public <T> T selectFirst(Select<T> query) {
         List<T> objects = select(query);
 
-        return (objects == null || objects.isEmpty()) ? null : objects.get(0);
+        return (objects == null || objects.isEmpty()) ? null : 
objects.getFirst();
     }
 
     /**
@@ -925,7 +931,7 @@ public class DataContext implements ObjectContext {
     @Override
     public <T> void iterate(Select<T> query, ResultIteratorCallback<T> 
callback) {
 
-        try (ResultIterator<T> it = iterator(query);) {
+        try (ResultIterator<T> it = iterator(query)) {
             for (T t : it) {
                 callback.next(t);
             }
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextDeleteAction.java 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextDeleteAction.java
index 9eb4784da..b14126f37 100644
--- 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextDeleteAction.java
+++ 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextDeleteAction.java
@@ -19,11 +19,6 @@
 
 package org.apache.cayenne.access;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DeleteDenyException;
 import org.apache.cayenne.ObjectContext;
@@ -40,14 +35,19 @@ import org.apache.cayenne.reflect.PropertyVisitor;
 import org.apache.cayenne.reflect.ToManyProperty;
 import org.apache.cayenne.reflect.ToOneProperty;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
 /**
  * A CayenneContext helper that processes object deletion.
- * 
+ *
  * @since 1.2
  */
 class DataContextDeleteAction {
 
-    private ObjectContext context;
+    private final ObjectContext context;
 
     DataContextDeleteAction(ObjectContext context) {
         this.context = context;
@@ -73,19 +73,19 @@ class DataContextDeleteAction {
 
         if (object.getObjectContext() != context) {
             throw new CayenneRuntimeException(
-                    "Attempt to delete object regsitered in a different 
ObjectContext. Object: %s, context: %s"
-                            , object, context);
+                    "Attempt to delete object registered in a different 
ObjectContext. Object: %s, context: %s",
+                    object,
+                    context);
         }
 
         // must resolve HOLLOW objects before delete... needed
         // to process relationships and optimistic locking...
 
         context.prepareForAccess(object, null, false);
-        
+
         if (oldState == PersistenceState.NEW) {
             deleteNew(object);
-        }
-        else {
+        } else {
             deletePersistent(object);
         }
 
@@ -95,7 +95,7 @@ class DataContextDeleteAction {
     private void deleteNew(Persistent object) throws DeleteDenyException {
         object.setPersistenceState(PersistenceState.TRANSIENT);
         processDeleteRules(object, PersistenceState.NEW);
-        
+
         // if an object was NEW, we must throw it out
         context.getGraphManager().unregisterNode(object.getObjectId());
     }
@@ -104,7 +104,7 @@ class DataContextDeleteAction {
         context.getEntityResolver().getCallbackRegistry().performCallbacks(
                 LifecycleEvent.PRE_REMOVE,
                 object);
-        
+
         int oldState = object.getPersistenceState();
         object.setPersistenceState(PersistenceState.DELETED);
         processDeleteRules(object, oldState);
@@ -114,18 +114,16 @@ class DataContextDeleteAction {
     @SuppressWarnings("unchecked")
     private Collection<Persistent> toCollection(Object object) {
 
-        if (object == null) {
-            return Collections.emptyList();
-        }
+        return switch (object) {
+            case null -> Collections.emptyList();
+
+
+            // create copies of collections to avoid iterator exceptions
+            case Collection ignored -> new 
ArrayList<>((Collection<Persistent>) object);
+            case Map ignored -> new ArrayList<>(((Map<?, Persistent>) 
object).values());
+            default -> Collections.singleton((Persistent) object);
+        };
 
-        // create copies of collections to avoid iterator exceptions
-        if (object instanceof Collection) {
-            return new ArrayList<>((Collection<Persistent>) object);
-        } else if (object instanceof Map) {
-            return new ArrayList<>(((Map<?, Persistent>) object).values());
-        } else {
-            return Collections.singleton((Persistent)object);
-        }
     }
 
     private void processDeleteRules(final Persistent object, int oldState)
@@ -149,7 +147,7 @@ class DataContextDeleteAction {
             final Collection<Persistent> relatedObjects = 
toCollection(property.readProperty(object));
 
             // no related object, bail out
-            if (relatedObjects.size() == 0) {
+            if (relatedObjects.isEmpty()) {
                 continue;
             }
 
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java
index 8bed8e676..40a02fbef 100644
--- 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java
+++ 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java
@@ -47,7 +47,7 @@ import org.apache.cayenne.reflect.ToOneProperty;
 class DataContextMergeHandler implements GraphChangeHandler, 
DataChannelListener {
 
     private boolean active;
-    private DataContext context;
+    private final DataContext context;
 
     DataContextMergeHandler(DataContext context) {
         this.active = true;
@@ -136,10 +136,6 @@ class DataContextMergeHandler implements 
GraphChangeHandler, DataChannelListener
     public void graphRolledback(GraphEvent event) {
         // TODO: andrus, 3/26/2006 - enable this once all ObjectStore diffs 
implement
         // working undo operation
-
-        // if(shouldProcessEvent(e)) {
-        // event.getDiff().undo(this);
-        // }
     }
 
     // *** GraphChangeHandler methods
@@ -177,16 +173,16 @@ class DataContextMergeHandler implements 
GraphChangeHandler, DataChannelListener
 
     @Override
     public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
-        arcChanged(nodeId, targetNodeId, arcId);
+        arcChanged(nodeId, arcId);
     }
 
     @Override
     public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
-        arcChanged(nodeId, targetNodeId, arcId);
+        arcChanged(nodeId, arcId);
     }
 
     // works the same for add and remove as long as we don't get too smart per 
TODO below.
-    private void arcChanged(Object nodeId, Object targetNodeId, Object arcId) {
+    private void arcChanged(Object nodeId, Object arcId) {
 
         final Persistent source = (Persistent) 
context.getGraphManager().getNode(nodeId);
         if (source != null && source.getPersistenceState() != 
PersistenceState.HOLLOW) {
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
index 2aa45cc1d..4222daa9f 100644
--- 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
+++ 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
@@ -19,12 +19,6 @@
 
 package org.apache.cayenne.access;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.Persistent;
@@ -36,6 +30,12 @@ import org.apache.cayenne.query.RefreshQuery;
 import org.apache.cayenne.util.ListResponse;
 import org.apache.cayenne.util.ObjectContextQueryAction;
 
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
 /**
  * A DataContext-specific version of
  * {@link org.apache.cayenne.util.ObjectContextQueryAction}.
@@ -140,7 +140,7 @@ class DataContextQueryAction extends 
ObjectContextQueryAction {
             if(rsMapping.size() > 1) {
                 mixedResults = true;
             } else if(rsMapping.size() == 1) {
-                mixedResults = !(rsMapping.get(0) instanceof 
EntityResultSegment)
+                mixedResults = !(rsMapping.getFirst() instanceof 
EntityResultSegment)
                         || !metadata.isSingleResultSetMapping();
             }
         }
@@ -172,7 +172,7 @@ class DataContextQueryAction extends 
ObjectContextQueryAction {
             }
 
             // 2. invalidate object collection
-            Collection objects = refreshQuery.getObjects();
+            Collection<?> objects = refreshQuery.getObjects();
             if (objects != null && !objects.isEmpty()) {
 
                 synchronized (context.getObjectStore()) {
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
index 4eb72dd1f..5183488fe 100644
--- 
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
+++ 
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
@@ -121,7 +121,7 @@ class DataContextSnapshotBuilder implements PropertyVisitor 
{
                         + ". Object may have been deleted externally.", 
object.getObjectId());
             }
 
-            DbRelationship dbRel = rel.getDbRelationships().get(0);
+            DbRelationship dbRel = rel.getDbRelationships().getFirst();
             for (DbJoin join : dbRel.getJoins()) {
                 String key = join.getSourceName();
                 snapshot.put(key, storedSnapshot.get(key));
@@ -134,13 +134,12 @@ class DataContextSnapshotBuilder implements 
PropertyVisitor {
         Persistent target = (Persistent) targetObject;
         Map<String, Object> idParts = target.getObjectId().getIdSnapshot();
 
-        // this may happen in uncommitted objects - see the warning in
-        // the JavaDoc of this method.
+        // this may happen in uncommitted objects - see the warning in the 
JavaDoc of this method.
         if (idParts.isEmpty()) {
             return true;
         }
 
-        DbRelationship dbRel = rel.getDbRelationships().get(0);
+        DbRelationship dbRel = rel.getDbRelationships().getFirst();
         Map<String, Object> fk = 
dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
         snapshot.putAll(fk);
         return true;
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java 
b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
index 9b2811bf5..440d626c9 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
@@ -64,777 +64,766 @@ import java.util.concurrent.CopyOnWriteArrayList;
  */
 public class DataDomain implements QueryEngine, DataChannel {
 
-       public static final String SHARED_CACHE_ENABLED_PROPERTY = 
"cayenne.DataDomain.sharedCache";
-       public static final boolean SHARED_CACHE_ENABLED_DEFAULT = true;
+    public static final String SHARED_CACHE_ENABLED_PROPERTY = 
"cayenne.DataDomain.sharedCache";
+    public static final boolean SHARED_CACHE_ENABLED_DEFAULT = true;
 
-       public static final String VALIDATING_OBJECTS_ON_COMMIT_PROPERTY = 
"cayenne.DataDomain.validatingObjectsOnCommit";
-       public static final boolean VALIDATING_OBJECTS_ON_COMMIT_DEFAULT = true;
+    public static final String VALIDATING_OBJECTS_ON_COMMIT_PROPERTY = 
"cayenne.DataDomain.validatingObjectsOnCommit";
+    public static final boolean VALIDATING_OBJECTS_ON_COMMIT_DEFAULT = true;
 
-       /**
-        * @since 3.1
-        */
-       @Inject
-       protected JdbcEventLogger jdbcEventLogger;
+    /**
+     * @since 3.1
+     */
+    @Inject
+    protected JdbcEventLogger jdbcEventLogger;
 
-       /**
-        * @since 4.0
-        */
-       @Inject
-       protected TransactionManager transactionManager;
+    /**
+     * @since 4.0
+     */
+    @Inject
+    protected TransactionManager transactionManager;
 
-       /**
+    /**
      * @since 4.0
      */
     protected DataRowStoreFactory dataRowStoreFactory;
 
     /**
-        * @since 3.1
-        */
-       protected int maxIdQualifierSize;
-
-       /**
-        * @since 4.1
-        */
-       protected List<DataChannelQueryFilter> queryFilters;
-
-       /**
-        * @since 4.1
-        */
-       protected List<DataChannelSyncFilter> syncFilters;
-
-       /**
-        * @since 4.2
-        */
-       @Inject
-       protected DataDomainFlushActionFactory flushActionFactory;
-
-       /**
-        * @since 4.2
-        */
-       @Inject
-       protected AdhocObjectFactory objectFactory;
-
-       protected Map<String, DataNode> nodes;
-       protected Map<String, DataNode> nodesByDataMapName;
-       protected DataNode defaultNode;
-       protected Map<String, String> properties;
-
-       protected EntityResolver entityResolver;
-       protected DataRowStore sharedSnapshotCache;
-       protected String name;
-       protected QueryCache queryCache;
-
-       // these are initialized from properties...
-       protected boolean sharedCacheEnabled;
-       protected boolean validatingObjectsOnCommit;
-
-       /**
-        * @since 1.2
-        */
-       protected EventManager eventManager;
-
-       /**
-        * @since 1.2
-        */
-       protected EntitySorter entitySorter;
-
-       protected boolean stopped;
-
-       /**
-        * Creates a DataDomain and assigns it a name.
-        */
-       public DataDomain(String name) {
-               init(name);
-               resetProperties();
-       }
-
-       /**
-        * Creates new DataDomain.
-        *
-        * @param name
-        *            DataDomain name. Domain can be located using its name in 
the
-        *            Configuration object.
-        * @param properties
-        *            A Map containing domain configuration properties.
-        * @deprecated since 4.0 unused
-        */
-       @Deprecated
-       public DataDomain(String name, Map<String, String> properties) {
-               init(name);
-               initWithProperties(properties);
-       }
-
-       private void init(String name) {
-
-               this.queryFilters = new CopyOnWriteArrayList<>();
-               this.syncFilters = new CopyOnWriteArrayList<>();
-               this.nodesByDataMapName = new ConcurrentHashMap<>();
-               this.nodes = new ConcurrentHashMap<>();
-
-               // properties are read-only, so no need for concurrent map, or 
any
-               // specific map
-               // for that matter
-               this.properties = Collections.emptyMap();
-
-               setName(name);
-       }
-
-       /**
-        * Checks that Domain is not stopped. Throws DomainStoppedException
-        * otherwise.
-        *
-        * @since 3.0
-        */
-       protected void checkStopped() throws DomainStoppedException {
-               if (stopped) {
-                       throw new DomainStoppedException("Domain " + name
-                                       + " was shutdown and can no longer be 
used to access the database");
-               }
-       }
-
-       /**
-        * @since 3.1
-        */
-       public EntitySorter getEntitySorter() {
-               return entitySorter;
-       }
-
-       /**
-        * @since 3.1
-        */
-       public void setEntitySorter(EntitySorter entitySorter) {
-               this.entitySorter = entitySorter;
-       }
-
-       /**
-        * @since 1.1
-        */
-       protected void resetProperties() {
-               properties = Collections.emptyMap();
-
-               sharedCacheEnabled = SHARED_CACHE_ENABLED_DEFAULT;
-               validatingObjectsOnCommit = 
VALIDATING_OBJECTS_ON_COMMIT_DEFAULT;
-       }
-
-       /**
-        * Reinitializes domain state with a new set of properties.
-        *
-        * @since 1.1
-        * @deprecated since 4.0 properties are processed by the DI provider.
-        */
-       @Deprecated
-       public void initWithProperties(Map<String, String> properties) {
-
-               // clone properties to ensure that it is read-only internally
-               properties = properties != null ? new HashMap<>(properties) : 
Collections.<String, String>emptyMap();
-
-               String sharedCacheEnabled = 
properties.get(SHARED_CACHE_ENABLED_PROPERTY);
-               String validatingObjectsOnCommit = 
properties.get(VALIDATING_OBJECTS_ON_COMMIT_PROPERTY);
-
-               // init ivars from properties
-               this.sharedCacheEnabled = (sharedCacheEnabled != null) ? 
"true".equalsIgnoreCase(sharedCacheEnabled)
-                               : SHARED_CACHE_ENABLED_DEFAULT;
-               this.validatingObjectsOnCommit = (validatingObjectsOnCommit != 
null) ? "true"
-                               .equalsIgnoreCase(validatingObjectsOnCommit) : 
VALIDATING_OBJECTS_ON_COMMIT_DEFAULT;
-
-               this.properties = properties;
-       }
-
-       /**
-        * Returns EventManager used by this DataDomain.
-        *
-        * @since 1.2
-        */
-       public EventManager getEventManager() {
-               return eventManager;
-       }
-
-       /**
-        * Sets EventManager used by this DataDomain.
-        *
-        * @since 1.2
-        */
-       public void setEventManager(EventManager eventManager) {
-               this.eventManager = eventManager;
-
-               if (sharedSnapshotCache != null) {
-                       sharedSnapshotCache.setEventManager(eventManager);
-               }
-       }
-
-       /**
-        * Returns "name" property value.
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Sets "name" property to a new value.
-        */
-       public synchronized void setName(String name) {
-               this.name = name;
-               if (sharedSnapshotCache != null) {
-                       this.sharedSnapshotCache.setName(name);
-               }
-       }
-
-       /**
-        * Returns <code>true</code> if DataContexts produced by this 
DataDomain are
-        * using shared DataRowStore. Returns <code>false</code> if each 
DataContext
-        * would work with its own DataRowStore. Note that this setting can be
-        * overwritten per DataContext.
-        */
-       public boolean isSharedCacheEnabled() {
-               return sharedCacheEnabled;
-       }
-
-       public void setSharedCacheEnabled(boolean sharedCacheEnabled) {
-               this.sharedCacheEnabled = sharedCacheEnabled;
-       }
-
-       /**
-        * Returns whether child DataContexts default behavior is to perform 
object
-        * validation before commit is executed.
-        *
-        * @since 1.1
-        */
-       public boolean isValidatingObjectsOnCommit() {
-               return validatingObjectsOnCommit;
-       }
-
-       /**
-        * Sets the property defining whether child DataContexts should perform
-        * object validation before commit is executed.
-        *
-        * @since 1.1
-        */
-       public void setValidatingObjectsOnCommit(boolean flag) {
-               this.validatingObjectsOnCommit = flag;
-       }
-
-       /**
-        * @since 1.1
-        * @return a Map of properties for this DataDomain.
-        */
-       public Map<String, String> getProperties() {
-               return properties;
-       }
-
-       /**
-        * Returns snapshots cache for this DataDomain, lazily initializing it 
on
-        * the first call if 'sharedCacheEnabled' flag is true.
-        */
-       public DataRowStore getSharedSnapshotCache() {
-               if (sharedSnapshotCache == null && sharedCacheEnabled) {
-                       this.sharedSnapshotCache = nonNullSharedSnapshotCache();
-               }
-
-               return sharedSnapshotCache;
-       }
-
-       /**
-        * Returns a guaranteed non-null shared snapshot cache regardless of the
-        * 'sharedCacheEnabled' flag setting.
-        */
-       synchronized DataRowStore nonNullSharedSnapshotCache() {
-               if (sharedSnapshotCache == null) {
-                       this.sharedSnapshotCache = 
dataRowStoreFactory.createDataRowStore(name);
-               }
-
-               return sharedSnapshotCache;
-       }
-
-       /**
-        * Shuts down the previous cache instance, sets cache to the new
-        * DataSowStore instance and updates two properties of the new 
DataSowStore:
-        * name and eventManager.
-        */
-       public synchronized void setSharedSnapshotCache(DataRowStore 
snapshotCache) {
-               if (this.sharedSnapshotCache != snapshotCache) {
-                       if (this.sharedSnapshotCache != null) {
-                               this.sharedSnapshotCache.shutdown();
-                       }
-                       this.sharedSnapshotCache = snapshotCache;
-
-                       if (snapshotCache != null) {
-                               
snapshotCache.setEventManager(getEventManager());
-                               snapshotCache.setName(getName());
-                       }
-               }
-       }
-
-       public void addDataMap(DataMap dataMap) {
-               getEntityResolver().addDataMap(dataMap);
-               refreshEntitySorter();
-       }
-
-       /**
-        * @since 3.1
-        */
-       public DataMap getDataMap(String mapName) {
-               return getEntityResolver().getDataMap(mapName);
-       }
-
-       /**
-        * Removes named DataMap from this DataDomain and any underlying 
DataNodes
-        * that include it.
-        *
-        * @since 3.1
-        */
-       public void removeDataMap(String mapName) {
-               DataMap map = getDataMap(mapName);
-               if (map == null) {
-                       return;
-               }
-
-               // remove from data nodes
-               for (DataNode node : nodes.values()) {
-                       node.removeDataMap(mapName);
-               }
-
-               nodesByDataMapName.remove(mapName);
-
-               // remove from EntityResolver
-               getEntityResolver().removeDataMap(map);
-
-               refreshEntitySorter();
-       }
-
-       /**
-        * Removes a DataNode from DataDomain. Any maps previously associated 
with
-        * this node within domain will still be kept around, however they 
wan't be
-        * mapped to any node.
-        */
-       public void removeDataNode(String nodeName) {
-               DataNode removed = nodes.remove(nodeName);
-               if (removed != null) {
-                       removed.setEntityResolver(null);
-                       nodesByDataMapName.values().removeIf(dataNode -> 
dataNode == removed);
-               }
-       }
-
-       /**
-        * Returns a collection of registered DataMaps.
-        */
-       public Collection<DataMap> getDataMaps() {
-               return getEntityResolver().getDataMaps();
-       }
-
-       /**
-        * Returns an unmodifiable collection of DataNodes associated with this
-        * domain.
-        */
-       public Collection<DataNode> getDataNodes() {
-               return Collections.unmodifiableCollection(nodes.values());
-       }
-
-       /**
-        * Adds new DataNode.
-        */
-       public void addNode(DataNode node) {
-
-               // add node to name->node map
-               nodes.put(node.getName(), node);
-               node.setEntityResolver(getEntityResolver());
-
-               // add node to "ent name->node" map
-               for (DataMap map : node.getDataMaps()) {
-                       addDataMap(map);
-                       nodesByDataMapName.put(map.getName(), node);
-               }
-       }
-
-       /**
-        * Returns registered DataNode whose name matches <code>name</code>
-        * parameter.
-        *
-        * @since 3.1
-        */
-       public DataNode getDataNode(String nodeName) {
-               return nodes.get(nodeName);
-       }
-
-       /**
-        * Returns a DataNode that should handle queries for all entities in a
-        * DataMap.
-        *
-        * @since 1.1
-        */
-       public DataNode lookupDataNode(DataMap map) {
-
-               DataNode node = nodesByDataMapName.get(map.getName());
-               if (node == null) {
-
-                       // see if one of the node states has changed, and the 
map is now
-                       // linked...
-                       for (DataNode n : getDataNodes()) {
-                               for (DataMap m : n.getDataMaps()) {
-                                       if (m == map) {
-                                               
nodesByDataMapName.put(map.getName(), n);
-                                               node = n;
-                                               break;
-                                       }
-                               }
-
-                               if (node != null) {
-                                       break;
-                               }
-                       }
-
-                       if (node == null) {
-
-                               if (defaultNode != null) {
-                                       nodesByDataMapName.put(map.getName(), 
defaultNode);
-                                       node = defaultNode;
-                               } else {
-                                       throw new CayenneRuntimeException("No 
DataNode configured for DataMap '%s'"
-                                                       + " and no default 
DataNode set", map.getName());
-                               }
-                       }
-               }
-
-               return node;
-       }
-
-       /**
-        * Sets EntityResolver. If not set explicitly, DataDomain creates a 
default
-        * EntityResolver internally on demand.
-        *
-        * @since 1.1
-        */
-       public void setEntityResolver(EntityResolver entityResolver) {
-               this.entityResolver = entityResolver;
-       }
-
-       // creates default entity resolver if there is none set yet
-       private synchronized void createEntityResolver() {
-               if (entityResolver == null) {
-                       // entity resolver will be self-indexing as we add all 
our maps
-                       // to it as they are added to the DataDomain
-                       entityResolver = new EntityResolver();
-               }
-       }
-
-       /**
-        * Shutdowns all owned data nodes and marks this domain as stopped.
-        */
-       @BeforeScopeEnd
-       public void shutdown() {
-               if (!stopped) {
-                       stopped = true;
-
-                       if (sharedSnapshotCache != null) {
-                               sharedSnapshotCache.shutdown();
-                       }
-               }
-       }
-
-       /**
-        * Routes queries to appropriate DataNodes for execution.
-        */
-       public void performQueries(final Collection<? extends Query> queries, 
final OperationObserver callback) {
-               transactionManager.performInTransaction(() -> {
-                       new DataDomainLegacyQueryAction(DataDomain.this, new 
QueryChain(queries), callback).execute();
-                       return null;
-               });
-       }
-
-       // ****** DataChannel methods:
-
-       /**
-        * Runs query returning generic QueryResponse.
-        *
-        * @since 1.2
-        */
-       @Override
-       public QueryResponse onQuery(final ObjectContext originatingContext, 
final Query query) {
-               checkStopped();
-
-               return new 
DataDomainQueryFilterChain().onQuery(originatingContext, query);
-       }
-
-       QueryResponse onQueryNoFilters(final ObjectContext originatingContext, 
final Query query) {
-               // transaction note:
-               // we don't wrap this code in transaction to reduce transaction 
scope to
-               // just the DB operation for better performance ... query 
action will
-               // start a transaction itself when and if needed
-               return new DataDomainQueryAction(originatingContext, 
DataDomain.this, query).execute();
-       }
-
-       /**
-        * Returns an EntityResolver that stores mapping information for this
-        * domain.
-        */
-       @Override
-       public EntityResolver getEntityResolver() {
-               if (entityResolver == null) {
-                       createEntityResolver();
-               }
-
-               return entityResolver;
-       }
-
-       /**
-        * Only handles commit-type synchronization, ignoring any other type.
-        *
-        * @since 1.2
-        */
-       @Override
-       public GraphDiff onSync(final ObjectContext originatingContext, final 
GraphDiff changes, int syncType) {
-
-               checkStopped();
-
-               return new 
DataDomainSyncFilterChain().onSync(originatingContext, changes, syncType);
-       }
-
-       GraphDiff onSyncNoFilters(final ObjectContext originatingContext, final 
GraphDiff changes, int syncType) {
-
-        GraphDiff result;
-        switch (syncType) {
-               case DataChannel.ROLLBACK_CASCADE_SYNC:
-                       result = onSyncRollback(originatingContext);
-                       break;
-               // "cascade" and "no_cascade" are the same from the DataDomain 
perspective
-               case DataChannel.FLUSH_NOCASCADE_SYNC:
-               case DataChannel.FLUSH_CASCADE_SYNC:
-                       result =  onSyncFlush(originatingContext, changes);
-                       break;
-               default:
-                       throw new CayenneRuntimeException("Invalid 
synchronization type: %d", syncType);
-               }
-
-               return result;
-       }
-
-       GraphDiff onSyncRollback(ObjectContext originatingContext) {
-               // if there is a transaction in progress, roll it back
-
-               Transaction transaction = 
BaseTransaction.getThreadTransaction();
-               if (transaction != null) {
-                       transaction.setRollbackOnly();
-               }
-
-               return new CompoundDiff();
-       }
-
-       GraphDiff onSyncFlush(ObjectContext originatingContext, GraphDiff 
childChanges) {
-
-               if (!(originatingContext instanceof DataContext)) {
-                       throw new CayenneRuntimeException("No support for 
committing ObjectContexts that are not DataContexts yet. "
-                                                       + "Unsupported context: 
%s", originatingContext);
-               }
-
-               DataDomainFlushAction action = 
flushActionFactory.createFlushAction(this);
-               return action.flush((DataContext) originatingContext, 
childChanges);
-       }
-
-       @Override
-       public String toString() {
-               return new ToStringBuilder(this).append("name", 
name).toString();
-       }
-
-       /**
-        * Returns shared {@link QueryCache} used by this DataDomain.
-        *
-        * @since 3.0
-        */
-       public QueryCache getQueryCache() {
-               return queryCache;
-       }
-
-       public void setQueryCache(QueryCache queryCache) {
-               this.queryCache = queryCache;
-       }
-
-       /**
-        * @since 4.0
-        */
-       public DataRowStoreFactory getDataRowStoreFactory() {
-               return dataRowStoreFactory;
-       }
-
-       /**
-        * @since 4.0
-        */
-       public void setDataRowStoreFactory(DataRowStoreFactory 
dataRowStoreFactory) {
-               this.dataRowStoreFactory = dataRowStoreFactory;
-       }
-
-       /**
-        * @since 3.1
-        */
-       JdbcEventLogger getJdbcEventLogger() {
-               return jdbcEventLogger;
-       }
-
-       void refreshEntitySorter() {
-               if (entitySorter != null) {
-                       entitySorter.setEntityResolver(getEntityResolver());
-               }
-       }
-
-       /**
-        * Returns an unmodifiable list of query filters registered with this 
DataDomain.
-        * <p>
-        * Filter ordering note: filters are applied in reverse order of their
-        * occurrence in the filter list. I.e. the last filter in the list 
called
-        * first in the chain.
-        *
-        * @since 4.1
-        */
-       public List<DataChannelQueryFilter> getQueryFilters() {
-               return Collections.unmodifiableList(queryFilters);
-       }
-
-       /**
-        * Returns an unmodifiable list of sync filters registered with this 
DataDomain.
-        * <p>
-        * Filter ordering note: filters are applied in reverse order of their
-        * occurrence in the filter list. I.e. the last filter in the list 
called
-        * first in the chain.
-        *
-        * @since 4.1
-        */
-       public List<DataChannelSyncFilter> getSyncFilters() {
-               return Collections.unmodifiableList(syncFilters);
-       }
-
-       /**
-        * Adds a new query filter.
-        * Also registers passed filter as an event listener, if any of its 
methods have event annotations.
-        *
-        * @since 4.1
-        */
-       public void addQueryFilter(DataChannelQueryFilter filter) {
-               // skip double listener registration, if filter already in sync 
filters list
-               if(!syncFilters.contains(filter)) {
-                       addListener(filter);
-               }
-               queryFilters.add(filter);
-       }
-
-       /**
-        * Adds a new sync filter.
-        * Also registers passed filter as an event listener, if any of its 
methods have event annotations.
-        *
-        * @since 4.1
-        */
-       public void addSyncFilter(DataChannelSyncFilter filter) {
-               // skip double listener registration, if filter already in 
query filters list
-               if(!queryFilters.contains(filter)) {
-                       addListener(filter);
-               }
-               syncFilters.add(filter);
-       }
-
-       /**
-        * Removes a query filter from the filter chain.
-        *
-        * @since 4.1
-        */
-       public void removeQueryFilter(DataChannelQueryFilter filter) {
-               queryFilters.remove(filter);
-       }
-
-       /**
-        * Removes a sync filter from the filter chain.
-        *
-        * @since 4.1
-        */
-       public void removeSyncFilter(DataChannelSyncFilter filter) {
-               syncFilters.remove(filter);
-       }
-
-       /**
-        * Adds a listener, mapping its methods to events based on annotations. 
This
-        * is a shortcut for
-        * 'getEntityResolver().getCallbackRegistry().addListener(listener)'.
-        *
-        * @since 4.0
-        */
-       public void addListener(Object listener) {
-               getEntityResolver().getCallbackRegistry().addListener(listener);
-       }
-
-       final class DataDomainQueryFilterChain implements 
DataChannelQueryFilterChain {
-
-               private int idx;
-
-               DataDomainQueryFilterChain() {
-                       idx = queryFilters.size();
-               }
-
-               @Override
-               public QueryResponse onQuery(ObjectContext originatingContext, 
Query query) {
-                       return --idx >= 0
-                                       ? 
queryFilters.get(idx).onQuery(originatingContext, query, this)
-                                       : onQueryNoFilters(originatingContext, 
query);
-               }
-       }
-
-       final class DataDomainSyncFilterChain implements 
DataChannelSyncFilterChain {
-
-               private int idx;
-
-               DataDomainSyncFilterChain() {
-                       idx = syncFilters.size();
-               }
-
-               @Override
-               public GraphDiff onSync(ObjectContext originatingContext, final 
GraphDiff changes, int syncType) {
-                       return --idx >= 0
-                                       ? 
syncFilters.get(idx).onSync(originatingContext, changes, syncType, this)
-                                       : onSyncNoFilters(originatingContext, 
changes, syncType);
-               }
-       }
-
-       /**
-        * An optional DataNode that is used for DataMaps that are not linked 
to a
-        * DataNode explicitly.
-        *
-        * @since 3.1
-        */
-       public DataNode getDefaultNode() {
-               return defaultNode;
-       }
-
-       /**
-        * @since 3.1
-        */
-       public void setDefaultNode(DataNode defaultNode) {
-               this.defaultNode = defaultNode;
-       }
-
-       /**
-        * Returns a maximum number of object IDs to match in a single query for
-        * queries that select objects based on collection of ObjectIds. This
-        * affects queries generated by Cayenne when processing paginated 
queries
-        * and DISJOINT_BY_ID prefetches and is intended to address database
-        * limitations on the size of SQL statements as well as to cap memory 
use in
-        * Cayenne when generating such queries. The default is 10000. It can be
-        * changed either by calling {@link #setMaxIdQualifierSize(int)} or 
changing
-        * the value for property
-        * {@link Constants#MAX_ID_QUALIFIER_SIZE_PROPERTY}.
-        *
-        * @since 3.1
-        */
-       public int getMaxIdQualifierSize() {
-               return maxIdQualifierSize;
-       }
-
-       /**
-        * @since 3.1
-        */
-       public void setMaxIdQualifierSize(int maxIdQualifierSize) {
-               this.maxIdQualifierSize = maxIdQualifierSize;
-       }
-
-       TransactionManager getTransactionManager() {
-               return transactionManager;
-       }
-
-       AdhocObjectFactory getObjectFactory() {
-               return objectFactory;
-       }
+     * @since 3.1
+     */
+    protected int maxIdQualifierSize;
+
+    /**
+     * @since 4.1
+     */
+    protected List<DataChannelQueryFilter> queryFilters;
+
+    /**
+     * @since 4.1
+     */
+    protected List<DataChannelSyncFilter> syncFilters;
+
+    /**
+     * @since 4.2
+     */
+    @Inject
+    protected DataDomainFlushActionFactory flushActionFactory;
+
+    /**
+     * @since 4.2
+     */
+    @Inject
+    protected AdhocObjectFactory objectFactory;
+
+    protected Map<String, DataNode> nodes;
+    protected Map<String, DataNode> nodesByDataMapName;
+    protected DataNode defaultNode;
+    protected Map<String, String> properties;
+
+    protected EntityResolver entityResolver;
+    protected DataRowStore sharedSnapshotCache;
+    protected String name;
+    protected QueryCache queryCache;
+
+    // these are initialized from properties...
+    protected boolean sharedCacheEnabled;
+    protected boolean validatingObjectsOnCommit;
+
+    /**
+     * @since 1.2
+     */
+    protected EventManager eventManager;
+
+    /**
+     * @since 1.2
+     */
+    protected EntitySorter entitySorter;
+
+    protected boolean stopped;
+
+    /**
+     * Creates a DataDomain and assigns it a name.
+     */
+    public DataDomain(String name) {
+        init(name);
+        resetProperties();
+    }
+
+    /**
+     * Creates new DataDomain.
+     *
+     * @param name       DataDomain name. Domain can be located using its name 
in the
+     *                   Configuration object.
+     * @param properties A Map containing domain configuration properties.
+     * @deprecated since 4.0 unused
+     */
+    @Deprecated
+    public DataDomain(String name, Map<String, String> properties) {
+        init(name);
+        initWithProperties(properties);
+    }
+
+    private void init(String name) {
+
+        this.queryFilters = new CopyOnWriteArrayList<>();
+        this.syncFilters = new CopyOnWriteArrayList<>();
+        this.nodesByDataMapName = new ConcurrentHashMap<>();
+        this.nodes = new ConcurrentHashMap<>();
+
+        // properties are read-only, so no need for concurrent map, or any
+        // specific map
+        // for that matter
+        this.properties = Collections.emptyMap();
+
+        setName(name);
+    }
+
+    /**
+     * Checks that Domain is not stopped. Throws DomainStoppedException
+     * otherwise.
+     *
+     * @since 3.0
+     */
+    protected void checkStopped() throws DomainStoppedException {
+        if (stopped) {
+            throw new DomainStoppedException("Domain " + name
+                    + " was shutdown and can no longer be used to access the 
database");
+        }
+    }
+
+    /**
+     * @since 3.1
+     */
+    public EntitySorter getEntitySorter() {
+        return entitySorter;
+    }
+
+    /**
+     * @since 3.1
+     */
+    public void setEntitySorter(EntitySorter entitySorter) {
+        this.entitySorter = entitySorter;
+    }
+
+    /**
+     * @since 1.1
+     */
+    protected void resetProperties() {
+        properties = Collections.emptyMap();
+
+        sharedCacheEnabled = SHARED_CACHE_ENABLED_DEFAULT;
+        validatingObjectsOnCommit = VALIDATING_OBJECTS_ON_COMMIT_DEFAULT;
+    }
+
+    /**
+     * Reinitializes domain state with a new set of properties.
+     *
+     * @since 1.1
+     * @deprecated since 4.0 properties are processed by the DI provider.
+     */
+    @Deprecated
+    public void initWithProperties(Map<String, String> properties) {
+
+        // clone properties to ensure that it is read-only internally
+        properties = properties != null ? new HashMap<>(properties) : 
Collections.emptyMap();
+
+        String sharedCacheEnabled = 
properties.get(SHARED_CACHE_ENABLED_PROPERTY);
+        String validatingObjectsOnCommit = 
properties.get(VALIDATING_OBJECTS_ON_COMMIT_PROPERTY);
+
+        // init ivars from properties
+        this.sharedCacheEnabled = (sharedCacheEnabled != null) ? 
"true".equalsIgnoreCase(sharedCacheEnabled)
+                : SHARED_CACHE_ENABLED_DEFAULT;
+        this.validatingObjectsOnCommit = (validatingObjectsOnCommit != null) ? 
"true"
+                .equalsIgnoreCase(validatingObjectsOnCommit) : 
VALIDATING_OBJECTS_ON_COMMIT_DEFAULT;
+
+        this.properties = properties;
+    }
+
+    /**
+     * Returns EventManager used by this DataDomain.
+     *
+     * @since 1.2
+     */
+    public EventManager getEventManager() {
+        return eventManager;
+    }
+
+    /**
+     * Sets EventManager used by this DataDomain.
+     *
+     * @since 1.2
+     */
+    public void setEventManager(EventManager eventManager) {
+        this.eventManager = eventManager;
+
+        if (sharedSnapshotCache != null) {
+            sharedSnapshotCache.setEventManager(eventManager);
+        }
+    }
+
+    /**
+     * Returns "name" property value.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets "name" property to a new value.
+     */
+    public synchronized void setName(String name) {
+        this.name = name;
+        if (sharedSnapshotCache != null) {
+            this.sharedSnapshotCache.setName(name);
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if DataContexts produced by this DataDomain 
are
+     * using shared DataRowStore. Returns <code>false</code> if each 
DataContext
+     * would work with its own DataRowStore. Note that this setting can be
+     * overwritten per DataContext.
+     */
+    public boolean isSharedCacheEnabled() {
+        return sharedCacheEnabled;
+    }
+
+    public void setSharedCacheEnabled(boolean sharedCacheEnabled) {
+        this.sharedCacheEnabled = sharedCacheEnabled;
+    }
+
+    /**
+     * Returns whether child DataContexts default behavior is to perform object
+     * validation before commit is executed.
+     *
+     * @since 1.1
+     */
+    public boolean isValidatingObjectsOnCommit() {
+        return validatingObjectsOnCommit;
+    }
+
+    /**
+     * Sets the property defining whether child DataContexts should perform
+     * object validation before commit is executed.
+     *
+     * @since 1.1
+     */
+    public void setValidatingObjectsOnCommit(boolean flag) {
+        this.validatingObjectsOnCommit = flag;
+    }
+
+    /**
+     * @return a Map of properties for this DataDomain.
+     * @since 1.1
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * Returns snapshots cache for this DataDomain, lazily initializing it on
+     * the first call if 'sharedCacheEnabled' flag is true.
+     */
+    public DataRowStore getSharedSnapshotCache() {
+        if (sharedSnapshotCache == null && sharedCacheEnabled) {
+            this.sharedSnapshotCache = nonNullSharedSnapshotCache();
+        }
+
+        return sharedSnapshotCache;
+    }
+
+    /**
+     * Returns a guaranteed non-null shared snapshot cache regardless of the
+     * 'sharedCacheEnabled' flag setting.
+     */
+    synchronized DataRowStore nonNullSharedSnapshotCache() {
+        if (sharedSnapshotCache == null) {
+            this.sharedSnapshotCache = 
dataRowStoreFactory.createDataRowStore(name);
+        }
+
+        return sharedSnapshotCache;
+    }
+
+    /**
+     * Shuts down the previous cache instance, sets cache to the new
+     * DataSowStore instance and updates two properties of the new 
DataSowStore:
+     * name and eventManager.
+     */
+    public synchronized void setSharedSnapshotCache(DataRowStore 
snapshotCache) {
+        if (this.sharedSnapshotCache != snapshotCache) {
+            if (this.sharedSnapshotCache != null) {
+                this.sharedSnapshotCache.shutdown();
+            }
+            this.sharedSnapshotCache = snapshotCache;
+
+            if (snapshotCache != null) {
+                snapshotCache.setEventManager(getEventManager());
+                snapshotCache.setName(getName());
+            }
+        }
+    }
+
+    public void addDataMap(DataMap dataMap) {
+        getEntityResolver().addDataMap(dataMap);
+        refreshEntitySorter();
+    }
+
+    /**
+     * @since 3.1
+     */
+    public DataMap getDataMap(String mapName) {
+        return getEntityResolver().getDataMap(mapName);
+    }
+
+    /**
+     * Removes named DataMap from this DataDomain and any underlying DataNodes
+     * that include it.
+     *
+     * @since 3.1
+     */
+    public void removeDataMap(String mapName) {
+        DataMap map = getDataMap(mapName);
+        if (map == null) {
+            return;
+        }
+
+        // remove from data nodes
+        for (DataNode node : nodes.values()) {
+            node.removeDataMap(mapName);
+        }
+
+        nodesByDataMapName.remove(mapName);
+
+        // remove from EntityResolver
+        getEntityResolver().removeDataMap(map);
+
+        refreshEntitySorter();
+    }
+
+    /**
+     * Removes a DataNode from DataDomain. Any maps previously associated with
+     * this node within domain will still be kept around, however they wan't be
+     * mapped to any node.
+     */
+    public void removeDataNode(String nodeName) {
+        DataNode removed = nodes.remove(nodeName);
+        if (removed != null) {
+            removed.setEntityResolver(null);
+            nodesByDataMapName.values().removeIf(dataNode -> dataNode == 
removed);
+        }
+    }
+
+    /**
+     * Returns a collection of registered DataMaps.
+     */
+    public Collection<DataMap> getDataMaps() {
+        return getEntityResolver().getDataMaps();
+    }
+
+    /**
+     * Returns an unmodifiable collection of DataNodes associated with this
+     * domain.
+     */
+    public Collection<DataNode> getDataNodes() {
+        return Collections.unmodifiableCollection(nodes.values());
+    }
+
+    /**
+     * Adds new DataNode.
+     */
+    public void addNode(DataNode node) {
+
+        // add node to name->node map
+        nodes.put(node.getName(), node);
+        node.setEntityResolver(getEntityResolver());
+
+        // add node to "ent name->node" map
+        for (DataMap map : node.getDataMaps()) {
+            addDataMap(map);
+            nodesByDataMapName.put(map.getName(), node);
+        }
+    }
+
+    /**
+     * Returns registered DataNode whose name matches <code>name</code>
+     * parameter.
+     *
+     * @since 3.1
+     */
+    public DataNode getDataNode(String nodeName) {
+        return nodes.get(nodeName);
+    }
+
+    /**
+     * Returns a DataNode that should handle queries for all entities in a
+     * DataMap.
+     *
+     * @since 1.1
+     */
+    public DataNode lookupDataNode(DataMap map) {
+
+        DataNode node = nodesByDataMapName.get(map.getName());
+        if (node == null) {
+
+            // see if one of the node states has changed, and the map is now
+            // linked...
+            for (DataNode n : getDataNodes()) {
+                for (DataMap m : n.getDataMaps()) {
+                    if (m == map) {
+                        nodesByDataMapName.put(map.getName(), n);
+                        node = n;
+                        break;
+                    }
+                }
+
+                if (node != null) {
+                    break;
+                }
+            }
+
+            if (node == null) {
+
+                if (defaultNode != null) {
+                    nodesByDataMapName.put(map.getName(), defaultNode);
+                    node = defaultNode;
+                } else {
+                    throw new CayenneRuntimeException("No DataNode configured 
for DataMap '%s'"
+                            + " and no default DataNode set", map.getName());
+                }
+            }
+        }
+
+        return node;
+    }
+
+    /**
+     * Sets EntityResolver. If not set explicitly, DataDomain creates a default
+     * EntityResolver internally on demand.
+     *
+     * @since 1.1
+     */
+    public void setEntityResolver(EntityResolver entityResolver) {
+        this.entityResolver = entityResolver;
+    }
+
+    // creates default entity resolver if there is none set yet
+    private synchronized void createEntityResolver() {
+        if (entityResolver == null) {
+            // entity resolver will be self-indexing as we add all our maps
+            // to it as they are added to the DataDomain
+            entityResolver = new EntityResolver();
+        }
+    }
+
+    /**
+     * Shutdowns all owned data nodes and marks this domain as stopped.
+     */
+    @BeforeScopeEnd
+    public void shutdown() {
+        if (!stopped) {
+            stopped = true;
+
+            if (sharedSnapshotCache != null) {
+                sharedSnapshotCache.shutdown();
+            }
+        }
+    }
+
+    /**
+     * Routes queries to appropriate DataNodes for execution.
+     */
+    @Override
+    public void performQueries(Collection<? extends Query> queries, 
OperationObserver callback) {
+        transactionManager.performInTransaction(() -> {
+            new DataDomainLegacyQueryAction(DataDomain.this, new 
QueryChain(queries), callback).execute();
+            return null;
+        });
+    }
+
+    // ****** DataChannel methods:
+
+    /**
+     * Runs query returning generic QueryResponse.
+     *
+     * @since 1.2
+     */
+    @Override
+    public QueryResponse onQuery(ObjectContext originatingContext, Query 
query) {
+        checkStopped();
+        return new DataDomainQueryFilterChain().onQuery(originatingContext, 
query);
+    }
+
+    QueryResponse onQueryNoFilters(ObjectContext originatingContext, Query 
query) {
+        // transaction note:
+        // we don't wrap this code in transaction to reduce transaction scope 
to
+        // just the DB operation for better performance ... query action will
+        // start a transaction itself when and if needed
+        return new DataDomainQueryAction(originatingContext, DataDomain.this, 
query).execute();
+    }
+
+    /**
+     * Returns an EntityResolver that stores mapping information for this 
domain.
+     */
+    @Override
+    public EntityResolver getEntityResolver() {
+        if (entityResolver == null) {
+            createEntityResolver();
+        }
+
+        return entityResolver;
+    }
+
+    /**
+     * Only handles commit-type synchronization, ignoring any other type.
+     *
+     * @since 1.2
+     */
+    @Override
+    public GraphDiff onSync(ObjectContext originatingContext, GraphDiff 
changes, int syncType) {
+
+        checkStopped();
+
+        return new DataDomainSyncFilterChain().onSync(originatingContext, 
changes, syncType);
+    }
+
+    GraphDiff onSyncNoFilters(ObjectContext originatingContext, GraphDiff 
changes, int syncType) {
+
+        return switch (syncType) {
+            case DataChannel.ROLLBACK_CASCADE_SYNC -> onSyncRollback();
+            // "cascade" and "no_cascade" are the same from the DataDomain 
perspective
+            case DataChannel.FLUSH_NOCASCADE_SYNC, 
DataChannel.FLUSH_CASCADE_SYNC ->
+                    onSyncFlush(originatingContext, changes);
+            default -> throw new CayenneRuntimeException("Invalid 
synchronization type: %d", syncType);
+        };
+    }
+
+    GraphDiff onSyncRollback() {
+        // if there is a transaction in progress, roll it back
+
+        Transaction transaction = BaseTransaction.getThreadTransaction();
+        if (transaction != null) {
+            transaction.setRollbackOnly();
+        }
+
+        return new CompoundDiff();
+    }
+
+    GraphDiff onSyncFlush(ObjectContext originatingContext, GraphDiff 
childChanges) {
+
+        if (!(originatingContext instanceof DataContext)) {
+            throw new CayenneRuntimeException("No support for committing 
ObjectContexts that are not DataContexts yet. "
+                    + "Unsupported context: %s", originatingContext);
+        }
+
+        DataDomainFlushAction action = 
flushActionFactory.createFlushAction(this);
+        return action.flush((DataContext) originatingContext, childChanges);
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this).append("name", name).toString();
+    }
+
+    /**
+     * Returns shared {@link QueryCache} used by this DataDomain.
+     *
+     * @since 3.0
+     */
+    public QueryCache getQueryCache() {
+        return queryCache;
+    }
+
+    public void setQueryCache(QueryCache queryCache) {
+        this.queryCache = queryCache;
+    }
+
+    /**
+     * @since 4.0
+     */
+    public DataRowStoreFactory getDataRowStoreFactory() {
+        return dataRowStoreFactory;
+    }
+
+    /**
+     * @since 4.0
+     */
+    public void setDataRowStoreFactory(DataRowStoreFactory 
dataRowStoreFactory) {
+        this.dataRowStoreFactory = dataRowStoreFactory;
+    }
+
+    /**
+     * @since 3.1
+     */
+    JdbcEventLogger getJdbcEventLogger() {
+        return jdbcEventLogger;
+    }
+
+    void refreshEntitySorter() {
+        if (entitySorter != null) {
+            entitySorter.setEntityResolver(getEntityResolver());
+        }
+    }
+
+    /**
+     * Returns an unmodifiable list of query filters registered with this 
DataDomain.
+     * <p>
+     * Filter ordering note: filters are applied in reverse order of their
+     * occurrence in the filter list. I.e. the last filter in the list called
+     * first in the chain.
+     *
+     * @since 4.1
+     */
+    public List<DataChannelQueryFilter> getQueryFilters() {
+        return Collections.unmodifiableList(queryFilters);
+    }
+
+    /**
+     * Returns an unmodifiable list of sync filters registered with this 
DataDomain.
+     * <p>
+     * Filter ordering note: filters are applied in reverse order of their
+     * occurrence in the filter list. I.e. the last filter in the list called
+     * first in the chain.
+     *
+     * @since 4.1
+     */
+    public List<DataChannelSyncFilter> getSyncFilters() {
+        return Collections.unmodifiableList(syncFilters);
+    }
+
+    /**
+     * Adds a new query filter.
+     * Also registers passed filter as an event listener, if any of its 
methods have event annotations.
+     *
+     * @since 4.1
+     */
+    public void addQueryFilter(DataChannelQueryFilter filter) {
+        // skip double listener registration, if filter already in sync 
filters list
+        if (!syncFilters.contains(filter)) {
+            addListener(filter);
+        }
+        queryFilters.add(filter);
+    }
+
+    /**
+     * Adds a new sync filter.
+     * Also registers passed filter as an event listener, if any of its 
methods have event annotations.
+     *
+     * @since 4.1
+     */
+    public void addSyncFilter(DataChannelSyncFilter filter) {
+        // skip double listener registration, if filter already in query 
filters list
+        if (!queryFilters.contains(filter)) {
+            addListener(filter);
+        }
+        syncFilters.add(filter);
+    }
+
+    /**
+     * Removes a query filter from the filter chain.
+     *
+     * @since 4.1
+     */
+    public void removeQueryFilter(DataChannelQueryFilter filter) {
+        queryFilters.remove(filter);
+    }
+
+    /**
+     * Removes a sync filter from the filter chain.
+     *
+     * @since 4.1
+     */
+    public void removeSyncFilter(DataChannelSyncFilter filter) {
+        syncFilters.remove(filter);
+    }
+
+    /**
+     * Adds a listener, mapping its methods to events based on annotations. 
This
+     * is a shortcut for
+     * 'getEntityResolver().getCallbackRegistry().addListener(listener)'.
+     *
+     * @since 4.0
+     */
+    public void addListener(Object listener) {
+        getEntityResolver().getCallbackRegistry().addListener(listener);
+    }
+
+    final class DataDomainQueryFilterChain implements 
DataChannelQueryFilterChain {
+
+        private int idx;
+
+        DataDomainQueryFilterChain() {
+            idx = queryFilters.size();
+        }
+
+        @Override
+        public QueryResponse onQuery(ObjectContext originatingContext, Query 
query) {
+            return --idx >= 0
+                    ? queryFilters.get(idx).onQuery(originatingContext, query, 
this)
+                    : onQueryNoFilters(originatingContext, query);
+        }
+    }
+
+    final class DataDomainSyncFilterChain implements 
DataChannelSyncFilterChain {
+
+        private int idx;
+
+        DataDomainSyncFilterChain() {
+            idx = syncFilters.size();
+        }
+
+        @Override
+        public GraphDiff onSync(ObjectContext originatingContext, GraphDiff 
changes, int syncType) {
+            return --idx >= 0
+                    ? syncFilters.get(idx).onSync(originatingContext, changes, 
syncType, this)
+                    : onSyncNoFilters(originatingContext, changes, syncType);
+        }
+    }
+
+    /**
+     * An optional DataNode that is used for DataMaps that are not linked to a
+     * DataNode explicitly.
+     *
+     * @since 3.1
+     */
+    public DataNode getDefaultNode() {
+        return defaultNode;
+    }
+
+    /**
+     * @since 3.1
+     */
+    public void setDefaultNode(DataNode defaultNode) {
+        this.defaultNode = defaultNode;
+    }
+
+    /**
+     * Returns a maximum number of object IDs to match in a single query for
+     * queries that select objects based on collection of ObjectIds. This
+     * affects queries generated by Cayenne when processing paginated queries
+     * and DISJOINT_BY_ID prefetches and is intended to address database
+     * limitations on the size of SQL statements as well as to cap memory use 
in
+     * Cayenne when generating such queries. The default is 10000. It can be
+     * changed either by calling {@link #setMaxIdQualifierSize(int)} or 
changing
+     * the value for property
+     * {@link Constants#MAX_ID_QUALIFIER_SIZE_PROPERTY}.
+     *
+     * @since 3.1
+     */
+    public int getMaxIdQualifierSize() {
+        return maxIdQualifierSize;
+    }
+
+    /**
+     * @since 3.1
+     */
+    public void setMaxIdQualifierSize(int maxIdQualifierSize) {
+        this.maxIdQualifierSize = maxIdQualifierSize;
+    }
+
+    TransactionManager getTransactionManager() {
+        return transactionManager;
+    }
+
+    AdhocObjectFactory getObjectFactory() {
+        return objectFactory;
+    }
 }
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
 
b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
index 31145ef96..acefda9e7 100644
--- 
a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
+++ 
b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
@@ -41,8 +41,6 @@ import java.util.Map;
  */
 class DataDomainLegacyQueryAction implements QueryRouter, OperationObserver {
 
-    static final boolean DONE = true;
-
     DataDomain domain;
     OperationObserver callback;
     Query query;
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java 
b/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java
index 1cfade84d..fbffa6f23 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java
@@ -49,7 +49,6 @@ import javax.sql.DataSource;
 import java.io.PrintWriter;
 import java.sql.Connection;
 import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -216,7 +215,9 @@ public class DataNode implements QueryEngine {
         * Returns a DataNode that should handle queries for all DataMap 
components.
         *
         * @since 1.1
+        * @deprecated unused and unneeded
         */
+       @Deprecated(since = "5.0", forRemoval = true)
        public DataNode lookupDataNode(DataMap dataMap) {
                // we don't know any better than to return ourselves...
                return this;
@@ -246,7 +247,7 @@ public class DataNode implements QueryEngine {
                // upper limit.
                getAdapter().getExtendedTypes();
 
-               Connection connection = null;
+               Connection connection;
 
                try {
                        connection = this.getDataSource().getConnection();
@@ -324,7 +325,7 @@ public class DataNode implements QueryEngine {
         * @since 4.0
         */
        public RowReader<?> rowReader(RowDescriptor descriptor, QueryMetadata 
queryMetadata) {
-               return rowReader(descriptor, queryMetadata, 
Collections.<ObjAttribute, ColumnDescriptor> emptyMap());
+               return rowReader(descriptor, queryMetadata, 
Collections.emptyMap());
        }
 
        /**
@@ -461,7 +462,7 @@ public class DataNode implements QueryEngine {
          * @since 3.0
          */
         @Override
-        public boolean isWrapperFor(Class<?> iface) throws SQLException {
+        public boolean isWrapperFor(Class<?> iface) {
             return iface.isAssignableFrom(dataSource.getClass());
         }
 
@@ -481,9 +482,8 @@ public class DataNode implements QueryEngine {
          * @since 3.1
          */
         @Override
-        public Logger getParentLogger() throws SQLFeatureNotSupportedException 
{
-            // don't throw SQLFeatureNotSupported - this will break JDK 1.5
-            // runtime
+        public Logger getParentLogger() {
+            // don't throw SQLFeatureNotSupported - this will break JDK 1.5 
runtime
             throw new UnsupportedOperationException();
         }
     }
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/QueryEngine.java 
b/cayenne/src/main/java/org/apache/cayenne/access/QueryEngine.java
index 4b73fb208..dbc9deaab 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/QueryEngine.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/QueryEngine.java
@@ -19,11 +19,11 @@
 
 package org.apache.cayenne.access;
 
-import java.util.Collection;
-
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.query.Query;
 
+import java.util.Collection;
+
 /**
  * Defines methods used to run Cayenne queries.
  */
@@ -32,12 +32,10 @@ public interface QueryEngine {
     /**
      * Executes a list of queries wrapping them in its own transaction. 
Results of
      * execution are passed to {@link OperationObserver}object via its 
callback methods.
-     * 
-     * @since 1.1 The signature has changed from List to Collection.
+     *
+     * @since 1.1
      */
-    void performQueries(
-            Collection<? extends Query> queries,
-            OperationObserver resultConsumer);
+    void performQueries(Collection<? extends Query> queries, OperationObserver 
resultConsumer);
 
     /**
      * Returns a resolver for this query engine that is capable of resolving 
between
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/Query.java 
b/cayenne/src/main/java/org/apache/cayenne/query/Query.java
index dd6c52ec0..5a60aa21f 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/Query.java
+++ b/cayenne/src/main/java/org/apache/cayenne/query/Query.java
@@ -19,12 +19,10 @@
 
 package org.apache.cayenne.query;
 
-import java.io.Serializable;
-
 import org.apache.cayenne.access.QueryEngine;
-import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.QueryDescriptor;
+
+import java.io.Serializable;
 
 /**
  * Defines minimal API of a query descriptor that is executable via Cayenne.

Reply via email to