CAY-1972 A property to override DataSources of multi-module projects
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/15297cca Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/15297cca Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/15297cca Branch: refs/heads/CAY-1946_1 Commit: 15297ccafa761bddb89eac98b35806d777cbbe74 Parents: 3c51a77 Author: aadamchik <aadamc...@apache.org> Authored: Sat Nov 22 18:06:09 2014 +0300 Committer: aadamchik <aadamc...@apache.org> Committed: Sat Nov 22 19:03:31 2014 +0300 ---------------------------------------------------------------------- .../apache/cayenne/configuration/Constants.java | 298 ++++++++++--------- .../server/DataDomainProvider.java | 241 ++++++++------- .../server/ServerRuntimeBuilder.java | 52 +++- .../server/SyntheticNodeDataDomainProvider.java | 27 +- .../server/ServerRuntimeBuilderIT.java | 109 +++++++ .../server/ServerRuntimeBuilderTest.java | 53 +++- .../ServerRuntimeBuilder_InAction_IT.java | 88 ------ docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + docs/doc/src/main/resources/UPGRADE.txt | 8 + 9 files changed, 491 insertions(+), 386 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java index 12953e6..1b0f887 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java @@ -26,151 +26,159 @@ package org.apache.cayenne.configuration; */ public interface Constants { - // DI "collections" + // DI "collections" + + /** + * A DI container key for the Map<String, String> storing properties + * used by built-in Cayenne service. + */ + public static final String PROPERTIES_MAP = "cayenne.properties"; + + /** + * A DI container key for the List<DbAdapterDetector> that contains + * objects that can discover the type of current database and install the + * correct DbAdapter in runtime. + */ + public static final String SERVER_ADAPTER_DETECTORS_LIST = "cayenne.server.adapter_detectors"; - /** - * A DI container key for the Map<String, String> storing properties - * used by built-in Cayenne service. - */ - public static final String PROPERTIES_MAP = "cayenne.properties"; - - /** - * A DI container key for the List<DbAdapterDetector> that contains - * objects that can discover the type of current database and install the - * correct DbAdapter in runtime. - */ - public static final String SERVER_ADAPTER_DETECTORS_LIST = "cayenne.server.adapter_detectors"; - - /** - * A DI container key for the List<DataChannelFilter> storing - * DataDomain filters. - */ - public static final String SERVER_DOMAIN_FILTERS_LIST = "cayenne.server.domain_filters"; - - /** - * A DI container key for the List<String> storing locations of the - * one of more project configuration files. - */ - public static final String SERVER_PROJECT_LOCATIONS_LIST = "cayenne.server.project_locations"; - - /** - * A DI container key for the List<ExtendedType> storing default - * adapter-agnostic ExtendedTypes. - */ - public static final String SERVER_DEFAULT_TYPES_LIST = "cayenne.server.default_types"; - - /** - * A DI container key for the List<ExtendedType> storing a - * user-provided ExtendedTypes. - */ - public static final String SERVER_USER_TYPES_LIST = "cayenne.server.user_types"; - - /** - * A DI container key for the List<ExtendedTypeFactory> storing - * default and user-provided ExtendedTypeFactories. - */ - public static final String SERVER_TYPE_FACTORIES_LIST = "cayenne.server.type_factories"; - - /** - * A server-side DI container key for the Map<String, String> storing - * event bridge properties passed to the ROP client on bootstrap. - */ - public static final String SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP = "cayenne.server.rop_event_bridge_properties"; - - // Runtime properties - - public static final String JDBC_DRIVER_PROPERTY = "cayenne.jdbc.driver"; - - public static final String JDBC_URL_PROPERTY = "cayenne.jdbc.url"; - - public static final String JDBC_USERNAME_PROPERTY = "cayenne.jdbc.username"; - - public static final String JDBC_PASSWORD_PROPERTY = "cayenne.jdbc.password"; - - public static final String JDBC_MIN_CONNECTIONS_PROPERTY = "cayenne.jdbc.min_connections"; - - public static final String JDBC_MAX_CONNECTIONS_PROPERTY = "cayenne.jdbc.max_connections"; - - /** - * An integer property defining the maximum number of entries in the query - * cache. Note that not all QueryCache providers may respect this property. - * MapQueryCache uses it, but the rest would use alternative configuration - * methods. - */ - public static final String QUERY_CACHE_SIZE_PROPERTY = "cayenne.querycache.size"; - - /** - * A boolean property defining whether cross-contexts synchronization is - * enabled. Possible values are "true" or "false". - */ - public static final String SERVER_CONTEXTS_SYNC_PROPERTY = "cayenne.server.contexts_sync_strategy"; - - /** - * A String property that defines how ObjectContexts should retain cached - * committed objects. Possible values are "weak", "soft", "hard". - */ - public static final String SERVER_OBJECT_RETAIN_STRATEGY_PROPERTY = "cayenne.server.object_retain_strategy"; - - /** - * A boolean property that defines whether runtime should use external - * transactions. Possible values are "true" or "false". - */ - public static final String SERVER_EXTERNAL_TX_PROPERTY = "cayenne.server.external_tx"; - - public static final String ROP_SERVICE_URL_PROPERTY = "cayenne.rop.service_url"; - - public static final String ROP_SERVICE_USERNAME_PROPERTY = "cayenne.rop.service_username"; - - public static final String ROP_SERVICE_PASSWORD_PROPERTY = "cayenne.rop.service_password"; - - public static final String ROP_SERVICE_SHARED_SESSION_PROPERTY = "cayenne.rop.shared_session_name"; - - public static final String ROP_SERVICE_TIMEOUT_PROPERTY = "cayenne.rop.service_timeout"; - - public static final String ROP_CHANNEL_EVENTS_PROPERTY = "cayenne.rop.channel_events"; - - public static final String ROP_CONTEXT_CHANGE_EVENTS_PROPERTY = "cayenne.rop.context_change_events"; - - public static final String ROP_CONTEXT_LIFECYCLE_EVENTS_PROPERTY = "cayenne.rop.context_lifecycle_events"; - - /** - * The name of the {@link org.apache.cayenne.event.EventBridgeFactory} that - * is passed from the ROP server to the client. Client would instantiate the - * factory to receive events from the server. Note that this property is - * stored in {@link #SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP}, not - * {@link #PROPERTIES_MAP}. - */ - public static final String SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY = "cayenne.server.rop_event_bridge_factory"; - - /** - * A property that defines a maximum number of ID qualifiers in where clause - * of queries that are generated for example in - * {@link org.apache.cayenne.access.IncrementalFaultList} or in - * DISJOINT_BY_ID prefetch processing. This is needed to avoid where clause - * size limitations and memory usage efficiency. - */ - public static final String SERVER_MAX_ID_QUALIFIER_SIZE_PROPERTY = "cayenne.server.max_id_qualifier_size"; - - /** - * Defines a maximum time in milliseconds that a connection request could - * wait in the connection queue. After this period expires, an exception - * will be thrown in the calling method. A value of zero will make the - * thread wait until a connection is available with no time out. Defaults to - * 20 seconds. - */ - public static final String SERVER_MAX_QUEUE_WAIT_TIME = "cayenne.jdbc.max_wait"; - - /** Defines if database uses case-insensitive collation */ - public final static String CI_PROPERTY = "cayenne.runtime.db.collation.assume.ci"; - - /** - * A integer property that enables logging for just long running queries - * (rather than all queries). The value is the minimum number of - * milliseconds a query must run before is logged. A value less than or - * equal to zero (the default) disables this feature. - * - * @since 4.0 - * */ - public final static String QUERY_EXECUTION_TIME_LOGGING_THRESHOLD_PROPERTY = "cayenne.server.query_execution_time_logging_threshold"; + /** + * A DI container key for the List<DataChannelFilter> storing + * DataDomain filters. + */ + public static final String SERVER_DOMAIN_FILTERS_LIST = "cayenne.server.domain_filters"; + + /** + * A DI container key for the List<String> storing locations of the + * one of more project configuration files. + */ + public static final String SERVER_PROJECT_LOCATIONS_LIST = "cayenne.server.project_locations"; + + /** + * A DI container key for the List<ExtendedType> storing default + * adapter-agnostic ExtendedTypes. + */ + public static final String SERVER_DEFAULT_TYPES_LIST = "cayenne.server.default_types"; + + /** + * A DI container key for the List<ExtendedType> storing a + * user-provided ExtendedTypes. + */ + public static final String SERVER_USER_TYPES_LIST = "cayenne.server.user_types"; + + /** + * A DI container key for the List<ExtendedTypeFactory> storing + * default and user-provided ExtendedTypeFactories. + */ + public static final String SERVER_TYPE_FACTORIES_LIST = "cayenne.server.type_factories"; + + /** + * A server-side DI container key for the Map<String, String> storing + * event bridge properties passed to the ROP client on bootstrap. + */ + public static final String SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP = "cayenne.server.rop_event_bridge_properties"; + + // Runtime properties + + public static final String JDBC_DRIVER_PROPERTY = "cayenne.jdbc.driver"; + + public static final String JDBC_URL_PROPERTY = "cayenne.jdbc.url"; + + public static final String JDBC_USERNAME_PROPERTY = "cayenne.jdbc.username"; + + public static final String JDBC_PASSWORD_PROPERTY = "cayenne.jdbc.password"; + + public static final String JDBC_MIN_CONNECTIONS_PROPERTY = "cayenne.jdbc.min_connections"; + + public static final String JDBC_MAX_CONNECTIONS_PROPERTY = "cayenne.jdbc.max_connections"; + + /** + * An integer property defining the maximum number of entries in the query + * cache. Note that not all QueryCache providers may respect this property. + * MapQueryCache uses it, but the rest would use alternative configuration + * methods. + */ + public static final String QUERY_CACHE_SIZE_PROPERTY = "cayenne.querycache.size"; + + /** + * An optional name of the runtime DataDomain. If not specified (which is + * normally the case), the name is inferred from the configuration name. + * + * @since 4.0 + */ + public static final String SERVER_DOMAIN_NAME_PROPERTY = "cayenne.server.domain.name"; + + /** + * A boolean property defining whether cross-contexts synchronization is + * enabled. Possible values are "true" or "false". + */ + public static final String SERVER_CONTEXTS_SYNC_PROPERTY = "cayenne.server.contexts_sync_strategy"; + + /** + * A String property that defines how ObjectContexts should retain cached + * committed objects. Possible values are "weak", "soft", "hard". + */ + public static final String SERVER_OBJECT_RETAIN_STRATEGY_PROPERTY = "cayenne.server.object_retain_strategy"; + + /** + * A boolean property that defines whether runtime should use external + * transactions. Possible values are "true" or "false". + */ + public static final String SERVER_EXTERNAL_TX_PROPERTY = "cayenne.server.external_tx"; + + public static final String ROP_SERVICE_URL_PROPERTY = "cayenne.rop.service_url"; + + public static final String ROP_SERVICE_USERNAME_PROPERTY = "cayenne.rop.service_username"; + + public static final String ROP_SERVICE_PASSWORD_PROPERTY = "cayenne.rop.service_password"; + + public static final String ROP_SERVICE_SHARED_SESSION_PROPERTY = "cayenne.rop.shared_session_name"; + + public static final String ROP_SERVICE_TIMEOUT_PROPERTY = "cayenne.rop.service_timeout"; + + public static final String ROP_CHANNEL_EVENTS_PROPERTY = "cayenne.rop.channel_events"; + + public static final String ROP_CONTEXT_CHANGE_EVENTS_PROPERTY = "cayenne.rop.context_change_events"; + + public static final String ROP_CONTEXT_LIFECYCLE_EVENTS_PROPERTY = "cayenne.rop.context_lifecycle_events"; + + /** + * The name of the {@link org.apache.cayenne.event.EventBridgeFactory} that + * is passed from the ROP server to the client. Client would instantiate the + * factory to receive events from the server. Note that this property is + * stored in {@link #SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP}, not + * {@link #PROPERTIES_MAP}. + */ + public static final String SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY = "cayenne.server.rop_event_bridge_factory"; + + /** + * A property that defines a maximum number of ID qualifiers in where clause + * of queries that are generated for example in + * {@link org.apache.cayenne.access.IncrementalFaultList} or in + * DISJOINT_BY_ID prefetch processing. This is needed to avoid where clause + * size limitations and memory usage efficiency. + */ + public static final String SERVER_MAX_ID_QUALIFIER_SIZE_PROPERTY = "cayenne.server.max_id_qualifier_size"; + + /** + * Defines a maximum time in milliseconds that a connection request could + * wait in the connection queue. After this period expires, an exception + * will be thrown in the calling method. A value of zero will make the + * thread wait until a connection is available with no time out. Defaults to + * 20 seconds. + */ + public static final String SERVER_MAX_QUEUE_WAIT_TIME = "cayenne.jdbc.max_wait"; + + /** Defines if database uses case-insensitive collation */ + public final static String CI_PROPERTY = "cayenne.runtime.db.collation.assume.ci"; + + /** + * A integer property that enables logging for just long running queries + * (rather than all queries). The value is the minimum number of + * milliseconds a query must run before is logged. A value less than or + * equal to zero (the default) disables this feature. + * + * @since 4.0 + * */ + public final static String QUERY_EXECUTION_TIME_LOGGING_THRESHOLD_PROPERTY = "cayenne.server.query_execution_time_logging_threshold"; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DataDomainProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DataDomainProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DataDomainProvider.java index f1a8d57..4815fe4 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DataDomainProvider.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DataDomainProvider.java @@ -55,173 +55,172 @@ import org.apache.commons.logging.LogFactory; */ public class DataDomainProvider implements Provider<DataDomain> { - /** - * @since 4.0 - */ - static final String DEFAULT_NAME = "cayenne"; + private static Log logger = LogFactory.getLog(DataDomainProvider.class); - private static Log logger = LogFactory.getLog(DataDomainProvider.class); + @Inject + protected ResourceLocator resourceLocator; - @Inject - protected ResourceLocator resourceLocator; + @Inject + protected DataChannelDescriptorMerger descriptorMerger; - @Inject - protected DataChannelDescriptorMerger descriptorMerger; + @Inject + protected DataChannelDescriptorLoader loader; - @Inject - protected DataChannelDescriptorLoader loader; + @Inject(Constants.SERVER_DOMAIN_FILTERS_LIST) + protected List<DataChannelFilter> filters; - @Inject(Constants.SERVER_DOMAIN_FILTERS_LIST) - protected List<DataChannelFilter> filters; + @Inject(Constants.SERVER_PROJECT_LOCATIONS_LIST) + protected List<String> locations; - @Inject(Constants.SERVER_PROJECT_LOCATIONS_LIST) - protected List<String> locations; + @Inject + protected Injector injector; - @Inject - protected Injector injector; + @Inject + protected QueryCache queryCache; - @Inject - protected QueryCache queryCache; + @Inject + protected RuntimeProperties runtimeProperties; - @Inject - protected RuntimeProperties runtimeProperties; + @Inject + protected DataNodeFactory dataNodeFactory; - @Inject - protected DataNodeFactory dataNodeFactory; + @Override + public DataDomain get() throws ConfigurationException { - @Override - public DataDomain get() throws ConfigurationException { + try { + return createAndInitDataDomain(); + } catch (ConfigurationException e) { + throw e; + } catch (Exception e) { + throw new DataDomainLoadException("Error loading DataChannel: '%s'", e, e.getMessage()); + } + } - try { - return createAndInitDataDomain(); - } catch (ConfigurationException e) { - throw e; - } catch (Exception e) { - throw new DataDomainLoadException("Error loading DataChannel: '%s'", e, e.getMessage()); - } - } + protected DataDomain createDataDomain(String name) { + return new DataDomain(name); + } - protected DataDomain createDataDomain(String name) { - return new DataDomain(name); - } + protected DataDomain createAndInitDataDomain() throws Exception { - protected DataDomain createAndInitDataDomain() throws Exception { + DataChannelDescriptor descriptor; - DataChannelDescriptor descriptor; + if (locations.isEmpty()) { + descriptor = new DataChannelDescriptor(); + } else { + descriptor = descriptorFromConfigs(); + } - if (locations.isEmpty()) { - descriptor = new DataChannelDescriptor(); - descriptor.setName(DEFAULT_NAME); - } else { - descriptor = descriptorFromConfigs(); - } + String nameOverride = runtimeProperties.get(Constants.SERVER_DOMAIN_NAME_PROPERTY); + if (nameOverride != null) { + descriptor.setName(nameOverride); + } - DataDomain dataDomain = createDataDomain(descriptor.getName()); + DataDomain dataDomain = createDataDomain(descriptor.getName()); - dataDomain.setMaxIdQualifierSize(runtimeProperties.getInt(Constants.SERVER_MAX_ID_QUALIFIER_SIZE_PROPERTY, -1)); + dataDomain.setMaxIdQualifierSize(runtimeProperties.getInt(Constants.SERVER_MAX_ID_QUALIFIER_SIZE_PROPERTY, -1)); - dataDomain.setQueryCache(new NestedQueryCache(queryCache)); - dataDomain.setEntitySorter(injector.getInstance(EntitySorter.class)); - dataDomain.setEventManager(injector.getInstance(EventManager.class)); + dataDomain.setQueryCache(new NestedQueryCache(queryCache)); + dataDomain.setEntitySorter(injector.getInstance(EntitySorter.class)); + dataDomain.setEventManager(injector.getInstance(EventManager.class)); - dataDomain.initWithProperties(descriptor.getProperties()); + dataDomain.initWithProperties(descriptor.getProperties()); - for (DataMap dataMap : descriptor.getDataMaps()) { - dataDomain.addDataMap(dataMap); - } + for (DataMap dataMap : descriptor.getDataMaps()) { + dataDomain.addDataMap(dataMap); + } - dataDomain.getEntityResolver().applyDBLayerDefaults(); - dataDomain.getEntityResolver().applyObjectLayerDefaults(); + dataDomain.getEntityResolver().applyDBLayerDefaults(); + dataDomain.getEntityResolver().applyObjectLayerDefaults(); - for (DataNodeDescriptor nodeDescriptor : descriptor.getNodeDescriptors()) { - addDataNode(dataDomain, nodeDescriptor); - } + for (DataNodeDescriptor nodeDescriptor : descriptor.getNodeDescriptors()) { + addDataNode(dataDomain, nodeDescriptor); + } - // init default node - DataNode defaultNode = null; + // init default node + DataNode defaultNode = null; - if (descriptor.getDefaultNodeName() != null) { - defaultNode = dataDomain.getDataNode(descriptor.getDefaultNodeName()); - } + if (descriptor.getDefaultNodeName() != null) { + defaultNode = dataDomain.getDataNode(descriptor.getDefaultNodeName()); + } - if (defaultNode == null) { - Collection<DataNode> allNodes = dataDomain.getDataNodes(); - if (allNodes.size() == 1) { - defaultNode = allNodes.iterator().next(); - } - } + if (defaultNode == null) { + Collection<DataNode> allNodes = dataDomain.getDataNodes(); + if (allNodes.size() == 1) { + defaultNode = allNodes.iterator().next(); + } + } - if (defaultNode != null) { - logger.info("setting DataNode '" + defaultNode.getName() + "' as default, used by all unlinked DataMaps"); + if (defaultNode != null) { + logger.info("setting DataNode '" + defaultNode.getName() + "' as default, used by all unlinked DataMaps"); - dataDomain.setDefaultNode(defaultNode); - } + dataDomain.setDefaultNode(defaultNode); + } - for (DataChannelFilter filter : filters) { - dataDomain.addFilter(filter); - } + for (DataChannelFilter filter : filters) { + dataDomain.addFilter(filter); + } - return dataDomain; - } + return dataDomain; + } - /** - * @since 4.0 - */ - protected DataNode addDataNode(DataDomain dataDomain, DataNodeDescriptor nodeDescriptor) throws Exception { - DataNode dataNode = dataNodeFactory.createDataNode(nodeDescriptor); + /** + * @since 4.0 + */ + protected DataNode addDataNode(DataDomain dataDomain, DataNodeDescriptor nodeDescriptor) throws Exception { + DataNode dataNode = dataNodeFactory.createDataNode(nodeDescriptor); - // DataMaps - for (String dataMapName : nodeDescriptor.getDataMapNames()) { - dataNode.addDataMap(dataDomain.getDataMap(dataMapName)); - } + // DataMaps + for (String dataMapName : nodeDescriptor.getDataMapNames()) { + dataNode.addDataMap(dataDomain.getDataMap(dataMapName)); + } - dataDomain.addNode(dataNode); - return dataNode; - } + dataDomain.addNode(dataNode); + return dataNode; + } - private DataChannelDescriptor descriptorFromConfigs() { + private DataChannelDescriptor descriptorFromConfigs() { - long t0 = System.currentTimeMillis(); + long t0 = System.currentTimeMillis(); - if (logger.isDebugEnabled()) { - logger.debug("starting configuration loading: " + locations); - } + if (logger.isDebugEnabled()) { + logger.debug("starting configuration loading: " + locations); + } - DataChannelDescriptor[] descriptors = new DataChannelDescriptor[locations.size()]; + DataChannelDescriptor[] descriptors = new DataChannelDescriptor[locations.size()]; - for (int i = 0; i < locations.size(); i++) { + for (int i = 0; i < locations.size(); i++) { - String location = locations.get(i); + String location = locations.get(i); - Collection<Resource> configurations = resourceLocator.findResources(location); + Collection<Resource> configurations = resourceLocator.findResources(location); - if (configurations.isEmpty()) { - throw new DataDomainLoadException("Configuration resource \"%s\" is not found.", location); - } + if (configurations.isEmpty()) { + throw new DataDomainLoadException("Configuration resource \"%s\" is not found.", location); + } - Resource configurationResource = configurations.iterator().next(); + Resource configurationResource = configurations.iterator().next(); - // no support for multiple configs yet, but this is not a hard error - if (configurations.size() > 1) { - logger.info("found " + configurations.size() + " configurations for " + location - + ", will use the first one: " + configurationResource.getURL()); - } + // no support for multiple configs yet, but this is not a hard error + if (configurations.size() > 1) { + logger.info("found " + configurations.size() + " configurations for " + location + + ", will use the first one: " + configurationResource.getURL()); + } - ConfigurationTree<DataChannelDescriptor> tree = loader.load(configurationResource); - if (!tree.getLoadFailures().isEmpty()) { - // TODO: andrus 03/10/2010 - log the errors before throwing? - throw new DataDomainLoadException(tree, "Error loading DataChannelDescriptor"); - } + ConfigurationTree<DataChannelDescriptor> tree = loader.load(configurationResource); + if (!tree.getLoadFailures().isEmpty()) { + // TODO: andrus 03/10/2010 - log the errors before throwing? + throw new DataDomainLoadException(tree, "Error loading DataChannelDescriptor"); + } - descriptors[i] = tree.getRootNode(); - } + descriptors[i] = tree.getRootNode(); + } - long t1 = System.currentTimeMillis(); + long t1 = System.currentTimeMillis(); - if (logger.isDebugEnabled()) { - logger.debug("finished configuration loading in " + (t1 - t0) + " ms."); - } + if (logger.isDebugEnabled()) { + logger.debug("finished configuration loading in " + (t1 - t0) + " ms."); + } - return descriptorMerger.merge(descriptors); - } + return descriptorMerger.merge(descriptors); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java index 01115b7..33dc60e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java @@ -19,6 +19,7 @@ package org.apache.cayenne.configuration.server; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; @@ -40,6 +41,12 @@ import org.apache.cayenne.di.Module; */ public class ServerRuntimeBuilder { + /** + * @since 4.0 + */ + static final String DEFAULT_NAME = "cayenne"; + + private String name; private Collection<String> configs; private List<Module> modules; private DataSourceFactory dataSourceFactory; @@ -54,17 +61,18 @@ public class ServerRuntimeBuilder { * Creates an empty builder. */ public ServerRuntimeBuilder() { - this.configs = new LinkedHashSet<String>(); - this.modules = new ArrayList<Module>(); + this(null); } /** - * An equivalent to creating builder with default constructor and calling - * {@link #addConfig(String)}. + * Creates a builder with a fixed name of the DataDomain of the resulting + * ServerRuntime. Specifying explicit name is often needed for consistency + * in runtimes merged from multiple configs, each having its own name. */ - public ServerRuntimeBuilder(String configurationLocation) { - this(); - addConfig(configurationLocation); + public ServerRuntimeBuilder(String name) { + this.configs = new LinkedHashSet<String>(); + this.modules = new ArrayList<Module>(); + this.name = name; } /** @@ -134,6 +142,13 @@ public class ServerRuntimeBuilder { return this; } + public ServerRuntimeBuilder addConfigs(String... configurationLocations) { + if (configurationLocations != null) { + configs.addAll(Arrays.asList(configurationLocations)); + } + return this; + } + public ServerRuntimeBuilder addConfigs(Collection<String> configurationLocations) { configs.addAll(configurationLocations); return this; @@ -160,6 +175,28 @@ public class ServerRuntimeBuilder { private void buildModules() { + String nameOverride = name; + + if (nameOverride == null) { + // check if we need to force the default name ... we do when no + // configs or multiple configs are supplied. + if (configs.size() != 1) { + nameOverride = DEFAULT_NAME; + } + } + + if (nameOverride != null) { + + final String finalNameOverride = nameOverride; + prepend(new Module() { + @Override + public void configure(Binder binder) { + binder.bindMap(Constants.PROPERTIES_MAP).put(Constants.SERVER_DOMAIN_NAME_PROPERTY, + finalNameOverride); + } + }); + } + if (dataSourceFactory != null) { prepend(new Module() { @@ -196,6 +233,7 @@ public class ServerRuntimeBuilder { if (jdbcMaxConnections > 0) { props.put(Constants.JDBC_MAX_CONNECTIONS_PROPERTY, Integer.toString(jdbcMaxConnections)); } + } }); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/SyntheticNodeDataDomainProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/SyntheticNodeDataDomainProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/SyntheticNodeDataDomainProvider.java index 56751fe..dbd0ec2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/SyntheticNodeDataDomainProvider.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/SyntheticNodeDataDomainProvider.java @@ -28,24 +28,23 @@ import org.apache.cayenne.configuration.DataNodeDescriptor; */ class SyntheticNodeDataDomainProvider extends DataDomainProvider { - @Override - protected DataDomain createAndInitDataDomain() throws Exception { + @Override + protected DataDomain createAndInitDataDomain() throws Exception { - DataDomain dataDomain = super.createAndInitDataDomain(); + DataDomain dataDomain = super.createAndInitDataDomain(); - // no nodes... add a synthetic node... it will become the default - if (dataDomain.getDataNodes().isEmpty()) { + // no nodes... add a synthetic node... it will become the default + if (dataDomain.getDataNodes().isEmpty()) { - DataChannelDescriptor channelDescriptor = new DataChannelDescriptor(); - channelDescriptor.setName(DEFAULT_NAME); + DataChannelDescriptor channelDescriptor = new DataChannelDescriptor(); - DataNodeDescriptor nodeDescriptor = new DataNodeDescriptor(DEFAULT_NAME); - nodeDescriptor.setDataChannelDescriptor(channelDescriptor); + DataNodeDescriptor nodeDescriptor = new DataNodeDescriptor(ServerRuntimeBuilder.DEFAULT_NAME); + nodeDescriptor.setDataChannelDescriptor(channelDescriptor); - DataNode node = addDataNode(dataDomain, nodeDescriptor); - dataDomain.setDefaultNode(node); - } - return dataDomain; - } + DataNode node = addDataNode(dataDomain, nodeDescriptor); + dataDomain.setDefaultNode(node); + } + return dataDomain; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderIT.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderIT.java new file mode 100644 index 0000000..27b64e4 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderIT.java @@ -0,0 +1,109 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.configuration.server; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import javax.sql.DataSource; + +import org.apache.cayenne.DataRow; +import org.apache.cayenne.conn.DataSourceInfo; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.query.SQLSelect; +import org.apache.cayenne.test.jdbc.DBHelper; +import org.apache.cayenne.test.jdbc.TableHelper; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public class ServerRuntimeBuilderIT extends ServerCase { + + @Inject + private DBHelper dbHelper; + + @Inject + private ServerRuntime runtime; + + @Inject + private DataSourceInfo dsi; + + private ServerRuntime localRuntime; + private DataSource dataSource; + + @After + public void stopLocalRuntime() { + + // even though we don't supply real configs here, we sometimes access + // DataDomain, and this starts EventManager threads that need to be + // shutdown + if (localRuntime != null) { + localRuntime.shutdown(); + } + } + + @Before + public void testSetUp() throws Exception { + TableHelper tArtist = new TableHelper(dbHelper, "ARTIST"); + tArtist.setColumns("ARTIST_ID", "ARTIST_NAME"); + tArtist.insert(33001, "AA1"); + tArtist.insert(33002, "AA2"); + + this.dataSource = runtime.getDataSource("testmap"); + } + + @Test + public void testConfigFree_WithDataSource() { + + localRuntime = new ServerRuntimeBuilder().dataSource(dataSource).build(); + + List<DataRow> result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST").select(localRuntime.newContext()); + assertEquals(2, result.size()); + } + + @Test + public void testConfigFree_WithDBParams() { + + localRuntime = new ServerRuntimeBuilder().jdbcDriver(dsi.getJdbcDriver()).url(dsi.getDataSourceUrl()) + .password(dsi.getPassword()).user(dsi.getUserName()).minConnections(1).maxConnections(2).build(); + + List<DataRow> result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST").select(localRuntime.newContext()); + assertEquals(2, result.size()); + } + + @Test + public void test_UnnamedDomain_MultiLocation() { + localRuntime = new ServerRuntimeBuilder().addConfigs(CayenneProjects.TESTMAP_PROJECT, + CayenneProjects.EMBEDDABLE_PROJECT).build(); + + assertEquals("cayenne", localRuntime.getDataDomain().getName()); + } + + @Test + public void test_NamedDomain_MultiLocation() { + localRuntime = new ServerRuntimeBuilder("myd").addConfigs(CayenneProjects.TESTMAP_PROJECT, + CayenneProjects.EMBEDDABLE_PROJECT).build(); + assertEquals("myd", localRuntime.getDataDomain().getName()); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java index 19c9447..9177fac 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java @@ -18,8 +18,10 @@ ****************************************************************/ package org.apache.cayenne.configuration.server; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -32,15 +34,29 @@ import org.apache.cayenne.configuration.Constants; import org.apache.cayenne.configuration.ModuleCollection; import org.apache.cayenne.di.Key; import org.apache.cayenne.di.Module; +import org.junit.After; import org.junit.Test; public class ServerRuntimeBuilderTest { + private ServerRuntime runtime; + + @After + public void stopRuntime() { + + // even though we don't supply real configs here, we sometimes access + // DataDomain, and this starts EventManager threads that need to be + // shutdown + if (runtime != null) { + runtime.shutdown(); + } + } + @Test public void test_NoLocation() { // this is meaningless (no DataSource), but should work... - ServerRuntime runtime = new ServerRuntimeBuilder().build(); + runtime = new ServerRuntimeBuilder().build(); List<?> locations = runtime.getInjector().getInstance( Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST)); @@ -49,14 +65,14 @@ public class ServerRuntimeBuilderTest { assertTrue(runtime.getModule() instanceof ModuleCollection); Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules(); - assertEquals(1, modules.size()); - assertTrue(modules.iterator().next() instanceof ServerModule); + assertEquals(2, modules.size()); + assertThat(modules.iterator().next(), instanceOf(ServerModule.class)); } @Test public void test_SingleLocation() { - ServerRuntime runtime = new ServerRuntimeBuilder("xxxx").build(); + runtime = new ServerRuntimeBuilder().addConfig("xxxx").build(); List<?> locations = runtime.getInjector().getInstance( Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST)); @@ -65,14 +81,14 @@ public class ServerRuntimeBuilderTest { Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules(); assertEquals(1, modules.size()); - assertTrue(modules.iterator().next() instanceof ServerModule); + assertThat(modules.iterator().next(), instanceOf(ServerModule.class)); } @Test public void test_MultipleLocations() { - ServerRuntime runtime = new ServerRuntimeBuilder("xxxx").addConfig("yyyy").build(); + runtime = new ServerRuntimeBuilder().addConfigs("xxxx", "yyyy").build(); List<?> locations = runtime.getInjector().getInstance( Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST)); @@ -80,8 +96,8 @@ public class ServerRuntimeBuilderTest { assertEquals(Arrays.asList("xxxx", "yyyy"), locations); Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules(); - assertEquals(1, modules.size()); - assertTrue(modules.iterator().next() instanceof ServerModule); + assertEquals(2, modules.size()); + assertThat(modules.iterator().next(), instanceOf(ServerModule.class)); } @Test @@ -89,15 +105,30 @@ public class ServerRuntimeBuilderTest { Module m = mock(Module.class); - ServerRuntime runtime = new ServerRuntimeBuilder("xxxx").addModule(m).build(); + runtime = new ServerRuntimeBuilder().addModule(m).build(); Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules(); - assertEquals(2, modules.size()); + assertEquals(3, modules.size()); Iterator<Module> it = modules.iterator(); - assertTrue(it.next() instanceof ServerModule); + assertThat(it.next(), instanceOf(ServerModule.class)); + + // rewind - this module is name fix module + it.next(); + assertSame(m, it.next()); } + @Test + public void test_UnnamedDomain_NoLocation() { + runtime = new ServerRuntimeBuilder().build(); + assertEquals("cayenne", runtime.getDataDomain().getName()); + } + + @Test + public void test_NamedDomain_NoLocation() { + runtime = new ServerRuntimeBuilder("myd").build(); + assertEquals("myd", runtime.getDataDomain().getName()); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder_InAction_IT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder_InAction_IT.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder_InAction_IT.java deleted file mode 100644 index 25c0aa4..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder_InAction_IT.java +++ /dev/null @@ -1,88 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ -package org.apache.cayenne.configuration.server; - -import org.apache.cayenne.DataRow; -import org.apache.cayenne.conn.DataSourceInfo; -import org.apache.cayenne.di.Inject; -import org.apache.cayenne.query.SQLSelect; -import org.apache.cayenne.test.jdbc.DBHelper; -import org.apache.cayenne.test.jdbc.TableHelper; -import org.apache.cayenne.unit.di.server.CayenneProjects; -import org.apache.cayenne.unit.di.server.ServerCase; -import org.apache.cayenne.unit.di.server.UseServerRuntime; -import org.junit.Before; -import org.junit.Test; - -import javax.sql.DataSource; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) -public class ServerRuntimeBuilder_InAction_IT extends ServerCase { - - @Inject - private DBHelper dbHelper; - - @Inject - private ServerRuntime runtime; - - @Inject - private DataSourceInfo dsi; - - private DataSource dataSource; - - @Before - public void testSetUp() throws Exception { TableHelper tArtist = new TableHelper(dbHelper, "ARTIST"); - tArtist.setColumns("ARTIST_ID", "ARTIST_NAME"); - tArtist.insert(33001, "AA1"); - tArtist.insert(33002, "AA2"); - - this.dataSource = runtime.getDataSource("testmap"); - } - - @Test - public void testConfigFree_WithDataSource() { - - ServerRuntime localRuntime = new ServerRuntimeBuilder().dataSource(dataSource).build(); - - try { - List<DataRow> result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST").select(localRuntime.newContext()); - assertEquals(2, result.size()); - } finally { - localRuntime.shutdown(); - } - } - - @Test - public void testConfigFree_WithDBParams() { - - ServerRuntime localRuntime = new ServerRuntimeBuilder().jdbcDriver(dsi.getJdbcDriver()) - .url(dsi.getDataSourceUrl()).password(dsi.getPassword()).user(dsi.getUserName()).minConnections(1) - .maxConnections(2).build(); - - try { - List<DataRow> result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST").select(localRuntime.newContext()); - assertEquals(2, result.size()); - } finally { - localRuntime.shutdown(); - } - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/docs/doc/src/main/resources/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt index ec297f8..ddc6f5c 100644 --- a/docs/doc/src/main/resources/RELEASE-NOTES.txt +++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt @@ -74,6 +74,7 @@ CAY-1966 SQLTemplate/SQLSelect positional parameter binding CAY-1967 Deprecate SQLTemplate parameter batches CAY-1968 SQLSelect cleanup and omissions CAY-1971 Variants of Property.like(..) : contains(..), startsWith(..), endsWith(..) +CAY-1972 A property to override DataSources of multi-module projects Bug Fixes: http://git-wip-us.apache.org/repos/asf/cayenne/blob/15297cca/docs/doc/src/main/resources/UPGRADE.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/UPGRADE.txt b/docs/doc/src/main/resources/UPGRADE.txt index 94f8f72..c7ade60 100644 --- a/docs/doc/src/main/resources/UPGRADE.txt +++ b/docs/doc/src/main/resources/UPGRADE.txt @@ -35,9 +35,17 @@ UPGRADING TO 4.0.M2 * External transactions are no longer configured in the Modeler. Instead they are provided as a DI property defined in Constants.SERVER_EXTERNAL_TX_PROPERTY. + * TransactionDelegate is no longer present. Similar functionality can be achieved by writing a decorator for Transaction interface and using a custom TransactionFactory to decorate standard transactions. +* When switching to ServerRuntimeBuilder, users of multi-config projects may erroneously assume it has the same + behavior as 3.1 ServerRuntime in assigning domain name to the resulting merged project. Which is to use the + name of the last project config. We are trying to move away from this behavior, so ServerRuntimeBuilder + will only use config name if there's only one config and no override. Otherwise it will use the override, + or if not set - "cayenne" as the default name. Reference Jira: CAY-1972 + + UPGRADING TO 3.1B1