com.sleepycat
diff --git a/quickfixj-core/src/main/doc/usermanual/installation.html b/quickfixj-core/src/main/doc/usermanual/installation.html
index f05bea37a7..a34f957737 100644
--- a/quickfixj-core/src/main/doc/usermanual/installation.html
+++ b/quickfixj-core/src/main/doc/usermanual/installation.html
@@ -81,16 +81,10 @@ Optional run-time libraries:
use Log4J logging.
- proxool.jar |
+ HikariCP.jar |
This JAR provided database connection pooling capabilities. It is required
if you are using the JDBC store or log. |
-
- jcl104-over-slf4j.jar |
- Adapts Jakarta Commons Logging to SLF4J. Required if you are using an optional
- library that depends on Jakarta Commons Logging. Currently, this includes Proxool
- (needed by JDBC store and log for connection pooling). |
-
sleepycat-je.jar |
Needed if the SleepyCat JE message store is used. |
diff --git a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html
index 4b39200dfa..679c1bec1c 100644
--- a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html
+++ b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html
@@ -970,7 +970,7 @@ QuickFIX Settings
JdbcDriver |
JDBC driver for JDBC logger. Also used for JDBC log. |
Class name for the JDBC driver. Specify driver properties directly will cause the
- creation of a Proxool data source that supports connection pooling. If you are using a
+ creation of a HikarCP data source that supports connection pooling. If you are using a
database with it's own pooling data source (e.g., Oracle) then use the setDataSource()
method on the Jdbc-related factories to set the data source directly. |
|
@@ -1031,39 +1031,69 @@ QuickFIX Settings
Any nonempty string. |
"" (empty string) |
-
JdbcMaxActiveConnection |
Specifies the maximum number of connections to the database. |
- Any number |
+ Positive number |
32 |
- JdbcMaxActiveTime |
- Specifies if the housekeeper comes across a thread that has been active for longer than
- this (milliseconds) then it will kill it. So make sure you set this to a number bigger than your
- slowest expected response! |
- Any number |
- 5000 |
+ JdbcMinIdleConnection |
+ Controls the minimum number of idle connections that HikariCP tries to maintain in
+ the pool, including both idle and in-use connections. If the idle connections dip
+ below this value, HikariCP will make the best effort to restore them quickly and
+ efficiently.
+ |
+ [0, JdbcMaxActiveConnection] |
+ Same as JdbcMaxActiveConnection |
JdbcMaxConnectionLifeTime |
Specifies the maximum amount of time that a connection exists for before
it is killed (milliseconds). |
- Any number |
- 28800000 |
-
-
- JdbcSimultaneousBuildThrottle |
- Specifies the maximum number of connections we can be building at any one time.
- That is, the number of new connections that have been requested but aren't yet
- available for use. Because connections can be built using more than one thread
- (for instance, when they are built on demand) and it takes a finite time between
- deciding to build the connection and it becoming available we need some way of
- ensuring that a lot of threads don't all decide to build a connection at once.
- (We could solve this in a smarter way - and indeed we will one day) |
- Any number |
- 32 |
+ Positive |
+ 28800000 ms (8 hours) |
+
+
+ JdbcConnectionTimeout |
+ Set the maximum number of milliseconds that a client will wait for a connection from the
+ pool. If this time is exceeded without a connection becoming available, a SQLException
+ will be thrown from javax.sql.DataSource.getConnection(). |
+ Non-negative number |
+ 250 ms |
+
+
+ JdbcConnectionIdleTimeout |
+ Controls the maximum amount of time that a connection is allowed to sit idle in the pool.
+ Whether a connection is retired as idle or not is subject to a maximum variation of +30 seconds, and
+ average variation of +15 seconds. A connection will never be retired as idle before this timeout.
+ A value of 0 means that idle connections are never removed from the pool. |
+ Non-negative number |
+ 600000 ms (10 minutes) |
+
+
+ JdbcConnectionKeepaliveTime |
+ Controls the keepalive interval for a connection in the pool. An in-use connection will never be
+ tested by the keepalive thread, only when it is idle will it be tested. |
+ Non-negative number |
+ 0 ms |
+
+
+ JdbcConnectionKeepaliveTime |
+ Controls the keepalive interval for a connection in the pool. An in-use connection will never be
+ tested by the keepalive thread, only when it is idle will it be tested. |
+ Non-negative number |
+ 0 ms |
+
+
+ JdbcConnectionTestQuery |
+ Set the SQL query to be executed to test the validity of connections. Using the JDBC4
+ Connection.isValid() method to test connection validity can be more efficient on some
+ databases and is recommended. If your driver supports JDBC4 we strongly recommend not
+ setting this property. |
+ Valid SQL query |
+ |
+
diff --git a/quickfixj-core/src/main/java/quickfix/JdbcLog.java b/quickfixj-core/src/main/java/quickfix/JdbcLog.java
index 308e37fade..51f7d124af 100644
--- a/quickfixj-core/src/main/java/quickfix/JdbcLog.java
+++ b/quickfixj-core/src/main/java/quickfix/JdbcLog.java
@@ -55,7 +55,7 @@ class JdbcLog extends AbstractLog {
private final Map deleteItemsSqlCache = new HashMap<>();
public JdbcLog(SessionSettings settings, SessionID sessionID, DataSource ds)
- throws SQLException, ClassNotFoundException, ConfigError, FieldConvertError {
+ throws SQLException, ConfigError, FieldConvertError {
this.sessionID = sessionID;
dataSource = ds == null
? JdbcUtil.getDataSource(settings, sessionID)
@@ -109,8 +109,8 @@ private void createCachedSql() {
}
private void createInsertItemSql(String tableName) {
- insertItemSqlCache.put(tableName, "INSERT INTO " + tableName + " (time, "
- + getIDColumns(extendedSessionIdSupported) + ", text) " + "VALUES (?,"
+ insertItemSqlCache.put(tableName, "INSERT INTO " + tableName + " (time,"
+ + getIDColumns(extendedSessionIdSupported) + ",text) " + "VALUES (?,"
+ getIDPlaceholders(extendedSessionIdSupported) + ",?)");
}
diff --git a/quickfixj-core/src/main/java/quickfix/JdbcSetting.java b/quickfixj-core/src/main/java/quickfix/JdbcSetting.java
index 59c31d68e9..75df8aa74e 100644
--- a/quickfixj-core/src/main/java/quickfix/JdbcSetting.java
+++ b/quickfixj-core/src/main/java/quickfix/JdbcSetting.java
@@ -19,6 +19,8 @@
package quickfix;
+import javax.sql.DataSource;
+
/**
* Class for storing JDBC setting constants shared by both the log and message
* store classes.
@@ -103,40 +105,62 @@ public class JdbcSetting {
public static final String SETTING_JDBC_SESSION_ID_DEFAULT_PROPERTY_VALUE = "JdbcSessionIdDefaultPropertyValue";
/**
- * Specifies the maximum number of connections to the database
- *
- * @see http://proxool.sourceforge.net/properties.html
+ * Controls the maximum size that the pool is allowed to reach, including both idle and in-use connections.
+ * Basically this value will determine the maximum number of actual connections to the database backend.
+ * A reasonable value for this is best determined by your execution environment. When the pool reaches this size,
+ * and no idle connections are available, calls to {@link DataSource#getConnection()} will block for up to
+ * {@link JdbcSetting#SETTING_JDBC_CONNECTION_TIMEOUT} milliseconds before timing out.
*/
public static final String SETTING_JDBC_MAX_ACTIVE_CONNECTION = "JdbcMaxActiveConnection";
/**
- * Specifies if the housekeeper comes across a thread that has been active for longer than
- * this then it will kill it. So make sure you set this to a number bigger than your
- * slowest expected response!
- *
- * @see http://proxool.sourceforge.net/properties.html
+ * Controls the minimum number of idle connections that HikariCP tries to maintain in the pool.
+ * If the idle connections dip below this value and total connections in the pool are less than
+ * {@link JdbcSetting#SETTING_JDBC_MAX_ACTIVE_CONNECTION}, HikariCP will make the best effort to add
+ * additional connections quickly and efficiently. However, for maximum performance and responsiveness
+ * to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed
+ * size connection pool.
*/
- public static final String SETTING_JDBC_MAX_ACTIVE_TIME = "JdbcMaxActiveTime";
+ public static final String SETTING_JDBC_MIN_IDLE_CONNECTION = "JdbcMinIdleConnection";
/**
- * Specifies the maximum amount of time that a connection exists for before
- * it is killed (milliseconds).
- *
- * @see http://proxool.sourceforge.net/properties.html
+ * Controls the maximum lifetime of a connection in the pool. An in-use connection will never be retired, only when
+ * it is closed will it then be removed. On a connection-by-connection basis, minor negative attenuation is applied to
+ * avoid mass-extinction in the pool. We strongly recommend setting this value, and it should be several seconds shorter
+ * than any database or infrastructure imposed connection time limit. A value of 0 indicates no maximum lifetime (infinite
+ * lifetime), subject of course to the {@link JdbcSetting#SETTING_JDBC_CONNECTION_IDLE_TIMEOUT} setting.
*/
public static final String SETTING_JDBC_MAX_CONNECTION_LIFETIME = "JdbcMaxConnectionLifeTime";
/**
- * Specifies the maximum number of connections we can be building at any one time.
- * That is, the number of new connections that have been requested but aren't yet
- * available for use. Because connections can be built using more than one thread
- * (for instance, when they are built on demand) and it takes a finite time between
- * deciding to build the connection and it becoming available we need some way of
- * ensuring that a lot of threads don't all decide to build a connection at once.
- * (We could solve this in a smarter way - and indeed we will one day)
- *
- * @see http://proxool.sourceforge.net/properties.html
+ * Controls the maximum number of milliseconds that a client (that's you) will wait for a connection from the pool. If this time
+ * is exceeded without a connection becoming available, a SQLException will be thrown. Lowest acceptable connection timeout is 250 ms.
*/
- public static final String SETTING_JDBC_SIMULTANEOUS_BUILD_THROTTLE = "JdbcSimultaneousBuildThrottle";
+ public static final String SETTING_JDBC_CONNECTION_TIMEOUT = "JdbcConnectionTimeout";
+ /**
+ * Controls the maximum amount of time that a connection is allowed to sit idle in the pool.
+ * This setting only applies when {@link JdbcSetting#SETTING_JDBC_MIN_IDLE_CONNECTION} is defined to be less than
+ * {@link JdbcSetting#SETTING_JDBC_MAX_ACTIVE_CONNECTION}. Idle connections will not be retired once the pool
+ * reaches {@link JdbcSetting#SETTING_JDBC_MIN_IDLE_CONNECTION} connections.
+ */
+ public static final String SETTING_JDBC_CONNECTION_IDLE_TIMEOUT = "JdbcConnectionIdleTimeout";
+
+ /**
+ * Controls how frequently HikariCP will attempt to keep a connection alive, in order to prevent it from being timed out by the
+ * database or network infrastructure. This value must be less than the {@link JdbcSetting#SETTING_JDBC_MAX_CONNECTION_LIFETIME} value.
+ * A "keepalive" will only occur on an idle connection. When the time arrives for a "keepalive" against a given connection, that connection
+ * will be removed from the pool, "pinged", and then returned to the pool. The 'ping' is one of either: invocation of the JDBC4
+ * {@link java.sql.Connection#isValid(int)} method, or execution of the {@link JdbcSetting#SETTING_JDBC_CONNECTION_TEST_QUERY}. Typically, the duration
+ * out-of-the-pool should be measured in single digit milliseconds or even sub-millisecond, and therefore should have little or no noticeable
+ * performance impact. The minimum allowed value is 30000ms (30 seconds), but a value in the range of minutes is most desirable.
+ */
+ public static final String SETTING_JDBC_CONNECTION_KEEPALIVE_TIME = "JdbcConnectionKeepaliveTime";
+
+ /**
+ * If your driver supports JDBC4 we strongly recommend not setting this property. This is for "legacy" drivers that do not support the
+ * JDBC4 {@link java.sql.Connection#isValid(int)} API. This is the query that will be executed just before a connection is given to you
+ * from the pool to validate that the connection to the database is still alive.
+ */
+ public static final String SETTING_JDBC_CONNECTION_TEST_QUERY = "JdbcConnectionTestQuery";
}
diff --git a/quickfixj-core/src/main/java/quickfix/JdbcUtil.java b/quickfixj-core/src/main/java/quickfix/JdbcUtil.java
index 14ffc259e1..c6ce2c2fda 100644
--- a/quickfixj-core/src/main/java/quickfix/JdbcUtil.java
+++ b/quickfixj-core/src/main/java/quickfix/JdbcUtil.java
@@ -19,7 +19,9 @@
package quickfix;
-import org.logicalcobwebs.proxool.ProxoolDataSource;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
import javax.naming.InitialContext;
import javax.naming.NamingException;
@@ -31,89 +33,96 @@
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
class JdbcUtil {
static final String CONNECTION_POOL_ALIAS = "quickfixj";
+ static final int DEFAULT_MAX_CONNECTION_COUNT = 32;
+ static final long DEFAULT_MAX_CONNECTION_LIFETIME = TimeUnit.HOURS.toMillis(8);
+ static final long DEFAULT_CONNECTION_TIMEOUT = 250L;
+ static final long DEFAULT_CONNECTION_IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10);
+ static final long DEFAULT_CONNECTION_KEEPALIVE_TIME = 0;
- private static final Map dataSources = new ConcurrentHashMap<>();
+ private static final Map dataSources = new ConcurrentHashMap<>();
private static final AtomicInteger dataSourceCounter = new AtomicInteger();
- static DataSource getDataSource(SessionSettings settings, SessionID sessionID)
- throws ConfigError, FieldConvertError {
+ static DataSource getDataSource(SessionSettings settings, SessionID sessionID) throws ConfigError, FieldConvertError {
if (settings.isSetting(sessionID, JdbcSetting.SETTING_JDBC_DS_NAME)) {
- String jndiName = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_DS_NAME);
- try {
- return (DataSource) new InitialContext().lookup(jndiName);
- } catch (NamingException e) {
- throw new ConfigError(e);
- }
+ return getJNDIDataSource(settings, sessionID);
} else {
- String jdbcDriver = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_DRIVER);
- String connectionURL = settings.getString(sessionID,
- JdbcSetting.SETTING_JDBC_CONNECTION_URL);
- String user = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_USER);
- String password = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_PASSWORD);
- int maxConnCount = settings
- .isSetting(JdbcSetting.SETTING_JDBC_MAX_ACTIVE_CONNECTION) ?
- settings.getInt(sessionID, JdbcSetting.SETTING_JDBC_MAX_ACTIVE_CONNECTION) :
- 32;
- int simultaneousBuildThrottle = settings
- .isSetting(JdbcSetting.SETTING_JDBC_SIMULTANEOUS_BUILD_THROTTLE) ?
- settings.getInt(sessionID, JdbcSetting.SETTING_JDBC_SIMULTANEOUS_BUILD_THROTTLE) :
- maxConnCount;
- long maxActiveTime = settings
- .isSetting(JdbcSetting.SETTING_JDBC_MAX_ACTIVE_TIME) ?
- settings.getLong(sessionID, JdbcSetting.SETTING_JDBC_MAX_ACTIVE_TIME) :
- 5000;
- int maxConnLifetime = settings
- .isSetting(JdbcSetting.SETTING_JDBC_MAX_CONNECTION_LIFETIME) ?
- settings.getInt(sessionID, JdbcSetting.SETTING_JDBC_MAX_CONNECTION_LIFETIME) :
- 28800000;
-
- return getDataSource(jdbcDriver, connectionURL, user, password, true, maxConnCount,
- simultaneousBuildThrottle, maxActiveTime, maxConnLifetime);
+ return getOrCreatePooledDataSource(settings, sessionID);
+ }
+ }
+
+ private static DataSource getJNDIDataSource(SessionSettings settings, SessionID sessionID) throws ConfigError {
+ String jndiName = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_DS_NAME);
+ try {
+ return (DataSource) new InitialContext().lookup(jndiName);
+ } catch (NamingException e) {
+ throw new ConfigError(e);
}
}
- static DataSource getDataSource(String jdbcDriver, String connectionURL, String user, String password, boolean cache) {
- return getDataSource(jdbcDriver, connectionURL, user, password, cache, 10, 10, 5000, 28800000);
+ private static DataSource getOrCreatePooledDataSource(SessionSettings settings, SessionID sessionID) throws ConfigError, FieldConvertError {
+ String jdbcDriver = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_DRIVER);
+ String connectionURL = settings.getString(sessionID,JdbcSetting.SETTING_JDBC_CONNECTION_URL);
+ String user = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_USER);
+ String password = settings.getString(sessionID, JdbcSetting.SETTING_JDBC_PASSWORD);
+ return getOrCreatePooledDataSource(settings, sessionID, jdbcDriver, connectionURL, user, password);
}
- /**
- * This is typically called from a single thread, but just in case we are using an atomic loading function
- * to avoid the creation of two data sources simultaneously. The cache itself is thread safe.
- */
- static DataSource getDataSource(String jdbcDriver, String connectionURL, String user, String password,
- boolean cache, int maxConnCount, int simultaneousBuildThrottle,
- long maxActiveTime, int maxConnLifetime) {
+ static DataSource getOrCreatePooledDataSource(SessionSettings settings, SessionID sessionID, String jdbcDriver, String connectionURL, String user, String password)
+ throws ConfigError, FieldConvertError {
String key = jdbcDriver + "#" + connectionURL + "#" + user + "#" + password;
- ProxoolDataSource ds = cache ? dataSources.get(key) : null;
-
- if (ds == null) {
- final Function loadingFunction = dataSourceKey -> {
- final ProxoolDataSource dataSource = new ProxoolDataSource(CONNECTION_POOL_ALIAS + "-" + dataSourceCounter.incrementAndGet());
-
- dataSource.setDriver(jdbcDriver);
- dataSource.setDriverUrl(connectionURL);
-
- // Bug in Proxool 0.9RC2. Must set both delegate properties and individual setters. :-(
- dataSource.setDelegateProperties("user=" + user + ","
- + (password != null && !"".equals(password) ? "password=" + password : ""));
- dataSource.setUser(user);
- dataSource.setPassword(password);
-
- dataSource.setMaximumActiveTime(maxActiveTime);
- dataSource.setMaximumConnectionLifetime(maxConnLifetime);
- dataSource.setMaximumConnectionCount(maxConnCount);
- dataSource.setSimultaneousBuildThrottle(simultaneousBuildThrottle);
- return dataSource;
- };
- ds = cache ? dataSources.computeIfAbsent(key, loadingFunction) : loadingFunction.apply(key);
+
+ HikariDataSource dataSource = dataSources.get(key);
+
+ if (dataSource != null) {
+ return dataSource;
+ }
+
+ HikariDataSource newDataSource = createPooledDataSource(settings, sessionID, jdbcDriver, connectionURL, user, password);
+
+ if (dataSources.putIfAbsent(key, newDataSource) == null) {
+ return newDataSource;
+ } else {
+ return dataSources.get(key);
}
- return ds;
+ }
+
+ private static HikariDataSource createPooledDataSource(SessionSettings settings, SessionID sessionID, String jdbcDriver, String connectionURL, String user, String password)
+ throws ConfigError, FieldConvertError {
+ HikariConfig configuration = new HikariConfig();
+ configuration.setPoolName(CONNECTION_POOL_ALIAS + "-" + dataSourceCounter.incrementAndGet());
+ configuration.setDriverClassName(jdbcDriver);
+ configuration.setJdbcUrl(connectionURL);
+ configuration.setUsername(user);
+ configuration.setPassword(password);
+
+ int maxConnectionCount = settings.getIntOrDefault(sessionID, JdbcSetting.SETTING_JDBC_MAX_ACTIVE_CONNECTION, DEFAULT_MAX_CONNECTION_COUNT);
+ configuration.setMaximumPoolSize(maxConnectionCount);
+
+ int minIdleConnectionCount = settings.getIntOrDefault(sessionID, JdbcSetting.SETTING_JDBC_MIN_IDLE_CONNECTION, maxConnectionCount);
+ configuration.setMinimumIdle(minIdleConnectionCount);
+
+ long maxConnectionLifetime = settings.getLongOrDefault(sessionID, JdbcSetting.SETTING_JDBC_MAX_CONNECTION_LIFETIME, DEFAULT_MAX_CONNECTION_LIFETIME);
+ configuration.setMaxLifetime(maxConnectionLifetime);
+
+ long connectionTimeout = settings.getLongOrDefault(sessionID, JdbcSetting.SETTING_JDBC_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
+ configuration.setConnectionTimeout(connectionTimeout);
+
+ long connectionIdleTimeout = settings.getLongOrDefault(sessionID, JdbcSetting.SETTING_JDBC_CONNECTION_IDLE_TIMEOUT, DEFAULT_CONNECTION_IDLE_TIMEOUT);
+ configuration.setIdleTimeout(connectionIdleTimeout);
+
+ long connectionKeepaliveTime = settings.getLongOrDefault(sessionID, JdbcSetting.SETTING_JDBC_CONNECTION_KEEPALIVE_TIME, DEFAULT_CONNECTION_KEEPALIVE_TIME);
+ configuration.setKeepaliveTime(connectionKeepaliveTime);
+
+ String connectionTestQuery = settings.getStringOrDefault(sessionID, JdbcSetting.SETTING_JDBC_CONNECTION_TEST_QUERY, null);
+ configuration.setConnectionTestQuery(connectionTestQuery);
+
+ return new HikariDataSource(configuration);
}
static void close(SessionID sessionID, Connection connection) {
@@ -165,7 +174,7 @@ private static boolean isColumn(DatabaseMetaData metaData, String tableName, Str
static String getIDWhereClause(boolean isExtendedSessionID) {
return isExtendedSessionID
? ("beginstring=? and sendercompid=? and sendersubid=? and senderlocid=? and "
- + "targetcompid=? and targetsubid=? and targetlocid=? and session_qualifier=? ")
+ + "targetcompid=? and targetsubid=? and targetlocid=? and session_qualifier=? ")
: "beginstring=? and sendercompid=? and targetcompid=? and session_qualifier=? ";
}
@@ -201,5 +210,4 @@ static int setSessionIdParameters(SessionID sessionID, PreparedStatement query,
private static String getSqlValue(String javaValue, String defaultSqlValue) {
return !SessionID.NOT_SET.equals(javaValue) ? javaValue : defaultSqlValue;
}
-
}
diff --git a/quickfixj-core/src/main/java/quickfix/SessionSettings.java b/quickfixj-core/src/main/java/quickfix/SessionSettings.java
index 319e44e1d2..3765c35c1f 100644
--- a/quickfixj-core/src/main/java/quickfix/SessionSettings.java
+++ b/quickfixj-core/src/main/java/quickfix/SessionSettings.java
@@ -186,6 +186,13 @@ public String getString(String key) throws ConfigError {
return getString(DEFAULT_SESSION_ID, key);
}
+ /**
+ * Gets a string from the default section if present or use default value.
+ */
+ public String getStringOrDefault(String key, String defaultValue) throws ConfigError {
+ return isSetting(key) ? getString(key) : defaultValue;
+ }
+
/**
* Get a settings string.
*
@@ -202,6 +209,13 @@ public String getString(SessionID sessionID, String key) throws ConfigError {
return value;
}
+ /**
+ * Get a settings string if present or use default value.
+ */
+ public String getStringOrDefault(SessionID sessionID, String key, String defaultValue) throws ConfigError {
+ return isSetting(sessionID, key) ? getString(sessionID, key) : defaultValue;
+ }
+
/**
* Return the settings for a session as a Properties object.
*
@@ -265,6 +279,13 @@ public long getLong(String key) throws ConfigError, FieldConvertError {
return getLong(DEFAULT_SESSION_ID, key);
}
+ /**
+ * Gets a long from the default section of settings if present or use default value.
+ */
+ public long getLongOrDefault(String key, long defaultValue) throws ConfigError, FieldConvertError {
+ return isSetting(key) ? getLong(key) : defaultValue;
+ }
+
/**
* Get a settings value as a long integer.
*
@@ -282,6 +303,13 @@ public long getLong(SessionID sessionID, String key) throws ConfigError, FieldCo
}
}
+ /**
+ * Get an existing settings value as a long if present or use default value.
+ */
+ public long getLongOrDefault(SessionID sessionID, String key, long defaultValue) throws ConfigError, FieldConvertError {
+ return isSetting(sessionID, key) ? getLong(sessionID, key) : defaultValue;
+ }
+
/**
* Gets an int from the default section of settings.
*
@@ -294,6 +322,13 @@ public int getInt(String key) throws ConfigError, FieldConvertError {
return getInt(DEFAULT_SESSION_ID, key);
}
+ /**
+ * Gets an int from the default section of settings if present or use default value.
+ */
+ public int getIntOrDefault(String key, int defaultValue) throws ConfigError, FieldConvertError {
+ return isSetting(key) ? getInt(key) : defaultValue;
+ }
+
/**
* Get a settings value as an integer.
*
@@ -311,6 +346,13 @@ public int getInt(SessionID sessionID, String key) throws ConfigError, FieldConv
}
}
+ /**
+ * Get an existing settings value as an integer if present or use default value.
+ */
+ public int getIntOrDefault(SessionID sessionID, String key, int defaultValue) throws ConfigError, FieldConvertError {
+ return isSetting(sessionID, key) ? getInt(sessionID, key) : defaultValue;
+ }
+
private Properties getOrCreateSessionProperties(SessionID sessionID) {
return sections.computeIfAbsent(sessionID, k -> new Properties(sections.get(DEFAULT_SESSION_ID)));
}
diff --git a/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java b/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java
index 9c1d5147b2..189f9e1449 100644
--- a/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java
+++ b/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java
@@ -127,16 +127,17 @@ private void dropTable(String tableName) throws SQLException {
private void setUpJdbcLog(boolean filterHeartbeats, DataSource dataSource) throws ClassNotFoundException, SQLException, ConfigError {
connection = JdbcTestSupport.getConnection();
+ long now = System.currentTimeMillis();
+ sessionID = new SessionID("FIX.4.2", "SENDER-" + now, "TARGET-" + now);
SessionSettings settings = new SessionSettings();
if (filterHeartbeats) {
settings.setBool(JdbcSetting.SETTING_JDBC_LOG_HEARTBEATS, false);
}
+ settings.setString(sessionID, JdbcSetting.SETTING_JDBC_CONNECTION_TEST_QUERY, "SELECT COUNT(1) FROM INFORMATION_SCHEMA.SYSTEM_USERS WHERE 1 = 0;");
JdbcTestSupport.setHypersonicSettings(settings);
initializeTableDefinitions(connection);
logFactory = new JdbcLogFactory(settings);
logFactory.setDataSource(dataSource);
- long now = System.currentTimeMillis();
- sessionID = new SessionID("FIX.4.2", "SENDER-" + now, "TARGET-" + now);
settings.setString(sessionID, "ConnectionType", "acceptor");
log = (JdbcLog) logFactory.create(sessionID);
assertEquals(0, getRowCount(connection, log.getIncomingMessagesTableName()));
diff --git a/quickfixj-core/src/test/java/quickfix/JdbcStoreLegacyTest.java b/quickfixj-core/src/test/java/quickfix/JdbcStoreLegacyTest.java
index 49e1622787..fc38ce7c5d 100644
--- a/quickfixj-core/src/test/java/quickfix/JdbcStoreLegacyTest.java
+++ b/quickfixj-core/src/test/java/quickfix/JdbcStoreLegacyTest.java
@@ -33,7 +33,7 @@ protected void initializeTableDefinitions(String sessionsTableName, String messa
throws ConfigError, SQLException, IOException {
Connection connection = null;
try {
- connection = getDataSource().getConnection();
+ connection = getTestDataSource().getConnection();
JdbcTestSupport.loadSQL(connection,
"config/sql/hsqldb/messages_table.sql",
new JdbcTestSupport.HypersonicLegacyPreprocessor(messagesTableName));
diff --git a/quickfixj-core/src/test/java/quickfix/JdbcStoreTest.java b/quickfixj-core/src/test/java/quickfix/JdbcStoreTest.java
index 647e595224..b3fe5f93cb 100644
--- a/quickfixj-core/src/test/java/quickfix/JdbcStoreTest.java
+++ b/quickfixj-core/src/test/java/quickfix/JdbcStoreTest.java
@@ -54,7 +54,7 @@ protected void setUp() throws Exception {
}
protected void tearDown() throws Exception {
- assertNoActiveConnections();
+ assertNoActiveConnections(getTestDataSource());
if (initialContextFactory != null) {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
}
@@ -62,7 +62,7 @@ protected void tearDown() throws Exception {
}
private void bindDataSource() throws NamingException {
- new InitialContext().rebind("TestDataSource", getDataSource());
+ new InitialContext().rebind("TestDataSource", getTestDataSource());
}
protected MessageStoreFactory getMessageStoreFactory() throws ConfigError, SQLException,
@@ -91,7 +91,7 @@ private JdbcStoreFactory getMessageStoreFactory(String sessionTableName, String
public void testExplicitDataSource() throws Exception {
// No JNDI data source name is set up here
JdbcStoreFactory factory = new JdbcStoreFactory(new SessionSettings());
- factory.setDataSource(getDataSource());
+ factory.setDataSource(getTestDataSource());
factory.create(new SessionID("FIX4.4", "SENDER", "TARGET"));
}
@@ -124,7 +124,7 @@ protected void initializeTableDefinitions(String sessionsTableName, String messa
throws ConfigError, SQLException, IOException {
Connection connection = null;
try {
- connection = getDataSource().getConnection();
+ connection = getTestDataSource().getConnection();
if (messagesTableName != null) {
dropTable(connection, messagesTableName);
}
@@ -140,8 +140,8 @@ protected void initializeTableDefinitions(String sessionsTableName, String messa
}
}
- protected DataSource getDataSource() {
- return JdbcUtil.getDataSource(HSQL_DRIVER, HSQL_CONNECTION_URL, HSQL_USER, "", true);
+ protected DataSource getTestDataSource() {
+ return JdbcTestSupport.getTestDataSource(HSQL_DRIVER, HSQL_CONNECTION_URL, HSQL_USER, "");
}
public void testCreationTime() throws Exception {
diff --git a/quickfixj-core/src/test/java/quickfix/JdbcTestSupport.java b/quickfixj-core/src/test/java/quickfix/JdbcTestSupport.java
index a21357af25..a2f573f472 100644
--- a/quickfixj-core/src/test/java/quickfix/JdbcTestSupport.java
+++ b/quickfixj-core/src/test/java/quickfix/JdbcTestSupport.java
@@ -19,6 +19,9 @@
package quickfix;
+import com.zaxxer.hikari.HikariDataSource;
+
+import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
@@ -26,13 +29,11 @@
import java.sql.SQLException;
import java.sql.Statement;
-import org.junit.Assert;
-
-import org.logicalcobwebs.proxool.ProxoolException;
-import org.logicalcobwebs.proxool.ProxoolFacade;
-import org.logicalcobwebs.proxool.admin.SnapshotIF;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class JdbcTestSupport {
+
public static final String HSQL_DRIVER = "org.hsqldb.jdbcDriver";
public static final String HSQL_CONNECTION_URL = "jdbc:hsqldb:mem:quickfixj";
public static final String HSQL_USER = "sa";
@@ -102,7 +103,7 @@ public static void dropTable(Connection connection, String tableName) throws SQL
execSQL(connection, "drop table " + tableName + " if exists");
}
- public static void execSQL(Connection connection, String sql) throws SQLException, IOException {
+ public static void execSQL(Connection connection, String sql) throws SQLException {
Statement stmt = connection.createStatement();
stmt.execute(sql);
stmt.close();
@@ -115,12 +116,24 @@ private static String getString(InputStream in) throws IOException {
return new String(b);
}
- static void assertNoActiveConnections() throws ProxoolException {
- for (String alias : ProxoolFacade.getAliases()) {
- SnapshotIF snapshot = ProxoolFacade.getSnapshot(alias, true);
- Assert.assertEquals("unclosed connections: " + alias, 0, snapshot
- .getActiveConnectionCount());
- }
+ static void assertNoActiveConnections(DataSource dataSource) {
+ assertTrue(dataSource instanceof HikariDataSource);
+
+ HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
+ assertEquals("Some connections are still alive", 0, hikariDataSource.getHikariPoolMXBean().getActiveConnections());
}
+ static DataSource getTestDataSource(String jdbcDriver, String connectionURL, String user, String password) {
+ SessionID sessionID = new SessionID("TEST", "", "");
+
+ SessionSettings settings = new SessionSettings();
+ // HSQL doesn't support JDBC4 which means that test query has to be supplied to HikariCP
+ settings.setString(sessionID, JdbcSetting.SETTING_JDBC_CONNECTION_TEST_QUERY, "SELECT COUNT(1) FROM INFORMATION_SCHEMA.SYSTEM_USERS WHERE 1 = 0;");
+
+ try {
+ return JdbcUtil.getOrCreatePooledDataSource(settings, sessionID, jdbcDriver, connectionURL, user, password);
+ } catch (ConfigError | FieldConvertError e) {
+ throw new RuntimeException("Unable to get or create pooled data source", e);
+ }
+ }
}
diff --git a/quickfixj-core/src/test/java/quickfix/SessionSettingsTest.java b/quickfixj-core/src/test/java/quickfix/SessionSettingsTest.java
index c6b5435f3f..2de5d1ad10 100644
--- a/quickfixj-core/src/test/java/quickfix/SessionSettingsTest.java
+++ b/quickfixj-core/src/test/java/quickfix/SessionSettingsTest.java
@@ -232,6 +232,17 @@ public void testDefaultsSet() throws Exception {
assertEquals("bargle", settings.getString("FileStorePath"));
}
+ @Test
+ public void testMissingValues() throws ConfigError, FieldConvertError {
+ final SessionSettings settings = new SessionSettings();
+ assertEquals("1", settings.getStringOrDefault("a", "1"));
+ assertEquals("2", settings.getStringOrDefault("b", "2"));
+ assertEquals(3, settings.getIntOrDefault("c", 3));
+ assertEquals(4, settings.getIntOrDefault("d", 4));
+ assertEquals(5L, settings.getLongOrDefault("e", 5L));
+ assertEquals(6L, settings.getLongOrDefault("f", 6L));
+ }
+
@Test
public void testSpecialCharactersInKeys() throws Exception {
final SessionSettings settings = setUpSession("$$$foo bar.baz@@@=value\n");
diff --git a/quickfixj-core/src/test/java/quickfix/test/acceptance/AcceptanceTestSuite.java b/quickfixj-core/src/test/java/quickfix/test/acceptance/AcceptanceTestSuite.java
index 72317fb7bd..30e3b91461 100644
--- a/quickfixj-core/src/test/java/quickfix/test/acceptance/AcceptanceTestSuite.java
+++ b/quickfixj-core/src/test/java/quickfix/test/acceptance/AcceptanceTestSuite.java
@@ -7,9 +7,6 @@
import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.apache.mina.util.AvailablePortFinder;
-import org.logicalcobwebs.proxool.ProxoolException;
-import org.logicalcobwebs.proxool.ProxoolFacade;
-import org.logicalcobwebs.proxool.admin.SnapshotIF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import quickfix.Session;
@@ -93,20 +90,6 @@ public void run(TestResult result) {
//printDatabasePoolingStatistics();
}
- @SuppressWarnings("unused")
- protected void printDatabasePoolingStatistics() {
- try {
- for (String alias : ProxoolFacade.getAliases()) {
- SnapshotIF snapshot = ProxoolFacade.getSnapshot(alias, true);
- System.out.println("active:" + snapshot.getActiveConnectionCount() + ",max:"
- + snapshot.getMaximumConnectionCount() + ",served:"
- + snapshot.getServedCount());
- }
- } catch (ProxoolException e) {
- e.printStackTrace();
- }
- }
-
private List load(String filename) throws IOException {
ArrayList steps = new ArrayList<>();
log.info("load test: " + filename);