org.tdmx.lib.control.datasource.DynamicDataSource.java Source code

Java tutorial

Introduction

Here is the source code for org.tdmx.lib.control.datasource.DynamicDataSource.java

Source

/*
 * TDMX - Trusted Domain Messaging eXchange
 * 
 * Enterprise B2B messaging between separate corporations via interoperable cloud service providers.
 * 
 * Copyright (C) 2014 Peter Klauser (http://tdmx.org)
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses/.
 */

package org.tdmx.lib.control.datasource;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Manages a pooled DataSource per PartitionId. The PartitionId is shared with the DynamicDataSource via ThreadLocal
 * storage since the javax.sql.DataSource does not foresee passing in context.
 * 
 * @author Peter
 * 
 */
public class DynamicDataSource implements javax.sql.DataSource {

    // -------------------------------------------------------------------------
    // PUBLIC CONSTANTS
    // -------------------------------------------------------------------------

    public static final String VALIDATION_PARTITION_ID = "VALIDATION";
    public static final String UNITTEST_PARTITION_ID = "UNITTEST";

    // -------------------------------------------------------------------------
    // PROTECTED AND PRIVATE VARIABLES AND CONSTANTS
    // -------------------------------------------------------------------------

    private static final Log log = LogFactory.getLog(DynamicDataSource.class);

    private DataSourceConfigurationProvider configurationProvider;
    private final Map<String, DatabaseConnectionInfo> partitionConnectionInfoMap = new ConcurrentHashMap<String, DatabaseConnectionInfo>();
    private final Map<DatabaseConnectionInfo, BasicDataSource> connectionDataSourceMap = new ConcurrentHashMap<DatabaseConnectionInfo, BasicDataSource>();

    private PartitionIdProvider partitionIdProvider;

    /**
     * The PrintWriter to which log messages should be directed.
     */
    protected PrintWriter logWriter = new PrintWriter(System.out);

    // -------------------------------------------------------------------------
    // CONSTRUCTORS
    // -------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // PUBLIC METHODS
    // -------------------------------------------------------------------------

    private synchronized void createDataSource(DatabaseConnectionInfo dci) throws SQLException {
        // race condition avoidance
        if (connectionDataSourceMap.get(dci) != null) {
            return;
        }
        BasicDataSource bds = new BasicDataSource();
        bds.setUrl(dci.getUrl());
        bds.setDriverClassName(dci.getDriverClassname());
        bds.setUsername(dci.getUsername());
        bds.setPassword(dci.getPassword());
        bds.setMaxActive(100);
        bds.setMinIdle(0);
        bds.setInitialSize(1);
        bds.setMinEvictableIdleTimeMillis(60000);
        bds.setLogWriter(logWriter);
        connectionDataSourceMap.put(dci, bds);
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (getPartitionIdProvider() == null) {
            throw new SQLException("No partitionIdProvider.");
        }
        String partitionId = getPartitionIdProvider().getPartitionId();
        if (partitionId == null) {
            log.info("Partition defaulting to " + VALIDATION_PARTITION_ID);
            partitionId = VALIDATION_PARTITION_ID;
        }
        // must be fast. Caching at the DatabasePartitionServiceRepositoryImpl supports this.
        DatabaseConnectionInfo ci = getConfigurationProvider().getPartitionInfo(partitionId);
        if (ci == null) {
            throw new SQLException("No DatabaseConnectionInfo provided for partitionId " + partitionId);
        }
        DatabaseConnectionInfo existingCi = partitionConnectionInfoMap.get(partitionId);
        if (existingCi == null) {
            partitionConnectionInfoMap.put(partitionId, ci);
            log.info("First connection use for partitionId " + partitionId);
        } else if (!existingCi.equals(ci)) {
            // there's been a change of DB connection for a partition
            partitionConnectionInfoMap.put(partitionId, ci);
            log.warn("DatabasePartition change for partitionId " + partitionId);
        }
        BasicDataSource bds = connectionDataSourceMap.get(ci);
        if (bds == null) {
            createDataSource(ci);
            bds = connectionDataSourceMap.get(ci);
        }
        if (bds == null) {
            throw new SQLException("Unable to create BasicDataSource for " + ci);
        }
        return bds.getConnection();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        throw new SQLException("DynamicDataSource is not a wrapper.");
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return logWriter;
    }

    @Override
    public void setLogWriter(PrintWriter logWriter) throws SQLException {
        this.logWriter = logWriter;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // This method isn't supported by the PoolingDataSource returned by
        // the createDataSource
        throw new UnsupportedOperationException("Not supported by DynamicDataSource");
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // This method isn't supported by the PoolingDataSource returned by
        // the createDataSource
        throw new UnsupportedOperationException("Not supported by DynamicDataSource");
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        // This method isn't supported by the PoolingDataSource returned by
        // the createDataSource
        throw new UnsupportedOperationException("Not supported by DynamicDataSource");
    }

    // -------------------------------------------------------------------------
    // PROTECTED METHODS
    // -------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // PRIVATE METHODS
    // -------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // PUBLIC ACCESSORS (GETTERS / SETTERS)
    // -------------------------------------------------------------------------

    public DataSourceConfigurationProvider getConfigurationProvider() {
        return configurationProvider;
    }

    public void setConfigurationProvider(DataSourceConfigurationProvider configurationProvider) {
        this.configurationProvider = configurationProvider;
    }

    public PartitionIdProvider getPartitionIdProvider() {
        return partitionIdProvider;
    }

    public void setPartitionIdProvider(PartitionIdProvider partitionIdProvider) {
        this.partitionIdProvider = partitionIdProvider;
    }

}