rapture.postgres.connection.cache.ConnectionCacheLoader.java Source code

Java tutorial

Introduction

Here is the source code for rapture.postgres.connection.cache.ConnectionCacheLoader.java

Source

/**
 * Copyright (C) 2011-2015 Incapture Technologies LLC
 *
 * This is an autogenerated license statement. When copyright notices appear below
 * this one that copyright supercedes this statement.
 *
 * Unless required by applicable law or agreed to in writing, software is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied.
 *
 * Unless explicit permission obtained in writing this software cannot be distributed.
 */
package rapture.postgres.connection.cache;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.jdbc.support.DatabaseMetaDataCallback;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;

import rapture.common.connection.ConnectionType;
import rapture.common.connection.PostgresConnectionInfoConfigurer;
import rapture.common.exception.ExceptionToString;
import rapture.common.exception.RaptureExceptionFactory;
import rapture.kernel.ContextFactory;
import rapture.kernel.Kernel;
import rapture.postgres.connection.DataSourceMonitor;
import rapture.repo.jdbc.TransactionAwareDataSource;
import rapture.repo.postgres.PostgresSanitizer;

import com.google.common.cache.CacheLoader;
import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * @author bardhi
 * @since 3/30/15.
 */
public class ConnectionCacheLoader extends CacheLoader<String, ConnectionInfo> {
    private static final Logger log = Logger.getLogger(ConnectionCacheLoader.class);

    public static final Map<String, Integer> DEFAULT_OPTIONS = new HashMap<>();
    static {
        DEFAULT_OPTIONS.put("initialPoolSize", 10);
        DEFAULT_OPTIONS.put("minPoolSize", 10);
        DEFAULT_OPTIONS.put("maxPoolSize", 50);
        DEFAULT_OPTIONS.put("maxIdleTimeExcessConnections", 600);
        DEFAULT_OPTIONS.put("maxStatements", 10);
        DEFAULT_OPTIONS.put("statementCacheNumDeferredCloseThreads", 0);
        DEFAULT_OPTIONS.put("idleConnectionTestPeriod", 600);
    }

    private final DataSourceMonitor monitor;

    public ConnectionCacheLoader(DataSourceMonitor monitor) {
        this.monitor = monitor;
    }

    @Override
    public ConnectionInfo load(String instanceName) throws Exception {
        ConnectionInfo info = new ConnectionInfo();
        TransactionAwareDataSource dataSource = createDataSource(instanceName);
        info.setDataSource(dataSource);
        info.setSanitizer(createSanitizer(dataSource));
        return info;
    }

    private TransactionAwareDataSource createDataSource(String instanceName) {
        rapture.common.ConnectionInfo info = Kernel.getSys()
                .getConnectionInfo(ContextFactory.getKernelUser(), ConnectionType.POSTGRES.toString())
                .get(instanceName);
        if (info == null) {
            throw RaptureExceptionFactory.create("Postgres instance is not defined: " + instanceName);
        }
        log.info("connection info = " + info);
        try {
            return createFromConfig(instanceName, info);
        } catch (PropertyVetoException e) {
            throw RaptureExceptionFactory.create("Connection to Postgres failed: " + ExceptionToString.format(e));
        }
    }

    private static final String DRIVER_CLASS = "org.postgresql.Driver";
    private static final int DEFAULT_CHECKOUT_TIMEOUT = 3000; // milliseconds

    private TransactionAwareDataSource createFromConfig(String instanceName, rapture.common.ConnectionInfo info)
            throws PropertyVetoException {

        String url = PostgresConnectionInfoConfigurer.getUrl(info);
        String user = info.getUsername();
        validateConfig(url, user);
        log.info("Host is " + url);

        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDataSourceName(createDataSourceName(instanceName));
        dataSource.setDriverClass(DRIVER_CLASS); // loads the jdbc driver
        dataSource.setJdbcUrl(url);
        dataSource.setUser(user);
        dataSource.setCheckoutTimeout(DEFAULT_CHECKOUT_TIMEOUT);
        String password = info.getPassword();
        if (!StringUtils.isBlank(password)) {
            dataSource.setPassword(password);
        } else {
            throw RaptureExceptionFactory.create("Password cannot be null!");
        }

        // pool size configuration
        dataSource.setInitialPoolSize(getOptionAsInt(info, "initialPoolSize"));
        dataSource.setMinPoolSize(getOptionAsInt(info, "minPoolSize"));
        dataSource.setMaxPoolSize(getOptionAsInt(info, "maxPoolSize"));
        dataSource.setMaxIdleTimeExcessConnections(getOptionAsInt(info, "maxIdleTimeExcessConnections"));

        // statement size configuration
        dataSource.setMaxStatements(getOptionAsInt(info, "maxStatements"));
        dataSource.setStatementCacheNumDeferredCloseThreads(
                getOptionAsInt(info, "statementCacheNumDeferredCloseThreads"));

        // connection testing
        dataSource.setIdleConnectionTestPeriod(getOptionAsInt(info, "idleConnectionTestPeriod"));
        boolean testConnectionOnCheckin = true;
        if (info.getOption("testConnectionOnCheckin") != null) {
            testConnectionOnCheckin = (boolean) info.getOption("testConnectionOnCheckin");
        }
        dataSource.setTestConnectionOnCheckin(testConnectionOnCheckin);

        monitor.monitor(dataSource);

        try {
            Connection connection = DriverManager.getConnection(url, user, password);
            connection.close();
        } catch (SQLException e) {
            throw RaptureExceptionFactory.create(ExceptionToString.format(e));
        }

        return new TransactionAwareDataSource(dataSource);
    }

    private int getOptionAsInt(rapture.common.ConnectionInfo info, String key) {
        if (info.getOption(key) != null) {
            return (int) info.getOption(key);
        } else {
            return DEFAULT_OPTIONS.get(key);
        }
    }

    private String createDataSourceName(String instanceName) {
        return "Postgres-" + instanceName;
    }

    private void validateConfig(String postgresUrl, String user) {
        if (StringUtils.isBlank(postgresUrl)) {
            throw RaptureExceptionFactory.create("postgres url must be defined in the config");
        } else if (StringUtils.isBlank(user)) {
            throw RaptureExceptionFactory.create("postgres user must be defined in the config");
        }
    }

    public static PostgresSanitizer createSanitizer(DataSource dataSource) {
        DatabaseMetaDataCallback callback = new DatabaseMetaDataCallback() {
            @Override
            public String processMetaData(DatabaseMetaData dbmd) throws SQLException, MetaDataAccessException {
                return dbmd.getIdentifierQuoteString();
            }
        };
        try {
            return new PostgresSanitizer(JdbcUtils.extractDatabaseMetaData(dataSource, callback).toString());
        } catch (MetaDataAccessException e) {
            throw RaptureExceptionFactory.create("Unable to get quote identifier: " + ExceptionToString.format(e));
        }
    }
}