001package io.ebean; 002 003import io.ebean.config.ContainerConfig; 004import io.ebean.config.DatabaseConfig; 005import io.ebean.service.SpiContainer; 006import io.ebean.service.SpiContainerFactory; 007 008import javax.persistence.PersistenceException; 009import java.util.Iterator; 010import java.util.Properties; 011import java.util.ServiceLoader; 012import java.util.concurrent.locks.ReentrantLock; 013 014/** 015 * Creates Database instances. 016 * <p> 017 * This uses either DatabaseConfig or properties in the application.properties file to 018 * configure and create a Database instance. 019 * </p> 020 * <p> 021 * The Database instance can either be registered with the DB singleton or 022 * not. The DB singleton effectively holds a map of Database by a name. 023 * If the Database is registered with the DB singleton you can retrieve it 024 * later via {@link DB#byName(String)}. 025 * </p> 026 * <p> 027 * One Database can be nominated as the 'default/primary' Database. Many 028 * methods on the DB singleton such as {@link DB#find(Class)} are just a 029 * convenient way of using the 'default/primary' Database. 030 * </p> 031 */ 032public class DatabaseFactory { 033 034 private static final ReentrantLock lock = new ReentrantLock(); 035 private static SpiContainer container; 036 private static String defaultServerName; 037 038 static { 039 EbeanVersion.getVersion(); 040 } 041 042 /** 043 * Initialise the container with clustering configuration. 044 * <p> 045 * Call this prior to creating any Database instances or alternatively set the 046 * ContainerConfig on the DatabaseConfig when creating the first Database instance. 047 */ 048 public static void initialiseContainer(ContainerConfig containerConfig) { 049 lock.lock(); 050 try { 051 getContainer(containerConfig); 052 } finally { 053 lock.unlock(); 054 } 055 } 056 057 /** 058 * Create using properties to configure the database. 059 */ 060 public static Database create(String name) { 061 lock.lock(); 062 try { 063 return getContainer(null).createServer(name); 064 } finally { 065 lock.unlock(); 066 } 067 } 068 069 /** 070 * Create using the DatabaseConfig object to configure the database. 071 */ 072 public static Database create(DatabaseConfig config) { 073 lock.lock(); 074 try { 075 if (config.getName() == null) { 076 throw new PersistenceException("The name is null (it is required)"); 077 } 078 Database server = createInternal(config); 079 if (config.isRegister()) { 080 if (config.isDefaultServer()) { 081 if (defaultServerName != null) { 082 throw new IllegalStateException("Registering [" + config.getName() + "] as the default server but [" + defaultServerName + "] is already registered as the default"); 083 } 084 defaultServerName = config.getName(); 085 } 086 DbPrimary.setSkip(true); 087 DbContext.getInstance().register(server, config.isDefaultServer()); 088 } 089 return server; 090 } finally { 091 lock.unlock(); 092 } 093 } 094 095 /** 096 * Create using the DatabaseConfig additionally specifying a classLoader to use as the context class loader. 097 */ 098 public static Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) { 099 lock.lock(); 100 try { 101 ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader(); 102 Thread.currentThread().setContextClassLoader(classLoader); 103 try { 104 return DatabaseFactory.create(config); 105 } finally { 106 // set the currentContextLoader back 107 Thread.currentThread().setContextClassLoader(currentContextLoader); 108 } 109 } finally { 110 lock.unlock(); 111 } 112 } 113 114 /** 115 * Shutdown gracefully all Database instances cleaning up any resources as required. 116 * <p> 117 * This is typically invoked via JVM shutdown hook and not explicitly called. 118 * </p> 119 */ 120 public static void shutdown() { 121 lock.lock(); 122 try { 123 container.shutdown(); 124 } finally { 125 lock.unlock(); 126 } 127 } 128 129 private static Database createInternal(DatabaseConfig config) { 130 return getContainer(config.getContainerConfig()).createServer(config); 131 } 132 133 /** 134 * Get the EbeanContainer initialising it if necessary. 135 * 136 * @param containerConfig the configuration controlling clustering communication 137 */ 138 private static SpiContainer getContainer(ContainerConfig containerConfig) { 139 // thread safe in that all calling methods hold lock 140 if (container != null) { 141 return container; 142 } 143 144 if (containerConfig == null) { 145 // effectively load configuration from ebean.properties 146 Properties properties = DbPrimary.getProperties(); 147 containerConfig = new ContainerConfig(); 148 containerConfig.loadFromProperties(properties); 149 } 150 container = createContainer(containerConfig); 151 return container; 152 } 153 154 /** 155 * Create the container instance using the configuration. 156 */ 157 protected static SpiContainer createContainer(ContainerConfig containerConfig) { 158 Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator(); 159 if (factories.hasNext()) { 160 return factories.next().create(containerConfig); 161 } 162 throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?"); 163 } 164}