/******************************************************************************* * Copyright (c) quickfixengine.org All rights reserved. * * This file is part of the QuickFIX FIX Engine * * This file may be distributed under the terms of the quickfixengine.org * license as defined by quickfixengine.org and appearing in the file * LICENSE included in the packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. * * See http://www.quickfixengine.org/LICENSE for licensing information. * * Contact ask@quickfixengine.org if any conditions of this licensing * are not clear to you. ******************************************************************************/ package quickfix; import org.logicalcobwebs.proxool.ProxoolDataSource; import org.quickfixj.QFJException; import org.slf4j.LoggerFactory; import com.github.lburgazzoli.quickfixj.core.IFIXContext; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; class JdbcUtil { static final String CONNECTION_POOL_ALIAS = "quickfixj"; private static Map<String, ProxoolDataSource> dataSources = new ConcurrentHashMap<String, ProxoolDataSource>(); private static int dataSourceCounter = 1; static DataSource getDataSource(SessionSettings settings, SessionID sessionID) throws ConfigError, FieldConvertError { if (settings.isSetting(JdbcSetting.SETTING_JDBC_DS_NAME)) { String jndiName = settings.getString(JdbcSetting.SETTING_JDBC_DS_NAME); try { return (DataSource) new InitialContext().lookup(jndiName); } catch (NamingException e) { throw new ConfigError(e); } } else { String jdbcDriver = settings.getString(JdbcSetting.SETTING_JDBC_DRIVER); String connectionURL = settings.getString(JdbcSetting.SETTING_JDBC_CONNECTION_URL); String user = settings.getString(JdbcSetting.SETTING_JDBC_USER); String password = settings.getString(JdbcSetting.SETTING_JDBC_PASSWORD); return getDataSource(jdbcDriver, connectionURL, user, password, true); } } /** * This is typically called from a single thread, but just in case we are synchronizing modification * of the cache. The cache itself is thread safe. */ static synchronized DataSource getDataSource(String jdbcDriver, String connectionURL, String user, String password, boolean cache) { String key = jdbcDriver + "#" + connectionURL + "#" + user + "#" + password; ProxoolDataSource ds = cache ? dataSources.get(key) : null; if (ds == null) { ds = new ProxoolDataSource(JdbcUtil.CONNECTION_POOL_ALIAS + "-" + dataSourceCounter++); ds.setDriver(jdbcDriver); ds.setDriverUrl(connectionURL); // Bug in Proxool 0.9RC2. Must set both delegate properties and individual setters. :-( ds.setDelegateProperties("user=" + user + "," + (password != null && !"".equals(password) ? "password=" + password : "")); ds.setUser(user); ds.setPassword(password); // TODO JDBC Make these configurable setMaximumActiveTime(ds, 5000); ds.setMaximumConnectionLifetime(28800000); ds.setMaximumConnectionCount(10); ds.setSimultaneousBuildThrottle(10); if (cache) { dataSources.put(key, ds); } } return ds; } private static void setMaximumActiveTime(ProxoolDataSource ds, long ms) { // This is a hack for Proxool support in Java 4. The Proxool library changed // the argument type for setMaximumActiveTime from int to long. The retrotranslated // library was still referencing the long setter and it wasn't defined in the // Java 4-compatible Proxool library. Therefore, we are using reflection to // workaround the problem until Java 4 support is dropped. // TODO Use normal setter when Java 4 support is dropped. String methodName = "setMaximumActiveTime"; Method setter = null; try { setter = ds.getClass().getMethod(methodName, long.class); } catch (NoSuchMethodException e) { try { setter = ds.getClass().getMethod(methodName, int.class); } catch (NoSuchMethodException e1) { // ignore } } if (setter != null) { try { setter.invoke(ds, (int) ms); } catch (Exception e) { throw new QFJException(e); } } else { LoggerFactory.getLogger(LogUtil.class).warn( "Couldn't set maximum active time on Proxool data source"); } } static void close(IFIXContext context,SessionID sessionID, Connection connection) { if (connection != null) { try { connection.close(); } catch (SQLException e) { LogUtil.logThrowable(context,sessionID, e.getMessage(), e); } } } static void close(IFIXContext context,SessionID sessionID, PreparedStatement statement) { if (statement != null) { try { statement.close(); } catch (SQLException e) { LogUtil.logThrowable(context,sessionID, e.getMessage(), e); } } } static void close(IFIXContext context,SessionID sessionID, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { LogUtil.logThrowable(context,sessionID, e.getMessage(), e); } } } static boolean determineSessionIdSupport(DataSource dataSource, String tableName) throws SQLException { Connection connection = dataSource.getConnection(); try { DatabaseMetaData metaData = connection.getMetaData(); String columnName = "sendersubid"; return isColumn(metaData, tableName.toUpperCase(), columnName.toUpperCase()) || isColumn(metaData, tableName, columnName); } finally { connection.close(); } } private static boolean isColumn(DatabaseMetaData metaData, String tableName, String columnName) throws SQLException { ResultSet columns = metaData.getColumns(null, null, tableName, columnName); try { return columns.next(); } finally { columns.close(); } } static String getIDWhereClause(boolean isExtendedSessionID) { return isExtendedSessionID ? ("beginstring=? and sendercompid=? and sendersubid=? and senderlocid=? and " + "targetcompid=? and targetsubid=? and targetlocid=? and session_qualifier=? ") : "beginstring=? and sendercompid=? and targetcompid=? and session_qualifier=? "; } static String getIDColumns(boolean isExtendedSessionID) { return isExtendedSessionID ? "beginstring,sendercompid,sendersubid,senderlocid,targetcompid,targetsubid,targetlocid,session_qualifier" : "beginstring,sendercompid,targetcompid,session_qualifier"; } static String getIDPlaceholders(boolean isExtendedSessionID) { return isExtendedSessionID ? "?,?,?,?,?,?,?,?" : "?,?,?,?"; } static int setSessionIdParameters(SessionID sessionID, PreparedStatement query, int offset, boolean isExtendedSessionID, String defaultSqlValue) throws SQLException { if (isExtendedSessionID) { query.setString(offset++, getSqlValue(sessionID.getBeginString(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getSenderCompID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getSenderSubID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getSenderLocationID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getTargetCompID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getTargetSubID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getTargetLocationID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getSessionQualifier(), defaultSqlValue)); } else { query.setString(offset++, getSqlValue(sessionID.getBeginString(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getSenderCompID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getTargetCompID(), defaultSqlValue)); query.setString(offset++, getSqlValue(sessionID.getSessionQualifier(), defaultSqlValue)); } return offset; } private static String getSqlValue(String javaValue, String defaultSqlValue) { return !SessionID.NOT_SET.equals(javaValue) ? javaValue : defaultSqlValue; } }