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