Refactor properties bootup, remove GlobalProperties and allow ServerConfig.loadFromProperties()

This commit is contained in:
rbygrave
2014-11-30 17:08:02 +13:00
parent fe65a0ccb9
commit 370267ab6f
105 changed files with 3772 additions and 3963 deletions
+3 -10
View File
@@ -15,7 +15,6 @@ import org.slf4j.LoggerFactory;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
@@ -155,14 +154,14 @@ public final class Ebean {
// skipDefaultServer is set by EbeanServerFactory
// ... when it is creating the primaryServer
if (GlobalProperties.isSkipPrimaryServer()) {
if (PrimaryServer.isSkip()) {
// primary server being created by EbeanServerFactory
// ... so we should not try and create it here
logger.debug("GlobalProperties.isSkipPrimaryServer()");
logger.debug("PrimaryServer.isSkip()");
} else {
// look to see if there is a default server defined
String primaryName = getPrimaryServerName();
String primaryName = PrimaryServer.getPrimaryServerName();
logger.debug("primaryName:" + primaryName);
if (primaryName != null && primaryName.trim().length() > 0) {
primaryServer = getWithCreate(primaryName.trim());
@@ -170,12 +169,6 @@ public final class Ebean {
}
}
private String getPrimaryServerName() {
String serverName = GlobalProperties.get("ebean.default.datasource", null);
return GlobalProperties.get("datasource.default", serverName);
}
private EbeanServer getPrimaryServer() {
if (primaryServer == null) {
String msg = "The default EbeanServer has not been defined?";
@@ -1,11 +1,12 @@
package com.avaje.ebean;
import javax.persistence.PersistenceException;
import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import javax.persistence.PersistenceException;
import java.lang.reflect.Constructor;
import java.util.Properties;
/**
* Creates EbeanServer instances.
@@ -27,31 +28,43 @@ import com.avaje.ebean.util.ClassUtil;
*/
public class EbeanServerFactory {
private static BootupEbeanManager serverFactory = createServerFactory();
private static BootupEbeanManager bootupEbeanManager;
/**
* Initialise the container with clustering configuration.
*
* Call this prior to creating any EbeanServer instances or alternatively set the
* ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
*/
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
getServerFactory(containerConfig);
}
/**
* Create using ebean.properties to configure the server.
*/
public static EbeanServer create(String name) {
public static synchronized EbeanServer create(String name) {
EbeanServer server = serverFactory.createServer(name);
return server;
// construct based on loading properties files
// and if invoked by Ebean then it handles registration
BootupEbeanManager serverFactory = getServerFactory(null);
return serverFactory.createServer(name);
}
/**
* Create using the ServerConfig object to configure the server.
*/
public static EbeanServer create(ServerConfig config) {
public static synchronized EbeanServer create(ServerConfig config) {
if (config.getName() == null) {
throw new PersistenceException("The name is null (it is required)");
}
EbeanServer server = serverFactory.createServer(config);
EbeanServer server = createInternal(config);
if (config.isDefaultServer()) {
GlobalProperties.setSkipPrimaryServer(true);
PrimaryServer.setSkip(true);
}
if (config.isRegister()) {
Ebean.register(server, config.isDefaultServer());
@@ -60,13 +73,45 @@ public class EbeanServerFactory {
return server;
}
private static BootupEbeanManager createServerFactory() {
private static EbeanServer createInternal(ServerConfig config) {
return getServerFactory(config.getContainerConfig()).createServer(config);
}
/**
* Get the BootupEbeanManager initialising it if necessary.
*
* @param containerConfig the configuration controlling clustering communication
*/
private static BootupEbeanManager getServerFactory(ContainerConfig containerConfig) {
if (bootupEbeanManager != null) {
return bootupEbeanManager;
}
if (containerConfig == null) {
// effectively load configuration from ebean.properties
Properties properties = PrimaryServer.getProperties();
containerConfig = new ContainerConfig();
containerConfig.loadFromProperties(properties);
}
bootupEbeanManager = createServerFactory(containerConfig);
return bootupEbeanManager;
}
/**
* Create the container instance using the configuration.
*/
private static BootupEbeanManager createServerFactory(ContainerConfig containerConfig) {
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = System.getProperty("ebean.serverfactory", dflt);
try {
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
Class<?> cls = Class.forName(implClassName);
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
return (BootupEbeanManager) constructor.newInstance(containerConfig);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@@ -0,0 +1,55 @@
package com.avaje.ebean;
import com.avaje.ebean.config.PropertyMap;
import java.util.Properties;
/**
* Provides singleton state for the default server.
* <p/>
* Intended for internal use as part of bootup, construction, registration of the default server.
*/
class PrimaryServer {
private static Properties globalProperties;
private static String defaultServerName;
private static boolean skip;
/**
* Set whether to skip automatically creating the primary server.
*/
static synchronized void setSkip(boolean skip) {
PrimaryServer.skip = skip;
}
/**
* Return true to skip automatically creating the primary server.
*/
static synchronized boolean isSkip() {
return skip;
}
/**
* Return the default server name.
*/
static synchronized String getPrimaryServerName() {
getProperties();
return defaultServerName;
}
/**
* Return the default configuration Properties.
*/
static synchronized Properties getProperties() {
if (globalProperties == null) {
globalProperties = PropertyMap.defaultProperties();
}
defaultServerName = globalProperties.getProperty("datasource.default");
if (defaultServerName == null) {
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
}
return globalProperties;
}
}
@@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory;
/**
* Provides some base implementation for NamingConventions.
*
*
* @author emcgreal
*/
public abstract class AbstractNamingConvention implements NamingConvention {
@@ -41,7 +41,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
/** Used to trim off extra prefix for M2M. */
protected int rhsPrefixLength = 3;
protected boolean useForeignKeyPrefix = true;
protected boolean useForeignKeyPrefix;
/**
* Construct with a sequence format and useForeignKeyPrefix setting.
@@ -53,12 +53,12 @@ public abstract class AbstractNamingConvention implements NamingConvention {
/**
* Construct with a sequence format.
*
* @param sequenceFormat
* the sequence format
*
* @param sequenceFormat the sequence format
*/
public AbstractNamingConvention(String sequenceFormat) {
this.sequenceFormat = sequenceFormat;
this.useForeignKeyPrefix = true;
}
/**
@@ -125,10 +125,9 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* The format should include "{table}". When generating the sequence name
* {table} is replaced with the actual table name.
* </p>
*
* @param sequenceFormat
* string containing "{table}" which is replaced with the actual
* table name to generate the sequence name.
*
* @param sequenceFormat string containing "{table}" which is replaced with the actual
* table name to generate the sequence name.
*/
public void setSequenceFormat(String sequenceFormat) {
this.sequenceFormat = sequenceFormat;
@@ -173,7 +172,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* This first checks for the @Table annotation and if not present uses the
* naming convention to define the table name.
* </p>
*
*
* @see #getTableNameFromAnnotation(Class)
* @see #getTableNameByConvention(Class)
*/
@@ -240,8 +239,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
if (t != null && !isEmpty(t.name())) {
// Note: empty catalog and schema are converted to null
// Only need to convert quoted identifiers from annotations
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()),
quoteIdentifiers(t.name()));
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
}
// No annotation
@@ -279,4 +277,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
}
return false;
}
/**
* Load settings from properties.
*/
@Override
public void loadFromProperties(PropertiesWrapper properties) {
useForeignKeyPrefix = properties.getBoolean("namingConvention.useForeignKeyPrefix", useForeignKeyPrefix);
sequenceFormat = properties.get("namingConvention.sequenceFormat", sequenceFormat);
schema = properties.get("namingConvention.schema", schema);
}
}
@@ -155,14 +155,6 @@ public class AutofetchConfig {
return logDirectory;
}
/**
* Return the log directory substituting any expressions such as
* ${catalina.base} etc.
*/
public String getLogDirectoryWithEval() {
return GlobalProperties.evaluateExpressions(logDirectory);
}
/**
* Set the directory to put the autofetch log in.
*/
@@ -234,22 +226,20 @@ public class AutofetchConfig {
/**
* Load the settings from the properties file.
*/
public void loadSettings(GlobalProperties.PropertySource p) {
public void loadSettings(PropertiesWrapper p) {
logDirectory = p.get("autofetch.logDirectory", null);
queryTuning = p.getBoolean("autofetch.querytuning", false);
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", false);
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", false);
logDirectory = p.get("autofetch.logDirectory", logDirectory);
queryTuning = p.getBoolean("autofetch.querytuning", queryTuning);
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion);
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown);
profiling = p.getBoolean("autofetch.profiling", false);
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", AutofetchMode.DEFAULT_ONIFEMPTY);
profiling = p.getBoolean("autofetch.profiling", profiling);
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode);
profilingMin = p.getInt("autofetch.profiling.min", 1);
profilingBase = p.getInt("autofetch.profiling.base", 10);
profilingMin = p.getInt("autofetch.profiling.min", profilingMin);
profilingBase = p.getInt("autofetch.profiling.base", profilingBase);
String rate = p.get("autofetch.profiling.rate", "0.05");
profilingRate = Double.parseDouble(rate);
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", 60);
profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate);
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency);
}
}
@@ -1,50 +0,0 @@
package com.avaje.ebean.config;
import com.avaje.ebean.config.GlobalProperties.PropertySource;
/**
* Helper to read server specific properties from ebean.properties.
*/
class ConfigPropertyMap implements PropertySource {
private final String serverName;
public ConfigPropertyMap(String serverName) {
this.serverName = serverName;
}
public String getServerName() {
return serverName;
}
public String get(String key, String defaultValue) {
String namedKey = "ebean." + serverName + "." + key;
String inheritKey = "ebean." + key;
String value = GlobalProperties.get(namedKey, null);
if (value == null) {
value = GlobalProperties.get(inheritKey, null);
}
if (value == null) {
return defaultValue;
} else {
return value;
}
}
public int getInt(String key, int defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Integer.parseInt(value);
}
public boolean getBoolean(String key, boolean defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Boolean.parseBoolean(value);
}
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
String level = get(key, defaultValue.name());
return Enum.valueOf(enumType, level.toUpperCase());
}
}
@@ -0,0 +1,464 @@
package com.avaje.ebean.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* Configuration for the container that holds the EbeanServer instances.
* <p>
* Provides configuration for cluster communication (if clustering is used). The cluster communication is
* used to invalidate appropriate parts of the L2 cache across the cluster.
*/
public class ContainerConfig {
/**
* Communication mode used for clustering.
*/
public enum ClusterMode {
/**
* No clustering.
*/
NONE,
/**
* Use Multicast networking for cluster wide communication.
*/
MULTICAST,
/**
* Use TCP Sockets for cluster wide communication.
*/
SOCKET
}
/**
* The cluster mode to use.
*/
ClusterMode mode = ClusterMode.NONE;
/**
* Configuration if using TCP sockets for clustering communication.
*/
SocketConfig socketConfig = new SocketConfig();
/**
* Configuration if using Multicast for clustering communication.
*/
MulticastConfig multicastConfig = new MulticastConfig();
// -------------------------------------------------------------------------------------------
// MulticastConfig
/**
* The configuration for clustering using Multicast networking.
*/
public static class MulticastConfig {
int managerSleepMillis = 80;
int lastSendTimeFreqSecs = 300;//5mins
int lastStatusTimeFreqSecs = 600;//10mins
int maxResendOutgoingAttempts = 200;
int maxResendIncomingRequests = 50;
int listenPort;
String listenAddress;
int sendPort;
String sendAddress;
// Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507
int maxSendPacketSize = 1500;
// Whether to send packets even when there are no other members online
boolean sendWithNoMembers = true;
// When multiple instances are on same box you need to broadcast back locally
boolean disableLoopback;
int listenTimeToLive = -1;
int listenTimeout = 1000;
int listenBufferSize = 65500;
// For multihomed environment the address the listener should bind to
String listenBindAddress;
/**
* Return the manager sleep millis.
*/
public int getManagerSleepMillis() {
return managerSleepMillis;
}
/**
* Set the manager sleep millis.
*/
public void setManagerSleepMillis(int managerSleepMillis) {
this.managerSleepMillis = managerSleepMillis;
}
/**
* Return the last send time frequency.
*/
public int getLastSendTimeFreqSecs() {
return lastSendTimeFreqSecs;
}
/**
* Set the last send time frequency.
*/
public void setLastSendTimeFreqSecs(int lastSendTimeFreqSecs) {
this.lastSendTimeFreqSecs = lastSendTimeFreqSecs;
}
/**
* Return the last status time frequency.
*/
public int getLastStatusTimeFreqSecs() {
return lastStatusTimeFreqSecs;
}
/**
* Set the last status time frequency.
*/
public void setLastStatusTimeFreqSecs(int lastStatusTimeFreqSecs) {
this.lastStatusTimeFreqSecs = lastStatusTimeFreqSecs;
}
/**
* Return the maximum number of times we will try to re-send a given packet before giving up sending
*/
public int getMaxResendOutgoingAttempts() {
return maxResendOutgoingAttempts;
}
/**
* Set the maximum retry attempts for outgoing messages.
*/
public void setMaxResendOutgoingAttempts(int maxResendOutgoingAttempts) {
this.maxResendOutgoingAttempts = maxResendOutgoingAttempts;
}
/**
* Return the maximum number of times we will ask for a packet to be resent to us before giving up asking.
*/
public int getMaxResendIncomingRequests() {
return maxResendIncomingRequests;
}
/**
* Set the maximum retry attempts for incoming messages.
*/
public void setMaxResendIncomingRequests(int maxResendIncomingRequests) {
this.maxResendIncomingRequests = maxResendIncomingRequests;
}
/**
* Return the listen port.
*/
public int getListenPort() {
return listenPort;
}
/**
* Set the listen port.
*/
public void setListenPort(int port) {
this.listenPort = port;
}
/**
* Return the listen address.
*/
public String getListenAddress() {
return listenAddress;
}
/**
* Set the listen address.
*/
public void setListenAddress(String listenAddress) {
this.listenAddress = listenAddress;
}
/**
* Return the send port.
*/
public int getSendPort() {
return sendPort;
}
/**
* Set the send port.
*/
public void setSendPort(int sendPort) {
this.sendPort = sendPort;
}
/**
* Return the send address.
*/
public String getSendAddress() {
return sendAddress;
}
/**
* Set the send address.
*/
public void setSendAddress(String sendAddress) {
this.sendAddress = sendAddress;
}
/**
* Return the maximum send packet size.
*/
public int getMaxSendPacketSize() {
return maxSendPacketSize;
}
/**
* Set the maximum send packet size. Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507.
*/
public void setMaxSendPacketSize(int maxSendPacketSize) {
this.maxSendPacketSize = maxSendPacketSize;
}
/**
* Return true if send messages when no other members in the cluster are up.
*/
public boolean isSendWithNoMembers() {
return sendWithNoMembers;
}
/**
* Set true if send messages when no other members in the cluster are up.
*/
public void setSendWithNoMembers(boolean sendWithNoMembers) {
this.sendWithNoMembers = sendWithNoMembers;
}
/**
* Return true if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
*/
public boolean isDisableLoopback() {
return disableLoopback;
}
/**
* Set if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
*/
public void setDisableLoopback(boolean disableLoopback) {
this.disableLoopback = disableLoopback;
}
/**
* Return the listen time to live.
*/
public int getListenTimeToLive() {
return listenTimeToLive;
}
/**
* Set the listen time to live.
*/
public void setListenTimeToLive(int listenTimeToLive) {
this.listenTimeToLive = listenTimeToLive;
}
/**
* Return the listen timeout.
*/
public int getListenTimeout() {
return listenTimeout;
}
/**
* set the listen timeout.
*/
public void setListenTimeout(int listenTimeout) {
this.listenTimeout = listenTimeout;
}
/**
* Return the listen buffer size.
*/
public int getListenBufferSize() {
return listenBufferSize;
}
/**
* Set the listen buffer size.
*/
public void setListenBufferSize(int listenBufferSize) {
this.listenBufferSize = listenBufferSize;
}
/**
* Return the listener bind address (optional). For multihomed environment the address the listener should bind to.
*/
public String getListenBindAddress() {
return listenBindAddress;
}
/**
* Set the listener bind address (optional). For multihomed environment the address the listener should bind to.
*/
public void setListenBindAddress(String listenBindAddress) {
this.listenBindAddress = listenBindAddress;
}
}
// -------------------------------------------------------------------------------------------
// SocketConfig
/**
* Configuration for clustering using TCP sockets.
* <p>
* This is good for when there are relatively small number of cluster members.
*/
public static class SocketConfig {
/**
* This local server in host:port format.
*/
String localHostPort;
/**
* All the cluster members in host:port format.
*/
List<String> members = new ArrayList<String>();
/**
* core threads for the associated thread pool.
*/
int coreThreads = 2;
/**
* Max threads for the associated thread pool.
*/
int maxThreads = 16;
String threadPoolName = "EbeanCluster";
/**
* Return the host and port for this server instance.
*/
public String getLocalHostPort() {
return localHostPort;
}
/**
* Set the host and port for this server instance.
*/
public void setLocalHostPort(String localHostPort) {
this.localHostPort = localHostPort;
}
/**
* Return all the host and port for all the members of the cluster.
*/
public List<String> getMembers() {
return members;
}
/**
* Set all the host and port for all the members of the cluster.
*/
public void setMembers(List<String> members) {
this.members = members;
}
/**
* Return the number of core threads to use.
*/
public int getCoreThreads() {
return coreThreads;
}
/**
* Set the number of core threads to use.
*/
public void setCoreThreads(int coreThreads) {
this.coreThreads = coreThreads;
}
/**
* Return the number of max threads to use.
*/
public int getMaxThreads() {
return maxThreads;
}
/**
* Set the number of max threads to use.
*/
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}
/**
* Return the thread pool name.
*/
public String getThreadPoolName() {
return threadPoolName;
}
/**
* Set the thread pool name.
*/
public void setThreadPoolName(String threadPoolName) {
this.threadPoolName = threadPoolName;
}
}
// -------------------------------------------------------------------------------------------
// Members
/**
* Load the settings from properties.
*/
public void loadFromProperties(Properties properties) {
//TODO
}
/**
* Return the cluster mode.
*/
public ClusterMode getMode() {
return mode;
}
/**
* Set the cluster mode.
*/
public void setMode(ClusterMode mode) {
this.mode = mode;
}
/**
* Return the socket communication configuration.
*/
public SocketConfig getSocketConfig() {
return socketConfig;
}
/**
* Set the socket communication configuration.
*/
public void setSocketConfig(SocketConfig socketConfig) {
this.socketConfig = socketConfig;
}
/**
* Return the multicast communication configuration.
*/
public MulticastConfig getMulticastConfig() {
return multicastConfig;
}
/**
* Set the multicast communication configuration.
*/
public void setMulticastConfig(MulticastConfig multicastConfig) {
this.multicastConfig = multicastConfig;
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebean.config;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.util.StringHelper;
@@ -448,48 +449,63 @@ public class DataSourceConfig {
this.customProperties = customProperties;
}
/**
* Load the settings by reading the ebean.properties file.
*
* @param serverName name of the server
*/
public void loadSettings(String serverName) {
loadSettingsCustomPrefix("datasource." + serverName + ".", new GlobalProperties.DelegatedGlobalPropertySource(serverName));
loadSettings(new PropertiesWrapper("datasource", serverName, PropertyMap.defaultProperties()));
}
/**
* Load the settings from ebean.properties.
* Load the settings from the properties supplied.
* <p>
* You can use this when you have your own properties to use for configuration.
* </p>
*
* @param properties the properties to configure the datasource
* @param serverName the name of the specific datasource (optional)
*/
public void loadSettingsCustomPrefix(String prefix, GlobalProperties.PropertySource properties) {
public void loadSettings(Properties properties, String serverName) {
PropertiesWrapper dbProps = new PropertiesWrapper("datasource", serverName, properties);
loadSettings(dbProps);
}
this.username = properties.get(prefix + "username", null);
this.password = properties.get(prefix + "password", null);
/**
* Load the settings from the PropertiesWrapper.
*/
public void loadSettings(PropertiesWrapper properties) {
String dbDriver = properties.get(prefix + "databaseDriver", null);
this.driver = properties.get(prefix + "driver", dbDriver);
username = properties.get("username", username);
password = properties.get("password", password);
driver = properties.get("driver", properties.get("databaseDriver", driver));
url = properties.get("url", properties.get("databaseUrl", url));
String dbUrl = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", dbUrl);
autoCommit = properties.getBoolean("autoCommit", autoCommit);
captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace);
maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize);
leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes);
maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs);
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
minConnections = properties.getInt("minConnections", minConnections);
maxConnections = properties.getInt("maxConnections", maxConnections);
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);
this.minConnections = properties.getInt(prefix + "minConnections", 0);
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
this.pstmtCacheSize = properties.getInt(prefix + "pstmtCacheSize", 20);
this.cstmtCacheSize = properties.getInt(prefix + "cstmtCacheSize", 20);
waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis);
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
heartbeatSql = properties.get("heartbeatSql", heartbeatSql);
heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds);
poolListener = properties.get("poolListener", poolListener);
offline = properties.getBoolean("offline", offline);
this.heartbeatSql = properties.get(prefix + "heartbeatSql", null);
this.heartbeatTimeoutSeconds = properties.getInt(prefix + "heartbeatTimeoutSeconds", 3);
this.poolListener = properties.get(prefix + "poolListener", null);
this.offline = properties.getBoolean(prefix + "offline", false);
String isoLevel = properties.get(prefix + "isolationlevel", "READ_COMMITTED");
String isoLevel = properties.get("isolationlevel", getTransactionIsolationLevel(isolationLevel));
this.isolationLevel = getTransactionIsolationLevel(isoLevel);
String customProperties = properties.get(prefix + "customProperties", null);
String customProperties = properties.get("customProperties", null);
if (customProperties != null && customProperties.length() > 0) {
Map<String, String> custProps = StringHelper.delimitedToMap(customProperties, ";", "=");
this.customProperties = custProps;
@@ -498,7 +514,21 @@ public class DataSourceConfig {
}
/**
* return the isolation level for a given string description.
* Return the isolation level description from the associated Connection int value.
*/
public String getTransactionIsolationLevel(int level) {
switch (level) {
case Connection.TRANSACTION_NONE : return "NONE";
case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED";
case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED";
case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ";
case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE";
default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
/**
* Return the isolation level for a given string description.
*/
public int getTransactionIsolationLevel(String level) {
level = level.toUpperCase();
@@ -522,6 +552,6 @@ public class DataSourceConfig {
return Connection.TRANSACTION_SERIALIZABLE;
}
throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known.");
throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
@@ -1,211 +0,0 @@
package com.avaje.ebean.config;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import com.avaje.ebean.util.ClassUtil;
/**
* Provides access to properties loaded from the ebean.properties file.
*/
public final class GlobalProperties {
private static volatile PropertyMap globalMap;
private static boolean skipPrimaryServer;
/**
* Set whether to skip automatically creating the primary server.
*/
public static synchronized void setSkipPrimaryServer(boolean skip) {
skipPrimaryServer = skip;
}
/**
* Return true to skip automatically creating the primary server.
*/
public static synchronized boolean isSkipPrimaryServer() {
return skipPrimaryServer;
}
/**
* Parse the string replacing any expressions like ${catalina.base}.
* <p>
* This will evaluate expressions using first environment variables, than java
* system variables and lastly properties in ebean.properties - in that order.
* </p>
* <p>
* Expressions start with "${" and end with "}".
* </p>
*/
public static String evaluateExpressions(String val) {
return getPropertyMap().eval(val);
}
/**
* Parse and evaluate any expressions that have not already been evaluated.
*/
public static synchronized void evaluateExpressions() {
getPropertyMap().evaluateProperties();
}
/**
* In a servlet container environment this will additionally look in WEB-INF
* for the ebean.properties file.
*/
public static synchronized void setServletContext(ServletContext servletContext) {
PropertyMapLoader.setServletContext(servletContext);
}
/**
* Return the ServletContext (if setup in a servlet container environment).
*/
public static synchronized ServletContext getServletContext() {
return PropertyMapLoader.getServletContext();
}
private static void initPropertyMap() {
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName == null) {
fileName = "ebean.properties";
}
}
globalMap = PropertyMapLoader.load(null, fileName);
if (globalMap == null) {
// ebean.properties file was not found... but that
// is ok because we are likely doing programmatic config
globalMap = new PropertyMap();
}
String loaderCn = globalMap.get("ebean.properties.loader");
if (loaderCn != null) {
// a Runnable that can be used to customise the initialisation
// of the GlobalProperties
try {
Runnable r = (Runnable) ClassUtil.newInstance(loaderCn);
r.run();
} catch (Exception e) {
String m = "Error creating or running properties loader " + loaderCn;
throw new RuntimeException(m, e);
}
}
}
/**
* Return the property map loading it if required.
*/
private static synchronized PropertyMap getPropertyMap() {
if (globalMap == null) {
initPropertyMap();
}
return globalMap;
}
/**
* Return a String property with a default value.
*/
public static synchronized String get(String key, String defaultValue) {
return getPropertyMap().get(key, defaultValue);
}
/**
* Return a int property with a default value.
*/
public static synchronized int getInt(String key, int defaultValue) {
return getPropertyMap().getInt(key, defaultValue);
}
/**
* Return a boolean property with a default value.
*/
public static synchronized boolean getBoolean(String key, boolean defaultValue) {
return getPropertyMap().getBoolean(key, defaultValue);
}
/**
* Set a property return the previous value. This will evaluate any
* expressions in the value.
*/
public static synchronized String put(String key, String value) {
return getPropertyMap().putEval(key, value);
}
/**
* Set a Map of key value properties.
*/
public static synchronized void putAll(Map<String, String> keyValueMap) {
for (Entry<String, String> e : keyValueMap.entrySet()) {
getPropertyMap().putEval(e.getKey(), e.getValue());
}
}
public static PropertySource getPropertySource(String name) {
return new ConfigPropertyMap(name);
}
public static interface PropertySource {
/**
* Return the name of the server. This is also the dataSource name.
*/
public String getServerName();
/**
* Get a property. This will prepend "ebean" and the server name to lookup
* the value.
*/
public String get(String key, String defaultValue);
public int getInt(String key, int defaultValue);
public boolean getBoolean(String key, boolean defaultValue);
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue);
}
public static class DelegatedGlobalPropertySource implements PropertySource {
private String serverName;
public DelegatedGlobalPropertySource(String serverName) {
this.serverName = serverName;
}
@Override
public String getServerName() {
return serverName;
}
@Override
public String get(String key, String defaultValue) {
return GlobalProperties.get(key, defaultValue);
}
@Override
public int getInt(String key, int defaultValue) {
return GlobalProperties.getInt(key, defaultValue);
}
@Override
public boolean getBoolean(String key, boolean defaultValue) {
return GlobalProperties.getBoolean(key, defaultValue);
}
@Override
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
String level = get(key, defaultValue.name());
return Enum.valueOf(enumType, level.toUpperCase());
}
}
}
@@ -112,4 +112,9 @@ public interface NamingConvention {
*/
public boolean isUseForeignKeyPrefix();
/**
* Load setting from properties.
*/
public void loadFromProperties(PropertiesWrapper properties);
}
@@ -0,0 +1,148 @@
package com.avaje.ebean.config;
import java.util.Properties;
public class PropertiesWrapper {
protected final Properties properties;
protected final String prefix;
protected final String serverName;
protected final PropertyMap propertyMap;
/**
* Construct with a prefix, serverName and properties.
*/
public PropertiesWrapper(String prefix, String serverName, Properties properties) {
this.serverName = serverName;
this.prefix = prefix;
this.propertyMap = PropertyMapLoader.load(null, properties);
this.properties = propertyMap.asProperties();
}
/**
* Construct without prefix of serverName.
*/
public PropertiesWrapper(Properties properties) {
this(null, null, properties);
}
/**
* Internal copy constructor when changing prefix.
*/
protected PropertiesWrapper(String prefix, String serverName, PropertyMap propertyMap, Properties properties) {
this.serverName = serverName;
this.prefix = prefix;
this.propertyMap = propertyMap;
this.properties = properties;
}
/**
* Return a PropertiesWrapper instance with a different prefix but same underlying properties.
* <p/>
* Used when wanting to use "datasource" as the prefix rather than "ebean".
* <p/>
* The returning instance should only be used in a read only fashion.
*/
public PropertiesWrapper withPrefix(String prefix) {
return new PropertiesWrapper(prefix, serverName, propertyMap, properties);
}
/**
* Return the serverName (optional).
*/
public String getServerName() {
return serverName;
}
/**
* Return as Properties with lower case keys and after evaluation and additional properties loading has occurred.
* <p>
* Ebean has historically ignored the case of keys hence returning the Properties with all the keys lower cased.
* </p>
*/
public Properties asPropertiesLowerCase() {
return properties;
}
/**
* Get a property with no default value.
*/
public String get(String key) {
return get(key, null);
}
/**
* Get a property with a default value.
* <p>
* This performs a search using the prefix and server name (if supplied) to search for the property
* value in order based on:
* <pre>{@code
* prefix.serverName.key
* prefix.key
* key
* }</pre>
* </p>
*/
public String get(String key, String defaultValue) {
String value = null;
if (serverName != null && prefix != null) {
value = propertyMap.get(prefix + "." + serverName + "." + key, null);
}
if (value == null && prefix != null) {
value = propertyMap.get(prefix + "." + key, null);
}
if (value == null) {
value = propertyMap.get(key, null);
}
return value == null ? defaultValue : value;
}
/**
* Return a double property value.
*/
public double getDouble(String key, double defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Double.parseDouble(value);
}
/**
* Return an int property value.
*/
public int getInt(String key, int defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Integer.parseInt(value);
}
/**
* Return a long property value.
*/
public long getLong(String key, long defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Long.parseLong(value);
}
/**
* Return a boolean property value.
*/
public boolean getBoolean(String key, boolean defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Boolean.parseBoolean(value);
}
/**
* Return a Enum property value.
*/
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
String level = get(key, defaultValue.name());
return Enum.valueOf(enumType, level.toUpperCase());
}
}
@@ -4,21 +4,40 @@ import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
/**
* A map like structure of properties.
* <p/>
* Handles evaluation of expressions like ${home} and provides convenience methods for int, long and boolean.
*/
final class PropertyMap implements Serializable {
public final class PropertyMap implements Serializable {
private static final long serialVersionUID = 1L;
private LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
public static Properties defaultProperties() {
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
}
public String toString() {
return map.toString();
}
/**
* Return as standard Properties.
*/
public Properties asProperties() {
Properties properties = new Properties();
for (Entry<String, String> e : entrySet()) {
properties.put(e.getKey(), e.getValue());
}
return properties;
}
/**
* Go through all the properties and evaluate any expressions that have not
* been resolved.
@@ -35,10 +54,16 @@ final class PropertyMap implements Serializable {
}
}
/**
* Returns the value with expressions like ${home} evaluated using system properties and environment variables.
*/
public synchronized String eval(String val) {
return PropertyExpression.eval(val, this);
}
/**
* Return the boolean property value with a given default.
*/
public synchronized boolean getBoolean(String key, boolean defaultValue) {
String value = get(key);
if (value == null) {
@@ -48,6 +73,9 @@ final class PropertyMap implements Serializable {
}
}
/**
* Return the int property value with a given default.
*/
public synchronized int getInt(String key, int defaultValue) {
String value = get(key);
if (value == null) {
@@ -57,36 +85,69 @@ final class PropertyMap implements Serializable {
}
}
/**
* Return the long property value with a given default.
*/
public synchronized long getLong(String key, long defaultValue) {
String value = get(key);
if (value == null) {
return defaultValue;
} else {
return Long.parseLong(value);
}
}
/**
* Return the string property value with a given default.
*/
public synchronized String get(String key, String defaultValue) {
String value = map.get(key.toLowerCase());
return value == null ? defaultValue : value;
}
/**
* Return the property value returning null if there is no value defined.
*/
public synchronized String get(String key) {
return map.get(key.toLowerCase());
}
synchronized void putAll(Map<String, String> keyValueMap) {
/**
* Put all evaluating any expressions in the values.
*/
public synchronized void putEvalAll(Map<String, String> keyValueMap) {
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
put(entry.getKey(), entry.getValue());
putEval(entry.getKey(), entry.getValue());
}
}
synchronized String putEval(String key, String value) {
/**
* Put a single key value evaluating any expressions in the value.
*/
public synchronized String putEval(String key, String value) {
value = PropertyExpression.eval(value, this);
return map.put(key.toLowerCase(), value);
}
synchronized String put(String key, String value) {
/**
* Put a single key value with no expression evaluation.
*/
public synchronized String put(String key, String value) {
return map.put(key.toLowerCase(), value);
}
synchronized String remove(String key) {
/**
* Remove an entry.
*/
public synchronized String remove(String key) {
return map.remove(key.toLowerCase());
}
synchronized Set<Entry<String, String>> entrySet() {
/**
* Return the entries.
*/
public synchronized Set<Entry<String, String>> entrySet() {
return map.entrySet();
}
@@ -1,40 +1,30 @@
package com.avaje.ebean.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Map;
import java.util.Properties;
/**
* Helper used to load the PropertyMap.
* Helper used to load the ebean.properties into a PropertyMap.
*/
final class PropertyMapLoader {
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
private static ServletContext servletContext;
public static PropertyMap loadGlobalProperties() {
/**
* Return the servlet context when in a web environment.
*/
public static ServletContext getServletContext() {
return servletContext;
}
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName == null) {
fileName = "ebean.properties";
}
}
/**
* Set the ServletContext for when ebean.properties is in WEB-INF in a web
* application environment.
*/
public static void setServletContext(ServletContext servletContext) {
PropertyMapLoader.servletContext = servletContext;
return load(null, fileName);
}
/**
@@ -64,15 +54,20 @@ final class PropertyMapLoader {
* @param in
* the InputStream of the properties file to load.
*/
private static PropertyMap load(PropertyMap p, InputStream in) {
public static PropertyMap load(PropertyMap p, InputStream in) {
Properties props = new Properties();
try {
props.load(in);
in.close();
return load(p, props);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static PropertyMap load(PropertyMap p, Properties props) {
if (p == null) {
p = new PropertyMap();
@@ -117,18 +112,6 @@ final class PropertyMapLoader {
throw new NullPointerException("fileName is null?");
}
if (servletContext == null) {
logger.debug("No servletContext so not looking in WEB-INF for " + fileName);
} else {
// first look in WEB-INF ...
InputStream in = servletContext.getResourceAsStream("/WEB-INF/" + fileName);
if (in != null) {
logger.debug(fileName + " found in WEB-INF");
return in;
}
}
try {
File f = new File(fileName);
@@ -5,7 +5,6 @@ import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.annotation.Encrypted;
import com.avaje.ebean.cache.ServerCacheFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.GlobalProperties.PropertySource;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.event.*;
@@ -16,6 +15,7 @@ import com.fasterxml.jackson.core.JsonFactory;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* The configuration used for creating a EbeanServer.
@@ -30,9 +30,9 @@ import java.util.List;
* classes and listeners etc.
* </p>
*
* <pre class="code">
* <pre>{@code
* ServerConfig c = new ServerConfig();
* c.setName(&quot;ordh2&quot;);
* c.setName("ordh2");
*
* // read the ebean.properties and load
* // those settings into this serverConfig object
@@ -43,18 +43,18 @@ import java.util.List;
* c.setDdlRun(true);
*
* // add any classes found in the app.data package
* c.addPackage(&quot;app.data&quot;);
* c.addPackage("app.data");
*
* // add the names of Jars that contain entities
* c.addJar(&quot;myJarContainingEntities.jar&quot;);
* c.addJar(&quot;someOtherJarContainingEntities.jar&quot;);
* c.addJar("myJarContainingEntities.jar");
* c.addJar("someOtherJarContainingEntities.jar");
*
* // register as the 'Default' server
* c.setDefaultServer(true);
*
* EbeanServer server = EbeanServerFactory.create(c);
*
* </pre>
* }</pre>
*
* @see EbeanServerFactory
*
@@ -62,17 +62,14 @@ import java.util.List;
* @author rbygrave
*/
public class ServerConfig {
/**
* The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100
*/
private final static int DEFAULT_QUERY_BATCH_SIZE = 100;
/**
* The EbeanServer name.
*/
private String name;
private ContainerConfig containerConfig;
/**
* The resource directory.
*/
@@ -111,6 +108,11 @@ public class ServerConfig {
*/
private List<String> searchJars = new ArrayList<String>();
/**
* Class name of a classPathReader implementation.
*/
private String classPathReaderClassName;
/**
* Config controlling the autofetch behaviour.
*/
@@ -146,9 +148,11 @@ public class ServerConfig {
private int lazyLoadBatchSize = 1;
/**
* The query batch size.
* The default batch size for 'query joins'.
*/
private int queryBatchSize = -1;
private int queryBatchSize = 100;
private boolean eagerFetchLobs;
private boolean ddlGenerate;
@@ -201,7 +205,7 @@ public class ServerConfig {
/**
* The naming convention.
*/
private NamingConvention namingConvention;
private NamingConvention namingConvention = new UnderscoreNamingConvention();
/**
* Behaviour of update to include on the change properties.
@@ -238,9 +242,9 @@ public class ServerConfig {
private ServerCacheManager serverCacheManager;
private boolean collectQueryStatsByNode;
private boolean collectQueryStatsByNode = true;
private boolean collectQueryOrigins;
private boolean collectQueryOrigins = true;
/**
* The default PersistenceContextScope used if one is not explicitly set on a query.
@@ -253,6 +257,33 @@ public class ServerConfig {
private boolean durationWithNanos;
private int maxCallStack = 5;
private boolean transactionRollbackOnChecked = true;
private boolean registerJmxMBeans = true;
// configuration for the background executor service (thread pool)
private int backgroundExecutorSchedulePoolSize = 1;
private int backgroundExecutorCorePoolSize = 1;
private int backgroundExecutorMaxPoolSize = 8;
private int backgroundExecutorIdleSecs = 60;
private int backgroundExecutorShutdownSecs = 30;
// defaults for the L2 bean caching
private int cacheWarmingDelay = 30;
private int cacheMaxSize = 10000;
private int cacheMaxIdleTime = 600;
private int cacheMaxTimeToLive = 60*60*6;
// defaults for the L2 query caching
private int queryCacheMaxSize = 1000;
private int queryCacheMaxIdleTime = 600;
private int queryCacheMaxTimeToLive = 60*60*6;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
@@ -306,6 +337,26 @@ public class ServerConfig {
this.name = name;
}
/**
* Return the container / clustering configuration.
* <p/>
* The container holds all the EbeanServer instances and provides clustering communication
* services to all the EbeanServer instances.
*/
public ContainerConfig getContainerConfig() {
return containerConfig;
}
/**
* Set the container / clustering configuration.
* <p/>
* The container holds all the EbeanServer instances and provides clustering communication
* services to all the EbeanServer instances.
*/
public void setContainerConfig(ContainerConfig containerConfig) {
this.containerConfig = containerConfig;
}
/**
* Return true if this server should be registered with the Ebean singleton
* when it is created.
@@ -362,15 +413,6 @@ public class ServerConfig {
return persistBatching;
}
/**
* Use isPersistBatching() instead.
*
* @deprecated
*/
public boolean isUsePersistBatching() {
return persistBatching;
}
/**
* Set to true if you what to use JDBC batching for persisting and deleting
* beans.
@@ -532,6 +574,234 @@ public class ServerConfig {
this.enhanceLogLevel = enhanceLogLevel;
}
/**
* Return true if LOB's should default to fetch eager.
* By default this is set to false and LOB's must be explicitly fetched.
*/
public boolean isEagerFetchLobs() {
return eagerFetchLobs;
}
/**
* Set to true if you want LOB's to be fetch eager by default.
* By default this is set to false and LOB's must be explicitly fetched.
*/
public void setEagerFetchLobs(boolean eagerFetchLobs) {
this.eagerFetchLobs = eagerFetchLobs;
}
/**
* Return the max call stack to use for origin location.
*/
public int getMaxCallStack() {
return maxCallStack;
}
/**
* Set the max call stack to use for origin location.
*/
public void setMaxCallStack(int maxCallStack) {
this.maxCallStack = maxCallStack;
}
/**
* Return true if transactions should rollback on checked exceptions.
*/
public boolean isTransactionRollbackOnChecked() {
return transactionRollbackOnChecked;
}
/**
* Set to true if transactions should by default rollback on checked exceptions.
*/
public void setTransactionRollbackOnChecked(boolean transactionRollbackOnChecked) {
this.transactionRollbackOnChecked = transactionRollbackOnChecked;
}
/**
* Return true if the server should register JMX MBeans.
*/
public boolean isRegisterJmxMBeans() {
return registerJmxMBeans;
}
/**
* Set if the server should register JMX MBeans.
*/
public void setRegisterJmxMBeans(boolean registerJmxMBeans) {
this.registerJmxMBeans = registerJmxMBeans;
}
/**
* Return the Background executor schedule pool size. Defaults to 1.
*/
public int getBackgroundExecutorSchedulePoolSize() {
return backgroundExecutorSchedulePoolSize;
}
/**
* Set the Background executor schedule pool size.
*/
public void setBackgroundExecutorSchedulePoolSize(int backgroundExecutorSchedulePoolSize) {
this.backgroundExecutorSchedulePoolSize = backgroundExecutorSchedulePoolSize;
}
/**
* Return the Background executor core pool size.
*/
public int getBackgroundExecutorCorePoolSize() {
return backgroundExecutorCorePoolSize;
}
/**
* Set the Background executor core pool size.
*/
public void setBackgroundExecutorCorePoolSize(int backgroundExecutorCorePoolSize) {
this.backgroundExecutorCorePoolSize = backgroundExecutorCorePoolSize;
}
/**
* Return the Background executor max pool size.
*/
public int getBackgroundExecutorMaxPoolSize() {
return backgroundExecutorMaxPoolSize;
}
/**
* Set the Background executor max pool size.
*/
public void setBackgroundExecutorMaxPoolSize(int backgroundExecutorMaxPoolSize) {
this.backgroundExecutorMaxPoolSize = backgroundExecutorMaxPoolSize;
}
/**
* Return the Background executor idle seconds.
*/
public int getBackgroundExecutorIdleSecs() {
return backgroundExecutorIdleSecs;
}
/**
* Set the Background executor idle seconds.
*/
public void setBackgroundExecutorIdleSecs(int backgroundExecutorIdleSecs) {
this.backgroundExecutorIdleSecs = backgroundExecutorIdleSecs;
}
/**
* Return the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
* before it is forced shutdown.
*/
public int getBackgroundExecutorShutdownSecs() {
return backgroundExecutorShutdownSecs;
}
/**
* Set the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
* before it is forced shutdown.
*/
public void setBackgroundExecutorShutdownSecs(int backgroundExecutorShutdownSecs) {
this.backgroundExecutorShutdownSecs = backgroundExecutorShutdownSecs;
}
/**
* Return the cache warming delay in seconds.
*/
public int getCacheWarmingDelay() {
return cacheWarmingDelay;
}
/**
* Set the cache warming delay in seconds.
*/
public void setCacheWarmingDelay(int cacheWarmingDelay) {
this.cacheWarmingDelay = cacheWarmingDelay;
}
/**
* Return the L2 cache default max size.
*/
public int getCacheMaxSize() {
return cacheMaxSize;
}
/**
* Set the L2 cache default max size.
*/
public void setCacheMaxSize(int cacheMaxSize) {
this.cacheMaxSize = cacheMaxSize;
}
/**
* Return the L2 cache default max idle time in seconds.
*/
public int getCacheMaxIdleTime() {
return cacheMaxIdleTime;
}
/**
* Set the L2 cache default max idle time in seconds.
*/
public void setCacheMaxIdleTime(int cacheMaxIdleTime) {
this.cacheMaxIdleTime = cacheMaxIdleTime;
}
/**
* Return the L2 cache default max time to live in seconds.
*/
public int getCacheMaxTimeToLive() {
return cacheMaxTimeToLive;
}
/**
* Set the L2 cache default max time to live in seconds.
*/
public void setCacheMaxTimeToLive(int cacheMaxTimeToLive) {
this.cacheMaxTimeToLive = cacheMaxTimeToLive;
}
/**
* Return the L2 query cache default max size.
*/
public int getQueryCacheMaxSize() {
return queryCacheMaxSize;
}
/**
* Set the L2 query cache default max size.
*/
public void setQueryCacheMaxSize(int queryCacheMaxSize) {
this.queryCacheMaxSize = queryCacheMaxSize;
}
/**
* Return the L2 query cache default max idle time in seconds.
*/
public int getQueryCacheMaxIdleTime() {
return queryCacheMaxIdleTime;
}
/**
* Set the L2 query cache default max idle time in seconds.
*/
public void setQueryCacheMaxIdleTime(int queryCacheMaxIdleTime) {
this.queryCacheMaxIdleTime = queryCacheMaxIdleTime;
}
/**
* Return the L2 query cache default max time to live in seconds.
*/
public int getQueryCacheMaxTimeToLive() {
return queryCacheMaxTimeToLive;
}
/**
* Set the L2 query cache default max time to live in seconds.
*/
public void setQueryCacheMaxTimeToLive(int queryCacheMaxTimeToLive) {
this.queryCacheMaxTimeToLive = queryCacheMaxTimeToLive;
}
/**
* Return the NamingConvention.
* <p>
@@ -1049,6 +1319,23 @@ public class ServerConfig {
this.searchJars = searchJars;
}
/**
* Return the class name of a classPathReader implementation.
*/
public String getClassPathReaderClassName() {
return classPathReaderClassName;
}
/**
* Set the class name of a classPathReader implementation.
*
* Refer to server.util.ClassPathReader, this should really by a plugin but doing this for now
* to be relatively compatible with current implementation.
*/
public void setClassPathReaderClassName(String classPathReaderClassName) {
this.classPathReaderClassName = classPathReaderClassName;
}
/**
* Set the list of classes (entities, listeners, scalarTypes etc) that should
* be used for this server.
@@ -1334,44 +1621,26 @@ public class ServerConfig {
}
/**
* Load the settings from the ebean.properties file.
* Load settings from ebean.properties.
*/
public void loadFromProperties() {
ConfigPropertyMap p = new ConfigPropertyMap(name);
loadFromProperties(PropertyMap.defaultProperties());
}
/**
* Load the settings from the given properties
*/
public void loadFromProperties(Properties properties) {
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
loadSettings(p);
}
/**
* Return a PropertySource for this server.
*/
public PropertySource getPropertySource() {
return GlobalProperties.getPropertySource(name);
}
/**
* Return a configuration property using a default value.
*/
public String getProperty(String propertyName, String defaultValue) {
PropertySource p = new ConfigPropertyMap(name);
return p.get(propertyName, defaultValue);
}
/**
* Return a configuration property.
*/
public String getProperty(String propertyName) {
return getProperty(propertyName, null);
}
@SuppressWarnings("unchecked")
private <T> T createInstance(PropertySource p, Class<T> type, String key) {
private <T> T createInstance(PropertiesWrapper p, Class<T> pluginType, String key) {
String classname = p.get(key, null);
if (classname == null) {
return null;
}
return (T) ClassUtil.newInstance(classname);
return classname == null ? null : (T) ClassUtil.newInstance(classname);
}
/**
@@ -1381,36 +1650,37 @@ public class ServerConfig {
*
* @param p - The defined property source passed to load settings
*/
protected void loadDataSourceSettings(PropertySource p) {
dataSourceConfig.loadSettings(p.getServerName());
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.withPrefix("datasource"));
}
/**
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
*/
protected void loadAutofetchConfig(PropertySource p) {
protected void loadAutofetchSettings(PropertiesWrapper p) {
autofetchConfig.loadSettings(p);
}
/**
* Load the configuration settings from the properties file.
*/
protected void loadSettings(PropertySource p) {
protected void loadSettings(PropertiesWrapper p) {
if (namingConvention != null) {
namingConvention.loadFromProperties(p);
}
if (autofetchConfig == null) {
autofetchConfig = new AutofetchConfig();
}
loadAutofetchConfig(p);
loadAutofetchSettings(p);
if (dataSourceConfig == null) {
dataSourceConfig = new DataSourceConfig();
}
loadDataSourceSettings(p);
autoCommitMode = p.getBoolean("autoCommitMode", false);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
namingConvention = createNamingConvention(p);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager");
@@ -1425,37 +1695,37 @@ public class ServerConfig {
searchJars = getSearchJarsPackages(jarsProp);
}
String packagesProp = p.get("search.packages", p.get("packages", null));
if (packages != null) {
String packagesProp = p.get("search.packages", p.get("packages", null));
packages = getSearchJarsPackages(packagesProp);
}
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", true);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren);
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
boolean batchMode = p.getBoolean("batch.mode", false);
boolean batchMode = p.getBoolean("batch.mode", persistBatching);
persistBatching = p.getBoolean("persistBatching", batchMode);
int batchSize = p.getInt("batch.size", 20);
int batchSize = p.getInt("batch.size", persistBatchSize);
persistBatchSize = p.getInt("persistBatchSize", batchSize);
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope","TRANSACTION"));
dataSourceJndiName = p.get("dataSourceJndiName", null);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", 20);
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
localTimeWithNanos = p.getBoolean("localTimeWithNanos", false);
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", uuidStoreAsBinary);
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
String jsonDateTimeFormat = p.get("jsonDateTime", null);
if (jsonDateTimeFormat != null) {
@@ -1464,27 +1734,27 @@ public class ServerConfig {
jsonDateTime = JsonConfig.DateTime.MILLIS;
}
ddlGenerate = p.getBoolean("ddl.generate", false);
ddlRun = p.getBoolean("ddl.run", false);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
classes = getClasses(p);
}
private NamingConvention createNamingConvention(PropertySource p) {
private NamingConvention createNamingConvention(PropertiesWrapper properties) {
NamingConvention nc = createInstance(p, NamingConvention.class, "namingconvention");
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention");
if (nc == null) {
return null;
}
if (nc instanceof AbstractNamingConvention) {
AbstractNamingConvention anc = (AbstractNamingConvention) nc;
String v = p.get("namingConvention.useForeignKeyPrefix", null);
String v = properties.get("namingConvention.useForeignKeyPrefix", null);
if (v != null) {
boolean useForeignKeyPrefix = Boolean.valueOf(v);
anc.setUseForeignKeyPrefix(useForeignKeyPrefix);
}
String sequenceFormat = p.get("namingConvention.sequenceFormat", null);
String sequenceFormat = properties.get("namingConvention.sequenceFormat", null);
if (sequenceFormat != null) {
anc.setSequenceFormat(sequenceFormat);
}
@@ -1495,20 +1765,20 @@ public class ServerConfig {
/**
* Build the list of classes from the comma delimited string.
*
* @param p
* the p
* @param properties
* the properties
*
* @return the classes
*/
private ArrayList<Class<?>> getClasses(PropertySource p) {
private List<Class<?>> getClasses(PropertiesWrapper properties) {
String classNames = p.get("classes", null);
String classNames = properties.get("classes", null);
if (classNames == null) {
return null;
}
ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
List<Class<?>> classes = new ArrayList<Class<?>>();
String[] split = classNames.split("[ ,;]");
for (int i = 0; i < split.length; i++) {
@@ -1538,5 +1808,4 @@ public class ServerConfig {
}
return hitList;
}
}
@@ -72,6 +72,10 @@ public class DatabasePlatform {
*/
protected String name = "generic";
protected String columnAliasPrefix = "c";
protected String tableAliasPlaceHolder = "${ta}";
/**
* Use a BackTick ` at the beginning and end of table or column names that you
* want to use quoted identifiers for. The backticks get converted to the
@@ -166,6 +170,34 @@ public class DatabasePlatform {
return dbDdlSyntax;
}
/**
* Return the column alias prefix.
*/
public String getColumnAliasPrefix() {
return columnAliasPrefix;
}
/**
* Set the column alias prefix.
*/
public void setColumnAliasPrefix(String columnAliasPrefix) {
this.columnAliasPrefix = columnAliasPrefix;
}
/**
* Return the table alias placeholder.
*/
public String getTableAliasPlaceHolder() {
return tableAliasPlaceHolder;
}
/**
* Set the table alias placeholder.
*/
public void setTableAliasPlaceHolder(String tableAliasPlaceHolder) {
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
}
/**
* Return the close quote for quoted identifiers.
*
@@ -1,7 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
@@ -14,15 +13,13 @@ public class H2Platform extends DatabasePlatform {
super();
this.name = "h2";
this.dbEncrypt = new H2DbEncrypt();
this.likeClause = "like ? escape''";
// like ? escape'' not working in the latest version H2 so just using no
// escape clause for now noting that backslash is an escape char for like in H2
this.likeClause = "like ?";
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
boolean useIdentity = GlobalProperties.getBoolean("ebean.h2platform.useIdentity", false);
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
this.dbIdentity.setIdType(idType);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -1,11 +1,9 @@
package com.avaje.ebean.config.dbplatform;
import java.sql.Types;
import com.avaje.ebean.BackgroundExecutor;
import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import java.sql.Types;
/**
* H2 specific platform.
@@ -16,14 +14,7 @@ public class HsqldbPlatform extends DatabasePlatform {
super();
this.name = "hsqldb";
this.dbEncrypt = new H2DbEncrypt();
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
boolean useIdentity = GlobalProperties.getBoolean("ebean.hsqldb.useIdentity", true);
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
this.dbIdentity.setIdType(idType);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -1,7 +1,5 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.GlobalProperties;
/**
* Oracle encryption support.
*
@@ -48,12 +46,19 @@ import com.avaje.ebean.config.GlobalProperties;
public class Oracle10DbEncrypt extends AbstractDbEncrypt {
/**
* Constructs the Oracle10DbEncrypt.
* Constructs the Oracle10DbEncrypt with default encrypt and decrypt stored procedures.
*/
public Oracle10DbEncrypt() {
this("eb_encrypt", "eb_decrypt");
}
String encryptfunction = GlobalProperties.get("ebean.oracle.encryptfunction", "eb_encrypt");
String decryptfunction = GlobalProperties.get("ebean.oracle.decryptfunction", "eb_decrypt");
/**
* Constructs the Oracle10DbEncrypt specifying encrypt and decrypt stored procedures.
*
* @param encryptfunction the encrypt stored procedure
* @param decryptfunction the decrypt stored procedure
*/
public Oracle10DbEncrypt(String encryptfunction, String decryptfunction) {
this.varcharEncryptFunction = new OraVarcharFunction(encryptfunction, decryptfunction);
this.dateEncryptFunction = new OraDateFunction(encryptfunction, decryptfunction);
@@ -1,7 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
@@ -27,12 +26,7 @@ public class Postgres8Platform extends DatabasePlatform {
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
if (colAlias == null) {
// Postgres requires the "as" keyword for column alias
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
}
this.columnAliasPrefix = "as c";
this.openQuote = "\"";
this.closeQuote = "\"";
@@ -1,10 +1,8 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
@@ -38,11 +36,7 @@ public class PostgresPlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
if (colAlias == null) {
// Postgres requires the "as" keyword for column alias
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
}
this.columnAliasPrefix = "as c";
this.openQuote = "\"";
this.closeQuote = "\"";
@@ -1,19 +1,11 @@
package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.transaction.BeanDelta;
import com.avaje.ebeaninternal.server.transaction.DeleteByIdMap;
import com.avaje.ebeaninternal.server.transaction.IndexInvalidate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.List;
/**
* Holds information for a transaction. There is one TransactionEvent instance
@@ -25,180 +17,107 @@ import org.slf4j.LoggerFactory;
*/
public class TransactionEvent implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(TransactionEvent.class);
private static final long serialVersionUID = 7230903304106097120L;
private static final long serialVersionUID = 7230903304106097120L;
/**
* Flag indicating this is a local transaction (not from another server in
* the cluster).
*/
private transient boolean local;
/**
* Flag indicating this is a local transaction (not from another server in
* the cluster).
*/
private transient boolean local;
private boolean invalidateAll;
private TransactionEventTable eventTables;
private TransactionEventTable eventTables;
private transient TransactionEventBeans eventBeans;
private transient TransactionEventBeans eventBeans;
private transient List<BeanDelta> beanDeltas;
private transient DeleteByIdMap deleteByIdMap;
private transient DeleteByIdMap deleteByIdMap;
private transient Set<IndexInvalidate> indexInvalidations;
private transient Set<String> pauseIndexInvalidate;
/**
* Create the TransactionEvent, one per Transaction.
*/
public TransactionEvent() {
this.local = true;
}
/**
* Set this to true to invalidate all table dependent cached objects.
*/
public void setInvalidateAll(boolean isInvalidateAll) {
this.invalidateAll = isInvalidateAll;
}
/**
* Create the TransactionEvent, one per Transaction.
*/
public TransactionEvent() {
this.local = true;
}
/**
* Return true if all table states should be invalidated. This will cause
* all cached objects to be invalidated.
*/
public boolean isInvalidateAll() {
return invalidateAll;
}
/**
* Temporarily pause/ignore any index invalidation for this bean type.
*/
public void pauseIndexInvalidate(Class<?> beanType) {
if (pauseIndexInvalidate == null){
pauseIndexInvalidate = new HashSet<String>();
}
pauseIndexInvalidate.add(beanType.getName());
}
/**
* Resume listening for index invalidation for this bean type.
*/
public void resumeIndexInvalidate(Class<?> beanType) {
if (pauseIndexInvalidate != null){
pauseIndexInvalidate.remove(beanType.getName());
}
public void addDeleteById(BeanDescriptor<?> desc, Object id) {
if (deleteByIdMap == null) {
deleteByIdMap = new DeleteByIdMap();
}
/**
* Add an IndexInvalidation notices to the transaction.
*/
public void addIndexInvalidate(IndexInvalidate indexEvent){
if (pauseIndexInvalidate != null && pauseIndexInvalidate.contains(indexEvent.getIndexName())){
logger.debug("--- IGNORE Invalidate on "+indexEvent.getIndexName());
return;
}
if (indexInvalidations == null){
indexInvalidations = new HashSet<IndexInvalidate>();
}
indexInvalidations.add(indexEvent);
}
public void addDeleteById(BeanDescriptor<?> desc, Object id){
if (deleteByIdMap == null){
deleteByIdMap = new DeleteByIdMap();
}
deleteByIdMap.add(desc, id);
}
public void addDeleteByIdList(BeanDescriptor<?> desc, List<Object> idList) {
if (deleteByIdMap == null) {
deleteByIdMap = new DeleteByIdMap();
}
deleteByIdMap.addList(desc, idList);
deleteByIdMap.add(desc, id);
}
public void addDeleteByIdList(BeanDescriptor<?> desc, List<Object> idList) {
if (deleteByIdMap == null) {
deleteByIdMap = new DeleteByIdMap();
}
public DeleteByIdMap getDeleteByIdMap() {
return deleteByIdMap;
deleteByIdMap.addList(desc, idList);
}
public DeleteByIdMap getDeleteByIdMap() {
return deleteByIdMap;
}
/**
* Return true if this was a local transaction. Returns false if this
* transaction originated on another server in the cluster.
*/
public boolean isLocal() {
return local;
}
/**
* For BeanListeners the requests they are interested in.
*/
public TransactionEventBeans getEventBeans() {
return eventBeans;
}
public TransactionEventTable getEventTables() {
return eventTables;
}
public void add(String tableName, boolean inserts, boolean updates, boolean deletes) {
if (eventTables == null) {
eventTables = new TransactionEventTable();
}
eventTables.add(tableName, inserts, updates, deletes);
}
public void addBeanDelta(BeanDelta delta) {
if (beanDeltas == null) {
beanDeltas = new ArrayList<BeanDelta>();
}
beanDeltas.add(delta);
public void add(TransactionEventTable table) {
if (eventTables == null) {
eventTables = new TransactionEventTable();
}
eventTables.add(table);
}
public List<BeanDelta> getBeanDeltas() {
return beanDeltas;
/**
* Add a inserted updated or deleted bean to the event.
*/
public void add(PersistRequestBean<?> request) {
if (request.isNotify(this)) {
// either a BeanListener or Cache is interested
if (eventBeans == null) {
eventBeans = new TransactionEventBeans();
}
eventBeans.add(request);
}
/**
* Return true if this was a local transaction. Returns false if this
* transaction originated on another server in the cluster.
*/
public boolean isLocal() {
return local;
}
}
/**
* For BeanListeners the requests they are interested in.
*/
public TransactionEventBeans getEventBeans() {
return eventBeans;
}
public TransactionEventTable getEventTables() {
return eventTables;
}
public Set<IndexInvalidate> getIndexInvalidations() {
return indexInvalidations;
/**
* Notify the cache of bean changes.
* <p>
* This returns the TransactionEventTable so that if any
* general table changes can also be used to invalidate
* parts of the cache.
* </p>
*/
public void notifyCache() {
if (eventBeans != null) {
eventBeans.notifyCache();
}
public void add(String tableName, boolean inserts, boolean updates, boolean deletes){
if (eventTables == null){
eventTables = new TransactionEventTable();
}
eventTables.add(tableName, inserts, updates, deletes);
}
public void add(TransactionEventTable table){
if (eventTables == null){
eventTables = new TransactionEventTable();
}
eventTables.add(table);
}
/**
* Add a inserted updated or deleted bean to the event.
*/
public void add(PersistRequestBean<?> request) {
if (request.isNotify(this)){
// either a BeanListener or Cache is interested
if (eventBeans == null) {
eventBeans = new TransactionEventBeans();
}
eventBeans.add(request);
}
}
/**
* Notify the cache of bean changes.
* <p>
* This returns the TransactionEventTable so that if any
* general table changes can also be used to invalidate
* parts of the cache.
* </p>
*/
public void notifyCache(){
if (eventBeans != null){
eventBeans.notifyCache();
}
if (deleteByIdMap != null) {
deleteByIdMap.notifyCache();
}
}
if (deleteByIdMap != null) {
deleteByIdMap.notifyCache();
}
}
}
@@ -75,7 +75,7 @@ public final class TransactionEventTable implements Serializable {
private boolean update;
private boolean delete;
private TableIUD(String table, boolean insert, boolean update, boolean delete){
public TableIUD(String table, boolean insert, boolean update, boolean delete){
this.table = table;
this.insert = insert;
this.update = update;
@@ -1,18 +1,16 @@
package com.avaje.ebeaninternal.server.autofetch;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class AutoFetchManagerFactory {
private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class);
@@ -37,7 +35,7 @@ public class AutoFetchManagerFactory {
AutoFetchManager autoFetchManager = null;
boolean readFile = GlobalProperties.getBoolean("autofetch.readfromfile", true);
boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile"));
if (readFile) {
autoFetchManager = deserializeAutoFetch(autoFetchFile);
}
@@ -22,6 +22,8 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The manager of all the usage/query statistics as well as the tuned fetch
@@ -29,6 +31,8 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
*/
public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManager.class);
private static final long serialVersionUID = -6826119882781771722L;
private final String statisticsMonitor = new String();
@@ -551,16 +555,16 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
Statistics stats = getQueryPointStats(origin);
if (logging.isTraceUsageCollection()){
System.out.println("... NodeUsageCollector "+usageCollector);
}
stats.collectUsageInfo(usageCollector);
if (logging.isTraceUsageCollection()){
System.out.println("stats\n"+stats);
}
}
if (logger.isTraceEnabled()) {
logger.trace("... NodeUsageCollector " + usageCollector);
}
stats.collectUsageInfo(usageCollector);
if (logger.isTraceEnabled()) {
logger.trace("stats\n" + stats);
}
}
private Statistics getQueryPointStats(ObjectGraphOrigin originQueryPoint) {
synchronized (statisticsMonitor) {
@@ -1,14 +1,12 @@
package com.avaje.ebeaninternal.server.autofetch;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* Handles the logging aspects for the DefaultAutoFetchListener.
@@ -23,15 +21,12 @@ public class DefaultAutoFetchManagerLogging {
private final DefaultAutoFetchManager manager;
private final boolean traceUsageCollection;
private final int updateFreqInSecs;
public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) {
this.manager = profileListener;
this.traceUsageCollection = GlobalProperties.getBoolean("ebean.autofetch.traceUsageCollection", false);
this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency();
this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency();
}
public void init(SpiEbeanServer ebeanServer) {
@@ -69,9 +64,4 @@ public class DefaultAutoFetchManagerLogging {
String msg = tunedFetch.getLogOutput(null);
logger.debug(msg);
}
public boolean isTraceUsageCollection() {
return traceUsageCollection;
}
}
@@ -17,9 +17,6 @@ import java.io.DataOutputStream;
* be common for many Ack, Resend and Control messages to all be contained in a
* single packet.
* </p>
*
* @author rbygrave
*
*/
public class BinaryMessage {
@@ -28,8 +25,7 @@ public class BinaryMessage {
public static final int TYPE_TABLEIUD = 2;
public static final int TYPE_BEANDELTA = 3;
public static final int TYPE_BEANPATHUPDATE = 4;
public static final int TYPE_INDEX_INVALIDATE = 6;
public static final int TYPE_INDEX = 7;
public static final int TYPE_MSGACK = 8;
public static final int TYPE_MSGRESEND = 9;
@@ -3,8 +3,7 @@ package com.avaje.ebeaninternal.server.cluster;
import java.util.concurrent.ConcurrentHashMap;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager;
import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
@@ -26,41 +25,36 @@ public class ClusterManager {
private boolean started;
public ClusterManager() {
public ClusterManager(ContainerConfig containerConfig) {
String clusterType = GlobalProperties.get("ebean.cluster.type", null);
if (clusterType == null || clusterType.trim().length() == 0) {
// not clustering this instance
this.broadcast = null;
} else {
try {
if ("mcast".equalsIgnoreCase(clusterType)) {
this.broadcast = new McastClusterManager();
} else if ("socket".equalsIgnoreCase(clusterType)) {
this.broadcast = new SocketClusterBroadcast();
} else {
logger.info("Clustering using [" + clusterType + "]");
this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType);
ContainerConfig.ClusterMode mode = containerConfig.getMode();
try {
switch (mode) {
case SOCKET: {
this.broadcast = new SocketClusterBroadcast(containerConfig);
break;
}
case MULTICAST: {
this.broadcast = new McastClusterManager(containerConfig);
break;
}
default: {
this.broadcast = null;
}
} catch (Exception e) {
String msg = "Error initialising ClusterManager type [" + clusterType + "]";
logger.error(msg, e);
throw new RuntimeException(e);
}
} catch (Exception e) {
logger.error("Error initialising ClusterManager type [" + mode + "]", e);
throw new RuntimeException(e);
}
}
public void registerServer(EbeanServer server) {
synchronized (monitor) {
serverMap.put(server.getName(), server);
if (!started) {
startup();
}
serverMap.put(server.getName(), server);
}
}
@@ -7,7 +7,6 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.transaction.BeanDelta;
import com.avaje.ebeaninternal.server.transaction.BeanPersistIds;
import com.avaje.ebeaninternal.server.transaction.IndexEvent;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
@@ -16,7 +15,6 @@ import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
* Due to the hard limit for UDP packet sizes a RemoteTransactionEvent
* is actually broken up into smaller messages.
* </p>
* @author rbygrave
*/
public class PacketTransactionEvent extends Packet {
@@ -63,10 +61,6 @@ public class PacketTransactionEvent extends Packet {
event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput));
break;
case BinaryMessage.TYPE_INDEX:
event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput));
break;
default:
throw new RuntimeException("Invalid Transaction msgType "+msgType);
}
@@ -7,49 +7,54 @@ import java.util.List;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Mechanism to convert RemoteTransactionEvent to/from byte[] content.
*/
public abstract class SerialiseTransactionHelper {
private final PacketWriter packetWriter;
private static final Logger logger = LoggerFactory.getLogger(SerialiseTransactionHelper.class);
public SerialiseTransactionHelper() {
packetWriter = new PacketWriter(Integer.MAX_VALUE);
private final PacketWriter packetWriter;
public SerialiseTransactionHelper() {
packetWriter = new PacketWriter(Integer.MAX_VALUE);
}
public abstract SpiEbeanServer getEbeanServer(String serverName);
/**
* Convert the RemoteTransactionEvent to byte[] content.
*/
public DataHolder createDataHolder(RemoteTransactionEvent transEvent) throws IOException {
List<Packet> packetList = packetWriter.write(transEvent);
if (packetList.size() != 1) {
throw new RuntimeException("Always expecting 1 Packet but got " + packetList.size());
}
byte[] data = packetList.get(0).getBytes();
return new DataHolder(data);
}
/**
* Convert the byte[] content to RemoteTransactionEvent.
*/
public RemoteTransactionEvent read(DataHolder dataHolder) throws IOException {
ByteArrayInputStream bi = new ByteArrayInputStream(dataHolder.getData());
DataInputStream dataInput = new DataInputStream(bi);
Packet header = Packet.readHeader(dataInput);
SpiEbeanServer server = getEbeanServer(header.getServerName());
if (server == null) {
logger.error("server [{}] not found/registered?", header.getServerName());
}
public abstract SpiEbeanServer getEbeanServer(String serverName);
/**
* Convert the RemoteTransactionEvent to byte[] content.
*/
public DataHolder createDataHolder(RemoteTransactionEvent transEvent) throws IOException {
List<Packet> packetList = packetWriter.write(transEvent);
if (packetList.size() != 1) {
throw new RuntimeException("Always expecting 1 Packet but got " + packetList.size());
}
byte[] data = packetList.get(0).getBytes();
return new DataHolder(data);
}
/**
* Convert the byte[] content to RemoteTransactionEvent.
*/
public RemoteTransactionEvent read(DataHolder dataHolder) throws IOException {
ByteArrayInputStream bi = new ByteArrayInputStream(dataHolder.getData());
DataInputStream dataInput = new DataInputStream(bi);
Packet header = Packet.readHeader(dataInput);
SpiEbeanServer server = getEbeanServer(header.getServerName());
PacketTransactionEvent tranEventPacket = PacketTransactionEvent.forRead(header, server);
tranEventPacket.read(dataInput);
return tranEventPacket.getEvent();
}
PacketTransactionEvent tranEventPacket = PacketTransactionEvent.forRead(header, server);
tranEventPacket.read(dataInput);
return tranEventPacket.getEvent();
}
}
File diff suppressed because it is too large Load Diff
@@ -19,6 +19,12 @@
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.cluster.Packet;
import com.avaje.ebeaninternal.server.cluster.PacketTransactionEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -28,13 +34,6 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.cluster.Packet;
import com.avaje.ebeaninternal.server.cluster.PacketTransactionEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Listens for Incoming packets.
*
@@ -56,8 +55,6 @@ public class McastListener implements Runnable {
private final InetAddress group;
private final boolean debugIgnore;
private DatagramPacket pack;
private byte[] receiveBuffer;
@@ -73,8 +70,6 @@ public class McastListener implements Runnable {
int bufferSize, int timeout, String localSenderHostPort,
boolean disableLoopback, int ttl, InetAddress mcastBindAddress) {
this.debugIgnore = GlobalProperties.getBoolean("ebean.debug.mcast.ignore", false);
this.owner = owner;
this.packetControl = packetControl;
this.localSenderHostPort = localSenderHostPort;
@@ -96,7 +91,7 @@ public class McastListener implements Runnable {
this.sock.setSoTimeout(timeout);
if (disableLoopback){
sock.setLoopbackMode(disableLoopback);
sock.setLoopbackMode(true);
}
if (mcastBindAddress != null) {
@@ -173,7 +168,7 @@ public class McastListener implements Runnable {
String senderHostPort = senderAddr.getAddress().getHostAddress()+":"+senderAddr.getPort();
if (senderHostPort.equals(localSenderHostPort)){
if (debugIgnore || logger.isDebugEnabled()){
if (logger.isTraceEnabled()){
logger.info("Ignoring message as sent by localSender: "+localSenderHostPort);
}
} else {
@@ -195,7 +190,7 @@ public class McastListener implements Runnable {
boolean processThisPacket = ackMsg || packetControl.isProcessPacket(senderHostPort, header.getPacketId());
if (!processThisPacket){
if (debugIgnore || logger.isDebugEnabled()){
if (logger.isTraceEnabled()){
logger.info("Already processed packet: "+header.getPacketId()+" type:"+header.getPacketType()+" len:"+data.length);
}
} else {
@@ -21,15 +21,18 @@ class RequestProcessor implements Runnable {
private final Socket clientSocket;
private final SocketClusterBroadcast owner;
/**
private final String hostPort;
/**
* Create including the Listener (used to lookup the Request Handler) and
* the socket itself.
*/
public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) {
this.clientSocket = clientSocket;
this.owner = owner;
}
this.hostPort = owner.getHostPort();
}
/**
* This will parse out the command. Lookup the appropriate Handler and
@@ -39,22 +42,20 @@ class RequestProcessor implements Runnable {
*/
public void run() {
try {
SocketConnection sc = new SocketConnection(clientSocket);
while(true){
if (owner.process(sc)) {
// got the offline message or timeout
break;
}
}
sc.disconnect();
} catch (IOException e) {
logger.error(null, e);
} catch (ClassNotFoundException e) {
logger.error(null, e);
logger.trace("start listening for cluster messages");
SocketConnection sc = new SocketConnection(clientSocket);
while (true) {
if (owner.process(sc)) {
// got the offline message or timeout
break;
}
}
}
logger.trace("disconnecting: {}", hostPort);
sc.disconnect();
} catch (Exception e) {
logger.error("Error listening for messages - "+owner.getHostPort(), e);
}
}
};
}
@@ -35,6 +35,10 @@ class SocketClient {
this.hostPort = address.getHostName()+":"+address.getPort();
}
public String toString() {
return address.toString();
}
public String getHostPort() {
return hostPort;
}
@@ -1,248 +1,256 @@
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.cluster.DataHolder;
import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.io.EOFException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Broadcast messages across the cluster using sockets.
* Broadcast messages across the cluster using sockets.
*/
public class SocketClusterBroadcast implements ClusterBroadcast {
private static final Logger logger = LoggerFactory.getLogger(SocketClusterBroadcast.class);
private final SocketClient local;
private final HashMap<String,SocketClient> clientMap;
private final SocketClusterListener listener;
private SocketClient[] members;
private static final Logger logger = LoggerFactory.getLogger(SocketClusterBroadcast.class);
private ClusterManager clusterManager;
private final SocketClient local;
private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
private final AtomicInteger txnOutgoing = new AtomicInteger();
private final AtomicInteger txnIncoming = new AtomicInteger();
public SocketClusterBroadcast( ){
String localHostPort = GlobalProperties.get("ebean.cluster.local", null);
String members = GlobalProperties.get("ebean.cluster.members", null);
private final HashMap<String, SocketClient> clientMap;
logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]");
this.local = new SocketClient(parseFullName(localHostPort));
this.clientMap = new HashMap<String, SocketClient>();
String[] memArray = StringHelper.delimitedToArray(members, ",", false);
for (int i = 0; i < memArray.length; i++) {
InetSocketAddress member = parseFullName(memArray[i]);
SocketClient client = new SocketClient(member);
if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
// don't add the local one ...
clientMap.put(client.getHostPort(), client);
}
}
this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
this.listener = new SocketClusterListener(this, local.getPort());
}
/**
* Return the current status of this instance.
*/
public SocketClusterStatus getStatus() {
// count of online members
int currentGroupSize = 0;
for (int i = 0; i < members.length; i++) {
if (members[i].isOnline()) {
++currentGroupSize;
}
}
int txnIn = txnIncoming.get();
int txnOut = txnOutgoing.get();
return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
}
public void startup(ClusterManager clusterManager) {
this.clusterManager = clusterManager;
try {
listener.startListening();
register();
private final SocketClusterListener listener;
} catch (IOException e) {
throw new PersistenceException(e);
}
private SocketClient[] members;
private ClusterManager clusterManager;
private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
private final AtomicInteger txnOutgoing = new AtomicInteger();
private final AtomicInteger txnIncoming = new AtomicInteger();
public SocketClusterBroadcast(ContainerConfig containerConfig) {
ContainerConfig.SocketConfig socketConfig = containerConfig.getSocketConfig();
String localHostPort = socketConfig.getLocalHostPort();
List<String> members = socketConfig.getMembers();
logger.info("Clustering using Sockets local[" + localHostPort + "] members[" + members + "]");
this.local = new SocketClient(parseFullName(localHostPort));
this.clientMap = new HashMap<String, SocketClient>();
for (String memberHostPort : members) {
InetSocketAddress member = parseFullName(memberHostPort);
SocketClient client = new SocketClient(member);
if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
// don't add the local one ...
clientMap.put(client.getHostPort(), client);
}
}
public void shutdown() {
deregister();
listener.shutdown();
}
/**
* Register with all the other members of the Cluster.
*/
private void register() {
this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
this.listener = new SocketClusterListener(this, local.getPort(), socketConfig.getCoreThreads(), socketConfig.getMaxThreads(), socketConfig.getThreadPoolName());
}
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
for (int i = 0; i < members.length; i++) {
boolean online = members[i].register(h);
String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]";
logger.info(msg);
}
public String getHostPort() {
return local.getHostPort();
}
/**
* Return the current status of this instance.
*/
public SocketClusterStatus getStatus() {
// count of online members
int currentGroupSize = 0;
for (int i = 0; i < members.length; i++) {
if (members[i].isOnline()) {
++currentGroupSize;
}
}
int txnIn = txnIncoming.get();
int txnOut = txnOutgoing.get();
protected void setMemberOnline(String fullName, boolean online) throws IOException {
synchronized (clientMap) {
String msg = "Cluster Member ["+fullName+"] online["+online+"]";
logger.info(msg);
SocketClient member = clientMap.get(fullName);
member.setOnline(online);
}
return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
}
public void startup(ClusterManager clusterManager) {
this.clusterManager = clusterManager;
try {
listener.startListening();
register();
} catch (IOException e) {
throw new PersistenceException(e);
}
}
private void send(SocketClient client, SocketClusterMessage msg) {
public void shutdown() {
deregister();
listener.shutdown();
}
try {
// alternative would be to connect/disconnect here
// but prefer to use keepalive
client.send(msg);
} catch (Exception ex){
logger.error("Error sending message", ex);
try {
client.reconnect();
} catch (IOException e) {
logger.error("Error trying to reconnect", ex);
}
}
/**
* Register with all the other members of the Cluster.
*/
private void register() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
for (int i = 0; i < members.length; i++) {
boolean online = members[i].register(h);
logger.info("Cluster Member [{}] online[{}]", members[i].getHostPort(), online);
}
}
/**
* Send the payload to all the members of the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
try {
txnOutgoing.incrementAndGet();
DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
broadcast(msg);
} catch (Exception e){
String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members.";
logger.error(msg, e);
}
protected void setMemberOnline(String fullName, boolean online) throws IOException {
synchronized (clientMap) {
logger.info("Cluster Member [{}] online[{}]", fullName, online);
SocketClient member = clientMap.get(fullName);
member.setOnline(online);
}
}
protected void broadcast(SocketClusterMessage msg) {
for (int i = 0; i < members.length; i++) {
send(members[i], msg);
}
}
/**
* Leave the cluster.
*/
private void deregister() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
broadcast(h);
for (int i = 0; i < members.length; i++) {
members[i].disconnect();
}
}
private void send(SocketClient client, SocketClusterMessage msg) {
/**
* Process a Cluster message.
*/
protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
try {
// alternative would be to connect/disconnect here but prefer to use keepalive
if (logger.isTraceEnabled()) {
logger.trace("... send to member {} broadcast msg: {}", client, msg);
}
client.send(msg);
try {
SocketClusterMessage h = (SocketClusterMessage)request.readObject();
if (h.isRegisterEvent()){
setMemberOnline(h.getRegisterHost(), h.isRegister());
} else {
txnIncoming.incrementAndGet();
DataHolder dataHolder = h.getDataHolder();
RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
transEvent.run();
}
if (h.isRegisterEvent() && !h.isRegister()){
// instance shutting down
return true;
} else {
return false;
}
} catch (InterruptedIOException e) {
String msg = "Timeout waiting for message";
logger.info(msg, e);
try {
request.disconnect();
} catch (IOException ex){
logger.info("Error disconnecting after timeout", ex);
}
return true;
}
} catch (Exception ex) {
logger.error("Error sending message", ex);
try {
client.reconnect();
} catch (IOException e) {
logger.error("Error trying to reconnect", ex);
}
}
}
/**
* Parse a host:port into a InetSocketAddress.
*/
private InetSocketAddress parseFullName(String hostAndPort) {
try {
hostAndPort = hostAndPort.trim();
int colonPos = hostAndPort.indexOf(":");
if (colonPos == -1) {
String msg = "No colon \":\" in "+hostAndPort;
throw new IllegalArgumentException(msg);
}
String host = hostAndPort.substring(0, colonPos);
String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
int port = Integer.parseInt(sPort);
return new InetSocketAddress(host, port);
} catch (Exception ex){
throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex);
}
/**
* Send the payload to all the members of the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
try {
txnOutgoing.incrementAndGet();
DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
broadcast(msg);
} catch (Exception e) {
logger.error("Error sending RemoteTransactionEvent " + remoteTransEvent + " to cluster members.", e);
}
class TxnSerialiseHelper extends SerialiseTransactionHelper {
}
@Override
public SpiEbeanServer getEbeanServer(String serverName) {
return (SpiEbeanServer)clusterManager.getServer(serverName);
}
protected void broadcast(SocketClusterMessage msg) {
if (logger.isTraceEnabled()) {
logger.trace("... broadcast msg: "+msg);
}
for (int i = 0; i < members.length; i++) {
send(members[i], msg);
}
}
/**
* Leave the cluster.
*/
private void deregister() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
broadcast(h);
for (int i = 0; i < members.length; i++) {
members[i].disconnect();
}
}
/**
* Process an incoming Cluster message.
*/
protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
try {
SocketClusterMessage h = (SocketClusterMessage) request.readObject();
if (logger.isTraceEnabled()) {
logger.trace("... received msg: {}", h);
}
if (h.isRegisterEvent()) {
setMemberOnline(h.getRegisterHost(), h.isRegister());
} else {
txnIncoming.incrementAndGet();
DataHolder dataHolder = h.getDataHolder();
RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
transEvent.run();
}
// instance shutting down
return h.isRegisterEvent() && !h.isRegister();
} catch (InterruptedIOException e) {
logger.info("Timeout waiting for message", e);
try {
request.disconnect();
} catch (IOException ex) {
logger.info("Error disconnecting after timeout", ex);
}
return true;
} catch (EOFException e) {
logger.info("EOF disconnecting");
return true;
} catch (IOException e) {
logger.info("IO Error waiting/reading message", e);
return true;
}
}
/**
* Parse a host:port into a InetSocketAddress.
*/
private InetSocketAddress parseFullName(String hostAndPort) {
try {
hostAndPort = hostAndPort.trim();
int colonPos = hostAndPort.indexOf(":");
if (colonPos == -1) {
String msg = "No colon \":\" in " + hostAndPort;
throw new IllegalArgumentException(msg);
}
String host = hostAndPort.substring(0, colonPos);
String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
int port = Integer.parseInt(sPort);
return new InetSocketAddress(host, port);
} catch (Exception ex) {
throw new RuntimeException("Error parsing [" + hostAndPort + "] for the form [host:port]", ex);
}
}
class TxnSerialiseHelper extends SerialiseTransactionHelper {
@Override
public SpiEbeanServer getEbeanServer(String serverName) {
return (SpiEbeanServer) clusterManager.getServer(serverName);
}
}
}
@@ -6,10 +6,10 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import com.avaje.ebeaninternal.server.lib.DaemonThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
/**
* Serverside multithreaded socket listener. Accepts connections and dispatches
@@ -26,141 +26,118 @@ import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
*/
class SocketClusterListener implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(SocketClusterListener.class);
/**
* The port the SocketListener uses.
*/
private final int port;
private static final Logger logger = LoggerFactory.getLogger(SocketClusterListener.class);
/**
* The length of the socket accept timeout.
*/
private final int listenTimeout = 60000;
/**
* The server socket used to listen for requests.
*/
private final ServerSocket serverListenSocket;
/**
* The server socket used to listen for requests.
*/
private final ServerSocket serverListenSocket;
/**
* The listening thread.
*/
private final Thread listenerThread;
/**
* The listening thread.
*/
private final Thread listenerThread;
/**
* The pool of threads that actually do the parsing execution of requests.
*/
private final DaemonThreadPool threadPool;
/**
* The pool of threads that actually do the parsing execution of requests.
*/
private final ThreadPool threadPool;
private final SocketClusterBroadcast owner;
private final SocketClusterBroadcast owner;
/**
* shutting down flag.
*/
boolean doingShutdown;
/**
* shutting down flag.
*/
boolean doingShutdown;
/**
* Whether the listening thread is busy assigning a request to a thread.
*/
boolean isActive;
/**
* Whether the listening thread is busy assigning a request to a thread.
*/
boolean isActive;
/**
* Construct with a given thread pool name.
*/
public SocketClusterListener(SocketClusterBroadcast owner, int port, int coreThreads, int maxThreads, String poolName) {
this.owner = owner;
this.threadPool = new DaemonThreadPool(coreThreads, maxThreads, 60, 30, poolName);
try {
this.serverListenSocket = new ServerSocket(port);
this.serverListenSocket.setSoTimeout(60000);
this.listenerThread = new Thread(this, "EbeanClusterListener");
/**
* Construct with a given thread pool name.
*/
public SocketClusterListener(SocketClusterBroadcast owner, int port) {
this.owner = owner;
this.threadPool = ThreadPool.createThreadPool("EbeanCluster");
this.port = port;
try {
this.serverListenSocket = new ServerSocket(port);
this.serverListenSocket.setSoTimeout(listenTimeout);
this.listenerThread = new Thread(this, "EbeanClusterListener");
} catch (IOException e){
String msg = "Error starting cluster socket listener on port "+port;
throw new RuntimeException(msg,e);
} catch (IOException e) {
String msg = "Error starting cluster socket listener on port " + port;
throw new RuntimeException(msg, e);
}
}
/**
* Start listening for requests.
*/
public void startListening() throws IOException {
logger.trace("... startListening()");
this.listenerThread.setDaemon(true);
this.listenerThread.start();
}
/**
* Shutdown this listener.
*/
public void shutdown() {
doingShutdown = true;
try {
if (isActive) {
synchronized (listenerThread) {
try {
listenerThread.wait(1000);
} catch (InterruptedException e) {
// OK to ignore as expected to Interrupt for shutdown.
}
}
}
listenerThread.interrupt();
serverListenSocket.close();
} catch (IOException e) {
logger.error("Error shutting down listener", e);
}
/**
* Returns the port the listener is using.
*/
public int getPort() {
return port;
}
threadPool.shutdown();
}
/**
* Start listening for requests.
*/
public void startListening() throws IOException {
this.listenerThread.setDaemon(true);
this.listenerThread.start();
}
/**
* Shutdown this listener.
*/
public void shutdown() {
doingShutdown = true;
try {
if (isActive) {
synchronized (listenerThread) {
try {
listenerThread.wait(1000);
} catch (InterruptedException e) {
// OK to ignore as expected to Interrupt for shutdown.
;
}
}
}
listenerThread.interrupt();
serverListenSocket.close();
} catch (IOException e) {
logger.error("Error shutting down listener", e);
/**
* This is a runnable and so this must be public. Don't call this externally
* but rather call the startListening() method.
*/
public void run() {
// run in loop until doingShutdown is true...
while (!doingShutdown) {
try {
synchronized (listenerThread) {
Socket clientSocket = serverListenSocket.accept();
isActive = true;
Runnable request = new RequestProcessor(owner, clientSocket);
threadPool.execute(request);
isActive = false;
}
threadPool.shutdown();
}
/**
* This is a runnable and so this must be public. Don't call this externally
* but rather call the startListening() method.
*/
public void run() {
// run in loop until doingShutdown is true...
while (!doingShutdown) {
try {
synchronized (listenerThread) {
Socket clientSocket = serverListenSocket.accept();
isActive = true;
Runnable request = new RequestProcessor(owner, clientSocket);
threadPool.assign(request, true);
isActive = false;
}
} catch (SocketException e) {
if (doingShutdown) {
String msg = "doingShutdown and accept threw:"+ e.getMessage();
logger.info(msg);
} else {
logger.error(null, e);
}
} catch (InterruptedIOException e) {
// this will happen when the server is very quiet.
// that is, no requests
logger.debug("Possibly expected due to accept timeout?" + e.getMessage());
} catch (IOException e) {
// log it and continue in the loop...
logger.error(null, e);
}
} catch (SocketException e) {
if (doingShutdown) {
logger.info("doingShutdown and accept threw:" + e.getMessage());
} else {
logger.error("Error while listening", e);
}
} catch (InterruptedIOException e) {
// this will happen when the server is very quiet.
// that is, no requests
logger.debug("Possibly expected due to accept timeout? {}", e.getMessage());
} catch (IOException e) {
// log it and continue in the loop...
logger.error("IOException processing cluster message", e);
}
}
}
}
@@ -1,13 +1,13 @@
package com.avaje.ebeaninternal.server.core;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.util.ClassPathSearch;
import com.avaje.ebeaninternal.server.util.ClassPathSearchFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Set;
/**
* Searches for interesting classes such as Entities, Embedded and ScalarTypes.
*/
@@ -20,17 +20,21 @@ public class BootupClassPathSearch {
private final ClassLoader classLoader;
private final List<String> packages;
private final List<String> jars;
private final List<String> jars;
private BootupClasses bootupClasses;
private final String classPathReaderClassName;
/**
* Construct and search for interesting classes.
*/
public BootupClassPathSearch(ClassLoader classLoader, List<String> packages, List<String> jars) {
public BootupClassPathSearch(ClassLoader classLoader, List<String> packages, List<String> jars, String classPathReaderClassName) {
this.classLoader = (classLoader == null) ? getClass().getClassLoader() : classLoader;
this.packages = packages;
this.jars = jars;
this.classPathReaderClassName = classPathReaderClassName;
}
public BootupClasses getBootupClasses() {
@@ -57,7 +61,7 @@ public class BootupClassPathSearch {
ClassPathSearchFilter filter = createFilter();
ClassPathSearch finder = new ClassPathSearch(classLoader, filter, bc);
ClassPathSearch finder = new ClassPathSearch(classLoader, filter, bc, classPathReaderClassName);
finder.findClasses();
Set<String> jars = finder.getJarHits();
@@ -1,25 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.config.ServerConfig;
/**
* Build a ServerConfig from ebean.properties.
*/
public class ConfigBuilder {
/**
* Create a ServerConfig and load it from ebean.properties.
*/
public ServerConfig build(String serverName) {
ServerConfig config = new ServerConfig();
config.setName(serverName);
config.loadFromProperties();
return config;
}
}
@@ -15,19 +15,21 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
private final DaemonScheduleThreadPool schedulePool;
/**
* Construct the default implementation of BackgroundExecutor.
*
* @param corePoolSize
* the core size of the thread pool.
* @param keepAliveSecs
* the time in seconds idle threads are keep alive
* @param shutdownWaitSeconds
* the time in seconds allowed for the pool to shutdown nicely.
* After this the pool is forced to shutdown.
*/
public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
this.pool = new DaemonThreadPool(corePoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix);
/**
* Construct the default implementation of BackgroundExecutor.
*
* @param corePoolSize
* the core size of the thread pool.
* @param maximumPoolSize
* the maximum pool size before jobs are queued
* @param keepAliveSecs
* the time in seconds idle threads are keep alive
* @param shutdownWaitSeconds
* the time in seconds allowed for the pool to shutdown nicely.
* After this the pool is forced to shutdown.
*/
public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, int maximumPoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
this.pool = new DaemonThreadPool(corePoolSize, maximumPoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix);
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
}
@@ -1,36 +1,10 @@
package com.avaje.ebeaninternal.server.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.FutureTask;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.PersistenceException;
import com.avaje.ebean.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.bean.*;
import com.avaje.ebean.bean.PersistenceContext.WithOption;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.EncryptKeyManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.event.BeanPersistController;
@@ -39,43 +13,16 @@ import com.avaje.ebean.meta.MetaBeanInfo;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
import com.avaje.ebeaninternal.api.LoadManyRequest;
import com.avaje.ebeaninternal.api.ScopeTrans;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.*;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiQuery.Type;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.BeanManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.DNativeQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.*;
import com.avaje.ebeaninternal.server.el.ElFilter;
import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.query.CQuery;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
import com.avaje.ebeaninternal.server.query.CallableQueryIds;
import com.avaje.ebeaninternal.server.query.CallableQueryList;
import com.avaje.ebeaninternal.server.query.CallableQueryRowCount;
import com.avaje.ebeaninternal.server.query.CallableSqlQueryList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagedList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery;
import com.avaje.ebeaninternal.server.query.QueryFutureIds;
import com.avaje.ebeaninternal.server.query.QueryFutureList;
import com.avaje.ebeaninternal.server.query.QueryFutureRowCount;
import com.avaje.ebeaninternal.server.query.SqlQueryFutureList;
import com.avaje.ebeaninternal.server.query.*;
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate;
import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery;
@@ -86,6 +33,16 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.PersistenceException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.FutureTask;
/**
* The default server side implementation of EbeanServer.
@@ -226,9 +183,9 @@ public final class DefaultServer implements SpiEbeanServer {
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
this.maxCallStack = serverConfig.getMaxCallStack();
this.rollbackOnChecked = GlobalProperties.getBoolean("ebean.transaction.rollbackOnChecked", true);
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
this.transactionManager = config.getTransactionManager();
this.transactionScopeManager = config.getTransactionScopeManager();
@@ -2081,9 +2038,7 @@ public final class DefaultServer implements SpiEbeanServer {
// create the 'interesting' part of the stackTrace
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
for (int i = 0; i < stackLength; i++) {
finalTrace[i] = stackTrace[i + startIndex];
}
System.arraycopy(stackTrace, 0 + startIndex, finalTrace, 0, stackLength);
if (stackLength < 1) {
// this should not really happen
@@ -2,10 +2,7 @@ package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import javax.management.MBeanServer;
@@ -13,6 +10,7 @@ import javax.management.MBeanServerFactory;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import com.avaje.ebean.config.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,11 +19,6 @@ import com.avaje.ebean.cache.ServerCacheFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.cache.ServerCacheOptions;
import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.PstmtDelegate;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.UnderscoreNamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -38,7 +31,6 @@ import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.lib.sql.DataSourceAlert;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.lib.sql.SimpleDataSourceAlert;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
/**
* Default Server side implementation of ServerFactory.
@@ -51,46 +43,18 @@ public class DefaultServerFactory implements BootupEbeanManager {
private final JndiDataSourceLookup jndiDataSourceFactory;
private final BootupClassPathSearch bootupClassSearch;
private final AtomicInteger serverId = new AtomicInteger(1);
private final XmlConfigLoader xmlConfigLoader;
public DefaultServerFactory(ContainerConfig containerConfig) {
private final XmlConfig xmlConfig;
public DefaultServerFactory() {
this.clusterManager = new ClusterManager();
this.clusterManager = new ClusterManager(containerConfig);
this.jndiDataSourceFactory = new JndiDataSourceLookup();
List<String> packages = getSearchJarsPackages(GlobalProperties.get("ebean.search.packages", null));
List<String> jars = getSearchJarsPackages(GlobalProperties.get("ebean.search.jars", null));
this.bootupClassSearch = new BootupClassPathSearch(null, packages, jars);
this.xmlConfigLoader = new XmlConfigLoader(null);
this.xmlConfig = xmlConfigLoader.load();
// register so that we can shutdown any Ebean wide
// resources such as clustering
ShutdownManager.registerServerFactory(this);
}
private List<String> getSearchJarsPackages(String searchPackages) {
List<String> hitList = new ArrayList<String>();
if (searchPackages != null) {
String[] entries = searchPackages.split("[ ,;]");
for (int i = 0; i < entries.length; i++) {
hitList.add(entries[i].trim());
}
}
return hitList;
}
public void shutdown() {
clusterManager.shutdown();
}
@@ -100,35 +64,26 @@ public class DefaultServerFactory implements BootupEbeanManager {
*/
public SpiEbeanServer createServer(String name) {
ConfigBuilder b = new ConfigBuilder();
ServerConfig config = b.build(name);
ServerConfig config = new ServerConfig();
config.setName(name);
Properties prop = PropertyMap.defaultProperties();
config.loadFromProperties(prop);
return createServer(config);
}
private SpiBackgroundExecutor createBackgroundExecutor(ServerConfig serverConfig, int uniqueServerId) {
private SpiBackgroundExecutor createBackgroundExecutor(ServerConfig serverConfig) {
String namePrefix = "Ebean-" + serverConfig.getName();
String namePrefix = "ebean-" + serverConfig.getName();
// the size of the pool for executing periodic tasks (such as cache flushing)
int schedulePoolSize = GlobalProperties.getInt("backgroundExecutor.schedulePoolsize", 1);
int schedulePoolSize = serverConfig.getBackgroundExecutorSchedulePoolSize();
int corePoolSize = serverConfig.getBackgroundExecutorCorePoolSize();
int maxPoolSize = serverConfig.getBackgroundExecutorMaxPoolSize();
int idleSecs = serverConfig.getBackgroundExecutorIdleSecs();
int shutdownSecs = serverConfig.getBackgroundExecutorShutdownSecs();
// the side of the main pool for immediate background task execution
int minPoolSize = GlobalProperties.getInt("backgroundExecutor.minPoolSize", 0);
int poolSize = GlobalProperties.getInt("backgroundExecutor.poolsize", 20);
int maxPoolSize = GlobalProperties.getInt("backgroundExecutor.maxPoolSize", poolSize);
int idleSecs = GlobalProperties.getInt("backgroundExecutor.idlesecs", 120);
int shutdownSecs = GlobalProperties.getInt("backgroundExecutor.shutdownSecs", 30);
boolean useTrad = GlobalProperties.getBoolean("backgroundExecutor.traditional", true);
if (useTrad) {
// this pool will use Idle seconds to maintain the thread count between min and max
ThreadPool pool = new ThreadPool(namePrefix, true, null, minPoolSize, maxPoolSize, idleSecs*1000);
return new TraditionalBackgroundExecutor(pool, schedulePoolSize, shutdownSecs, namePrefix);
} else {
return new DefaultBackgroundExecutor(schedulePoolSize, maxPoolSize, idleSecs, shutdownSecs, namePrefix);
}
return new DefaultBackgroundExecutor(schedulePoolSize, corePoolSize, maxPoolSize, idleSecs, shutdownSecs, namePrefix);
}
/**
@@ -163,8 +118,7 @@ public class DefaultServerFactory implements BootupEbeanManager {
pstmtDelegate = getOraclePstmtDelegate(serverConfig.getDataSource());
}
if (pstmtDelegate != null) {
// We can support JDBC batching with Oracle
// via OraclePreparedStatement
// We can support JDBC batching with Oracle via OraclePreparedStatement
pstmtBatch = new OraclePstmtBatch(pstmtDelegate);
}
if (pstmtBatch == null) {
@@ -180,27 +134,30 @@ public class DefaultServerFactory implements BootupEbeanManager {
ServerCacheManager cacheManager = getCacheManager(serverConfig);
int uniqueServerId = serverId.incrementAndGet();
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig, uniqueServerId);
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig);
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, bgExecutor, serverConfig, bootupClasses,
pstmtBatch);
XmlConfigLoader xmlConfigLoader = new XmlConfigLoader(null);
XmlConfig xmlConfig = xmlConfigLoader.load();
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, bgExecutor, serverConfig, bootupClasses, pstmtBatch);
DefaultServer server = new DefaultServer(c, cacheManager);
cacheManager.init(server);
MBeanServer mbeanServer;
ArrayList<?> list = MBeanServerFactory.findMBeanServer(null);
if (list.size() == 0) {
// probably not running in a server
mbeanServer = MBeanServerFactory.createMBeanServer();
} else {
// use the first MBeanServer
mbeanServer = (MBeanServer) list.get(0);
if (serverConfig.isRegisterJmxMBeans()) {
MBeanServer mbeanServer;
ArrayList<?> list = MBeanServerFactory.findMBeanServer(null);
if (list.size() == 0) {
// probably not running in a server
mbeanServer = MBeanServerFactory.createMBeanServer();
} else {
// use the first MBeanServer
mbeanServer = (MBeanServer) list.get(0);
}
server.registerMBeans(mbeanServer, uniqueServerId);
}
server.registerMBeans(mbeanServer, uniqueServerId);
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
server.executePlugins(online);
@@ -215,9 +172,7 @@ public class DefaultServerFactory implements BootupEbeanManager {
}
// warm the cache in 30 seconds
int delaySecs = GlobalProperties.getInt("ebean.cacheWarmingDelay", 30);
long sleepMillis = 1000 * delaySecs;
long sleepMillis = 1000 * serverConfig.getCacheWarmingDelay();
if (sleepMillis > 0) {
Timer t = new Timer("EbeanCacheWarmer", true);
t.schedule(new CacheWarmer(server), sleepMillis);
@@ -252,19 +207,15 @@ public class DefaultServerFactory implements BootupEbeanManager {
// reasonable default settings are for a cache per bean type
ServerCacheOptions beanOptions = new ServerCacheOptions();
beanOptions.setMaxSize(GlobalProperties.getInt("cache.maxSize", 1000));
// maxIdleTime 10 minutes
beanOptions.setMaxIdleSecs(GlobalProperties.getInt("cache.maxIdleTime", 60 * 10));
// maxTimeToLive 6 hrs
beanOptions.setMaxSecsToLive(GlobalProperties.getInt("cache.maxTimeToLive", 60 * 60 * 6));
beanOptions.setMaxSize(serverConfig.getCacheMaxSize());
beanOptions.setMaxIdleSecs(serverConfig.getCacheMaxIdleTime());
beanOptions.setMaxSecsToLive(serverConfig.getCacheMaxTimeToLive());
// reasonable default settings for the query cache per bean type
ServerCacheOptions queryOptions = new ServerCacheOptions();
queryOptions.setMaxSize(GlobalProperties.getInt("querycache.maxSize", 100));
// maxIdleTime 10 minutes
queryOptions.setMaxIdleSecs(GlobalProperties.getInt("querycache.maxIdleTime", 60 * 10));
// maxTimeToLive 6 hours
queryOptions.setMaxSecsToLive(GlobalProperties.getInt("querycache.maxTimeToLive", 60 * 60 * 6));
queryOptions.setMaxSize(serverConfig.getQueryCacheMaxSize());
queryOptions.setMaxIdleSecs(serverConfig.getQueryCacheMaxIdleTime());
queryOptions.setMaxSecsToLive(serverConfig.getQueryCacheMaxTimeToLive());
ServerCacheFactory cacheFactory = serverConfig.getServerCacheFactory();
if (cacheFactory == null) {
@@ -303,17 +254,8 @@ public class DefaultServerFactory implements BootupEbeanManager {
return new BootupClasses(serverConfig.getClasses());
}
List<String> jars = serverConfig.getJars();
List<String> packages = serverConfig.getPackages();
if ((packages != null && !packages.isEmpty()) || (jars != null && !jars.isEmpty())) {
// filter by package name
BootupClassPathSearch search = new BootupClassPathSearch(null, packages, jars);
return search.getBootupClasses();
}
// just use classes we can find via class path search
return bootupClassSearch.getBootupClasses().createCopy();
BootupClassPathSearch search = new BootupClassPathSearch(null, serverConfig.getPackages(), serverConfig.getJars(), serverConfig.getClassPathReaderClassName());
return search.getBootupClasses();
}
/**
@@ -323,22 +265,6 @@ public class DefaultServerFactory implements BootupEbeanManager {
if (config.getNamingConvention() == null) {
UnderscoreNamingConvention nc = new UnderscoreNamingConvention();
config.setNamingConvention(nc);
String v = config.getProperty("namingConvention.useForeignKeyPrefix");
if (v != null) {
boolean useForeignKeyPrefix = Boolean.valueOf(v);
nc.setUseForeignKeyPrefix(useForeignKeyPrefix);
}
String sequenceFormat = config.getProperty("namingConvention.sequenceFormat");
if (sequenceFormat != null) {
nc.setSequenceFormat(sequenceFormat);
}
String schema = config.getProperty("namingConvention.schema");
if (schema != null) {
nc.setSchema(schema);
}
}
}
@@ -351,7 +277,6 @@ public class DefaultServerFactory implements BootupEbeanManager {
if (dbPlatform == null) {
DatabasePlatformFactory factory = new DatabasePlatformFactory();
DatabasePlatform db = factory.create(config);
config.setDatabasePlatform(db);
logger.info("DatabasePlatform name:" + config.getName() + " platform:" + db.getName());
@@ -370,7 +295,7 @@ public class DefaultServerFactory implements BootupEbeanManager {
private DataSource getDataSourceFromConfig(ServerConfig config) {
DataSource ds = null;
DataSource ds;
if (config.getDataSourceJndiName() != null) {
ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName());
@@ -1,266 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
<<<<<<< HEAD
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
=======
>>>>>>> json-refactor
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
/**
* Used to extend the ServerConfig with additional objects used to configure and
* construct an EbeanServer.
*/
public class InternalConfiguration {
private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class);
private final ServerConfig serverConfig;
private final BootupClasses bootupClasses;
private final DeployInherit deployInherit;
private final ResourceManager resourceManager;
private final DeployOrmXml deployOrmXml;
private final TypeManager typeManager;
private final Binder binder;
private final DeployCreateProperties deployCreateProperties;
private final DeployUtil deployUtil;
private final BeanDescriptorManager beanDescriptorManager;
private final TransactionManager transactionManager;
private final TransactionScopeManager transactionScopeManager;
private final CQueryEngine cQueryEngine;
private final ClusterManager clusterManager;
private final ServerCacheManager cacheManager;
private final ExpressionFactory expressionFactory;
private final SpiBackgroundExecutor backgroundExecutor;
private final PstmtBatch pstmtBatch;
private final XmlConfig xmlConfig;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
this.xmlConfig = xmlConfig;
this.pstmtBatch = pstmtBatch;
this.clusterManager = clusterManager;
this.backgroundExecutor = backgroundExecutor;
this.cacheManager = cacheManager;
this.serverConfig = serverConfig;
this.bootupClasses = bootupClasses;
this.expressionFactory = new DefaultExpressionFactory();
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
this.binder = new Binder(typeManager);
this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig);
this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource());
this.deployInherit = new DeployInherit(bootupClasses);
this.deployCreateProperties = new DeployCreateProperties(typeManager);
this.deployUtil = new DeployUtil(typeManager, serverConfig);
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
externalTransactionManager = new JtaTransactionManager();
}
if (externalTransactionManager != null) {
externalTransactionManager.setTransactionManager(transactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
} else {
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
}
}
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
private TransactionManager createTransactionManager() {
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
// We know the DataSourcePool is using autoCommit
return true;
}
return false;
}
public JsonContext createJsonContext(SpiEbeanServer server) {
return new DJsonContext(server);
}
public XmlConfig getXmlConfig() {
return xmlConfig;
}
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
}
public RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue());
}
public OrmQueryEngine createOrmQueryEngine() {
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
}
public Persister createPersister(SpiEbeanServer server) {
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
}
public PstmtBatch getPstmtBatch() {
return pstmtBatch;
}
public ServerCacheManager getCacheManager() {
return cacheManager;
}
public BootupClasses getBootupClasses() {
return bootupClasses;
}
public DatabasePlatform getDatabasePlatform() {
return serverConfig.getDatabasePlatform();
}
public ServerConfig getServerConfig() {
return serverConfig;
}
public ExpressionFactory getExpressionFactory() {
return expressionFactory;
}
public TypeManager getTypeManager() {
return typeManager;
}
public Binder getBinder() {
return binder;
}
public BeanDescriptorManager getBeanDescriptorManager() {
return beanDescriptorManager;
}
public DeployInherit getDeployInherit() {
return deployInherit;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
public DeployOrmXml getDeployOrmXml() {
return deployOrmXml;
}
public DeployCreateProperties getDeployCreateProperties() {
return deployCreateProperties;
}
public DeployUtil getDeployUtil() {
return deployUtil;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public TransactionScopeManager getTransactionScopeManager() {
return transactionScopeManager;
}
public CQueryEngine getCQueryEngine() {
return cQueryEngine;
}
public ClusterManager getClusterManager() {
return clusterManager;
}
public SpiBackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
}
@@ -6,9 +6,6 @@ import javax.naming.NamingException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import com.avaje.ebean.config.GlobalProperties;
/**
* Helper to lookup a DataSource from JNDI.
*/
@@ -16,8 +13,6 @@ public class JndiDataSourceLookup {
private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/";
String jndiPrefix = GlobalProperties.get("ebean.datasource.jndi.prefix", DEFAULT_PREFIX);
public JndiDataSourceLookup() {
}
@@ -32,7 +27,7 @@ public class JndiDataSourceLookup {
try {
if (!jndiName.startsWith("java:")){
jndiName = jndiPrefix + jndiName;
jndiName = DEFAULT_PREFIX + jndiName;
}
Context ctx = new InitialContext();
@@ -1,13 +1,8 @@
package com.avaje.ebeaninternal.server.core;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContextEvent;
/**
* Listens for webserver server starting and stopping events.
@@ -19,8 +14,6 @@ import org.slf4j.LoggerFactory;
*/
public class ServletContextListener implements javax.servlet.ServletContextListener {
private static final Logger logger = LoggerFactory.getLogger(ServletContextListener.class);
/**
* The servlet container is stopping.
*/
@@ -29,29 +22,10 @@ public class ServletContextListener implements javax.servlet.ServletContextListe
}
/**
* The servlet container is starting.
* <p>
* Initialise the properties file using SystemProperties.initWebapp();
* and start Ebean.
* </p>
* Do nothing on startup.
*/
public void contextInitialized(ServletContextEvent event) {
try {
ServletContext servletContext = event.getServletContext();
GlobalProperties.setServletContext(servletContext);
if (servletContext != null) {
String servletRealPath = servletContext.getRealPath("");
GlobalProperties.put("servlet.realpath", servletRealPath);
logger.info("servlet.realpath=[" + servletRealPath + "]");
}
Ebean.getServer(null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@@ -1,55 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.server.lib.DaemonScheduleThreadPool;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
/**
* BackgroundExecutor using my traditional ThreadPool that will grow and trim.
*
* @author rbygrave
*/
public class TraditionalBackgroundExecutor implements SpiBackgroundExecutor {
private static Logger logger = LoggerFactory.getLogger(TraditionalBackgroundExecutor.class);
private final ThreadPool pool;
private final DaemonScheduleThreadPool schedulePool;
/**
* Construct the default implementation of BackgroundExecutor.
*/
public TraditionalBackgroundExecutor(ThreadPool pool, int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
this.pool = pool;
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
}
/**
* Execute a Runnable using a background thread.
*/
public void execute(Runnable r) {
pool.assign(r, true);
}
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
if (logger.isDebugEnabled()) {
logger.debug("Registering for executePeriodically {} delay:{} {}",r, delay, unit);
}
schedulePool.scheduleWithFixedDelay(r, delay, delay, unit);
}
public void shutdown() {
if (logger.isDebugEnabled()) {
logger.debug("Shutting down");
}
pool.shutdown();
schedulePool.shutdown();
}
}
@@ -1,25 +1,5 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Model;
import com.avaje.ebean.RawSql;
@@ -29,7 +9,6 @@ import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.EncryptKeyManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
@@ -38,35 +17,31 @@ import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.XmlConfig;
import com.avaje.ebeaninternal.server.core.*;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.deploy.parse.ReadAnnotations;
import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties;
import com.avaje.ebeaninternal.server.deploy.meta.*;
import com.avaje.ebeaninternal.server.deploy.parse.*;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory;
import com.avaje.ebeaninternal.server.type.TypeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Creates BeanDescriptors.
@@ -153,6 +128,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
private final boolean eagerFetchLobs;
/**
* Create for a given database dbConfig.
*/
@@ -167,6 +144,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
this.databasePlatform = config.getServerConfig().getDatabasePlatform();
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm());
this.eagerFetchLobs = config.getServerConfig().isEagerFetchLobs();
this.bootupClasses = config.getBootupClasses();
this.createProperties = config.getDeployCreateProperties();
@@ -397,12 +375,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented ";
msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType;
if (GlobalProperties.getBoolean("ebean.strict", true)) {
throw new PersistenceException(msg, source);
} else {
logger.error(msg, source);
}
throw new PersistenceException(msg, source);
}
/**
@@ -1017,7 +990,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
DeployBeanInfo<T> info = new DeployBeanInfo<T>(deployUtil, desc);
readAnnotations.readInitial(info);
readAnnotations.readInitial(info, eagerFetchLobs);
return info;
}
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
@@ -99,15 +98,11 @@ public class BeanTable {
}
if (complexKey){
// check to see if we want prefixes by default with complex keys
boolean usePrefixOnComplex = GlobalProperties.getBoolean("ebean.prefixComplexKeys", false);
if (!usePrefixOnComplex){
// just to copy the column name rather than prefix with the foreignKeyPrefix.
// I think that with complex keys this is the more common approach.
String msg = "On table["+baseTable+"] foreign key column ["+lc+"]";
logger.debug(msg);
fk = lc;
}
// just to copy the column name rather than prefix with the foreignKeyPrefix.
// I think that with complex keys this is the more common approach.
String msg = "On table["+baseTable+"] foreign key column ["+lc+"]";
logger.debug(msg);
fk = lc;
}
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
@@ -1,21 +1,11 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.sql.Types;
import java.util.Map;
import java.util.UUID;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.avaje.ebean.annotation.*;
import com.avaje.ebean.config.EncryptDeploy;
import com.avaje.ebean.config.EncryptDeploy.Mode;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
@@ -23,12 +13,14 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase;
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesEncrypted;
import com.avaje.ebeaninternal.server.type.ScalarTypeEncryptedWrapper;
import com.avaje.ebeaninternal.server.type.*;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.sql.Types;
import java.util.Map;
import java.util.UUID;
/**
* Read the field level deployment annotations.
@@ -42,10 +34,10 @@ public class AnnotationFields extends AnnotationParser {
private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory();
public AnnotationFields(DeployBeanInfo<?> info) {
public AnnotationFields(DeployBeanInfo<?> info, boolean eagerFetchLobs) {
super(info);
if (GlobalProperties.getBoolean("ebean.lobEagerFetch", false)) {
if (eagerFetchLobs) {
defaultLobFetchType = FetchType.EAGER;
}
}
@@ -15,11 +15,11 @@ public class ReadAnnotations {
* to resolve the relationships etc.
* </p>
*/
public void readInitial(DeployBeanInfo<?> info){
public void readInitial(DeployBeanInfo<?> info, boolean eagerFetchLobs){
try {
new AnnotationClass(info).parse();
new AnnotationFields(info).parse();
new AnnotationFields(info, eagerFetchLobs).parse();
} catch (RuntimeException e){
String msg = "Error reading annotations for "+info;
@@ -6,18 +6,15 @@ import com.avaje.ebean.config.PstmtDelegate;
import com.avaje.ebeaninternal.server.lib.sql.ExtendedPreparedStatement;
/**
* Implementation of PstmtDelegate from Ebean's own
* DataSource.
*
* @author rbygrave
* Implementation of PstmtDelegate from Ebean's own DataSource.
*/
public class StandardPstmtDelegate implements PstmtDelegate {
/**
* Unwrap the PreparedStatement from Ebean's DataSource implementation.
*/
public PreparedStatement unwrap(PreparedStatement pstmt) {
return ((ExtendedPreparedStatement)pstmt).getDelegate();
}
/**
* Unwrap the PreparedStatement from Ebean's DataSource implementation.
*/
public PreparedStatement unwrap(PreparedStatement pstmt) {
return ((ExtendedPreparedStatement) pstmt).getDelegate();
}
}
@@ -31,9 +31,9 @@ public final class DaemonThreadPool extends ThreadPoolExecutor {
* the time in seconds allowed for the pool to shutdown nicely. After
* this the pool is forced to shutdown.
*/
public DaemonThreadPool(int coreSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) {
public DaemonThreadPool(int coreSize, int maximumPoolSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) {
super(coreSize, coreSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory(namePrefix));
super(coreSize, maximumPoolSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory(namePrefix));
allowCoreThreadTimeOut(true);
this.shutdownWaitSeconds = shutdownWaitSeconds;
this.namePrefix = namePrefix;
@@ -1,5 +1,11 @@
package com.avaje.ebeaninternal.server.lib;
import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
@@ -7,14 +13,6 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
/**
* Manages the shutdown of the JVM Runtime.
* <p>
@@ -33,12 +31,9 @@ public final class ShutdownManager {
static BootupEbeanManager serverFactory;
static boolean whyShutdown;
static {
// Register the Shutdown hook
registerShutdownHook();
whyShutdown = GlobalProperties.getBoolean("debug.shutdown.why",false);
}
/**
@@ -118,19 +113,11 @@ public final class ShutdownManager {
logger.debug("Shutting down");
}
if (whyShutdown) {
try {
throw new RuntimeException("debug.shutdown.why=true ...");
} catch (Throwable e) {
logger.warn("Stacktrace showing why shutdown was fired", e);
}
}
stopping = true;
deregisterShutdownHook();
String shutdownRunner = GlobalProperties.get("system.shutdown.runnable", null);
String shutdownRunner = System.getProperty("ebean.shutdown.runnable");
if (shutdownRunner != null) {
try {
// A custom runnable executed at the start of shutdown
@@ -157,7 +144,7 @@ public final class ShutdownManager {
}
}
if (GlobalProperties.getBoolean("datasource.deregisterAllDrivers", false)) {
if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) {
deregisterAllJdbcDrivers();
}
}
@@ -1,13 +1,11 @@
package com.avaje.ebeaninternal.server.lib.sql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.lib.util.MailEvent;
import com.avaje.ebeaninternal.server.lib.util.MailListener;
import com.avaje.ebeaninternal.server.lib.util.MailMessage;
import com.avaje.ebeaninternal.server.lib.util.MailSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple smtp email alert that sends a email message on dataSourceDown and
@@ -23,7 +21,11 @@ public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
private static final Logger logger = LoggerFactory.getLogger(SimpleDataSourceAlert.class);
// boolean sendInBackGround = true;
private static String alertMailServerName = System.getProperty("ebean.datasource.alert.mailserver");
private static String fromUser = System.getProperty("ebean.datasource.alert.fromUser");
private static String fromEmail = System.getProperty("ebean.datasource.alert.fromEmail");
private static String toEmail = System.getProperty("ebean.datasource.alert.toEmail");
/**
* Create a SimpleAlerter.
@@ -79,15 +81,10 @@ public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
private void sendMessage(String subject, String msg) {
String mailServerName = GlobalProperties.get("datasource.alert.mailserver", null);
if (mailServerName == null) {
if (alertMailServerName == null) {
return;
}
String fromUser = GlobalProperties.get("datasource.alert.fromuser", null);
String fromEmail = GlobalProperties.get("datasource.alert.fromemail", null);
String toEmail = GlobalProperties.get("datasource.alert.toemail", null);
MailMessage data = new MailMessage();
data.setSender(fromUser, fromEmail);
data.addBodyLine(msg);
@@ -95,15 +92,15 @@ public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
String[] toList = toEmail.split(",");
if (toList.length == 0) {
throw new RuntimeException("alert.toemail has not been set?");
logger.error("alert.toemail has not been set?");
} else {
for (int i = 0; i < toList.length; i++) {
data.addRecipient(null, toList[i].trim());
}
MailSender sender = new MailSender(alertMailServerName);
sender.setMailListener(this);
sender.sendInBackground(data);
}
for (int i = 0; i < toList.length; i++) {
data.addRecipient(null, toList[i].trim());
}
MailSender sender = new MailSender(mailServerName);
sender.setMailListener(this);
sender.sendInBackground(data);
}
}
@@ -1,278 +0,0 @@
package com.avaje.ebeaninternal.server.lib.thread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A thread that belongs to a ThreadPool. It will return to the Threadpool when
* it has finished its assigned task.
*/
public class PooledThread implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(PooledThread.class);
/**
* Flag to indicate that the thread was interrupted.
*/
private boolean wasInterrupted;
/**
* The time the thread was last used.
*/
private long lastUsedTime;
/**
* The work to run
*/
private Work work;
/**
* Set to indicate the thread is stopping.
*/
private boolean isStopping;
/**
* Set when the thread has stopped.
*/
private boolean isStopped;
/**
* The background thread.
*/
private Thread thread;
/**
* The pool this worker belongs to.
*/
private ThreadPool threadPool;
/**
* The name of the Thread
*/
private String name;
/**
* The thread synchronization object.
*/
private Object threadMonitor = new Object();
/**
* The monitor for work notification.
*/
private Object workMonitor = new Object();
/**
* Total number of work performed.
*/
private int totalWorkCount;
/**
* Total work execution time.
*/
private long totalWorkExecutionTime;
/**
* Create the PooledThread.
*/
protected PooledThread(ThreadPool threadPool, String name, boolean isDaemon, Integer threadPriority) {
this.name = name;
this.threadPool = threadPool;
this.lastUsedTime = System.currentTimeMillis();
thread = new Thread(this, name);
thread.setDaemon(isDaemon);
if (threadPriority != null) {
thread.setPriority(threadPriority.intValue());
}
}
public String toString() {
return name;
}
protected void start() {
thread.start();
}
/**
* Assign work to this thread. The thread will notify the listener when it has
* finished the work.
*/
public boolean assignWork(Work work) {
synchronized (workMonitor) {
this.work = work;
workMonitor.notifyAll();
}
return true;
}
/**
* process any assigned work until stopped or interrupted.
*/
public void run() {
// process assigned work until we receive a shutdown signal
synchronized (workMonitor) {
while (!isStopping) {
try {
if (work == null) {
workMonitor.wait();
}
} catch (InterruptedException e) {
}
doTheWork();
}
}
// Tell stop() we have shut ourselves down successfully
synchronized (threadMonitor) {
threadMonitor.notifyAll();
}
if (logger.isTraceEnabled()) {
logger.trace("PooledThread [" + getName() + "] finished ");
}
isStopped = true;
}
/**
* Actually do the work and gather the appropriate measures.
*/
private void doTheWork() {
if (isStopping) {
return;
}
long startTime = System.currentTimeMillis();
if (work == null) {
// probably shutting down the thread
} else {
try {
if (logger.isTraceEnabled()) {
logger.trace("start work "+work);
}
work.setStartTime(startTime);
work.getRunnable().run();
if (logger.isTraceEnabled()) {
logger.trace("finished work "+work);
}
} catch (Throwable ex) {
logger.error(null, ex);
if (wasInterrupted) {
this.isStopping = true;
threadPool.removeThread(this);
if (logger.isInfoEnabled()) {
logger.info("PooledThread [" + name + "] removed due to interrupt");
}
try {
thread.interrupt();
} catch (Exception e) {
logger.error("Error interrupting PooledThead[" + name + "]", e);
}
return;
}
}
}
lastUsedTime = System.currentTimeMillis();
totalWorkCount++;
totalWorkExecutionTime = totalWorkExecutionTime + lastUsedTime - startTime;
this.work = null;
threadPool.returnThread(this);
}
/**
* Try to interrupt the thread.
* <p>
* If the Thread was interrupted then it will be removed from the pool.
* </p>
*/
public void interrupt() {
// set a flag so doTheWork knows that it was interrupted
// and removes rather than returns
wasInterrupted = true;
try {
if (logger.isTraceEnabled()) {
logger.trace("interrupt()");
}
thread.interrupt();
} catch (SecurityException ex) {
wasInterrupted = false;
throw ex;
}
}
/**
* Returns true if the thread has finished.
*/
public boolean isStopped() {
return isStopped;
}
/**
* Stop the thread relatively nicely. It will wait a maximum of 10 seconds for
* it to complete any existing work.
*/
protected void stop() {
isStopping = true;
synchronized (threadMonitor) {
assignWork(null);
if (logger.isTraceEnabled()) {
logger.trace("stopping thread ["+name+"]");
}
try {
threadMonitor.wait(10000);
} catch (InterruptedException e) {
;
}
}
thread = null;
threadPool.removeThread(this);
}
/**
* return the name of the thread.
*/
public String getName() {
return name;
}
/**
* Returns the currently executing work, otherwise null.
*/
public Work getWork() {
return work;
}
/**
* The total number of jobs this thread has run.
*/
public int getTotalWorkCount() {
return totalWorkCount;
}
/**
* The total time for performing all assigned work.
*/
public long getTotalWorkExecutionTime() {
return totalWorkExecutionTime;
}
/**
* Returns the time this thread was last used.
*/
public long getLastUsedTime() {
return lastUsedTime;
}
}
@@ -1,502 +0,0 @@
package com.avaje.ebeaninternal.server.lib.thread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.GlobalProperties;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
/**
* This is a pool of threads which can be assigned work.
* <p>
* The Pool will automatically grow as required up to its maximum pool size. The
* Pool will be automatically shrink by trimming threads that have been idle for
* some time.
* </p>
*/
public class ThreadPool {
private static final Logger logger = LoggerFactory.getLogger(ThreadPool.class);
/**
* The max idle time used to trim idle threads from the pool.
*/
private long maxIdleTime;
/**
* The name of the pool
*/
private String poolName;
/**
* The initial pool size.
*/
private int minSize;
/**
* Whether or not the threads are going to be Daemon threads.
*/
private boolean isDaemon;
/**
* The priority or the threads. Can be null, in which case the threads have
* the default priority.
*/
private Integer threadPriority;
/**
* Incrementing int for thread name. NB: currentThreadCount will go up and
* down as the pool grows and shrinks.
*/
private int uniqueThreadID;
/**
* The maximum number of threads to grow to. Hitting this limit will have
* performance ramifications.
*/
private int maxSize = 100;
/**
* Flag that the pool should terminate all the threads and stop.
*/
private boolean stopThePool;
/**
* Flag to indicate that the pool is being shutdown.
*/
private boolean isStopping;
/**
* List of PooledThread that are free for work.
*/
private Vector<PooledThread> freeList = new Vector<PooledThread>();
/**
* List of PooledThread that are busy.
*/
private Vector<PooledThread> busyList = new Vector<PooledThread>();
/**
* List holding queued work.
*/
private Vector<Work> workOverflowQueue = new Vector<Work>();
/**
* Create and return a ThreadPool reading configuration from GlobalProperties.
*/
public static ThreadPool createThreadPool(String poolName) {
int min = GlobalProperties.getInt("threadpool." + poolName + ".min", 0);
int max = GlobalProperties.getInt("threadpool." + poolName + ".max", 100);
int defaultIdleTime = GlobalProperties.getInt("threadpool.idletime", 60);
long idle = 1000 * GlobalProperties.getInt("threadpool." + poolName + ".idletime", defaultIdleTime);
Integer priority = null;
String threadPriority = GlobalProperties.get("threadpool." + poolName + ".priority", null);
if (threadPriority != null) {
priority = new Integer(threadPriority);
}
return new ThreadPool(poolName, true, priority, min, max, idle);
}
public ThreadPool(String poolName, boolean isDaemon, Integer threadPriority, int minSize, int maxSize, long maxIdleMillis) {
this(poolName, isDaemon, threadPriority);
this.minSize = minSize;
this.maxSize = maxSize;
this.maxIdleTime = maxIdleMillis;
}
/**
* Create the ThreadPool.
*/
public ThreadPool(String poolName, boolean isDaemon, Integer threadPriority) {
this.poolName = poolName;
this.isDaemon = isDaemon;
this.threadPriority = threadPriority;
}
/**
* Return true if the pool is shutting down.
*/
public boolean isStopping() {
synchronized (freeList) {
return isStopping;
}
}
/**
* Return the name of the thread pool.
*/
public String getName() {
return poolName;
}
/**
* Set the minimum size the pool should try to maintain.
*/
public void setMinSize(int minSize) {
if (minSize > 0) {
if (minSize > maxSize) {
this.maxSize = minSize;
}
this.minSize = minSize;
maintainPoolSize();
}
}
/**
* Return the minimum size the pool should maintain.
*/
public int getMinSize() {
return minSize;
}
/**
* Set the maximum size the pool should grow to.
*/
public void setMaxSize(int maxSize) {
if (maxSize > 0) {
if (minSize > maxSize) {
minSize = maxSize;
}
this.maxSize = maxSize;
maintainPoolSize();
}
}
/**
* Return the maximum size this pool can grow to.
*/
public int getMaxSize() {
return maxSize;
}
/**
* Return the total number of busy and free threads in the pool.
*/
public int size() {
return busyList.size() + freeList.size();
}
/**
* Return the number of currently busy threads.
*/
public int getBusyCount() {
return busyList.size();
}
/**
* Assign a task to the thread pool, specifing the options to wait or queue
* the task if the pool is fully busy and can't grow.
* <p>
* When the pool is fully busy...
* </p>
* <p>
* addToQueue=true -> work is added to queue, returns false<br>
* addToQueue=false -> work is not done or queued, returns false<br>
* </p>
*
* @param work
* the runnable work to do.
* @param addToQueueIfFull
* If the pool is maxed out and this is true then it queues the
* Runnable.
*/
public boolean assign(Runnable work, boolean addToQueueIfFull) {
if (stopThePool) {
throw new RuntimeException("Pool is stopping... no more work please.");
}
Work runWork = new Work(work);
// get the next available thread in the pool (block)
PooledThread thread = getNextAvailableThread();
if (thread != null) {
// assign the work to that thread
busyList.add(thread);
thread.assignWork(runWork);
return true;
} else {
if (addToQueueIfFull) {
runWork.setEnterQueueTime(System.currentTimeMillis());
workOverflowQueue.add(runWork);
}
return false;
}
}
/**
* Remove the thread from the pool. The thread should be stopped before it is
* removed.
*/
protected void removeThread(PooledThread thread) {
synchronized (freeList) {
busyList.remove(thread);
freeList.remove(thread);
freeList.notify();
if (logger.isTraceEnabled()) {
logger.trace("PooledThread stopped [" + getName() + "]");
}
}
}
/**
* fired when a Thread from the pool has finished, and can be put back into
* the pool.
*/
protected void returnThread(PooledThread thread) {
synchronized (freeList) {
// deregister from the busyList
busyList.remove(thread);
if (!workOverflowQueue.isEmpty()) {
// get the first bit of work off the queue
Work queuedWork = (Work) workOverflowQueue.remove(0);
// work out the queue time and counts etc
queuedWork.setExitQueueTime(System.currentTimeMillis());
busyList.add(thread);
thread.assignWork(queuedWork);
} else {
if (logger.isTraceEnabled()) {
logger.trace("returnThread - add to freeList [" + thread + "]");
}
// put the thread back onto the available list
freeList.add(thread);
// tell shutdown() one has returned
freeList.notify();
}
}
}
/**
* Get the next available thread. Block until thread is available. NB: The
* dispatcher is blocked but work can still be assigned to the dispatcher in a
* non-blocking way
*/
private PooledThread getNextAvailableThread() {
synchronized (freeList) {
if (!freeList.isEmpty()) {
return (PooledThread) freeList.remove(0);
}
if (size() < maxSize) {
return growPool(true);
}
return null;
}
}
/**
* Return an Iterator of PooledThread that are currently running. You should
* only use this for display. Use the getPooledThread() or interrupt() methods
* to interrupt a particular thread.
*
* @return an Iterator of busy PooledThread's.
*/
public Iterator<PooledThread> getBusyThreads() {
synchronized (freeList) {
return busyList.iterator();
}
}
/**
* Shutdown the threadpool stopping all the threads. This will wait until any
* busy threads have finished their assigned work.
*/
public void shutdown() {
synchronized (freeList) {
if (isStopping) {
logger.debug("already shutting down");
}
isStopping = true;
if (logger.isDebugEnabled()) {
logger.debug("ThreadPool [" + poolName + "] Shutting down; threadCount[" + size()+ "] busyCount[" + getBusyCount() + "]");
}
stopThePool = true;
while (!freeList.isEmpty()) {
PooledThread thread = (PooledThread) freeList.remove(0);
thread.stop();
}
try {
while (getBusyCount() > 0) {
String msg = "ThreadPool [" + poolName + "] has [" + getBusyCount()+ "] busy threads, waiting for those to finish.";
logger.info(msg);
Iterator<PooledThread> busyThreads = getBusyThreads();
while (busyThreads.hasNext()) {
PooledThread busyThread = (PooledThread) busyThreads.next();
String busymsg = "Busy thread [" + busyThread.getName() + "] work["+ busyThread.getWork() + "]";
logger.info(busymsg);
}
freeList.wait();
PooledThread thread = (PooledThread) freeList.remove(0);
logger.debug("wait finished on thread[" + thread.getName() + "]");
if (thread != null) {
thread.stop();
}
}
} catch (InterruptedException e) {
logger.error("Error during threadpool shutdown", e);
}
}
}
/**
* Trim or grow the pool leaving at least min free.
*/
protected void maintainPoolSize() {
synchronized (freeList) {
if (isStopping) {
// don't bother as the pool is shutting down
return;
}
int numToStop = size() - minSize;
if (numToStop > 0) {
// should trim idle threads as we are over the minSize
long usedAfter = System.currentTimeMillis() - maxIdleTime;
ArrayList<PooledThread> stopList = new ArrayList<PooledThread>();
Iterator<PooledThread> it = freeList.iterator();
while (it.hasNext() && numToStop > 0) {
PooledThread thread = (PooledThread) it.next();
if (thread.getLastUsedTime() < usedAfter) {
stopList.add(thread);
numToStop--;
}
}
Iterator<PooledThread> stopIt = stopList.iterator();
while (stopIt.hasNext()) {
PooledThread thread = (PooledThread) stopIt.next();
if (logger.isDebugEnabled()) {
logger.debug("trimming pool - stopping thread "+thread);
}
thread.stop();
}
}
int numToAdd = minSize - size();
if (numToAdd > 0) {
// should add some more to the pool
for (int i = 0; i < numToAdd; i++) {
growPool(false);
}
}
}
}
/**
* Interrupt a named thread that is currently busy.
* <p>
* Returns the thread that was interrupted or null if the thread was not
* found. If the thread was interrupted then it will automatically be stopped
* and removed from the pool.
* </p>
* <p>
* Note that it may take some time to actually interrupt the thread so an
* immediate test to see if the thread stopped will probably be wrong.
*
* <pre>
* <code>
* ThreadPool test = ThreadPoolManager.getThreadPool("test");
* PooledThread pt = test.interrupt("test.1");
* if (pt == null) {
* // the thread was not found, perhaps finished?
* } else {
* // give interrupt a little time to execute
* Thread.sleep(1000);
* boolean hasStopped = pt.isStopped();
* //..
* }
* </code>
* </pre>
*
* </p>
*
* @return the thread that was interrupted
*/
public PooledThread interrupt(String threadName) {
PooledThread thread = getBusyThread(threadName);
if (thread != null) {
thread.interrupt();
return thread;
}
return null;
}
/**
* Find a thread using its name from the busy list. Returns null if the thread
* is not found in the busy list.
*/
public PooledThread getBusyThread(String threadName) {
synchronized (freeList) {
Iterator<PooledThread> it = getBusyThreads();
while (it.hasNext()) {
PooledThread pt = (PooledThread) it.next();
if (pt.getName().equals(threadName)) {
return pt;
}
}
return null;
}
}
/**
* Grow the pool with the option of either putting it on the available list,
* or returning it.
*/
private PooledThread growPool(boolean andReturn) {
synchronized (freeList) {
String threadName = poolName + "." + uniqueThreadID++;
PooledThread bgw = new PooledThread(this, threadName, isDaemon, threadPriority);
bgw.start();
if (logger.isDebugEnabled()) {
logger.debug("ThreadPool grow created [" + threadName + "] size[" + size() + "]");
}
if (andReturn) {
return bgw;
} else {
freeList.add(bgw);
return null;
}
}
}
/**
* Return the maximum amount of time in millis that Threads can be idle before
* they are trimmed.
*/
public long getMaxIdleTime() {
return maxIdleTime;
}
/**
* Set the maxiumium amount of time in millis that Threads can be idle before
* they are trimed.
*/
public void setMaxIdleTime(long maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
}
@@ -1,96 +0,0 @@
package com.avaje.ebeaninternal.server.lib.thread;
/**
* Used internally by the ThreadPool to wrap a Runnable that is
* going to be run.
*
* <p>Used to maintains some useful times about the Runnable in terms of when
* it was queued and then eventually run.</p>
*/
public class Work {
/**
* Create a Runnable Work.
*/
public Work(Runnable runnable) {
this.runnable = runnable;
}
/**
* Return the associated Runnable object.
*/
public Runnable getRunnable() {
return runnable;
}
/**
* Return the time this work actually started.
*/
public long getStartTime() {
return startTime;
}
/**
* Sets the time this work actually started.
*/
public void setStartTime(long startTime) {
this.startTime = startTime;
}
/**
* Return the time this entered the queue.
*/
public long getEnterQueueTime() {
return enterQueueTime;
}
/**
* Set the time this entered the queue.
*/
public void setEnterQueueTime(long enterQueueTime) {
this.enterQueueTime = enterQueueTime;
}
/**
* Return the time this left the queue.
*/
public long getExitQueueTime() {
return exitQueueTime;
}
/**
* Set the time this work left the queue.
*/
public void setExitQueueTime(long exitQueueTime) {
this.exitQueueTime = exitQueueTime;
}
/**
* The same as getDescription().
*/
public String toString() {
return getDescription();
}
/**
* Return a description of this work.
*/
public String getDescription() {
StringBuffer sb = new StringBuffer();
sb.append("Work[");
if (runnable != null){
sb.append(runnable.toString());
}
sb.append("]");
return sb.toString();
}
private Runnable runnable;
private long exitQueueTime;
private long enterQueueTime;
private long startTime;
};
@@ -1,12 +0,0 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
<TITLE>AvajeLib</TITLE>
</HEAD>
<Body BGCOLOR="#ffffff">
ThreadPool service.
<P>
Service providing thread pooling for executing background tasks.
</P>
</Body>
</HTML>
@@ -1,13 +1,8 @@
package com.avaje.ebeaninternal.server.persist;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
import com.avaje.ebeaninternal.server.core.*;
import com.avaje.ebeaninternal.server.deploy.BeanManager;
/**
@@ -32,19 +27,17 @@ public final class DefaultPersistExecute implements PersistExecute {
/**
* Default for whether to call getGeneratedKeys after batch insert.
*/
private final boolean defaultBatchGenKeys;
private final boolean defaultBatchGenKeys = true;
/**
* Construct this DmlPersistExecute.
*/
public DefaultPersistExecute(Binder binder, PstmtBatch pstmtBatch) {
public DefaultPersistExecute(Binder binder, PstmtBatch pstmtBatch, int defaultBatchSize) {
this.exeOrmUpdate = new ExeOrmUpdate(binder, pstmtBatch);
this.exeUpdateSql = new ExeUpdateSql(binder, pstmtBatch);
this.exeCallableSql = new ExeCallableSql(binder, pstmtBatch);
this.defaultBatchGenKeys = GlobalProperties.getBoolean("batch.getgeneratedkeys", true);
this.defaultBatchSize = GlobalProperties.getInt("batch.size", 20);
this.defaultBatchSize = defaultBatchSize;
}
public BatchControl createBatchControl(SpiTransaction t) {
@@ -79,7 +79,7 @@ public final class DefaultPersister implements Persister {
this.server = server;
this.updatesDeleteMissingChildren = server.getServerConfig().isUpdatesDeleteMissingChildren();
this.beanDescriptorManager = descMgr;
this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch);
this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch, server.getServerConfig().getPersistBatchSize());
}
/**
@@ -182,7 +182,6 @@ public final class DefaultPersister implements Persister {
}
req.commitTransIfRequired();
return;
} catch (RuntimeException ex) {
req.rollbackTransIfRequired();
@@ -469,7 +468,7 @@ public final class DefaultPersister implements Persister {
int rows = executeSqlUpdate(deleteById, t);
// Delete from the persistence context so that it can't be fetched again later
PersistenceContext persistenceContext = ((SpiTransaction)t).getPersistenceContext();
PersistenceContext persistenceContext = t.getPersistenceContext();
if (idList != null) {
for (Object idValue : idList) {
persistenceContext.deleted(descriptor.getBeanType(), idValue);
@@ -879,7 +878,7 @@ public final class DefaultPersister implements Persister {
Collection<?> additions = null;
Collection<?> deletions = null;
boolean vanillaCollection = (value instanceof BeanCollection<?> == false);
boolean vanillaCollection = !(value instanceof BeanCollection<?>);
if (vanillaCollection || deleteMissingChildren) {
// delete all intersection rows and then treat all
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.*;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
@@ -45,7 +44,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private static final Logger logger = LoggerFactory.getLogger(CQuery.class);
private static final int GLOBAL_ROW_LIMIT = GlobalProperties.getInt("query.globallimit",1000000);
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit","1000000"));
/**
* The resultSet rows read.
@@ -1,15 +1,9 @@
package com.avaje.ebeaninternal.server.query;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.ColumnMapping.Column;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitRequest;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
@@ -27,6 +21,10 @@ import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import javax.persistence.PersistenceException;
import java.util.Iterator;
import java.util.Set;
/**
* Generates the SQL SELECT statements taking into account the physical
* deployment properties.
@@ -53,8 +51,8 @@ public class CQueryBuilder implements Constants {
public CQueryBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.tableAliasPlaceHolder = GlobalProperties.get("ebean.tableAliasPlaceHolder", "${ta}");
this.columnAliasPrefix = GlobalProperties.get("ebean.columnAliasPrefix", "c");
this.tableAliasPlaceHolder = dbPlatform.getTableAliasPlaceHolder();
this.columnAliasPrefix = dbPlatform.getColumnAliasPrefix();
this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder);
this.sqlLimiter = dbPlatform.getSqlLimiter();
@@ -1,21 +1,8 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.SqlQueryListener;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
@@ -27,6 +14,12 @@ import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.sql.*;
import java.util.ArrayList;
/**
* Perform native sql fetches.
@@ -35,7 +28,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(DefaultRelationalQueryEngine.class);
private final int defaultMaxRows;
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit","1000000"));
private final Binder binder;
@@ -43,7 +36,6 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue) {
this.binder = binder;
this.defaultMaxRows = GlobalProperties.getInt("nativesql.defaultmaxrows",100000);
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
}
@@ -70,7 +62,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
try {
String bindLog = "";
String[] propNames = null;
String[] propNames;
synchronized (query) {
if (query.isCancelled()){
@@ -111,7 +103,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
int estimateCapacity = (int) initCap + 1;
// determine the maxRows limit
int maxRows = defaultMaxRows;
int maxRows = GLOBAL_ROW_LIMIT;
if (query.getMaxRows() >= 1) {
maxRows = query.getMaxRows();
}
@@ -130,7 +122,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
synchronized (query) {
// synchronise for query.cancel() support
if (!query.isCancelled()){
bean = readRow(request, rset, propNames, estimateCapacity);
bean = readRow(rset, propNames, estimateCapacity);
}
}
if (bean != null){
@@ -210,14 +202,13 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
propNames.add(columnName);
}
return (String[]) propNames.toArray(new String[propNames.size()]);
return propNames.toArray(new String[propNames.size()]);
}
/**
* Read the row from the ResultSet and return as a MapBean.
*/
protected SqlRow readRow(RelationalQueryRequest request, ResultSet rset,
String[] propNames, int initialCapacity) throws SQLException {
protected SqlRow readRow(ResultSet rset, String[] propNames, int initialCapacity) throws SQLException {
// by default a map will rehash on the 12th entry
// it will be pretty common to have 12 or more entries so
@@ -1,19 +1,15 @@
package com.avaje.ebeaninternal.server.resource;
import java.io.File;
import javax.servlet.ServletContext;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.lib.resource.DirectoryFinder;
import com.avaje.ebeaninternal.server.lib.resource.FileResourceSource;
import com.avaje.ebeaninternal.server.lib.resource.ResourceSource;
import com.avaje.ebeaninternal.server.lib.resource.UrlResourceSource;
import com.avaje.ebeaninternal.server.lib.util.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
/**
* Creates a ResourceManager for a server depending on the avaje.properties.
* <p>
@@ -50,7 +46,7 @@ public class ResourceManagerFactory {
String dir = null;
if (serverConfig.getAutofetchConfig() != null) {
dir = serverConfig.getAutofetchConfig().getLogDirectoryWithEval();
dir = serverConfig.getAutofetchConfig().getLogDirectory();
}
if (dir != null) {
return new File(dir);
@@ -75,18 +71,7 @@ public class ResourceManagerFactory {
// default for web application, override this for file system
String defaultDir = serverConfig.getResourceDirectory();
// the default... check if a webapp first...
ServletContext sc = GlobalProperties.getServletContext();
if (sc != null) {
// servlet container so use ServletContext.getResource()
if (defaultDir == null) {
defaultDir = "WEB-INF/ebean";
}
return new UrlResourceSource(sc, defaultDir);
}
// use File System directory
return createFileSource(defaultDir);
}
@@ -1,49 +0,0 @@
package com.avaje.ebeaninternal.server.transaction;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class IndexEvent {
public static final int COMMIT_EVENT = 1;
public static final int OPTIMISE_EVENT = 2;
private final int eventType;
private final String indexName;
public IndexEvent(int eventType, String indexName) {
this.eventType = eventType;
this.indexName = indexName;
}
public int getEventType() {
return eventType;
}
public String getIndexName() {
return indexName;
}
public static IndexEvent readBinaryMessage(DataInput dataInput) throws IOException {
int eventType = dataInput.readInt();
String indexName = dataInput.readUTF();
return new IndexEvent(eventType, indexName);
}
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
BinaryMessage msg = new BinaryMessage(indexName.length()+10);
DataOutputStream os = msg.getOs();
os.writeInt(BinaryMessage.TYPE_INDEX);
os.writeInt(eventType);
os.writeUTF(indexName);
msgList.add(msg);
}
}
@@ -1,54 +0,0 @@
package com.avaje.ebeaninternal.server.transaction;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class IndexInvalidate {
private final String indexName;
public IndexInvalidate(String indexName) {
this.indexName = indexName;
}
public String getIndexName() {
return indexName;
}
@Override
public int hashCode() {
int hc = IndexInvalidate.class.hashCode();
hc = hc * 31 + indexName.hashCode();
return hc;
}
@Override
public boolean equals(Object o){
if (o instanceof IndexInvalidate == false){
return false;
}
return indexName.equals(((IndexInvalidate)o).indexName);
}
public static IndexInvalidate readBinaryMessage(DataInput dataInput) throws IOException {
String indexName = dataInput.readUTF();
return new IndexInvalidate(indexName);
}
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
BinaryMessage msg = new BinaryMessage(indexName.length()+10);
DataOutputStream os = msg.getOs();
os.writeInt(BinaryMessage.TYPE_INDEX_INVALIDATE);
os.writeUTF(indexName);
msgList.add(msg);
}
}
@@ -1,9 +1,5 @@
package com.avaje.ebeaninternal.server.transaction;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEvent;
import com.avaje.ebeaninternal.api.TransactionEventBeans;
import com.avaje.ebeaninternal.api.TransactionEventTable;
@@ -14,6 +10,8 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Performs post commit processing using a background thread.
* <p>
@@ -36,8 +34,6 @@ public final class PostCommitProcessing {
private final BeanPersistIdMap beanPersistIdMap;
// private final BeanDeltaMap beanDeltaMap;
private final RemoteTransactionEvent remoteTransactionEvent;
private final DeleteByIdMap deleteByIdMap;
@@ -45,7 +41,7 @@ public final class PostCommitProcessing {
/**
* Create for a TransactionManager and event.
*/
public PostCommitProcessing(ClusterManager clusterManager, TransactionManager manager, SpiTransaction transaction, TransactionEvent event) {
public PostCommitProcessing(ClusterManager clusterManager, TransactionManager manager, TransactionEvent event) {
this.clusterManager = clusterManager;
this.manager = manager;
@@ -53,10 +49,7 @@ public final class PostCommitProcessing {
this.event = event;
this.deleteByIdMap = event.getDeleteByIdMap();
this.persistBeanRequests = createPersistBeanRequests();
this.beanPersistIdMap = createBeanPersistIdMap();
//this.beanDeltaMap = new BeanDeltaMap(event.getBeanDeltas());
this.remoteTransactionEvent = createRemoteTransactionEvent();
}
@@ -87,8 +80,8 @@ public final class PostCommitProcessing {
public void notifyCluster() {
if (remoteTransactionEvent != null && !remoteTransactionEvent.isEmpty()) {
// send the interesting events to the cluster
if (manager.getClusterDebugLevel() > 0 || logger.isDebugEnabled()) {
logger.info("Cluster Send: " + remoteTransactionEvent.toString());
if (logger.isDebugEnabled()) {
logger.debug("Cluster Send: {}", remoteTransactionEvent);
}
clusterManager.broadcast(remoteTransactionEvent);
@@ -147,12 +140,6 @@ public final class PostCommitProcessing {
RemoteTransactionEvent remoteTransactionEvent = new RemoteTransactionEvent(serverName);
// if (beanDeltaMap != null) {
// for (BeanDeltaList deltaList : beanDeltaMap.deltaLists()) {
// remoteTransactionEvent.addBeanDeltaList(deltaList);
// }
// }
if (beanPersistIdMap != null) {
for (BeanPersistIds beanPersist : beanPersistIdMap.values()) {
remoteTransactionEvent.addBeanPersistIds(beanPersist);
@@ -170,13 +157,6 @@ public final class PostCommitProcessing {
}
}
Set<IndexInvalidate> indexInvalidations = event.getIndexInvalidations();
if (indexInvalidations != null) {
for (IndexInvalidate indexInvalidate : indexInvalidations) {
remoteTransactionEvent.addIndexInvalidate(indexInvalidate);
}
}
return remoteTransactionEvent;
}
@@ -1,15 +1,13 @@
package com.avaje.ebeaninternal.server.transaction;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class RemoteTransactionEvent implements Runnable {
private List<BeanPersistIds> beanPersistList = new ArrayList<BeanPersistIds>();
@@ -19,10 +17,6 @@ public class RemoteTransactionEvent implements Runnable {
private List<BeanDeltaList> beanDeltaLists;
private BeanDeltaMap beanDeltaMap;
private List<IndexEvent> indexEventList;
private Set<IndexInvalidate> indexInvalidations;
private DeleteByIdMap deleteByIdMap;
@@ -56,12 +50,6 @@ public class RemoteTransactionEvent implements Runnable {
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
if (indexInvalidations != null){
for (IndexInvalidate indexInvalidate : indexInvalidations) {
indexInvalidate.writeBinaryMessage(msgList);
}
}
if (tableList != null){
for (int i = 0; i < tableList.size(); i++) {
tableList.get(i).writeBinaryMessage(msgList);
@@ -85,12 +73,6 @@ public class RemoteTransactionEvent implements Runnable {
beanDeltaLists.get(i).writeBinaryMessage(msgList);
}
}
if (indexEventList != null){
for (int i = 0; i < indexEventList.size(); i++) {
indexEventList.get(i).writeBinaryMessage(msgList);
}
}
}
public boolean isEmpty() {
@@ -101,13 +83,6 @@ public class RemoteTransactionEvent implements Runnable {
beanPersistList.add(beanPersist);
}
public void addIndexInvalidate(IndexInvalidate indexInvalidate){
if (indexInvalidations == null){
indexInvalidations = new HashSet<IndexInvalidate>();
}
indexInvalidations.add(indexInvalidate);
}
public void addTableIUD(TableIUD tableIud){
if (tableList == null){
tableList = new ArrayList<TableIUD>(4);
@@ -129,13 +104,6 @@ public class RemoteTransactionEvent implements Runnable {
beanDeltaMap.addBeanDelta(beanDelta);
}
public void addIndexEvent(IndexEvent indexEvent){
if (indexEventList == null){
indexEventList = new ArrayList<IndexEvent>(2);
}
indexEventList.add(indexEvent);
}
public String getServerName() {
return serverName;
}
@@ -156,14 +124,6 @@ public class RemoteTransactionEvent implements Runnable {
this.deleteByIdMap = deleteByIdMap;
}
public Set<IndexInvalidate> getIndexInvalidations() {
return indexInvalidations;
}
public List<IndexEvent> getIndexEventList() {
return indexEventList;
}
public List<TableIUD> getTableIUDList() {
return tableList;
}
@@ -1,19 +1,6 @@
package com.avaje.ebeaninternal.server.transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebeaninternal.api.SpiTransaction;
@@ -24,6 +11,16 @@ import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* Manages transactions.
@@ -97,8 +94,6 @@ public class TransactionManager {
* Id's for transaction logging.
*/
protected AtomicLong transactionCounter = new AtomicLong(1000);
protected int clusterDebugLevel;
protected final BulkEventListenerMap bulkEventListenerMap;
@@ -119,16 +114,12 @@ public class TransactionManager {
List<TransactionEventListener> transactionEventListeners = bootupClasses.getTransactionEventListeners();
this.transactionEventListeners = transactionEventListeners.toArray(new TransactionEventListener[transactionEventListeners.size()]);
// log some transaction events using a java util logger
this.clusterDebugLevel = GlobalProperties.getInt("ebean.cluster.debuglevel", 0);
this.defaultBatchMode = config.isPersistBatching();
this.prefix = "";
this.externalTransPrefix = "e";
this.prefix = GlobalProperties.get("transaction.prefix", "");
this.externalTransPrefix = GlobalProperties.get("transaction.prefix", "e");
String value = GlobalProperties.get("transaction.onqueryonly", "CLOSE").toUpperCase().trim();
String value = System.getProperty("ebean.transaction.onqueryonly", "CLOSE").toUpperCase().trim();
this.onQueryOnly = getOnQueryOnly(value, dataSource);
initialiseHeartbeat();
@@ -219,20 +210,6 @@ public class TransactionManager {
return dataSource;
}
/**
* Return the cluster debug level.
*/
public int getClusterDebugLevel() {
return clusterDebugLevel;
}
/**
* Set the cluster debug level.
*/
public void setClusterDebugLevel(int clusterDebugLevel) {
this.clusterDebugLevel = clusterDebugLevel;
}
/**
* Defines the type of behavior to use when closing a transaction that was used to query data only.
*/
@@ -416,7 +393,7 @@ public class TransactionManager {
TXN_LOGGER.debug(transaction.getLogPrefix()+"Commit");
}
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction, transaction.getEvent());
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction.getEvent());
postCommit.notifyLocalCacheIndex();
postCommit.notifyCluster();
@@ -445,7 +422,7 @@ public class TransactionManager {
TransactionEvent event = new TransactionEvent();
event.add(tableEvents);
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, null, event);
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, event);
// invalidate parts of local cache and index
postCommit.notifyLocalCacheIndex();
@@ -458,8 +435,8 @@ public class TransactionManager {
*/
public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) {
if (clusterDebugLevel > 0 || logger.isDebugEnabled()) {
logger.info("Cluster Received: " + remoteEvent.toString());
if (logger.isDebugEnabled()) {
logger.debug("Cluster Received: " + remoteEvent.toString());
}
List<TableIUD> tableIUDList = remoteEvent.getTableIUDList();
@@ -1,30 +1,19 @@
package com.avaje.ebeaninternal.server.util;
import com.avaje.ebeaninternal.api.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
/**
* Can search the class path for classes using a ClassPathSearchMatcher. A
* ClassPathSearch should only be used once in a single threaded manor. It is
@@ -55,22 +44,21 @@ public class ClassPathSearch {
private ArrayList<URI> scannedUris = new ArrayList<URI>();
public ClassPathSearch(ClassLoader classLoader, ClassPathSearchFilter filter, ClassPathSearchMatcher matcher) {
public ClassPathSearch(ClassLoader classLoader, ClassPathSearchFilter filter, ClassPathSearchMatcher matcher, String classPathReaderClassName) {
this.classLoader = classLoader;
this.filter = filter;
this.matcher = matcher;
initClassPaths();
initClassPaths(classPathReaderClassName);
}
private void initClassPaths() {
private void initClassPaths(String classPathReaderCN) {
try {
String cn = GlobalProperties.get("ebean.classpathreader", null);
if (cn != null) {
if (classPathReaderCN != null) {
// use a user defined classPathReader
logger.info("Using [" + cn + "] to read the searchable class path");
classPathReader = (ClassPathReader) ClassUtil.newInstance(cn, this.getClass());
logger.info("Using [" + classPathReaderCN + "] to read the searchable class path");
classPathReader = (ClassPathReader) ClassUtil.newInstance(classPathReaderCN, this.getClass());
}
Object[] rawClassPaths = classPathReader.readPath(classLoader);