001package io.ebean;
002
003import io.ebean.config.ContainerConfig;
004import io.ebean.config.ServerConfig;
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;
012
013/**
014 * Creates EbeanServer instances.
015 * <p>
016 * This uses either a ServerConfig or properties in the ebean.properties file to
017 * configure and create a EbeanServer instance.
018 * </p>
019 * <p>
020 * The EbeanServer instance can either be registered with the Ebean singleton or
021 * not. The Ebean singleton effectively holds a map of EbeanServers by a name.
022 * If the EbeanServer is registered with the Ebean singleton you can retrieve it
023 * later via {@link Ebean#getServer(String)}.
024 * </p>
025 * <p>
026 * One EbeanServer can be nominated as the 'default/primary' EbeanServer. Many
027 * methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
028 * convenient way of using the 'default/primary' EbeanServer.
029 * </p>
030 */
031public class EbeanServerFactory {
032
033
034  private static SpiContainer container;
035
036  static {
037    EbeanVersion.getVersion(); // initalizes the version class and logs the version.
038  }
039
040  /**
041   * Initialise the container with clustering configuration.
042   * <p>
043   * Call this prior to creating any EbeanServer instances or alternatively set the
044   * ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
045   */
046  public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
047    getContainer(containerConfig);
048  }
049
050  /**
051   * Create using ebean.properties to configure the server.
052   */
053  public static synchronized EbeanServer create(String name) {
054
055    // construct based on loading properties files
056    // and if invoked by Ebean then it handles registration
057    SpiContainer serverFactory = getContainer(null);
058    return serverFactory.createServer(name);
059  }
060
061  /**
062   * Create using the ServerConfig object to configure the server.
063   */
064  public static synchronized EbeanServer create(ServerConfig config) {
065
066    if (config.getName() == null) {
067      throw new PersistenceException("The name is null (it is required)");
068    }
069
070    EbeanServer server = createInternal(config);
071
072    if (config.isRegister()) {
073      PrimaryServer.setSkip(true);
074      Ebean.register(server, config.isDefaultServer());
075    }
076
077    return server;
078  }
079
080  /**
081   * Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
082   */
083  public static synchronized EbeanServer createWithContextClassLoader(ServerConfig config, ClassLoader classLoader) {
084
085    ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
086    Thread.currentThread().setContextClassLoader(classLoader);
087    try {
088      return EbeanServerFactory.create(config);
089
090    } finally {
091      // set the currentContextLoader back
092      Thread.currentThread().setContextClassLoader(currentContextLoader);
093    }
094  }
095
096  /**
097   * Shutdown gracefully all EbeanServers cleaning up any resources as required.
098   * <p>
099   * This is typically invoked via JVM shutdown hook and not explicitly called.
100   * </p>
101   */
102  public static synchronized void shutdown() {
103    container.shutdown();
104  }
105
106
107  private static EbeanServer createInternal(ServerConfig config) {
108
109    return getContainer(config.getContainerConfig()).createServer(config);
110  }
111
112  /**
113   * Get the EbeanContainer initialising it if necessary.
114   *
115   * @param containerConfig the configuration controlling clustering communication
116   */
117  private static SpiContainer getContainer(ContainerConfig containerConfig) {
118
119    // thread safe in that all calling methods are synchronized
120    if (container != null) {
121      return container;
122    }
123
124    if (containerConfig == null) {
125      // effectively load configuration from ebean.properties
126      Properties properties = PrimaryServer.getProperties();
127      containerConfig = new ContainerConfig();
128      containerConfig.loadFromProperties(properties);
129    }
130    container = createContainer(containerConfig);
131    return container;
132  }
133
134  /**
135   * Create the container instance using the configuration.
136   */
137  protected static SpiContainer createContainer(ContainerConfig containerConfig) {
138
139    Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
140    if (factories.hasNext()) {
141      return factories.next().create(containerConfig);
142    }
143    throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?");
144  }
145}