package io.ebean; import io.ebean.config.ContainerConfig; import io.ebean.config.DatabaseConfig; import io.ebean.service.SpiContainer; import io.ebean.service.SpiContainerFactory; import javax.persistence.PersistenceException; import java.util.Iterator; import java.util.Properties; import java.util.ServiceLoader; import java.util.concurrent.locks.ReentrantLock; /** * Creates Database instances. *

* This uses either DatabaseConfig or properties in the application.properties file to * configure and create a Database instance. *

* The Database instance can either be registered with the DB singleton or * not. The DB singleton effectively holds a map of Database by a name. * If the Database is registered with the DB singleton you can retrieve it * later via {@link DB#byName(String)}. *

* One Database can be nominated as the 'default/primary' Database. Many * methods on the DB singleton such as {@link DB#find(Class)} are just a * convenient way of using the 'default/primary' Database. */ public class DatabaseFactory { private static final ReentrantLock lock = new ReentrantLock(); private static SpiContainer container; private static String defaultServerName; static { EbeanVersion.getVersion(); } /** * Initialise the container with clustering configuration. *

* Call this prior to creating any Database instances or alternatively set the * ContainerConfig on the DatabaseConfig when creating the first Database instance. */ public static void initialiseContainer(ContainerConfig containerConfig) { lock.lock(); try { container(containerConfig); } finally { lock.unlock(); } } /** * Create using properties to configure the database. */ public static Database create(String name) { lock.lock(); try { return container(null).createServer(name); } finally { lock.unlock(); } } /** * Create using the DatabaseConfig object to configure the database. * *

{@code
   *
   *   DatabaseConfig config = new DatabaseConfig();
   *   config.setName("db");
   *   config.loadProperties();
   *
   *   Database database = DatabaseFactory.create(config);
   *
   * }
*/ public static Database create(DatabaseConfig config) { lock.lock(); try { if (config.getName() == null) { throw new PersistenceException("The name is null (it is required)"); } Database server = createInternal(config); if (config.isRegister()) { if (config.isDefaultServer()) { if (defaultServerName != null && !defaultServerName.equals(config.getName())) { throw new IllegalStateException("Registering [" + config.getName() + "] as the default server but [" + defaultServerName + "] is already registered as the default"); } defaultServerName = config.getName(); } DbPrimary.setSkip(true); DbContext.getInstance().register(server, config.isDefaultServer()); } return server; } finally { lock.unlock(); } } /** * Create using the DatabaseConfig additionally specifying a classLoader to use as the context class loader. */ public static Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) { lock.lock(); try { ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(classLoader); try { return DatabaseFactory.create(config); } finally { // set the currentContextLoader back Thread.currentThread().setContextClassLoader(currentContextLoader); } } finally { lock.unlock(); } } /** * Shutdown gracefully all Database instances cleaning up any resources as required. *

* This is typically invoked via JVM shutdown hook and not explicitly called. */ public static void shutdown() { lock.lock(); try { container.shutdown(); } finally { lock.unlock(); } } private static Database createInternal(DatabaseConfig config) { return container(config.getContainerConfig()).createServer(config); } /** * Return the SpiContainer initialising it if necessary. * * @param containerConfig the configuration controlling clustering communication */ private static SpiContainer container(ContainerConfig containerConfig) { // thread safe in that all calling methods hold lock if (container != null) { return container; } if (containerConfig == null) { // effectively load configuration from ebean.properties Properties properties = DbPrimary.getProperties(); containerConfig = new ContainerConfig(); containerConfig.loadFromProperties(properties); } container = createContainer(containerConfig); return container; } /** * Create the container instance using the configuration. */ protected static SpiContainer createContainer(ContainerConfig containerConfig) { Iterator factories = ServiceLoader.load(SpiContainerFactory.class).iterator(); if (factories.hasNext()) { return factories.next().create(containerConfig); } throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?"); } }