com.legstar.pool.manager.ConnectionPool.java Source code

Java tutorial

Introduction

Here is the source code for com.legstar.pool.manager.ConnectionPool.java

Source

/*******************************************************************************
 * Copyright (c) 2010 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.pool.manager;

import java.rmi.server.UID;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

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

import com.legstar.messaging.HostEndpoint;
import com.legstar.messaging.LegStarAddress;
import com.legstar.messaging.LegStarConnection;
import com.legstar.messaging.ConnectionException;
import com.legstar.messaging.RequestException;

/**
 * Implements a pool of available host connections. The objective is to
 * throttle the number of simultaneous connections used to prevent
 * MAXTASK situations on the host. A pool also make it possible to reuse a
 * connection for multiple requests.
 * All requests serviced by a pool must share the same host endpoint
 * and the same user credentials.
 * The connection pool is transport agnostic via an abstract connection
 * factory.
 * 
 */
public class ConnectionPool {

    /** Logger. */
    private final Log _log = LogFactory.getLog(getClass());

    /** Target host endpoint associated with this connection pool. */
    private HostEndpoint _hostEndpoint;

    /** Target address used to connect. */
    private LegStarAddress _address;

    /** Queue of available host connections. */
    private BlockingStack<LegStarConnection> _connections;

    /** Will be true when shutdown is initiated. */
    private boolean _shuttingDown;

    /** Manage idle connections in the pool. */
    private TimerIdleConnectionsPolicy _idleConnectionsPolicy;

    /**
     * Construct a connection pool for an endpoint.
     * The connection pool is organized around a fixed size blocking queue.
     * Upon instantiation, all connections are created but not actually
     * connected to the host.
     * 
     * @param address the target host address
     * @param hostEndpoint the target host endpoint
     * @throws ConnectionPoolException if pool cannot be created
     */
    public ConnectionPool(final LegStarAddress address, final HostEndpoint hostEndpoint)
            throws ConnectionPoolException {

        _address = address;
        _hostEndpoint = hostEndpoint;

        int poolSize = hostEndpoint.getHostConnectionPoolSize();

        /* Create the blocking queue */
        _connections = new BlockingStack<LegStarConnection>(poolSize);

        /*
         * Create all connections with a unique ID each. This ID is used
         * for traceability. Because
         */
        try {
            for (int i = 0; i < poolSize; i++) {
                _connections.add(hostEndpoint.getHostConnectionfactory().createConnection(new UID().toString(),
                        address, hostEndpoint));
            }
        } catch (ConnectionException e) {
            throw new ConnectionPoolException(e);
        }
        _idleConnectionsPolicy = new TimerIdleConnectionsPolicy(this,
                hostEndpoint.getPooledMaxIdleTimeCheckPeriod(), hostEndpoint.getPooledMaxIdleTime());

        _shuttingDown = false;
        _log.info("Pool of size " + poolSize + ", created for endpoint: " + hostEndpoint.toString());
    }

    /**
     * Every time a client needs a connection, he will use this method to
     * checkout a connection which physically removes it from the queue.
     * This request will block if no connections are available from the pool.
     * 
     * @param timeout maximum time (in milliseconds) to wait for a connection
     * @return a pooled connection
     * @throws ConnectionPoolException if no pooled connection can be obtained
     */
    public LegStarConnection take(final long timeout) throws ConnectionPoolException {
        LegStarConnection connection = null;
        if (!_shuttingDown) {
            try {
                connection = _connections.poll(timeout, TimeUnit.MILLISECONDS);
                if (connection == null) {
                    throw new ConnectionPoolException("Timed out waiting for pooled connection.");
                }
                return connection;
            } catch (InterruptedException e) {
                throw new ConnectionPoolException(e);
            }
        } else {
            throw new ConnectionPoolException("Pool is shutting down.");
        }
    }

    /**
     * This method is used to recycle connections into the pool. Normally, only
     * connections which were previously taken from the pool should be returned
     * into it.
     * 
     * @param connection the connection to recycle
     * @throws ConnectionPoolException if pooled connection cannot be recycled
     */
    public void put(final LegStarConnection connection) throws ConnectionPoolException {
        if (!_shuttingDown) {
            try {
                _connections.add(connection);
            } catch (IllegalStateException e) {
                /*
                 * If we fail to return the connection to the pool that should
                 * not prevent further processing but the pool capacity is
                 * actually reduced by one. We should warn the administrator.
                 */
                _log.warn("Connection could not be recycled.");
            }
        } else {
            throw new ConnectionPoolException("Pool is shutting down.");
        }
    }

    /**
     * On an idle system, the connection pool should be full at shutdown time.
     * If it is not, then it means some connections are being used. If it is
     * empty, there might even be requests blocking on the <code>take</code>
     * method.
     */
    public void shutDown() {
        _log.info("Shutting down Pool " + getHostEndpoint().getName());
        _shuttingDown = true;
        _idleConnectionsPolicy.stop();
        if (_connections.remainingCapacity() > 0) {
            if (_connections.size() == 0) {
                _log.warn("Some requests might be waiting for connections.");
            } else {
                _log.warn("There are " + (_connections.remainingCapacity() - _connections.size())
                        + " connections in use.");
            }
        }
        for (LegStarConnection connection : getConnections()) {
            try {
                connection.close();
            } catch (RequestException e) {
                _log.warn("Unable to close a connection " + e.toString());
            }
        }
    }

    /**
     * Close connections which have been opened for too long.
     * This is a no op if the timespan is negative. Observe that a timespan of
     * zero is equivalent to no reusing.
     * 
     * @param maxIdleTime the maximum idle time for a connection
     * @throws ConnectionPoolException if obsolete connections cannot be closed
     */
    public void closeObsoleteConnections(final long maxIdleTime) throws ConnectionPoolException {
        if (maxIdleTime < 0) {
            return;
        }
        long now = System.currentTimeMillis();
        try {
            Iterator<LegStarConnection> iter = _connections.iterator();
            while (iter.hasNext()) {
                LegStarConnection connection = iter.next();
                if (connection.getLastUsedTime() > 0) {
                    if ((now - connection.getLastUsedTime()) > maxIdleTime) {
                        if (_log.isDebugEnabled()) {
                            _log.debug("Closing obsolete Connection:" + connection.getConnectionID()
                                    + ". Has been opened for: " + (now - connection.getLastUsedTime()) + " ms");
                        }
                        /* Make sure there are no concurrent threads. */
                        _connections.lock();
                        try {
                            /*
                             * Iterator does not reflect the content of the
                             * stack
                             * over time so this connection might be gone
                             * already.
                             */
                            if (_connections.contains(connection)) {
                                connection.close();
                            }
                        } finally {
                            _connections.unlock();
                        }
                    }
                }
            }
        } catch (RequestException e) {
            throw new ConnectionPoolException(e);
        }
    }

    /**
     * Retrieve the current set of connections.
     * 
     * @return a list of available connections in the pool
     */
    public List<LegStarConnection> getConnections() {
        return _connections.getElementsList();
    }

    /**
     * @return the target host endpoint associated with this connection pool
     */
    public HostEndpoint getHostEndpoint() {
        return _hostEndpoint;
    }

    /**
     * @return the target address associated with this connection pool
     */
    public LegStarAddress getAddress() {
        return _address;
    }

    /**
     * @return the idle connections policy
     */
    public TimerIdleConnectionsPolicy getIdleConnectionsPolicy() {
        return _idleConnectionsPolicy;
    }

}