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 037 static { 038 EbeanVersion.getVersion(); 039 } 040 041 /** 042 * Initialise the container with clustering configuration. 043 * <p> 044 * Call this prior to creating any Database instances or alternatively set the 045 * ContainerConfig on the DatabaseConfig when creating the first Database instance. 046 */ 047 public static void initialiseContainer(ContainerConfig containerConfig) { 048 lock.lock(); 049 try { 050 getContainer(containerConfig); 051 } finally { 052 lock.unlock(); 053 } 054 } 055 056 /** 057 * Create using properties to configure the database. 058 */ 059 public static Database create(String name) { 060 lock.lock(); 061 try { 062 return getContainer(null).createServer(name); 063 } finally { 064 lock.unlock(); 065 } 066 } 067 068 /** 069 * Create using the DatabaseConfig object to configure the database. 070 */ 071 public static Database create(DatabaseConfig config) { 072 lock.lock(); 073 try { 074 if (config.getName() == null) { 075 throw new PersistenceException("The name is null (it is required)"); 076 } 077 Database server = createInternal(config); 078 if (config.isRegister()) { 079 DbPrimary.setSkip(true); 080 DbContext.getInstance().register(server, config.isDefaultServer()); 081 } 082 return server; 083 } finally { 084 lock.unlock(); 085 } 086 } 087 088 /** 089 * Create using the DatabaseConfig additionally specifying a classLoader to use as the context class loader. 090 */ 091 public static Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) { 092 lock.lock(); 093 try { 094 ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader(); 095 Thread.currentThread().setContextClassLoader(classLoader); 096 try { 097 return DatabaseFactory.create(config); 098 } finally { 099 // set the currentContextLoader back 100 Thread.currentThread().setContextClassLoader(currentContextLoader); 101 } 102 } finally { 103 lock.unlock(); 104 } 105 } 106 107 /** 108 * Shutdown gracefully all Database instances cleaning up any resources as required. 109 * <p> 110 * This is typically invoked via JVM shutdown hook and not explicitly called. 111 * </p> 112 */ 113 public static void shutdown() { 114 lock.lock(); 115 try { 116 container.shutdown(); 117 } finally { 118 lock.unlock(); 119 } 120 } 121 122 private static Database createInternal(DatabaseConfig config) { 123 return getContainer(config.getContainerConfig()).createServer(config); 124 } 125 126 /** 127 * Get the EbeanContainer initialising it if necessary. 128 * 129 * @param containerConfig the configuration controlling clustering communication 130 */ 131 private static SpiContainer getContainer(ContainerConfig containerConfig) { 132 // thread safe in that all calling methods hold lock 133 if (container != null) { 134 return container; 135 } 136 137 if (containerConfig == null) { 138 // effectively load configuration from ebean.properties 139 Properties properties = DbPrimary.getProperties(); 140 containerConfig = new ContainerConfig(); 141 containerConfig.loadFromProperties(properties); 142 } 143 container = createContainer(containerConfig); 144 return container; 145 } 146 147 /** 148 * Create the container instance using the configuration. 149 */ 150 protected static SpiContainer createContainer(ContainerConfig containerConfig) { 151 Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator(); 152 if (factories.hasNext()) { 153 return factories.next().create(containerConfig); 154 } 155 throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?"); 156 } 157}