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

commit 482e800b85d1e33098a5a8bc235307fcc35829b2
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sun Jul 26 17:58:06 2026 +0300

    CAY-2985 DataDomain init and structure cleanup
---
 RELEASE-NOTES.txt                                  |  1 +
 UPGRADE.md                                         | 17 +++++++
 .../java/org/apache/cayenne/access/DataDomain.java | 41 +++------------
 .../configuration/runtime/DataDomainProvider.java  | 34 ++++++++-----
 .../org/apache/cayenne/access/DataDomainIT.java    | 58 ++++++++++++++--------
 .../DataContextFactory_DedicatedCacheIT.java       |  4 +-
 .../runtime/DataContextFactory_ValidationIT.java   | 43 ++++++++++------
 .../org/apache/cayenne/unit/CayenneProjects.java   |  2 +
 .../src/test/resources/cayenne-dedicated-cache.xml |  8 +++
 .../src/test/resources/cayenne-no-validation.xml   |  8 +++
 10 files changed, 134 insertions(+), 82 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index d1e8ca322..aa94aa8f3 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -31,6 +31,7 @@ CAY-2980 Improve model name generation
 CAY-2982 Modeler: when upgrading projects allow upgraders to send outcome 
messages
 CAY-2983 CayenneDataSource: Public-facing DataSource builder
 CAY-2984 Remove "DataChannel" injectable dependency
+CAY-2985 DataDomain init and structure cleanup
 
 Bug Fixes:
 
diff --git a/UPGRADE.md b/UPGRADE.md
index 7974bc634..f6931d43a 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -94,6 +94,23 @@ solution may be changing to "joint" prefetches.
   `DeferredValue` instead — it is a `@FunctionalInterface`, so an existing 
lambda or `Supplier` implementation can
   usually be adapted with a minimal change.
 
+* Per [CAY-2985](https://issues.apache.org/jira/browse/CAY-2985) `DataDomain` 
became mostly immutable. The `DataDomain(String)` constructor and all the 
setters 
+below were removed in favor of a single full constructor that takes every 
collaborator and setting. Only DataNodes, 
+DataMaps, filters and listeners can still be added (and removed) after 
creation. Replacements for the removed setters:
+  - `setName(String)` — the name comes from the project XML, and can be 
overridden with the
+    `cayenne.domain.name` property (`Constants.DOMAIN_NAME_PROPERTY`).
+  - `setEntityResolver(EntityResolver)` — keep using `addDataMap(..)` / 
`removeDataMap(..)` to change resolver contents.
+  - `setEntitySorter(EntitySorter)` — the sorter is produced by the new 
`EntitySorterFactory` DI service. Bind your
+    own `EntitySorterFactory` to replace it.
+  - `setEventManager(EventManager)` — bind `EventManager` in a DI module 
instead.
+  - `setQueryCache(QueryCache)` — bind `QueryCache` in a DI module instead.
+  - `setSharedSnapshotCache(DataRowStore)`, 
`setDataRowStoreFactory(DataRowStoreFactory)` and
+    `getDataRowStoreFactory()` — bind `DataRowStoreFactory` in a DI module to 
customize the cache.
+  - `setSharedCacheEnabled(boolean)` — use the "Shared Cache" checkbox in the 
Modeler.
+  - `setValidatingObjectsOnCommit(boolean)` — use the "Object Validation" 
checkbox in the Modeler.
+  - `setMaxIdQualifierSize(int)` — use the `cayenne.max_id_qualifier_size` 
property
+    (`Constants.MAX_ID_QUALIFIER_SIZE_PROPERTY`).
+
 ## Upgrading to 5.0-M2
 
 * Per [CAY-2947](https://issues.apache.org/jira/browse/CAY-2947) the 
`cayenne-commitlog` artifact has been removed. Commit log support is now part 
of the
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 3ced1dab4..80ce99d1b 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
@@ -69,18 +69,18 @@ public class DataDomain implements DataChannel {
     protected final EventManager eventManager;
     protected final EntitySorter entitySorter;
     protected final QueryCache queryCache;
+    protected final DataRowStore sharedSnapshotCache;
 
     protected final EntityResolver entityResolver;
     protected final int maxIdQualifierSize;
+    protected final boolean validatingObjectsOnCommit;
 
     protected List<DataChannelQueryFilter> queryFilters;
     protected List<DataChannelSyncFilter> syncFilters;
     protected Map<String, DataNode> nodes;
     protected Map<String, DataNode> nodesByDataMapName;
     protected DataNode defaultNode;
-    protected DataRowStore sharedSnapshotCache;
 
-    protected boolean validatingObjectsOnCommit;
     protected boolean stopped;
 
     public DataDomain(
@@ -92,7 +92,9 @@ public class DataDomain implements DataChannel {
             AdhocObjectFactory objectFactory,
             EventManager eventManager,
             QueryCache queryCache,
+            DataRowStore sharedSnapshotCache,
             int maxIdQualifierSize,
+            boolean validatingObjectsOnCommit,
 
             EntityResolver entityResolver,
             EntitySorter entitySorter
@@ -106,8 +108,10 @@ public class DataDomain implements DataChannel {
         this.eventManager = eventManager;
         this.entitySorter = entitySorter;
         this.queryCache = queryCache;
+        this.sharedSnapshotCache = sharedSnapshotCache;
         this.entityResolver = entityResolver;
         this.maxIdQualifierSize = maxIdQualifierSize;
+        this.validatingObjectsOnCommit = validatingObjectsOnCommit;
 
         this.queryFilters = new CopyOnWriteArrayList<>();
         this.syncFilters = new CopyOnWriteArrayList<>();
@@ -171,42 +175,13 @@ public class DataDomain implements DataChannel {
     }
 
     /**
-     * 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;
-    }
-
-    /**
-     * Returns snapshots cache for this DataDomain, lazily initializing it on
-     * the first call if 'sharedCacheEnabled' flag is true.
+     * Returns the snapshots cache shared by the DataContexts of this 
DataDomain, or null if the domain runs with
+     * per-DataContext caches.
      */
     public DataRowStore getSharedSnapshotCache() {
         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);
         entitySorter.reindex();
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java
 
b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java
index 8ce33a81c..c405ebc31 100644
--- 
a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java
+++ 
b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java
@@ -151,6 +151,10 @@ public class DataDomainProvider implements 
Provider<DataDomain> {
         EntityResolver entityResolver = createEntityResolver(descriptor);
         EntitySorter entitySorter = 
entitySorterFactory.createEntitySorter(entityResolver);
 
+        Map<String, String> properties = descriptor.getProperties();
+        boolean validatingOnCommit = "true".equals(
+                properties.getOrDefault(VALIDATING_OBJECTS_ON_COMMIT_PROPERTY, 
VALIDATING_OBJECTS_ON_COMMIT_DEFAULT));
+
         DataDomain domain = new DataDomain(
                 descriptor.getName(),
                 transactionManager,
@@ -159,23 +163,13 @@ public class DataDomainProvider implements 
Provider<DataDomain> {
                 objectFactory,
                 eventManager,
                 new NestedQueryCache(queryCache),
+                createSharedSnapshotCache(descriptor),
                 
runtimeProperties.getInt(Constants.MAX_ID_QUALIFIER_SIZE_PROPERTY, -1),
+                validatingOnCommit,
                 entityResolver,
                 entitySorter
         );
 
-        Map<String, String> properties = descriptor.getProperties();
-
-        boolean sharedCache = 
"true".equals(properties.getOrDefault(SHARED_CACHE_ENABLED_PROPERTY, 
SHARED_CACHE_ENABLED_DEFAULT));
-        if (sharedCache) {
-            DataRowStore cache = 
injector.getInstance(DataRowStoreFactory.class).createDataRowStore(descriptor.getName());
-            // TODO: setSharedSnapshotCache mutates cache internally
-            domain.setSharedSnapshotCache(cache);
-        }
-
-        boolean validatingOnCommit = 
"true".equals(properties.getOrDefault(VALIDATING_OBJECTS_ON_COMMIT_PROPERTY, 
VALIDATING_OBJECTS_ON_COMMIT_DEFAULT));
-        domain.setValidatingObjectsOnCommit(validatingOnCommit);
-
         for (DataNodeDescriptor nodeDescriptor : 
descriptor.getNodeDescriptors()) {
             addDataNode(domain, nodeDescriptor);
         }
@@ -201,6 +195,22 @@ public class DataDomainProvider implements 
Provider<DataDomain> {
         return domain;
     }
 
+    /**
+     * Returns a snapshot cache shared by all DataContexts of the domain, or 
null if the descriptor turns the shared
+     * cache off, and each DataContext is to use a cache of its own.
+     *
+     * @since 5.0
+     */
+    protected DataRowStore createSharedSnapshotCache(DataChannelDescriptor 
descriptor) {
+
+        String sharedCache = descriptor.getProperties()
+                .getOrDefault(SHARED_CACHE_ENABLED_PROPERTY, 
SHARED_CACHE_ENABLED_DEFAULT);
+
+        return "true".equals(sharedCache)
+                ? 
injector.getInstance(DataRowStoreFactory.class).createDataRowStore(descriptor.getName())
+                : null;
+    }
+
     /**
      * Returns a DataNode to use for DataMaps not linked to any node, either 
the one named in the descriptor, or the
      * only node of the domain. Returns null if neither applies.
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataDomainIT.java 
b/cayenne/src/test/java/org/apache/cayenne/access/DataDomainIT.java
index f7eb4f4a5..8050e280a 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataDomainIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataDomainIT.java
@@ -23,7 +23,8 @@ import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.annotation.PostAdd;
-import org.apache.cayenne.event.DefaultEventManager;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.event.EventManager;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.map.ObjEntity;
@@ -43,7 +44,8 @@ import static org.junit.jupiter.api.Assertions.*;
 public class DataDomainIT {
 
     @RegisterExtension
-    final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.TESTMAP_PROJECT);
+    final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.TESTMAP_PROJECT)
+            .withExtraModules(b -> 
b.bind(DataRowStoreFactory.class).to(ShutdownTrackingRowStoreFactory.class));
 
     @Test
     public void lookupDataNode() {
@@ -146,26 +148,11 @@ public class DataDomainIT {
     public void shutdownCache() {
         DataDomain domain = env.runtime().getDataDomain();
 
-        final boolean[] cacheShutdown = new boolean[1];
-
-        DefaultEventManager eventManager = new DefaultEventManager();
-        try {
-            DataRowStore cache = new DataRowStore("Y",
-                    DefaultDataRowStoreFactory.SNAPSHOT_CACHE_SIZE_DEFAULT,
-                    eventManager) {
-                @Override
-                public void shutdown() {
-                    cacheShutdown[0] = true;
-                }
-            };
-
-            domain.setSharedSnapshotCache(cache);
-            domain.shutdown();
-        } finally {
-            eventManager.shutdown();
-        }
+        ShutdownTrackingRowStore cache = (ShutdownTrackingRowStore) 
domain.getSharedSnapshotCache();
+        assertFalse(cache.shutdown);
 
-        assertTrue(cacheShutdown[0]);
+        domain.shutdown();
+        assertTrue(cache.shutdown);
     }
 
     @Test
@@ -190,6 +177,35 @@ public class DataDomainIT {
         assertEquals("e:Painting;", listener.getAndReset());
     }
 
+    public static class ShutdownTrackingRowStoreFactory implements 
DataRowStoreFactory {
+
+        private final EventManager eventManager;
+
+        public ShutdownTrackingRowStoreFactory(@Inject EventManager 
eventManager) {
+            this.eventManager = eventManager;
+        }
+
+        @Override
+        public DataRowStore createDataRowStore(String name) {
+            return new ShutdownTrackingRowStore(name, eventManager);
+        }
+    }
+
+    public static class ShutdownTrackingRowStore extends DataRowStore {
+
+        boolean shutdown;
+
+        public ShutdownTrackingRowStore(String name, EventManager 
eventManager) {
+            super(name, 
DefaultDataRowStoreFactory.SNAPSHOT_CACHE_SIZE_DEFAULT, eventManager);
+        }
+
+        @Override
+        public void shutdown() {
+            shutdown = true;
+            super.shutdown();
+        }
+    }
+
     class PostAddListener {
 
         StringBuilder callbackBuffer = new StringBuilder();
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_DedicatedCacheIT.java
 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_DedicatedCacheIT.java
index 2be76e9d9..2a254f55f 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_DedicatedCacheIT.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_DedicatedCacheIT.java
@@ -30,14 +30,14 @@ import static org.junit.jupiter.api.Assertions.*;
 
 public class DataContextFactory_DedicatedCacheIT {
 
+    // the project turns the shared snapshot cache off, so each DataContext 
gets a cache of its own
     @RegisterExtension
-    static final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.MULTI_TIER_PROJECT);
+    static final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.DEDICATED_CACHE_PROJECT);
 
     @Test
     public void createDataContextWithDedicatedCache() {
 
         DataDomain domain = env.runtime().getDataDomain();
-        domain.setSharedSnapshotCache(null);
 
         DataContext c3 = (DataContext) env.runtime().getInjector()
                 .getInstance(ObjectContextFactory.class)
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_ValidationIT.java
 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_ValidationIT.java
index 66a735141..44d4036b3 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_ValidationIT.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataContextFactory_ValidationIT.java
@@ -19,34 +19,49 @@
 package org.apache.cayenne.configuration.runtime;
 
 import org.apache.cayenne.access.DataContext;
-import org.apache.cayenne.access.DataDomain;
 import org.apache.cayenne.configuration.ObjectContextFactory;
 import org.apache.cayenne.unit.CayenneProjects;
 import org.apache.cayenne.unit.CayenneTestsEnv;
+import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+/**
+ * "validatingObjectsOnCommit" is immutable and comes from the project 
descriptor, so each of its values needs its own
+ * stack.
+ */
 public class DataContextFactory_ValidationIT {
 
-    @RegisterExtension
-    static final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.MULTI_TIER_PROJECT);
-
-    @Test
-    public void createDataContextValidation() {
-        DataDomain domain = env.runtime().getDataDomain();
-        domain.setValidatingObjectsOnCommit(true);
+    private static DataContext createContext(CayenneTestsEnv env) {
+        return (DataContext) 
env.runtime().getInjector().getInstance(ObjectContextFactory.class).createContext();
+    }
 
-        ObjectContextFactory factory = 
env.runtime().getInjector().getInstance(ObjectContextFactory.class);
-        DataContext c1 = (DataContext) factory.createContext();
-        assertTrue(c1.isValidatingObjectsOnCommit());
+    @Nested
+    public class ValidatingOnCommit {
 
-        domain.setValidatingObjectsOnCommit(false);
+        // validation on commit is on by default
+        @RegisterExtension
+        final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.MULTI_TIER_PROJECT);
 
-        DataContext c2 = (DataContext) factory.createContext();
-        assertFalse(c2.isValidatingObjectsOnCommit());
+        @Test
+        public void createDataContext() {
+            assertTrue(createContext(env).isValidatingObjectsOnCommit());
+        }
     }
 
+    @Nested
+    public class NotValidatingOnCommit {
+
+        // the project turns validation on commit off
+        @RegisterExtension
+        final CayenneTestsEnv env = 
CayenneTestsEnv.forProject(CayenneProjects.NO_VALIDATION_PROJECT);
+
+        @Test
+        public void createDataContext() {
+            assertFalse(createContext(env).isValidatingObjectsOnCommit());
+        }
+    }
 }
diff --git a/cayenne/src/test/java/org/apache/cayenne/unit/CayenneProjects.java 
b/cayenne/src/test/java/org/apache/cayenne/unit/CayenneProjects.java
index b7648d003..55c69357b 100644
--- a/cayenne/src/test/java/org/apache/cayenne/unit/CayenneProjects.java
+++ b/cayenne/src/test/java/org/apache/cayenne/unit/CayenneProjects.java
@@ -26,6 +26,7 @@ public class CayenneProjects {
     public static final String CAY_2032 = "cayenne-cay-2032.xml";
     public static final String COMPOUND_PROJECT = "cayenne-compound.xml";
     public static final String DATE_TIME_PROJECT = "cayenne-date-time.xml";
+    public static final String DEDICATED_CACHE_PROJECT = 
"cayenne-dedicated-cache.xml";
     public static final String DOMAIN_PROVIDER_PROJECT = 
"cayenne-domain-provider.xml";
     public static final String EMBEDDABLE_PROJECT = "cayenne-embeddable.xml";
     public static final String EMPTY_PROJECT = "cayenne-empty.xml";
@@ -48,6 +49,7 @@ public class CayenneProjects {
     public static final String MULTI_TIER_PROJECT = "cayenne-multi-tier.xml";
     public static final String MULTINODE_PROJECT = "cayenne-multinode.xml";
     public static final String NO_PK_PROJECT = "cayenne-no-pk.xml";
+    public static final String NO_VALIDATION_PROJECT = 
"cayenne-no-validation.xml";
     public static final String NUMERIC_TYPES_PROJECT = 
"cayenne-numeric-types.xml";
     public static final String ONEWAY_PROJECT = "cayenne-oneway-rels.xml";
     public static final String PEOPLE_PROJECT = "cayenne-people.xml";
diff --git a/cayenne/src/test/resources/cayenne-dedicated-cache.xml 
b/cayenne/src/test/resources/cayenne-dedicated-cache.xml
new file mode 100644
index 000000000..9b174919e
--- /dev/null
+++ b/cayenne/src/test/resources/cayenne-dedicated-cache.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain xmlns="http://cayenne.apache.org/schema/12/domain";
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+        xsi:schemaLocation="http://cayenne.apache.org/schema/12/domain 
https://cayenne.apache.org/schema/12/domain.xsd";
+        project-version="12">
+       <property name="cayenne.DataDomain.sharedCache" value="false"/>
+       <map name="multi-tier"/>
+</domain>
diff --git a/cayenne/src/test/resources/cayenne-no-validation.xml 
b/cayenne/src/test/resources/cayenne-no-validation.xml
new file mode 100644
index 000000000..be728c4bd
--- /dev/null
+++ b/cayenne/src/test/resources/cayenne-no-validation.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain xmlns="http://cayenne.apache.org/schema/12/domain";
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+        xsi:schemaLocation="http://cayenne.apache.org/schema/12/domain 
https://cayenne.apache.org/schema/12/domain.xsd";
+        project-version="12">
+       <property name="cayenne.DataDomain.validatingObjectsOnCommit" 
value="false"/>
+       <map name="multi-tier"/>
+</domain>

Reply via email to