diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index b4a83e5f0..cfe2537aa 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -2804,7 +2804,7 @@ public class ServerConfig { */ protected void loadDataSourceSettings(PropertiesWrapper p) { dataSourceConfig.loadSettings(p.properties, name); - readOnlyDataSourceConfig.loadSettings(p.properties, name); + readOnlyDataSourceConfig.loadSettings(p.properties, name + "-ro"); } /** diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java index a90f4869b..33b033324 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java @@ -8,10 +8,6 @@ import io.ebean.config.TenantMode; import io.ebean.config.UnderscoreNamingConvention; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.h2.H2Platform; -import io.ebean.datasource.DataSourceAlertFactory; -import io.ebean.datasource.DataSourceConfig; -import io.ebean.datasource.DataSourceFactory; -import io.ebean.datasource.DataSourcePoolListener; import io.ebean.service.SpiContainer; import io.ebeaninternal.api.SpiBackgroundExecutor; import io.ebeaninternal.api.SpiEbeanServer; @@ -24,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; -import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.List; @@ -39,13 +34,8 @@ public class DefaultContainer implements SpiContainer { private final ClusterManager clusterManager; - private final JndiDataSourceLookup jndiDataSourceFactory; - public DefaultContainer(ContainerConfig containerConfig) { - this.clusterManager = new ClusterManager(containerConfig); - this.jndiDataSourceFactory = new JndiDataSourceLookup(); - // register so that we can shutdown any Ebean wide // resources such as clustering ShutdownManager.registerContainer(this); @@ -230,75 +220,10 @@ public class DefaultContainer implements SpiContainer { * Set the DataSource if it has not already been set. */ private void setDataSource(ServerConfig config) { - if (config.getDataSource() == null) { - config.setDataSource(getDataSourceFromConfig(config, false)); - } - if (config.getReadOnlyDataSource() == null && config.isAutoReadOnlyDataSource()) { - config.setReadOnlyDataSource(getDataSourceFromConfig(config, true)); - } - } - - private DataSource getDataSourceFromConfig(ServerConfig config, boolean readOnly) { - if (isOfflineMode(config)) { logger.debug("... DbOffline using platform [{}]", DbOffline.getPlatform()); - return null; - } - - if (!readOnly && config.getDataSourceJndiName() != null) { - DataSource ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName()); - if (ds == null) { - throw new PersistenceException("JNDI lookup for DataSource " + config.getDataSourceJndiName() + " returned null."); - } else { - return ds; - } - } - - DataSourceConfig dsConfig = (readOnly) ? config.getReadOnlyDataSourceConfig() : config.getDataSourceConfig(); - if (dsConfig == null) { - throw new PersistenceException("No DataSourceConfig defined for " + config.getName()); - } - - if (dsConfig.isOffline()) { - if (config.getDatabasePlatformName() == null) { - throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline"); - } - return null; - } - - DataSourceFactory factory = DataSourceFactory.get(); - if (factory == null) { - throw new IllegalStateException("No DataSourceFactory service implementation found in class path." - + " Probably missing dependency to avaje-datasource?"); - } - - DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class); - if (alertFactory != null) { - dsConfig.setAlert(alertFactory.createAlert()); - } - - attachListener(config, dsConfig); - - if (readOnly) { - // setup to use AutoCommit such that we skip explicit commit - dsConfig.setAutoCommit(true); - //dsConfig.setReadOnly(true); - dsConfig.setDefaults(config.getDataSourceConfig()); - dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel()); - } - String poolName = config.getName() + (readOnly ? "-ro" : ""); - return factory.createPool(poolName, dsConfig); - } - - /** - * Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached. - */ - private void attachListener(ServerConfig config, DataSourceConfig dsConfig) { - if (dsConfig.getListener() == null) { - String poolListener = dsConfig.getPoolListener(); - if (poolListener != null) { - dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener)); - } + } else { + InitDataSource.init(config); } } diff --git a/src/main/java/io/ebeaninternal/server/core/InitDataSource.java b/src/main/java/io/ebeaninternal/server/core/InitDataSource.java new file mode 100644 index 000000000..51a45a404 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/core/InitDataSource.java @@ -0,0 +1,138 @@ +package io.ebeaninternal.server.core; + +import io.ebean.config.ServerConfig; +import io.ebean.datasource.DataSourceAlertFactory; +import io.ebean.datasource.DataSourceConfig; +import io.ebean.datasource.DataSourceFactory; +import io.ebean.datasource.DataSourcePoolListener; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +/** + * Initialise the main DataSource and read-only DataSource. + */ +class InitDataSource { + + private final JndiDataSourceLookup jndiDataSourceFactory = new JndiDataSourceLookup(); + + private final ServerConfig config; + + /** + * Create and set the main DataSource and read-only DataSource. + */ + static void init(ServerConfig config) { + new InitDataSource(config).initialise(); + } + + InitDataSource(ServerConfig config) { + this.config = config; + } + + private void initialise() { + if (config.getDataSource() == null) { + config.setDataSource(initDataSource()); + } + if (config.getReadOnlyDataSource() == null) { + config.setReadOnlyDataSource(initReadOnlyDataSource()); + } + } + + /** + * Initialise the "main" read write DataSource from configuration. + */ + private DataSource initDataSource() { + final String jndiName = config.getDataSourceJndiName(); + if (jndiName != null) { + return jndiDataSource(jndiName); + } + return createFromConfig(config.getDataSourceConfig(), false); + } + + private DataSource jndiDataSource(String jndiName) { + DataSource ds = jndiDataSourceFactory.lookup(jndiName); + if (ds == null) { + throw new PersistenceException("JNDI lookup for DataSource " + jndiName + " returned null."); + } + return ds; + } + + /** + * Initialise the "read only" DataSource from configuration. + */ + private DataSource initReadOnlyDataSource() { + DataSourceConfig roConfig = readOnlyConfig(); + if (roConfig == null) { + return null; + } + return createFromConfig(roConfig, true); + } + + DataSourceConfig readOnlyConfig() { + DataSourceConfig roConfig = config.getReadOnlyDataSourceConfig(); + if (roConfig == null) { + // it has explicitly been set to null, not expected but ok + return null; + } + if (roConfig.getUrl() == null && !config.isAutoReadOnlyDataSource()) { + // no read-only DataSource will be used + return null; + } + return roConfig; + } + + private DataSource createFromConfig(DataSourceConfig dsConfig, boolean readOnly) { + if (dsConfig == null) { + throw new PersistenceException("No DataSourceConfig defined for " + config.getName()); + } + if (dsConfig.isOffline()) { + if (config.getDatabasePlatformName() == null) { + throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline"); + } + return null; + } + + attachAlert(dsConfig); + attachListener(dsConfig); + + if (readOnly) { + // setup to use AutoCommit such that we skip explicit commit + dsConfig.setAutoCommit(true); + //dsConfig.setReadOnly(true); + dsConfig.setDefaults(config.getDataSourceConfig()); + dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel()); + } + return create(dsConfig, readOnly); + } + + private DataSource create(DataSourceConfig dsConfig, boolean readOnly) { + DataSourceFactory factory = DataSourceFactory.get(); + if (factory == null) { + throw new IllegalStateException("No DataSourceFactory service implementation found in class path. Missing dependency to ebean-datasource?"); + } + String poolName = config.getName() + (readOnly ? "-ro" : ""); + return factory.createPool(poolName, dsConfig); + } + + /** + * Attach DataSourceAlert via service loader if present. + */ + private void attachAlert(DataSourceConfig dsConfig) { + DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class); + if (alertFactory != null) { + dsConfig.setAlert(alertFactory.createAlert()); + } + } + + /** + * Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached. + */ + private void attachListener(DataSourceConfig dsConfig) { + if (dsConfig.getListener() == null) { + String poolListener = dsConfig.getPoolListener(); + if (poolListener != null) { + dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener)); + } + } + } +} diff --git a/src/test/java/io/ebeaninternal/server/core/InitDataSourceTest.java b/src/test/java/io/ebeaninternal/server/core/InitDataSourceTest.java new file mode 100644 index 000000000..f4472eedc --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/core/InitDataSourceTest.java @@ -0,0 +1,69 @@ +package io.ebeaninternal.server.core; + +import io.ebean.config.ServerConfig; +import io.ebean.datasource.DataSourceConfig; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class InitDataSourceTest { + + @Test + public void readOnlyConfig_nullByDefault() { + InitDataSource init = new InitDataSource(new ServerConfig()); + assertNull(init.readOnlyConfig()); + } + + @Test + public void readOnlyConfig_null_whenSetNullExplicitly() { + ServerConfig config = new ServerConfig(); + config.setReadOnlyDataSourceConfig(null); + + InitDataSource init = new InitDataSource(config); + assertNull(init.readOnlyConfig()); + } + + @Test + public void readOnlyConfig_null_whenSetNullExplicitly_2() { + ServerConfig config = new ServerConfig(); + DataSourceConfig roConfig = new DataSourceConfig(); + roConfig.setUrl(null); + config.setReadOnlyDataSourceConfig(roConfig); + + InitDataSource init = new InitDataSource(config); + assertNull(init.readOnlyConfig()); + } + + @Test + public void readOnlyConfig_when_autoReadOnlyDataSource() { + ServerConfig config = new ServerConfig(); + config.setAutoReadOnlyDataSource(true); + + InitDataSource init = new InitDataSource(config); + assertNotNull(init.readOnlyConfig()); + } + + @Test + public void readOnlyConfig_when_urlSet() { + ServerConfig config = new ServerConfig(); + DataSourceConfig dsConfig = new DataSourceConfig(); + dsConfig.setUrl("foo"); + config.setReadOnlyDataSourceConfig(dsConfig); + + InitDataSource init = new InitDataSource(config); + final DataSourceConfig roConfig = init.readOnlyConfig(); + assertNotNull(roConfig); + assertEquals("foo", roConfig.getUrl()); + } + + @Test + public void readOnlyConfig_when_urlSet_2() { + ServerConfig config = new ServerConfig(); + config.getReadOnlyDataSourceConfig().setUrl("foo"); + + InitDataSource init = new InitDataSource(config); + final DataSourceConfig roConfig = init.readOnlyConfig(); + assertNotNull(roConfig); + assertEquals("foo", roConfig.getUrl()); + } +} diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 21941d391..964dc2972 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -31,6 +31,7 @@ ebean.dumpMetricsOptions=sql,hash ebean.collectQueryPlans=true ebean.autoReadOnlyDataSource=true +#datasource.h2-ro.url=jdbc:h2:mem:tests #ebean.persistBatch=NONE