com.tesora.dve.worker.DirectConnectionCache.java Source code

Java tutorial

Introduction

Here is the source code for com.tesora.dve.worker.DirectConnectionCache.java

Source

package com.tesora.dve.worker;

/*
 * #%L
 * Tesora Inc.
 * Database Virtualization Engine
 * %%
 * Copyright (C) 2011 - 2014 Tesora Inc.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 * 
 * 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/>.
 * #L%
 */

import io.netty.channel.EventLoopGroup;

import java.nio.charset.Charset;
import java.sql.SQLException;
import java.util.Map;
import java.util.NoSuchElementException;

import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.log4j.Logger;

import com.tesora.dve.common.catalog.StorageSite;
import com.tesora.dve.concurrent.CompletionHandle;
import com.tesora.dve.db.DBConnection;
import com.tesora.dve.db.mysql.MysqlCommandResultsProcessor;
import com.tesora.dve.db.mysql.MysqlConnection;
import com.tesora.dve.db.mysql.SetVariableSQLBuilder;
import com.tesora.dve.db.mysql.SharedEventLoopHolder;
import com.tesora.dve.db.mysql.*;
import com.tesora.dve.db.mysql.portal.protocol.ClientCapabilities;
import com.tesora.dve.exceptions.PECommunicationsException;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.exceptions.PESQLException;

import java.util.UUID;

/**
 *
 */
public class DirectConnectionCache {
    static Logger logger = Logger.getLogger(DirectConnectionCache.class);

    static boolean suppressConnectionCaching = Boolean.getBoolean("SingleConnection.suppressConnectionCaching")
            || Boolean.getBoolean("DirectConnectionCache.suppressConnectionCaching");

    static final DBConnection.Factory MYSQL_FACTORY = new MysqlConnection.Factory();

    static GenericKeyedObjectPool<DSCacheKey, CachedConnection> connectionCache = new GenericKeyedObjectPool<DSCacheKey, CachedConnection>(
            new DSCacheEntryFactory(), /* maxActive (per key) */ -1, GenericObjectPool.WHEN_EXHAUSTED_BLOCK,
            /* maxWait */ 15000, /* maxIdle */ 2500, /* maxTotal */ -1, /* minIdle */ 0, /* tests */ false, false,
            /* timeBetweenEvictionRunsMillis */ 1000, /* numTestsPerEvictionRun */ 15000,
            /* minEvictableIdleTimeMillis */ 2000, /* testWhileIdle */ false);

    public static CachedConnection checkoutDatasource(EventLoopGroup preferredEventLoop, UserAuthentication auth,
            AdditionalConnectionInfo additionalConnInfo, StorageSite site) throws PESQLException {
        try {
            if (preferredEventLoop == null)
                preferredEventLoop = SharedEventLoopHolder.getLoop();

            DSCacheKey datasourceKey = new DirectConnectionCache.DSCacheKey(preferredEventLoop, auth,
                    additionalConnInfo, site);

            CachedConnection cacheEntry;
            if (DirectConnectionCache.suppressConnectionCaching) {
                cacheEntry = DirectConnectionCache.connect(datasourceKey);
            } else {
                cacheEntry = DirectConnectionCache.connectionCache.borrowObject(datasourceKey);
            }

            return cacheEntry;
        } catch (NoSuchElementException e) {
            if (e.getMessage().matches(".*java.net.ConnectException.*"))
                throw new PECommunicationsException(
                        "Cannot connect to '" + site.getMasterUrl() + "' as user '" + auth.userid + "'", e);

            throw new PESQLException(
                    "Unable to connect to site '" + site.getName() + "' as user '" + auth.userid + "'", e);
        } catch (Exception e) {
            throw new PESQLException(
                    "Unable to connect to site '" + site.getName() + "' as user '" + auth.userid + "'", e);
        }
    }

    public static void returnDatasource(CachedConnection entry) throws PESQLException {
        if (DirectConnectionCache.suppressConnectionCaching || entry == null || entry.hasActiveTransaction()) {
            DirectConnectionCache.discardDatasource(entry);
        } else {
            try {
                DirectConnectionCache.DSCacheKey datasourceKey = entry.cacheKey;

                if (logger.isDebugEnabled())
                    logger.debug("returnDatasource(): suppressCache = "
                            + DirectConnectionCache.suppressConnectionCaching);

                DirectConnectionCache.connectionCache.returnObject(datasourceKey, entry);
            } catch (Exception e) {
                throw new PESQLException(e);
            }
        }
    }

    public static void discardDatasource(CachedConnection entry) throws PESQLException {
        if (entry == null)
            return;

        if (logger.isDebugEnabled())
            logger.debug("discardDatasource()");

        entry.close();
    }

    public static void clearConnectionCache() {
        connectionCache.clear();
    }

    /**
     * @param key
     * @return
     * @throws java.sql.SQLException
     * @throws com.tesora.dve.exceptions.PEException
     */
    static CachedConnection connect(DSCacheKey key) throws SQLException, PEException {

        if (logger.isDebugEnabled())
            logger.debug("Allocating new JDBC connection to " + key.site.getName() + " ==> " + key.toString());

        DBConnection dbConnection = MYSQL_FACTORY.newInstance(key.eventLoop, key.site);
        dbConnection.connect(key.url, key.userId, key.password, key.clientCapabilities);

        return new CachedConnection(key, dbConnection);
    }

    static class CachedConnection implements DBConnection {
        final DSCacheKey cacheKey;
        final DBConnection dbConnection;

        public CachedConnection(DSCacheKey cacheKey, DBConnection dbConnection) {
            this.cacheKey = cacheKey;
            this.dbConnection = dbConnection;
        }

        @Override
        public void connect(String url, String userid, String password, long clientCapabilities)
                throws PEException {
            dbConnection.connect(url, userid, password, clientCapabilities);
        }

        @Override
        public void close() {
            dbConnection.close();
        }

        @Override
        public void start(DevXid xid, CompletionHandle<Boolean> promise) {
            dbConnection.start(xid, promise);
        }

        @Override
        public void end(DevXid xid, CompletionHandle<Boolean> promise) {
            dbConnection.end(xid, promise);
        }

        @Override
        public void prepare(DevXid xid, CompletionHandle<Boolean> promise) {
            dbConnection.prepare(xid, promise);
        }

        @Override
        public void commit(DevXid xid, boolean onePhase, CompletionHandle<Boolean> promise) {
            dbConnection.commit(xid, onePhase, promise);
        }

        @Override
        public void rollback(DevXid xid, CompletionHandle<Boolean> promise) {
            dbConnection.rollback(xid, promise);
        }

        @Override
        public void updateSessionVariables(Map<String, String> desiredVariables, SetVariableSQLBuilder setBuilder,
                CompletionHandle<Boolean> promise) {
            dbConnection.updateSessionVariables(desiredVariables, setBuilder, promise);
        }

        @Override
        public void setCatalog(String databaseName, CompletionHandle<Boolean> promise) {
            dbConnection.setCatalog(databaseName, promise);
        }

        @Deprecated
        @Override
        public void cancel() {
            dbConnection.cancel();
        }

        @Override
        public boolean hasPendingUpdate() {
            return dbConnection.hasPendingUpdate();
        }

        @Override
        public boolean hasActiveTransaction() {
            return dbConnection.hasActiveTransaction();
        }

        @Override
        public int getConnectionId() {
            return dbConnection.getConnectionId();
        }

        @Override
        public String getName() {
            return dbConnection.getName();
        }

        @Override
        public UUID getPhysicalID() {
            return dbConnection.getPhysicalID();
        }

        @Override
        public Charset getTargetCharset() {
            return dbConnection.getTargetCharset();
        }

        @Override
        public StorageSite getStorageSite() {
            return dbConnection.getStorageSite();
        }

        @Override
        public Monitor getMonitor() {
            return dbConnection.getMonitor();
        }

        @Override
        public boolean isOpen() {
            return dbConnection.isOpen();
        }

        @Override
        public boolean isWritable() {
            return dbConnection.isWritable();
        }

        @Override
        public void write(MysqlMessage outboundMessage, MysqlCommandResultsProcessor resultsProcessor) {
            dbConnection.write(outboundMessage, resultsProcessor);
        }

        @Override
        public void writeAndFlush(MysqlMessage outboundMessage, MysqlCommandResultsProcessor resultsProcessor) {
            dbConnection.writeAndFlush(outboundMessage, resultsProcessor);
        }

        @Override
        public CompletionHandle<Boolean> getExceptionDeferringPromise() {
            return dbConnection.getExceptionDeferringPromise();
        }

        @Override
        public Exception getAndClearPendingException() {
            return dbConnection.getAndClearPendingException();
        }

        @Override
        public Charset lookupCurrentConnectionCharset() {
            return dbConnection.lookupCurrentConnectionCharset();
        }
    }

    public static class DSCacheKey {
        final long CLIENT_CAPABILITIES_MASK = ClientCapabilities.DEFAULT_PSITE_CAPABILITIES
                | ClientCapabilities.CLIENT_FOUND_ROWS;
        private final EventLoopGroup eventLoop;
        private long clientCapabilities;
        private final String userId;
        private final String password;
        private final String url;
        private final boolean adminUser;
        private final StorageSite site;

        public DSCacheKey(EventLoopGroup eventLoop, UserAuthentication auth,
                AdditionalConnectionInfo additionalConnInfo, StorageSite site) {
            this.clientCapabilities = additionalConnInfo.getClientCapabilities() & CLIENT_CAPABILITIES_MASK;
            this.eventLoop = eventLoop;
            this.userId = auth.getUserid();
            this.password = auth.getPassword();
            this.url = site.getMasterUrl();
            this.adminUser = auth.isAdminUser();
            this.site = site;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + site.getName().hashCode();
            result = prime * result + ((url == null) ? 0 : url.hashCode());
            result = prime * result + ((userId == null) ? 0 : userId.hashCode());
            result = prime * result + (int) clientCapabilities;
            result = prime * result + eventLoop.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            DSCacheKey other = (DSCacheKey) obj;
            if (!site.getName().equals(other.site.getName()))
                return false;
            if (url == null) {
                if (other.url != null)
                    return false;
            } else if (!url.equals(other.url))
                return false;
            if (userId == null) {
                if (other.userId != null)
                    return false;
            } else if (!userId.equals(other.userId)) {
                return false;
            } else if (clientCapabilities != other.clientCapabilities) {
                return false;
            } else if (eventLoop != other.eventLoop)
                return false;
            return true;
        }

        @Override
        public String toString() {
            return new StringBuffer().append("DSCacheKey(").append(userId).append(adminUser ? "[admin], " : ", ")
                    .append(url).append(",").append(eventLoop).append(")").toString();
        }
    }

    private static class DSCacheEntryFactory extends BaseKeyedPoolableObjectFactory<DSCacheKey, CachedConnection> {
        @Override
        public CachedConnection makeObject(DSCacheKey key) throws Exception {
            return connect(key);
        }

        @Override
        public void activateObject(DSCacheKey key, CachedConnection entry) throws Exception {
            if (logger.isDebugEnabled())
                logger.debug("Re-activating JDBC connection to " + key.site.getName() + " ==> " + key.toString());

        }

        @Override
        public void destroyObject(DSCacheKey key, CachedConnection obj) throws Exception {
            obj.close();
            super.destroyObject(key, obj);
        }

    }
}