All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebeaninternal.server.core.DefaultContainer Maven / Gradle / Ivy

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.CoreLog;
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 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.
 */
public final class DefaultContainer implements SpiContainer {

  private static final Logger log = CoreLog.log;

  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.
   */
  @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.
   */
  @Override
  public SpiEbeanServer createServer(DatabaseConfig config) {
    lock.lock();
    try {
      long start = System.currentTimeMillis();
      applyConfigServices(config);
      setNamingConvention(config);
      BootupClasses bootupClasses = bootupClasses(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 plus other plugins
      if (!DbOffline.isGenerateMigration()) {
        startServer(online, server);
      }
      DbOffline.reset();
      log.info("Started database[{}] platform[{}] in {}ms", config.getName(), config.getDatabasePlatform().getPlatform(), System.currentTimeMillis() - start);
      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
      for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
        config.addAll(loader.classesFor(config.getName(), config.isDefaultServer()));
      }
    }
  }

  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.
   */
  private BootupClasses bootupClasses(DatabaseConfig config) {
    BootupClasses bootup = bootupClasses1(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.
   */
  private BootupClasses bootupClasses1(DatabaseConfig config) {
    List> 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.
   */
  private void setNamingConvention(DatabaseConfig config) {
    if (config.getNamingConvention() == null) {
      config.setNamingConvention(new UnderscoreNamingConvention());
    }
  }

  /**
   * 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);
    }
    platform.configure(config.getPlatformConfig());
  }

  /**
   * Set the DataSource if it has not already been set.
   */
  private void setDataSource(DatabaseConfig config) {
    if (isOfflineMode(config)) {
      log.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. *

*/ 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?"); } if (config.skipDataSourceCheck()) { return true; } try (Connection connection = config.getDataSource().getConnection()) { if (connection.getAutoCommit()) { log.warn("DataSource [{}] has autoCommit defaulting to true!", config.getName()); } return true; } catch (SQLException ex) { throw new PersistenceException(ex); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy