package io.ebeaninternal.server.core;
import io.ebean.config.ContainerConfig;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.DatabaseConfigProvider;
import io.ebean.config.ModuleInfoLoader;
import io.ebean.config.ServerConfig;
import io.ebean.config.ServerConfigProvider;
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.event.ShutdownManager;
import io.ebean.service.SpiContainer;
import io.ebeaninternal.api.DbOffline;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.cluster.ClusterManager;
import io.ebeaninternal.server.core.bootup.BootupClassPathSearch;
import io.ebeaninternal.server.core.bootup.BootupClasses;
import io.ebeaninternal.server.executor.DefaultBackgroundExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.locks.ReentrantLock;
Default Server side implementation of ServerFactory.
/**
* Default Server side implementation of ServerFactory.
*/
public class DefaultContainer implements SpiContainer {
private static final Logger logger = LoggerFactory.getLogger("io.ebean.internal.DefaultContainer");
private final ReentrantLock lock = new ReentrantLock();
private final ClusterManager clusterManager;
public DefaultContainer(ContainerConfig containerConfig) {
this.clusterManager = new ClusterManager(containerConfig);
// register so that we can shutdown any Ebean wide
// resources such as clustering
ShutdownManager.registerContainer(this);
}
@Override
public void shutdown() {
clusterManager.shutdown();
ShutdownManager.shutdown();
}
Create the server reading configuration information from ebean.properties.
/**
* Create the server reading configuration information from ebean.properties.
*/
@Override
public SpiEbeanServer createServer(String name) {
DatabaseConfig config = new DatabaseConfig();
config.setName(name);
config.loadFromProperties();
return createServer(config);
}
private SpiBackgroundExecutor createBackgroundExecutor(DatabaseConfig config) {
String namePrefix = "ebean-" + config.getName();
int schedulePoolSize = config.getBackgroundExecutorSchedulePoolSize();
int shutdownSecs = config.getBackgroundExecutorShutdownSecs();
return new DefaultBackgroundExecutor(schedulePoolSize, shutdownSecs, namePrefix);
}
Create the implementation from the configuration.
/**
* Create the implementation from the configuration.
*/
@Override
public SpiEbeanServer createServer(DatabaseConfig config) {
lock.lock();
try {
applyConfigServices(config);
setNamingConvention(config);
BootupClasses bootupClasses = getBootupClasses(config);
boolean online = true;
if (config.isDocStoreOnly()) {
config.setDatabasePlatform(new H2Platform());
} else {
TenantMode tenantMode = config.getTenantMode();
if (TenantMode.DB != tenantMode) {
setDataSource(config);
if (!tenantMode.isDynamicDataSource()) {
// check the autoCommit and Transaction Isolation
online = checkDataSource(config);
}
}
}
// determine database platform (Oracle etc)
setDatabasePlatform(config);
if (config.getDbEncrypt() != null) {
// use a configured DbEncrypt rather than the platform default
config.getDatabasePlatform().setDbEncrypt(config.getDbEncrypt());
}
// inform the NamingConvention of the associated DatabasePlatform
config.getNamingConvention().setDatabasePlatform(config.getDatabasePlatform());
// executor and l2 caching service setup early (used during server construction)
SpiBackgroundExecutor executor = createBackgroundExecutor(config);
InternalConfiguration c = new InternalConfiguration(online, clusterManager, executor, config, bootupClasses);
DefaultServer server = new DefaultServer(c, c.cacheManager());
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
if (!DbOffline.isGenerateMigration()) {
startServer(online, server);
}
DbOffline.reset();
return server;
} finally {
lock.unlock();
}
}
private void applyConfigServices(DatabaseConfig config) {
if (config.isDefaultServer()) {
boolean appliedConfig = false;
for (DatabaseConfigProvider configProvider : ServiceLoader.load(DatabaseConfigProvider.class)) {
configProvider.apply(config);
appliedConfig = true;
}
if (!appliedConfig && config instanceof ServerConfig) {
for (ServerConfigProvider configProvider : ServiceLoader.load(ServerConfigProvider.class)) {
configProvider.apply((ServerConfig)config);
}
}
if (config.isAutoLoadModuleInfo()) {
// auto register entity classes (default db)
for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
config.addAll(loader.entityClasses());
}
}
} else if (config.isAutoLoadModuleInfo()) {
// auto register entity classes (other named db)
for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
config.addAll(loader.entityClassesFor(config.getName()));
}
}
}
private void startServer(boolean online, DefaultServer server) {
server.executePlugins(online);
// initialise prior to registering with clusterManager
server.initialise();
if (online) {
if (clusterManager.isClustering()) {
clusterManager.registerServer(server);
}
}
// start any services after registering with clusterManager
server.start();
}
Get the entities, scalarTypes, Listeners etc combining the class registered
ones with the already created instances.
/**
* Get the entities, scalarTypes, Listeners etc combining the class registered
* ones with the already created instances.
*/
private BootupClasses getBootupClasses(DatabaseConfig config) {
BootupClasses bootup = getBootupClasses1(config);
bootup.addIdGenerators(config.getIdGenerators());
bootup.addPersistControllers(config.getPersistControllers());
bootup.addPostLoaders(config.getPostLoaders());
bootup.addPostConstructListeners(config.getPostConstructListeners());
bootup.addFindControllers(config.getFindControllers());
bootup.addPersistListeners(config.getPersistListeners());
bootup.addQueryAdapters(config.getQueryAdapters());
bootup.addServerConfigStartup(config.getServerConfigStartupListeners());
bootup.addChangeLogInstances(config);
bootup.runServerConfigStartup(config);
return bootup;
}
Get the class based entities, scalarTypes, Listeners etc.
/**
* Get the class based entities, scalarTypes, Listeners etc.
*/
private BootupClasses getBootupClasses1(DatabaseConfig config) {
List<Class<?>> entityClasses = config.getClasses();
if (config.isDisableClasspathSearch() || (entityClasses != null && !entityClasses.isEmpty())) {
// use classes we explicitly added via configuration
return new BootupClasses(entityClasses);
}
return BootupClassPathSearch.search(config);
}
Set the naming convention to underscore if it has not already been set.
/**
* Set the naming convention to underscore if it has not already been set.
*/
private void setNamingConvention(DatabaseConfig config) {
if (config.getNamingConvention() == null) {
config.setNamingConvention(new UnderscoreNamingConvention());
}
}
Set the DatabasePlatform if it has not already been set.
/**
* Set the DatabasePlatform if it has not already been set.
*/
private void setDatabasePlatform(DatabaseConfig config) {
DatabasePlatform platform = config.getDatabasePlatform();
if (platform == null) {
if (config.getTenantMode().isDynamicDataSource()) {
throw new IllegalStateException("DatabasePlatform must be explicitly set on DatabaseConfig for TenantMode "+config.getTenantMode());
}
// automatically determine the platform
platform = new DatabasePlatformFactory().create(config);
config.setDatabasePlatform(platform);
}
logger.info("DatabasePlatform name:{} platform:{}", config.getName(), platform.getName());
platform.configure(config.getPlatformConfig());
}
Set the DataSource if it has not already been set.
/**
* Set the DataSource if it has not already been set.
*/
private void setDataSource(DatabaseConfig config) {
if (isOfflineMode(config)) {
logger.debug("... DbOffline using platform [{}]", DbOffline.getPlatform());
} else {
InitDataSource.init(config);
}
}
private boolean isOfflineMode(DatabaseConfig config) {
return config.isDbOffline() || DbOffline.isSet();
}
Check the autoCommit and Transaction Isolation levels of the DataSource.
If autoCommit is true this could be a real problem.
If the Isolation level is not READ_COMMITTED then optimistic concurrency
checking may not work as expected.
/**
* Check the autoCommit and Transaction Isolation levels of the DataSource.
* <p>
* If autoCommit is true this could be a real problem.
* </p>
* <p>
* If the Isolation level is not READ_COMMITTED then optimistic concurrency
* checking may not work as expected.
* </p>
*/
private boolean checkDataSource(DatabaseConfig config) {
if (isOfflineMode(config)) {
return false;
}
if (config.getDataSource() == null) {
if (config.getDataSourceConfig().isOffline()) {
// this is ok - offline DDL generation etc
return false;
}
throw new RuntimeException("DataSource not set?");
}
try (Connection connection = config.getDataSource().getConnection()) {
if (connection.getAutoCommit()) {
logger.warn("DataSource [{}] has autoCommit defaulting to true!", config.getName());
}
return true;
} catch (SQLException ex) {
throw new PersistenceException(ex);
}
}
}