/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.server.storage;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.apache.log4j.Logger;
import fedora.server.DatastoreConfig;
import fedora.server.Module;
import fedora.server.Server;
import fedora.server.errors.ConnectionPoolNotFoundException;
import fedora.server.errors.ModuleInitializationException;
import fedora.server.errors.ModuleShutdownException;
import fedora.server.utilities.DDLConverter;
/**
* Implements <code>ConnectionPoolManager</code> to facilitate obtaining
* database connection pools. This class initializes the connection pools
* specified by parameters in the Fedora <code>fedora.fcfg</code> configuration
* file. The Fedora server must be instantiated in order for this class to
* function properly.
*
* @author Ross Wayland
*/
public class ConnectionPoolManagerImpl
extends Module
implements ConnectionPoolManager {
/** Logger for this class. */
private static final Logger LOG =
Logger.getLogger(ConnectionPoolManagerImpl.class.getName());
private static Hashtable<String, ConnectionPool> h_ConnectionPools =
new Hashtable<String, ConnectionPool>();
private static String defaultPoolName = null;
private String jdbcDriverClass = null;
private String dbUsername = null;
private String dbPassword = null;
private String jdbcURL = null;
private int maxActive = 0;
private int maxIdle = 0;
private long maxWait = 0;
private long minEvictableIdleTimeMillis = 0;
private int minIdle = 0;
private int numTestsPerEvictionRun = 0;
private String validationQuery;
private boolean testOnBorrow = false;
private boolean testOnReturn = false;
private boolean testWhileIdle = false;
private long timeBetweenEvictionRunsMillis = 0;
private byte whenExhaustedAction = 0;
/**
* <p>
* Constructs a new ConnectionPoolManagerImpl
* </p>
*
* @param moduleParameters
* The name/value pair map of module parameters.
* @param server
* The server instance.
* @param role
* The module role name.
* @throws ModuleInitializationException
* If initialization values are invalid or initialization fails for
* some other reason.
*/
public ConnectionPoolManagerImpl(Map<String, String> moduleParameters,
Server server,
String role)
throws ModuleInitializationException {
super(moduleParameters, server, role);
}
/**
* Initializes the Module based on configuration parameters. The
* implementation of this method is dependent on the schema used to define
* the parameter names for the role of
* <code>fedora.server.storage.ConnectionPoolManager</code>.
*
* @throws ModuleInitializationException
* If initialization values are invalid or initialization fails for
* some other reason.
*/
@Override
public void initModule() throws ModuleInitializationException {
try {
Server s_server = getServer();
defaultPoolName = this.getParameter("defaultPoolName");
if (defaultPoolName == null || defaultPoolName.equalsIgnoreCase("")) {
throw new ModuleInitializationException("Default Connection Pool "
+ "Name Not Specified",
getRole());
}
LOG.debug("DefaultPoolName: " + defaultPoolName);
String poolList = this.getParameter("poolNames");
// Pool names should be comma delimited
String[] poolNames = poolList.split(",");
// Initialize each connection pool
for (int i = 0; i < poolNames.length; i++) {
DatastoreConfig config =
s_server.getDatastoreConfig(poolNames[i]);
jdbcDriverClass = config.getParameter("jdbcDriverClass");
dbUsername = config.getParameter("dbUsername");
dbPassword = config.getParameter("dbPassword");
jdbcURL = config.getParameter("jdbcURL");
maxActive =
new Integer(config.getParameter("maxActive"))
.intValue();
maxIdle =
new Integer(config.getParameter("maxIdle")).intValue();
maxWait =
new Integer(config.getParameter("maxWait")).intValue();
minIdle =
new Integer(config.getParameter("minIdle")).intValue();
numTestsPerEvictionRun =
new Integer(config
.getParameter("numTestsPerEvictionRun"))
.intValue();
minEvictableIdleTimeMillis =
new Long(config
.getParameter("minEvictableIdleTimeMillis"))
.longValue();
timeBetweenEvictionRunsMillis =
new Long(config
.getParameter("timeBetweenEvictionRunsMillis"))
.longValue();
validationQuery = config.getParameter("validationQuery");
testOnBorrow =
new Boolean(config.getParameter("testOnBorrow"))
.booleanValue();
testOnReturn =
new Boolean(config.getParameter("testOnReturn"))
.booleanValue();
testWhileIdle =
new Boolean(config.getParameter("testWhileIdle"))
.booleanValue();
whenExhaustedAction =
new Byte(config.getParameter("whenExhaustedAction"))
.byteValue();
if (whenExhaustedAction != 0 && whenExhaustedAction != 1
&& whenExhaustedAction != 2) {
LOG
.debug("Valid values for whenExhaustedAction are: 0 - (fail), 1 - (block), or 2 - (grow)");
throw new ModuleInitializationException("A connection pool could "
+ "not be instantiated. The underlying error was an "
+ "invalid value for the whenExhaustedAction parameter."
+ "Valid values are 0 - (fail), 1 - (block), or 2 - (grow). Value specified"
+ "was \""
+ whenExhaustedAction
+ "\".",
getRole());
}
if (LOG.isDebugEnabled()) {
LOG.debug("poolName[" + i + "] = " + poolNames[i]);
LOG.debug("JDBC driver: " + jdbcDriverClass);
LOG.debug("Database username: " + dbUsername);
LOG.debug("Database password: " + dbPassword);
LOG.debug("JDBC connection URL: " + jdbcURL);
LOG.debug("Maximum active connections: " + maxActive);
LOG.debug("Maximum idle connections: " + maxIdle);
LOG.debug("Maximum wait time: " + maxWait);
LOG.debug("Minimum idle time: " + minIdle);
LOG.debug("Number of tests per eviction run: "
+ numTestsPerEvictionRun);
LOG.debug("Minimum Evictable Idle time: "
+ minEvictableIdleTimeMillis);
LOG.debug("Minimum Evictable Idle time: "
+ timeBetweenEvictionRunsMillis);
LOG.debug("Validation query: " + validationQuery);
LOG.debug("Test on borrow: " + testOnBorrow);
LOG.debug("Test on return: " + testOnReturn);
LOG.debug("Test while idle: " + testWhileIdle);
LOG.debug("whenExhaustedAction: " + whenExhaustedAction);
}
// Treat any parameters whose names start with "connection."
// as connection parameters
Map<String, String> cProps = new HashMap<String, String>();
for (String name : config.getParameters().keySet()) {
if (name.startsWith("connection.")) {
String realName = name.substring(11);
LOG.debug("Connection property " + realName + " = "
+ config.getParameter(name));
cProps.put(realName, config.getParameter(name));
}
}
// If a ddlConverter has been specified for the pool,
// try to instantiate it so the ConnectionPool can use
// it when it provides a TableCreatingConnection.
// If a ddlConverter has been specified, it is assumed
// that a failure to initialize (construct) it should
// trigger a ModuleInitializationException (a fatal startup error).
DDLConverter ddlConverter = null;
String ddlConverterClassName =
getServer().getDatastoreConfig(poolNames[i])
.getParameter("ddlConverter");
if (ddlConverterClassName != null) {
try {
ddlConverter =
(DDLConverter) Class
.forName(ddlConverterClassName)
.newInstance();
} catch (Throwable th) {
throw new ModuleInitializationException("A DDLConverter was "
+ "specified for the pool \""
+ poolNames[i]
+ "\", but it couldn't be instantiated.",
getRole(),
th);
}
}
// Create connection pool
try {
ConnectionPool connectionPool =
new ConnectionPool(jdbcDriverClass,
jdbcURL,
dbUsername,
dbPassword,
ddlConverter,
maxActive,
maxIdle,
maxWait,
minIdle,
minEvictableIdleTimeMillis,
numTestsPerEvictionRun,
timeBetweenEvictionRunsMillis,
validationQuery,
testOnBorrow,
testOnReturn,
testWhileIdle,
whenExhaustedAction);
connectionPool.setConnectionProperties(cProps);
LOG.debug("Initialized Pool: " + connectionPool);
h_ConnectionPools.put(poolNames[i], connectionPool);
LOG.debug("putPoolInHash: " + h_ConnectionPools.size());
} catch (SQLException sqle) {
LOG.error("Unable to initialize connection pool: "
+ poolNames[i] + ": " + sqle.getMessage());
}
}
} catch (Throwable th) {
th.printStackTrace();
throw new ModuleInitializationException("A connection pool could "
+ "not be instantiated. The underlying error was a "
+ th.getClass().getName() + "The message was \""
+ th.getMessage() + "\".", getRole());
}
}
/**
* <p>
* Gets a named connection pool.
* </p>
*
* @param poolName
* The name of the connection pool.
* @return The named connection pool.
* @throws ConnectionPoolNotFoundException
* If the specified connection pool cannot be found.
*/
public ConnectionPool getPool(String poolName)
throws ConnectionPoolNotFoundException {
ConnectionPool connectionPool = null;
try {
if (h_ConnectionPools.containsKey(poolName)) {
connectionPool = h_ConnectionPools.get(poolName);
} else {
// Error: pool was never initialized or name could not be found
throw new ConnectionPoolNotFoundException("Connection pool "
+ "not found: " + poolName);
}
} catch (Throwable th) {
throw new ConnectionPoolNotFoundException("The specified connection "
+ "pool \""
+ poolName
+ "\" could not be found. The underlying "
+ "error was a "
+ th.getClass().getName()
+ "The message was \""
+ th.getMessage() + "\".");
}
return connectionPool;
}
/**
* <p>
* Gets the default Connection Pool. This method overrides <code>
* getPool(String poolName)</code>
* .
* </p>
*
* @return The default connection pool.
* @throws ConnectionPoolNotFoundException
* If the default connection pool cannot be found.
*/
public ConnectionPool getPool() throws ConnectionPoolNotFoundException {
ConnectionPool connectionPool = null;
try {
if (h_ConnectionPools.containsKey(defaultPoolName)) {
connectionPool = h_ConnectionPools.get(defaultPoolName);
} else {
// Error: default pool was never initialized or could not be found
throw new ConnectionPoolNotFoundException("Default connection pool "
+ "not found: " + defaultPoolName);
}
} catch (Throwable th) {
throw new ConnectionPoolNotFoundException("The default connection "
+ "pool \"" + defaultPoolName
+ "\" could not be found. The " + "underlying error was a "
+ th.getClass().getName() + "The message was \""
+ th.getMessage() + "\".");
}
return connectionPool;
}
/**
* <p>
* Closes all connection pools. This method overrides
* <code> shutdownModule()</code> .
* </p>
*
* @throws ModuleShutdownException
* If the close operation for the connection pool(s) fails.
*/
@Override
public void shutdownModule() throws ModuleShutdownException {
for (Map.Entry<String, ConnectionPool> e : h_ConnectionPools.entrySet()) {
e.getValue().close();
}
super.shutdownModule();
}
}