/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.lens.driver.jdbc;
import static org.apache.lens.driver.jdbc.JDBCDriverConfConstants.*;
import static org.apache.lens.driver.jdbc.JDBCDriverConfConstants.ConnectionPoolProperties.*;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.lens.api.util.CommonUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.hadoop.conf.Configuration;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import lombok.extern.slf4j.Slf4j;
/**
* The Class DataSourceConnectionProvider.
*/
@Slf4j
public class DataSourceConnectionProvider implements ConnectionProvider {
/** The data source map. */
private Map<DriverConfig, ComboPooledDataSource> dataSourceMap;
/**
* Instantiates a new data source connection provider.
*/
public DataSourceConnectionProvider() {
dataSourceMap = new HashMap<>();
}
/**
* Gets the driver configfrom conf.
*
* @param conf the conf
* @return the driver configfrom conf
*/
public DriverConfig getDriverConfigfromConf(Configuration conf) {
return new DriverConfig(conf);
}
/**
* The Class DriverConfig.
*/
protected class DriverConfig {
/** The driver class. */
final String driverClass;
/** The jdbc uri. */
final String jdbcURI;
/** The user. */
final String user;
/** The password. */
final String password;
final Properties properties;
/** The has hash code. */
boolean hasHashCode = false;
/** The hash code. */
int hashCode;
/**
* Instantiates a new driver config.
*
* @param conf the configuration
*/
public DriverConfig(Configuration conf) {
this.driverClass = conf.get(JDBC_DRIVER_CLASS);
this.jdbcURI = conf.get(JDBC_DB_URI);
properties = new Properties();
properties.putAll(CommonUtils.parseMapFromString(conf.get(JDBC_CONNECTION_PROPERTIES)));
if (conf.get(JDBC_USER) != null) {
properties.setProperty("user", conf.get(JDBC_USER));
}
if (conf.get(JDBC_PASSWORD) != null) {
properties.setProperty("password", conf.get(JDBC_PASSWORD));
}
this.user = properties.getProperty("user");
this.password = properties.getProperty("password");
// Maximum number of connections allowed in the pool
setConnectionPoolProperties(properties, conf);
}
private void setConnectionPoolProperties(Properties properties, Configuration conf) {
for (JDBCDriverConfConstants.ConnectionPoolProperties property : JDBCDriverConfConstants
.ConnectionPoolProperties.values()) {
if (conf.get(property.getConfigKey()) != null) {
properties.put(property.getPoolProperty(), conf.get(property.getConfigKey()));
} else if (!properties.containsKey(property.getPoolProperty())) {
properties.put(property.getPoolProperty(), Integer.toString(property.getDefaultValue()));
}
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DriverConfig)) {
return false;
}
DriverConfig other = (DriverConfig) obj;
// Handling equals in a proper manner as the fields in the current class
// can be null
return new EqualsBuilder().append(this.driverClass, other.driverClass).append(this.jdbcURI, other.jdbcURI)
.append(this.user, other.user).append(this.password, other.password).isEquals();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
if (!hasHashCode) {
// Handling the hashcode in proper manner as the fields in the current
// class can be null
hashCode = new HashCodeBuilder().append(this.driverClass).append(jdbcURI).append(this.user)
.append(this.password).toHashCode();
hasHashCode = true;
}
return hashCode;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "jdbcDriverClass: " + driverClass + ", uri: " + jdbcURI + ", user: " + user;
}
public String getProperty(String key) {
return properties.getProperty(key);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.lens.driver.jdbc.ConnectionProvider#getConnection(org.apache.hadoop.conf.Configuration)
*/
@Override
public synchronized Connection getConnection(Configuration conf) throws SQLException {
DriverConfig config = getDriverConfigfromConf(conf);
if (!dataSourceMap.containsKey(config)) {
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass(config.driverClass);
} catch (PropertyVetoException e) {
throw new IllegalArgumentException("Unable to set driver class:" + config.driverClass, e);
}
cpds.setJdbcUrl(config.jdbcURI);
cpds.setProperties(config.properties);
cpds.setMaxPoolSize(Integer.parseInt(config.getProperty(JDBC_POOL_MAX_SIZE.getPoolProperty())));
cpds.setMaxIdleTime(Integer.parseInt(config.getProperty(JDBC_POOL_IDLE_TIME.getPoolProperty())));
cpds.setMaxIdleTimeExcessConnections(Integer.parseInt(config.getProperty(JDBC_MAX_IDLE_TIME_EXCESS_CONNECTIONS
.getPoolProperty())));
cpds.setMaxStatementsPerConnection(Integer.parseInt(config.getProperty(JDBC_MAX_STATEMENTS_PER_CONNECTION
.getPoolProperty())));
cpds.setCheckoutTimeout(Integer.parseInt(config.getProperty(JDBC_GET_CONNECTION_TIMEOUT.getPoolProperty())));
dataSourceMap.put(config, cpds);
log.info("Created new datasource for config: {}", config);
}
return dataSourceMap.get(config).getConnection();
}
/*
* (non-Javadoc)
*
* @see java.io.Closeable#close()
*/
@Override
public void close() throws IOException {
for (Map.Entry<DriverConfig, ComboPooledDataSource> entry : dataSourceMap.entrySet()) {
entry.getValue().close();
log.info("Closed datasource: {}", entry.getKey());
}
dataSourceMap.clear();
log.info("Closed datasource connection provider");
}
protected final ComboPooledDataSource getDataSource(Configuration conf) {
return dataSourceMap.get(getDriverConfigfromConf(conf));
}
}