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>
