it.cnr.icar.eric.server.persistence.rdb.ConnectionPool.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.server.persistence.rdb.ConnectionPool.java

Source

/*
 * ====================================================================
 * This file is part of the ebXML Registry by Icar Cnr v3.2 
 * ("eRICv32" in the following disclaimer).
 *
 * "eRICv32" is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * "eRICv32" 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License Version 3
 * along with "eRICv32".  If not, see <http://www.gnu.org/licenses/>.
 *
 * eRICv32 is a forked, derivative work, based on:
 *    - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard,
 *      which was published under the "freebxml License, Version 1.1";
 *   - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis.
 * 
 * All derivative software changes and additions are made under
 *
 * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it>
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the freebxml Software Foundation.  For more
 * information on the freebxml Software Foundation, please see
 * "http://www.freebxml.org/".
 *
 * This product includes software developed by the Apache Software
 * Foundation (http://www.apache.org/).
 *
 * ====================================================================
 */
package it.cnr.icar.eric.server.persistence.rdb;

import it.cnr.icar.eric.server.util.ServerResourceBundle;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;

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

/*
 *
 *
*/
class ConnectionPool {
    private Log log = LogFactory.getLog(this.getClass());
    @SuppressWarnings("unused")
    private String name;
    private String URL;
    private String user;
    private String password;
    private int transactionIsolation;
    private int initConns;
    private int maxConns;
    private int timeOut;
    private HashMap<Connection, String> checkedOutConnections = new HashMap<Connection, String>();
    private ArrayList<Connection> freeConnections = new ArrayList<Connection>();

    /**
    @param timeOut is the time in seconds after this has been elasped but the
    connection cannot be returned, getConnection() will return SQLException
    */
    public ConnectionPool(String name, String URL, String user, String password, int maxConns, int initConns,
            int timeOut, int transactionIsolation) {
        this.name = name;
        this.URL = URL;
        this.user = user;
        this.password = password;
        this.initConns = initConns;
        this.maxConns = maxConns;
        this.timeOut = (timeOut > 0) ? timeOut : 5;
        this.transactionIsolation = transactionIsolation;

        // initialise the pool
        for (int i = 0; i < initConns; i++) {
            try {
                Connection pc = newConnection();
                freeConnections.add(pc);
            } catch (SQLException e) {
                throw new java.lang.reflect.UndeclaredThrowableException(e,
                        ServerResourceBundle.getInstance().getString("message.noConnectionAvailable",
                                new Object[] { new Integer(freeConnections.size()), new Integer(initConns) }));
            }
        }

        if (log.isInfoEnabled()) {
            log.info(ServerResourceBundle.getInstance().getString("message.DatabaseConnectionPoolingEnabled"));
            log.info(getStats());
        }
    }

    public Connection getConnection(String contextId) throws SQLException {
        try {
            //System.err.println("**********getConnection: contextId=" + contextId + " " + getStats());
            if ((maxConns < initConns) || (maxConns <= 0)) {
                throw new SQLException(
                        ServerResourceBundle.getInstance().getString("message.invalidSizeOfConnectionPool"));
            }

            if ((freeConnections.size() == 0) && (initConns != 0)) {
                // for some reasons the pool cannot be initialised
                throw new SQLException(ServerResourceBundle.getInstance().getString("message.noConnectionAvailable",
                        new Object[] { new Integer(freeConnections.size()), new Integer(initConns) }));
            }

            Connection conn = getConnection(contextId, timeOut * 1000);

            return conn;
        } catch (SQLException e) {
            log.trace(getStats());
            throw e;
        }
    }

    private synchronized Connection getConnection(String contextId, long timeout) throws SQLException {
        // Get a pooled Connection from the cache or a new one.
        // Wait if all are checked out and the max limit has
        // been reached.
        long startTime = System.currentTimeMillis();
        long remaining = timeout;
        Connection conn = null;

        while ((conn = getPooledConnection(contextId)) == null) {
            try {
                wait(remaining);
            } catch (InterruptedException e) {
            }

            remaining = timeout - (System.currentTimeMillis() - startTime);

            if (remaining <= 0) {
                // Timeout has expired
                throw new SQLException(
                        ServerResourceBundle.getInstance().getString("message.databaseConnectionTimedOut"));
            }
        }

        // Check if the Connection is still OK
        if (!isConnectionOK(conn)) {
            // It was bad. Try again with the remaining timeout
            return getConnection(contextId, remaining);
        }

        //Got a good connection
        checkedOutConnections.put(conn, contextId);

        return conn;
    }

    private boolean isConnectionOK(Connection connection) {
        Statement testStmt = null;

        try {
            if (!connection.isClosed()) {
                // Try to createStatement to see if it's really alive
                testStmt = connection.createStatement();
                testStmt.close();
            } else {
                return false;
            }
        } catch (SQLException e) {
            if (testStmt != null) {
                try {
                    testStmt.close();
                } catch (SQLException se) {
                    testStmt = null;
                }
            }

            return false;
        }

        return true;
    }

    private Connection getPooledConnection(String contextId) throws SQLException {
        Connection conn = null;

        if (freeConnections.size() > 0) {
            // Pick the first Connection in the Vector
            // to get round-robin usage
            conn = freeConnections.remove(0);
        } else if (checkedOutConnections.size() < maxConns) {
            conn = newConnection();
        }

        return conn;
    }

    private Connection newConnection() throws SQLException {
        Connection conn = null;

        if ((user == null) || (user.length() == 0)) {
            conn = DriverManager.getConnection(URL);
        } else {
            conn = DriverManager.getConnection(URL, user, password);
        }

        // Set Transaction Isolation and AutoComit
        // WARNING: till present Oracle dirvers (9.2.0.5) do not accept
        // setTransactionIsolation being called after setAutoCommit(false)
        conn.setTransactionIsolation(transactionIsolation);
        conn.setAutoCommit(false);

        return conn;
    }

    public synchronized void freeConnection(Connection conn) throws SQLException {

        @SuppressWarnings("unused")
        String contextId = checkedOutConnections.get(conn);
        checkedOutConnections.remove(conn);
        //System.err.println("    **********freeConnection: contextId=" + contextId + " " + getStats());

        // Put the connection at the end of the Vector
        freeConnections.add(conn);
        notifyAll();
    }

    private String getStats() {
        return "Total connections: " + (freeConnections.size() + checkedOutConnections.size()) + " Available: "
                + freeConnections.size() + " Checked-out: " + checkedOutConnections.size() + " "
                + checkedOutConnections.values().toString();
    }
}