/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.database.connection.service.dbcp;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.database.connection.service.DatabaseConnectionServiceException;
import org.wso2.carbon.database.connection.service.config.DatabaseConnectionPoolConfig;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Implements ConnectionService interface to get connection from a Apache commons DBCP connection pool
*/
public class DBCPDatabaseConnectionService {
private static Log log = LogFactory.getLog(DBCPDatabaseConnectionService.class);
/**
* Map of, DBConnectionURL -> BasicDataSource
*/
private Map<String, BasicDataSource> dataSourceMap = new HashMap<String, BasicDataSource>();
/**
* Initialize Basic Data source
*/
private BasicDataSource dataSource = null;
/**
* Default values are defined, if they are not configured in configuration files
*/
private static final int DEFAULT_MAX_ACTIVE = 40;
private static final int DEFAULT_MAX_WAIT = 1000 * 60;
private static final int DEFAULT_MIN_IDLE = 5;
private static final int DEFAULT_MAX_IDLE = 6;
/**
* Hold the values of created and closed database connections
*/
private static AtomicInteger connectionsCreated = new AtomicInteger();
private static AtomicInteger connectionsClosed = new AtomicInteger();
/**
* This method returns the database connection for given database configuration
* @param databaseConnectionPoolConfig DatabaseConnectionPoolConfig object which has been
* encapsulated the DB configuration data
* @return database connection is return from connection pool
* @throws DatabaseConnectionServiceException DatabaseConnectionServiceException throws when
* db url is null and if database connection can not be created from connection pool
*/
public Connection getDBConnection(DatabaseConnectionPoolConfig databaseConnectionPoolConfig)
throws DatabaseConnectionServiceException {
Connection connection = null;
String databaseUrl = databaseConnectionPoolConfig.getDatabaseUrl();
if(databaseUrl != null && databaseUrl.equals("")) {
if (dataSourceMap.containsKey(databaseConnectionPoolConfig.getDatabaseUrl())) {
dataSource = dataSourceMap.get(databaseConnectionPoolConfig.getDatabaseUrl());
} else {
createDataSource(databaseConnectionPoolConfig);
}
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
log.error("Unable to create database connection ", e);
throw new DatabaseConnectionServiceException("Unable to create database connection ", e);
}
connectionsCreated.incrementAndGet();
} else {
throw new DatabaseConnectionServiceException("Database Url can not be null value");
}
return connection;
}
/**
* This method returns the database connection for given database Url
* @param databaseUrl database url which is
* @return database connection is return from connection pool
* @throws DatabaseConnectionServiceException DatabaseConnectionServiceException throws when
* db url is null and if database connection can not be created from connection pool
*/
public Connection getDBConnection(String databaseUrl)
throws DatabaseConnectionServiceException { // TODO we should decide whether to use this method
Connection connection;
if(databaseUrl != null && databaseUrl.equals("")) {
if (dataSourceMap.containsKey(databaseUrl)) {
dataSource = dataSourceMap.get(databaseUrl);
}
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
log.error("Unable to create database connection ", e);
throw new DatabaseConnectionServiceException("Unable to create database connection ", e);
}
connectionsCreated.incrementAndGet();
} else {
throw new DatabaseConnectionServiceException("Database Url can not be null value");
}
return connection;
}
/**
* This method creates the database connection pool and puts it in to the Map and returns
* @param databaseConnectionPoolConfig DatabaseConnectionPoolConfig object which has been
* encapsulated the DB configuration data
* @return DBCP connection pool is return
*/
private BasicDataSource createDataSource(DatabaseConnectionPoolConfig databaseConnectionPoolConfig) {
String maxActive = databaseConnectionPoolConfig.getMaxActive();
String minIdle = databaseConnectionPoolConfig.getMinIdle();
String maxIdle = databaseConnectionPoolConfig.getMaxIdle();
String maxWait = databaseConnectionPoolConfig.getMaxWait();
dataSource = new BasicDataSource();
dataSource.setDriverClassName(databaseConnectionPoolConfig.getDriverClassName());
dataSource.setUrl(databaseConnectionPoolConfig.getDatabaseUrl());
dataSource.setUsername(databaseConnectionPoolConfig.getUserName());
dataSource.setPassword(databaseConnectionPoolConfig.getPassword());
if(maxActive != null && !maxActive.equals("")){
dataSource.setMaxActive(Integer.parseInt(maxActive));
} else {
dataSource.setMaxActive(DEFAULT_MAX_ACTIVE);
}
if(minIdle != null && !minIdle.equals("")){
dataSource.setMaxActive(Integer.parseInt(minIdle));
} else {
dataSource.setMaxActive(DEFAULT_MIN_IDLE);
}
if(maxIdle != null && !maxIdle.equals("")){
dataSource.setMaxActive(Integer.parseInt(maxIdle));
} else {
dataSource.setMaxActive(DEFAULT_MAX_IDLE);
}
if(maxWait != null && !maxWait.equals("")){
dataSource.setMaxActive(Integer.parseInt(maxWait));
} else {
dataSource.setMaxActive(DEFAULT_MAX_WAIT);
}
dataSourceMap.put(databaseConnectionPoolConfig.getDatabaseUrl(), dataSource);
return dataSource;
}
/**
* This method closes the given database connection
* @param connection database connection
*/
public void closeDBConnection(Connection connection) {
try {
connection.close();
connectionsClosed.incrementAndGet();
} catch (SQLException e) {
log.error("Unable to close data base connection", e);
}
}
/**
* This method closes and releases all idle connections that are
* currently stored in the connection pool associated with this data source
*/
public void closeDatabasePoolConnection() {
if (dataSource != null) {
try {
dataSource.close();
dataSource = null;
} catch (SQLException e) {
log.error("Unable to close data source connection pool ", e);
}
}
}
/**
* Timer task to log the created and closed database connections, used only for debug purposes
*/
public static void logDatabaseConnections() {
final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(10);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
scheduler.shutdownNow();
}
});
Runnable runnable = new Runnable() {
public void run() {
if (log.isDebugEnabled()) {
log.debug("Total Number of Connections Created : " +
connectionsCreated.get());
log.debug("Total Number of Connections Closed : " +
connectionsClosed.get());
}
}
};
scheduler.scheduleAtFixedRate(runnable, 60, 60, TimeUnit.SECONDS);
}
}