Package com.sun.enterprise.resource.pool

Source Code of com.sun.enterprise.resource.pool.ConnectionPool

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package com.sun.enterprise.resource.pool;

import org.glassfish.resource.common.PoolInfo;
import com.sun.appserv.connectors.internal.spi.BadConnectionEventListener;
import com.sun.enterprise.connectors.ConnectorConnectionPool;
import com.sun.enterprise.connectors.ConnectorRuntime;
import com.sun.enterprise.connectors.service.ConnectorAdminServiceUtils;
import com.sun.enterprise.resource.ResourceHandle;
import com.sun.enterprise.resource.ResourceSpec;
import com.sun.enterprise.resource.ResourceState;
import com.sun.enterprise.resource.listener.PoolLifeCycleListener;
import com.sun.enterprise.resource.allocator.ResourceAllocator;
import com.sun.enterprise.resource.pool.datastructure.DataStructure;
import com.sun.enterprise.resource.pool.datastructure.DataStructureFactory;
import com.sun.enterprise.resource.pool.resizer.Resizer;
import com.sun.enterprise.resource.pool.waitqueue.PoolWaitQueue;
import com.sun.enterprise.resource.pool.waitqueue.PoolWaitQueueFactory;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.transaction.api.JavaEETransaction;
import com.sun.logging.LogDomains;
import com.sun.appserv.connectors.internal.api.PoolingException;

import javax.naming.NamingException;
import javax.resource.ResourceException;
import javax.resource.spi.RetryableUnavailableException;
import javax.transaction.Transaction;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Connection Pool for Connector & JDBC resources<br>
*
* @author Jagadish Ramu
*/
public class ConnectionPool implements ResourcePool, ConnectionLeakListener,
        ResourceHandler, PoolProperties {

    protected final static StringManager localStrings =
            StringManager.getManager(ConnectionPool.class);
    protected final static Logger _logger = LogDomains.getLogger(ConnectionPool.class,LogDomains.RSR_LOGGER);


    //pool life-cycle config properties
    protected int maxPoolSize;          // Max size of the pool
    protected int steadyPoolSize;       // Steady size of the pool
    protected int resizeQuantity;       // used by resizer to downsize the pool
    protected int maxWaitTime;          // The total time a thread is willing to wait for a resource object.
    protected long idletime;            // time (in ms) before destroying a free resource


    //pool config properties
    protected boolean failAllConnections = false;
    protected boolean matchConnections = false;
    protected boolean validation = false;
    protected boolean preferValidateOverRecreate = false;
    // hold on to the resizer task so we can cancel/reschedule it.
    protected Resizer resizerTask;


    protected volatile boolean poolInitialized = false;
    protected Timer timer;

    //advanced pool config properties
    protected boolean connectionCreationRetry_;
    protected int connectionCreationRetryAttempts_;
    protected long conCreationRetryInterval_;
    protected long validateAtmostPeriodInMilliSeconds_;
    protected int maxConnectionUsage_;
    //To validate a Sun RA Pool Connection if it hasnot been validated
    //  in the past x sec. (x=idle-timeout)
    //The property will be set from system property -
    //  com.sun.enterprise.connectors.ValidateAtmostEveryIdleSecs=true
    private boolean validateAtmostEveryIdleSecs = false;

    protected String resourceSelectionStrategyClass;

    protected PoolLifeCycleListener poolLifeCycleListener;

    //Gateway used to control the concurrency within the round-trip of resource access.
    protected ResourceGateway gateway;
    protected String resourceGatewayClass;

    protected ConnectionLeakDetector leakDetector;

    protected DataStructure ds;
    protected String dataStructureType;
    protected String dataStructureParameters;

    protected PoolWaitQueue waitQueue;
    protected PoolWaitQueue reconfigWaitQueue;
    private long reconfigWaitTime ;
    protected String poolWaitQueueClass;

    protected final PoolInfo poolInfo; //poolName

    private PoolTxHelper poolTxHelper;

    // NOTE: This resource allocator may not be the same as the allocator passed in to getResource()
    protected ResourceAllocator allocator;

    private boolean selfManaged_;

    private boolean blocked = false;
   
   
    public ConnectionPool(PoolInfo poolInfo, Hashtable env) throws PoolingException {
        this.poolInfo = poolInfo;
        setPoolConfiguration(env);
        initializePoolDataStructure();
        initializeResourceSelectionStrategy();
        initializePoolWaitQueue();
        poolTxHelper = new PoolTxHelper(this.poolInfo);
        gateway = ResourceGateway.getInstance(resourceGatewayClass);
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Connection Pool : " + this.poolInfo);
        }
    }

    protected void initializePoolWaitQueue() throws PoolingException {
        waitQueue = PoolWaitQueueFactory.createPoolWaitQueue(poolWaitQueueClass);
        reconfigWaitQueue = PoolWaitQueueFactory.createPoolWaitQueue(poolWaitQueueClass);
    }

    protected void initializePoolDataStructure() throws PoolingException {
        ds = DataStructureFactory.getDataStructure(dataStructureType, dataStructureParameters,
                maxPoolSize, this, resourceSelectionStrategyClass);
    }

    protected void initializeResourceSelectionStrategy() {
        //do nothing
    }

    private void setPoolConfiguration(Hashtable env) throws PoolingException {

        ConnectorConnectionPool poolResource = getPoolConfigurationFromJndi(env);
        idletime = Integer.parseInt(poolResource.getIdleTimeoutInSeconds()) * 1000L;
        maxPoolSize = Integer.parseInt(poolResource.getMaxPoolSize());
        steadyPoolSize = Integer.parseInt(poolResource.getSteadyPoolSize());

        if (maxPoolSize < steadyPoolSize) {
            maxPoolSize = steadyPoolSize;
        }
        resizeQuantity = Integer.parseInt(poolResource.getPoolResizeQuantity());

        maxWaitTime = Integer.parseInt(poolResource.getMaxWaitTimeInMillis());
        //Make sure it's not negative.
        if (maxWaitTime < 0) {
            maxWaitTime = 0;
        }

        failAllConnections = poolResource.isFailAllConnections();

        validation = poolResource.isIsConnectionValidationRequired();

        validateAtmostEveryIdleSecs = poolResource.isValidateAtmostEveryIdleSecs();
        dataStructureType = poolResource.getPoolDataStructureType();
        dataStructureParameters = poolResource.getDataStructureParameters();
        poolWaitQueueClass = poolResource.getPoolWaitQueue();
        resourceSelectionStrategyClass = poolResource.getResourceSelectionStrategyClass();
        resourceGatewayClass = poolResource.getResourceGatewayClass();
        reconfigWaitTime = poolResource.getDynamicReconfigWaitTimeout();

        setAdvancedPoolConfiguration(poolResource);
    }

    protected ConnectorConnectionPool getPoolConfigurationFromJndi(Hashtable env) throws PoolingException {
        ConnectorConnectionPool poolResource;
        try {
            String jndiNameOfPool = ConnectorAdminServiceUtils.getReservePrefixedJNDINameForPool(poolInfo);
            poolResource = (ConnectorConnectionPool)
                    ConnectorRuntime.getRuntime().getResourceNamingService().lookup(poolInfo, jndiNameOfPool, env);
        } catch (NamingException ex) {
            throw new PoolingException(ex);
        }
        return poolResource;
    }

    // This method does not need to be synchronized since all caller methods are,
    // but it does not hurt. Just to be safe.
    protected synchronized void initPool(ResourceAllocator allocator)
            throws PoolingException {

        if (poolInitialized) {
            return;
        }

        this.allocator = allocator;

        createResources(this.allocator, steadyPoolSize - ds.getResourcesSize());

        // if the idle time out is 0, then don't schedule the resizer task
        if (idletime > 0) {
            scheduleResizerTask();
        }

        //Need to set the numConnFree of monitoring statistics to the steadyPoolSize
        //as monitoring might be ON during the initialization of pool.
        //Need not worry about the numConnUsed here as it would be initialized to
        //0 automatically.
        if(poolLifeCycleListener != null) {
            poolLifeCycleListener.connectionsFreed(steadyPoolSize);
        }
       
        poolInitialized = true;
    }

    /**
     * Schedules the resizer timer task. If a task is currently scheduled,
     * it would be canceled and a new one is scheduled.
     */
    private void scheduleResizerTask() {
        if (resizerTask != null) {
            //cancel the current task
            resizerTask.cancel();
            resizerTask = null;
        }

        resizerTask = initializeResizer();

        if (timer == null) {
            timer = ConnectorRuntime.getRuntime().getTimer();
        }

        timer.scheduleAtFixedRate(resizerTask, idletime, idletime);
        if (_logger.isLoggable(Level.FINEST)) {
            _logger.finest("scheduled resizer task");
        }
    }

    protected Resizer initializeResizer() {
        return new Resizer(poolInfo, ds, this, this, preferValidateOverRecreate);
    }

    /**
     * add a resource with status busy and not enlisted
     *
     * @param alloc ResourceAllocator
     * @throws PoolingException when unable to add a resource
     */
    public void addResource(ResourceAllocator alloc) throws PoolingException {
        int numResCreated = ds.addResource(alloc, 1);
        if(numResCreated > 0) {
            for(int i=0; i< numResCreated; i++) {
                if(poolLifeCycleListener != null) {
                    poolLifeCycleListener.incrementNumConnFree(false, steadyPoolSize);
                }
            }
        }
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Pool: resource added");
        }
    }

    /**
     * marks resource as free. This method should be used instead of directly calling
     * resoureHandle.getResourceState().setBusy(false)
     * OR
     * getResourceState(resourceHandle).setBusy(false)
     * as this method handles stopping of connection leak tracing
     * If connection leak tracing is enabled, takes care of stopping
     * connection leak tracing
     *
     * @param resourceHandle Resource
     */
    protected void setResourceStateToFree(ResourceHandle resourceHandle) {
        getResourceState(resourceHandle).setBusy(false);
        leakDetector.stopConnectionLeakTracing(resourceHandle, this);
    }

    /**
     * marks resource as busy. This method should be used instead of directly calling
     * resoureHandle.getResourceState().setBusy(true)
     * OR
     * getResourceState(resourceHandle).setBusy(true)
     * as this method handles starting of connection leak tracing
     * If connection leak tracing is enabled, takes care of starting
     * connection leak tracing
     *
     * @param resourceHandle Resource
     */
    protected void setResourceStateToBusy(ResourceHandle resourceHandle) {
        getResourceState(resourceHandle).setBusy(true);
        leakDetector.startConnectionLeakTracing(resourceHandle, this);
    }


    /**
     * returns resource from the pool.
     *
     * @return a free pooled resource object matching the ResourceSpec
     * @throws PoolingException - if any error occurrs
     *                          - or the pool has reached its max size and the
     *                          max-connection-wait-time-in-millis has expired.
     */
    public ResourceHandle getResource(ResourceSpec spec, ResourceAllocator alloc, Transaction txn)
            throws PoolingException, RetryableUnavailableException  {
        //Note: this method should not be synchronized or the
        //      startTime would be incorrect for threads waiting to enter

       /*
        * Here are all the comments for the method put together for
        * easy reference.
        *  1.
           // - Try to get a free resource. Note: internalGetResource()
           // will create a new resource if none is free and the max has
           // not been reached.
           // - If can't get one, get on the wait queue.
           // - Repeat this until maxWaitTime expires.
           // - If maxWaitTime == 0, repeat indefinitely.

           2.
           //the doFailAllConnectionsProcessing method would already
           //have been invoked by now.
           //We simply go ahead and create a new resource here
           //from the allocator that we have and adjust the resources
           //list accordingly so as to not exceed the maxPoolSize ever
           //(i.e if steadyPoolSize == maxPoolSize )
           ///Also since we are creating the resource out of the allocator
           //that we came into this method with, we need not worry about
           //matching
        */
        ResourceHandle result = null;

        long startTime = System.currentTimeMillis();
        long elapsedWaitTime;
        long remainingWaitTime = 0;

        while (true) {
            if (gateway.allowed()) {
                //See comment #1 above
                JavaEETransaction jtx = ((JavaEETransaction) txn);
                Set resourcesSet = null;
                if(jtx != null){
                  resourcesSet = jtx.getResources(poolInfo);
            }
                //allow when the pool is not blocked or at-least one resource is
                //already obtained in the current transaction.
                if (!blocked || (resourcesSet != null && resourcesSet.size() > 0)) {
                    try {
                        result = internalGetResource(spec, alloc, txn);
                    } finally {
                        gateway.acquiredResource();
                    }
                }
            }
            if (result != null) {
                // got one, return it
                if (poolLifeCycleListener != null) {
                    poolLifeCycleListener.connectionAcquired(result.getId());
                    elapsedWaitTime = System.currentTimeMillis() - startTime;
                    poolLifeCycleListener.connectionRequestServed(elapsedWaitTime);
                    if (_logger.isLoggable( Level.FINE) ) {
                        _logger.log(Level.FINE, "Resource Pool: elapsed time " +
                                "(ms) to get connection for [" + spec + "] : " +
                                elapsedWaitTime);
                    }
                }
                //got one - seems we are not doing validation or matching
                //return it
                break;
            } else {
                // did not get a resource.
                if (maxWaitTime > 0) {
                    elapsedWaitTime = System.currentTimeMillis() - startTime;
                    if (elapsedWaitTime < maxWaitTime) {
                        // time has not expired, determine remaining wait time.
                        remainingWaitTime = maxWaitTime - elapsedWaitTime;
                    } else {
                        if (!blocked) {
                            // wait time has expired
                            if (poolLifeCycleListener != null) {
                                poolLifeCycleListener.connectionTimedOut();
                            }
                            String msg = localStrings.getStringWithDefault(
                                    "poolmgr.no.available.resource",
                                    "No available resource. Wait-time expired.");
                            throw new PoolingException(msg);
                        }
                    }
                }

                if (!blocked) {
                    //add to wait-queue
                    Object waitMonitor = waitQueue.addToQueue();
                    if (poolLifeCycleListener != null) {
                        poolLifeCycleListener.connectionRequestQueued();
                    }
                    synchronized (waitMonitor) {
                        try {
                            logFine("Resource Pool: getting on wait queue");
                            waitMonitor.wait(remainingWaitTime);

                        } catch (InterruptedException ex) {
                            //Could be system shutdown.
                            break;
                        }

                        //try to remove in case that the monitor has timed
                        // out.  We dont expect the queue to grow to great numbers
                        // so the overhead for removing inexistant objects is low.
                        if (_logger.isLoggable(Level.FINE)) {
                            _logger.log(Level.FINE, "removing wait monitor from queue: " + waitMonitor);
                        }
                        if (waitQueue.removeFromQueue(waitMonitor)) {
                            if (poolLifeCycleListener != null) {
                                poolLifeCycleListener.connectionRequestDequeued();
                            }
                        }
                    }
                } else {
                    //add to reconfig-wait-queue
                    Object reconfigWaitMonitor = reconfigWaitQueue.addToQueue();
                    synchronized (reconfigWaitMonitor) {
                        try {
                            if(reconfigWaitTime > 0){
                                if(_logger.isLoggable(Level.FINEST)) {
                                    _logger.finest("[DRC] getting into reconfig wait queue for time ["+reconfigWaitTime+"]");
                                }
                                reconfigWaitMonitor.wait(reconfigWaitTime);
                            }
                        } catch (InterruptedException ex) {
                            //Could be system shutdown.
                            break;
                        }
                        //try to remove in case that the monitor has timed
                        // out.  We don't expect the queue to grow to great numbers
                        // so the overhead for removing inexistent objects is low.
                        if(_logger.isLoggable(Level.FINEST)) {
                            _logger.log(Level.FINEST, "[DRC] removing wait monitor from reconfig-wait-queue: " +
                                reconfigWaitMonitor);
                        }

                        reconfigWaitQueue.removeFromQueue(reconfigWaitMonitor);

                        if(_logger.isLoggable(Level.FINEST)) {
                            _logger.log(Level.FINEST, "[DRC] throwing Retryable-Unavailable-Exception");
                        }
                        RetryableUnavailableException rue = new RetryableUnavailableException("Pool Reconfigured, " +
                                "Connection Factory can retry the lookup");
                        rue.setErrorCode(BadConnectionEventListener.POOL_RECONFIGURED_ERROR_CODE);
                        throw rue;
                    }
                }
            }
        }

        alloc.fillInResourceObjects(result);
        return result;
    }

    /**
     * Overridden in AssocWithThreadResourcePool to fetch the resource
     * cached in the ThreadLocal
     * In ConnectionPool this simply returns null.
     *
     * @param spec  ResourceSpec
     * @param alloc ResourceAllocator to create a resource
     * @param tran  Transaction
     * @return ResourceHandle resource from ThreadLocal
     */
    protected ResourceHandle prefetch(ResourceSpec spec,
                                      ResourceAllocator alloc, Transaction tran) {
        return null;
    }

    protected ResourceHandle internalGetResource(ResourceSpec spec,
                                                 ResourceAllocator alloc,
                                                 Transaction tran) throws PoolingException {
        if (!poolInitialized) {
            initPool(alloc);
        }
        ResourceHandle result;

        result = getResourceFromTransaction(tran, alloc, spec);
        if(result != null){
            return result;
        }
       
        result = prefetch(spec, alloc, tran);
        if (result != null) {
            return result;
        }

        // We didnt get a connection that is already enlisted in the current transaction (if any).
        result = getUnenlistedResource(spec, alloc, tran);
        if (result != null) {
            if (maxConnectionUsage_ > 0) {
                result.incrementUsageCount();
            }
            if (poolLifeCycleListener != null) {
                poolLifeCycleListener.connectionUsed(result.getId());
                //Decrement numConnFree
                poolLifeCycleListener.decrementNumConnFree();
            }
        }
        return result;
    }

    /**
     * Try to get a resource from current transaction if it is shareable<br>
     * @param tran Current Transaction
     * @param alloc ResourceAllocator
     * @param spec ResourceSpec
     * @return result ResourceHandle
     */
    private ResourceHandle getResourceFromTransaction(Transaction tran, ResourceAllocator alloc, ResourceSpec spec) {
        ResourceHandle result = null;
        try {
            //comment-1: sharing is possible only if caller is marked
            //shareable, so abort right here if that's not the case
            if (tran != null && alloc.shareableWithinComponent()) {
                //TODO should be handled by PoolTxHelper
                JavaEETransaction j2eetran = (JavaEETransaction) tran;
                // case 1. look for free and enlisted in same tx
                Set set = j2eetran.getResources(poolInfo);
                if (set != null) {
                    Iterator iter = set.iterator();
                    while (iter.hasNext()) {
                        ResourceHandle h = (ResourceHandle) iter.next();
                        if (h.hasConnectionErrorOccurred()) {
                            iter.remove();
                            continue;
                        }

                        ResourceState state = h.getResourceState();
                        /*
                         * One can share a resource only for the following conditions:
                         * 1. The caller resource is shareable (look at the outermost
                         *    if marked comment-1
                         * 2. The resource enlisted inside the transaction is shareable
                         * 3. We are dealing with XA resources OR
                         *    We are dealing with a non-XA resource that's not in use
                         *    Note that sharing a non-xa resource that's in use involves
                         *    associating physical connections.
                         * 4. The credentials of the resources match
                         */
                        if (h.getResourceAllocator().shareableWithinComponent()) {
                            if (spec.isXA() || poolTxHelper.isNonXAResourceAndFree(j2eetran, h)) {
                                if (matchConnections) {
                                    if (!alloc.matchConnection(h)) {
                                        if (poolLifeCycleListener != null) {
                                            poolLifeCycleListener.connectionNotMatched();
                                        }
                                        continue;
                                    }
                                    if (h.hasConnectionErrorOccurred()) {
                                        if (failAllConnections) {
                                            //if failAllConnections has happened, we flushed the
                                            //pool, so we don't have to do iter.remove else we
                                            //will get a ConncurrentModificationException
                                            result = null;
                                            break;
                                        }
                                        iter.remove();
                                        continue;
                                    }
                                    if (poolLifeCycleListener != null) {
                                        poolLifeCycleListener.connectionMatched();
                                    }
                                }
                                if (state.isFree())
                                    setResourceStateToBusy(h);
                                result = h;
                                break;
                            }
                        }
                    }
                }
            }
        } catch (ClassCastException e) {
            if(_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE, "Pool: getResource : " +
                    "transaction is not JavaEETransaction but a " + tran.getClass().getName(), e);
            }
        }
        return result;
    }


    /**
     * To provide an unenlisted,  valid, matched resource from pool.
     *
     * @param spec  ResourceSpec
     * @param alloc ResourceAllocator
     * @param tran  Transaction
     * @return ResourceHandle resource from pool
     * @throws PoolingException Exception while getting resource from pool
     */
    protected ResourceHandle getUnenlistedResource(ResourceSpec spec, ResourceAllocator alloc,
                                                   Transaction tran) throws PoolingException {
        return getResourceFromPool(alloc, spec);
    }

    /**
     * Check whether the connection is valid
     *
     * @param h     Resource to be validated
     * @param alloc Allocator to validate the resource
     * @return boolean representing validation result
     */
    protected boolean isConnectionValid(ResourceHandle h, ResourceAllocator alloc) {
        boolean connectionValid = true;

        if (validation || validateAtmostEveryIdleSecs) {
            long validationPeriod;
            //validation period is idle timeout if validateAtmostEveryIdleSecs is set to true
            //else it is validateAtmostPeriodInMilliSeconds_
            if (validation)
                validationPeriod = validateAtmostPeriodInMilliSeconds_;
            else
                validationPeriod = idletime;
            boolean validationRequired = true;
            long currentTime = h.getLastValidated();
            if (validationPeriod > 0) {
                currentTime = System.currentTimeMillis();
                long timeSinceValidation = currentTime - h.getLastValidated();
                if (timeSinceValidation < validationPeriod)
                    validationRequired = false;
            }
            if (validationRequired) {
                if (!alloc.isConnectionValid(h)) {
                    connectionValid = false;
                    incrementNumConnFailedValidation();
                } else {
                    h.setLastValidated(currentTime);
                }
            }
        }
        return connectionValid;
    }

    /**
     * check whether the connection retrieved from the pool matches with the request.
     *
     * @param resource Resource to be matched
     * @param alloc    ResourceAllocator used to match the connection
     * @return boolean representing the match status of the connection
     */
    protected boolean matchConnection(ResourceHandle resource, ResourceAllocator alloc) {
        boolean matched = true;
        if (matchConnections) {
            matched = alloc.matchConnection(resource);
            if (poolLifeCycleListener != null) {
                if (matched) {
                    poolLifeCycleListener.connectionMatched();
                } else {
                    poolLifeCycleListener.connectionNotMatched();
                }
            }
        }
        return matched;
    }

    /**
     * return resource in free list. If none is found, try to scale up the pool/purge pool and <br>
     * return a new resource. returns null if the pool new resources cannot be created. <br>
     *
     * @param alloc ResourceAllocator
     * @return ResourceHandle resource from pool
     * @throws PoolingException if unable to create a new resource
     */
    protected ResourceHandle getResourceFromPool(ResourceAllocator alloc, ResourceSpec spec)
            throws PoolingException {

        // the order of serving a resource request
        // 1. free and enlisted in the same transaction
        // 2. free and unenlisted
        // Do NOT give out a connection that is
        // free and enlisted in a different transaction
        ResourceHandle result = null;

        ResourceHandle h;
        ArrayList<ResourceHandle> freeResources = new ArrayList<ResourceHandle>();
        try{
            while ((h = ds.getResource()) != null) {

                if (h.hasConnectionErrorOccurred()) {
                    ds.removeResource(h);
                    continue;
                }

                if (matchConnection(h, alloc)) {

                    boolean isValid = isConnectionValid(h, alloc);
                    if (h.hasConnectionErrorOccurred() || !isValid) {
                        if (failAllConnections) {
                            h = createSingleResourceAndAdjustPool(alloc, spec);
                            //no need to match since the resource is created with the allocator of caller.
                            break;
                        } else {
                            ds.removeResource(h);
                            //resource is invalid, continue iteration.
                            continue;
                        }
                    }
                    if(h.isShareable() == alloc.shareableWithinComponent()){
                        // got a matched, valid resource
                        result = h;
                        break;
                    }else{
                        freeResources.add(h);
                    }
                } else {
                    freeResources.add(h);
                }
            }
        }finally{
            //return all unmatched, free resources
            for (ResourceHandle freeResource : freeResources) {
                ds.returnResource(freeResource);
            }
            freeResources.clear();
        }

        if (result != null) {
            // set correct state
            setResourceStateToBusy(result);
        } else {
            result = resizePoolAndGetNewResource(alloc);
        }
        return result;
    }

    /**
     * Scale-up the pool to serve the new request. <br>
     * If pool is at max-pool-size and free resources are found, purge unmatched<br>
     * resources, create new connections and serve the request.<br>
     *
     * @param alloc ResourceAllocator used to create new resources
     * @return ResourceHandle newly created resource
     * @throws PoolingException when not able to create resources
     */
    private ResourceHandle resizePoolAndGetNewResource(ResourceAllocator alloc) throws PoolingException {
        //Must be called from the thread holding the lock to this pool.
        ResourceHandle result = null;
        int numOfConnsToCreate = 0;
        if (ds.getResourcesSize() < steadyPoolSize) {
            // May be all invalid resources are destroyed as
            // a result no free resource found and no. of resources is less than steady-pool-size
            numOfConnsToCreate = steadyPoolSize - ds.getResourcesSize();
        } else if (ds.getResourcesSize() + resizeQuantity <= maxPoolSize) {
            //Create and add resources of quantity "resizeQuantity"
            numOfConnsToCreate = resizeQuantity;
        } else if (ds.getResourcesSize() < maxPoolSize) {
            // This else if "test condition" is not needed. Just to be safe.
            // still few more connections (less than "resizeQuantity" and to reach the count of maxPoolSize)
            // can be added
            numOfConnsToCreate = maxPoolSize - ds.getResourcesSize();
        }
        if (numOfConnsToCreate > 0) {
            createResources(alloc, numOfConnsToCreate);
            result = getMatchedResourceFromPool(alloc);
        } else if (ds.getFreeListSize() > 0) {
            //pool cannot create more connections as it is at max-pool-size.
            //If there are free resources at max-pool-size, then none of the free resources
            //has matched this allocator's request (credential). Hence purge free resources
            //of size <=resizeQuantity
            if (purgeResources(resizeQuantity) > 0) {
                result = resizePoolAndGetNewResource(alloc);
            }
        }
        return result;
    }

    //TODO can't this be replaced by getResourceFromPool ?
    private ResourceHandle getMatchedResourceFromPool(ResourceAllocator alloc) {
        ResourceHandle handle;
        ResourceHandle result = null;
        ArrayList<ResourceHandle> activeResources = new ArrayList<ResourceHandle>();

        try{
            while ((handle = ds.getResource()) != null) {
                if (matchConnection(handle, alloc)) {
                    result = handle;
                    setResourceStateToBusy(result);
                    break;
                } else {
                    activeResources.add(handle);
            }
        }
        }finally{
            //return unmatched resources
            for (ResourceHandle activeResource : activeResources) {
                ds.returnResource(activeResource);
            }
            activeResources.clear();
        }
        return result;
    }

    /**
     * Try to purge resources by size <=  quantity <br>
     *
     * @param quantity maximum no. of resources to remove. <br>
     * @return resourceCount No. of resources actually removed. <br>
     */
    private int purgeResources(int quantity) {
        //Must be called from the thread holding the lock to this pool.
        int totalResourcesRemoved = 0;
        int freeResourcesCount = ds.getFreeListSize();
        int resourcesCount = (freeResourcesCount >= quantity) ?
                quantity : freeResourcesCount;
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Purging resources of size : " + resourcesCount);
        }
        for (int i = resourcesCount - 1; i >= 0; i--) {

            ResourceHandle resource = ds.getResource();
            if (resource != null) {
                ds.removeResource(resource);
                totalResourcesRemoved += 1;
            }
        }
        return totalResourcesRemoved;
    }

    /**
     * This method will be called from the getUnenlistedResource method if
     * we detect a failAllConnection flag.
     * Here we simply create a new resource and replace a free resource in
     * the pool by this resource and then give it out.
     * This replacement is required since the steadypoolsize might equal
     * maxpoolsize and in that case if we were not to remove a resource
     * from the pool, our resource would be above maxPoolSize
     *
     * @param alloc ResourceAllocator to create resource
     * @param spec  ResourceSpec
     * @return newly created resource
     * @throws PoolingException when unable to create a resource
     */
    protected ResourceHandle createSingleResourceAndAdjustPool(
            ResourceAllocator alloc, ResourceSpec spec)
            throws PoolingException {

        ResourceHandle handle = ds.getResource();
        if (handle != null) {
            ds.removeResource(handle);
        }

        ResourceHandle result = getNewResource(alloc);
        if (result != null) {
            alloc.fillInResourceObjects(result);
            result.getResourceState().setBusy(true);
        }

        return result;
    }


    /**
     * Method to be used to create resource, instead of calling ResourceAllocator.createConfigBean().
     * This method handles the connection creation retrial in case of failure
     *
     * @param resourceAllocator ResourceAllocator
     * @return ResourceHandle newly created resource
     * @throws PoolingException when unable create a resource
     */
    protected ResourceHandle createSingleResource(ResourceAllocator resourceAllocator) throws PoolingException {
        ResourceHandle resourceHandle;
        int count = 0;
        long startTime = 0;
        while (true) {
            try {
                count++;
                startTime = System.currentTimeMillis();
                resourceHandle = resourceAllocator.createResource();
                if(_logger.isLoggable(Level.FINE)) {
                    _logger.log(Level.FINE, "Time taken to create a single "
                            + "resource : "
                            + resourceHandle.getResourceSpec().getResourceId()
                            + " and adding to the pool (ms) : "
                            + (System.currentTimeMillis() - startTime));
                }
                if (validation || validateAtmostEveryIdleSecs)
                    resourceHandle.setLastValidated(System.currentTimeMillis());
                break;
            } catch (Exception ex) {
                if(_logger.isLoggable(Level.FINE)) {
                    _logger.log(Level.FINE, "Connection creation failed for " + count + " time. It will be retried, "
                        + "if connection creation retrial is enabled.", ex);
                }
                if (!connectionCreationRetry_ || count > connectionCreationRetryAttempts_)
                    throw new PoolingException(ex);
                try {
                    Thread.sleep(conCreationRetryInterval_);
                } catch (InterruptedException ie) {
                    //ignore this exception
                }
            }
        }
        return resourceHandle;
    }

    /**
     * Create specified number of resources.
     *
     * @param alloc ResourceAllocator
     * @param size  number of resources to create.
     * @throws PoolingException When unable to create a resource
     */
    private void createResources(ResourceAllocator alloc, int size) throws PoolingException {
        for (int i = 0; i < size; i++) {
            createResourceAndAddToPool(alloc);
        }
    }


    public void setPoolLifeCycleListener(PoolLifeCycleListener listener) {
        this.poolLifeCycleListener = listener;
    }

    public void removePoolLifeCycleListener() {
        poolLifeCycleListener = null;
    }

    public void deleteResource(ResourceHandle resourceHandle) {
        try {
            resourceHandle.getResourceAllocator().destroyResource(resourceHandle);
        } catch (Exception ex) {
            Object[] args = new Object[] {
                    resourceHandle.getResourceSpec().getPoolInfo(),
                    ex.getMessage() == null ? "" : ex.getMessage() };
            _logger.log(Level.WARNING, "poolmgr.destroy_resource_failed", args);
            if (_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE, "poolmgr.destroy_resource_failed", ex);
            }
        } finally {
            //if connection leak tracing is running on connection being
            //destroyed due to error, then stop it
            if (resourceHandle.getResourceState().isBusy())
                leakDetector.stopConnectionLeakTracing(resourceHandle, this);
            if (poolLifeCycleListener != null) {
                poolLifeCycleListener.connectionDestroyed(resourceHandle.getId());

                if (resourceHandle.getResourceState().isBusy()) {
                    //Destroying a Connection due to error
                    poolLifeCycleListener.decrementConnectionUsed(resourceHandle.getId());
                    if(!resourceHandle.isMarkedForReclaim()) {
                        //If a connection is not reclaimed (in case of a reconfig)
                        //increment numConnFree
                        poolLifeCycleListener.incrementNumConnFree(true, steadyPoolSize);
                    }
                } else {
                    //Destroying a free Connection
                    poolLifeCycleListener.decrementNumConnFree();
                }
            }
        }
    }

    /**
     * this method is called to indicate that the resource is
     * not used by a bean/application anymore
     */
    public void resourceClosed(ResourceHandle h)
            throws IllegalStateException {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Pool: resourceClosed: " + h);
        }

        ResourceState state = getResourceState(h);
        if (state == null) {
            throw new IllegalStateException("State is null");
        }

        if (!state.isBusy()) {
            throw new IllegalStateException("state.isBusy() : false");
        }

        setResourceStateToFree(h)// mark as not busy
        state.touchTimestamp();

        if (state.isUnenlisted() || (poolTxHelper.isNonXAResource(h) &&
                poolTxHelper.isLocalTransactionInProgress()
                && poolTxHelper.isLocalResourceEligibleForReuse(h))) {
            freeUnenlistedResource(h);
        }
       
        if (poolLifeCycleListener != null) {
            poolLifeCycleListener.connectionReleased(h.getId());
        }
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Pool: resourceFreed: " + h);
        }
    }

    /**
     * If the resource is used for <i>maxConnectionUsage</i> times, destroy and create one
     *
     * @param handle Resource to be checked
     */
    protected void performMaxConnectionUsageOperation(ResourceHandle handle) {
        if (handle.getUsageCount() >= maxConnectionUsage_) {
            ds.removeResource(handle);
            _logger.log(Level.INFO, "resource_pool.remove_max_used_conn", handle.getUsageCount());

            //compensate with a new resource only when the pool-size is less than steady-pool-size
            if (ds.getResourcesSize() < steadyPoolSize) {
                try {
                    createResourceAndAddToPool(handle.getResourceAllocator());
                } catch (Exception e) {
                    _logger.log(Level.WARNING, "resource_pool.failed_creating_resource", e);
                }
            }
        }
    }

    protected void freeUnenlistedResource(ResourceHandle h) {
        freeResource(h);
    }

    protected void freeResource(ResourceHandle resourceHandle) {
        if(cleanupResource(resourceHandle)) {
            // Put it back to the free collection.
            ds.returnResource(resourceHandle);
            //update the monitoring data
            if (poolLifeCycleListener != null) {
                poolLifeCycleListener.decrementConnectionUsed(resourceHandle.getId());
                poolLifeCycleListener.incrementNumConnFree(false, steadyPoolSize);
            }
       
            if (maxConnectionUsage_ > 0) {
                performMaxConnectionUsageOperation(resourceHandle);
            }

            //for both the cases of free.add and maxConUsageOperation, a free resource is added.
            // Hence notify waiting threads
            notifyWaitingThreads();
        }
    }
   
    protected boolean cleanupResource(ResourceHandle handle) {
        boolean cleanupSuccessful = true;
        // cleanup resource
        try {
            ResourceAllocator alloc = handle.getResourceAllocator();
            alloc.cleanup(handle);
        } catch (PoolingException ex) {
            Object[] params = new Object[]{poolInfo, ex};
            _logger.log(Level.WARNING, "cleanup.resource.failed", params);
            cleanupSuccessful = false;
            resourceErrorOccurred(handle);
        }
        return cleanupSuccessful;
    }

    public void resourceErrorOccurred(ResourceHandle h)
            throws IllegalStateException {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Pool: resourceErrorOccurred: " + h);
        }

        if (failAllConnections) {
            doFailAllConnectionsProcessing();
            return;
        }

        ResourceState state = getResourceState(h);
        // The reason is that normally connection error is expected
        // to occur only when the connection is in use by the application.
        // When there is connection validation involved, the connection
        // can be checked for validity "before" it is passed to the
        // application i.e. when the resource is still free. Since,
        // the connection error can occur when the resource
        // is free, the following is being commented out.

        /*if (state == null ||
        state.isBusy() == false) {
            throw new IllegalStateException();
        } */

        if (state == null) {
            throw new IllegalStateException();
        }

        // changed order of commands

        //Commenting resources.remove() out since we will call an iter.remove()
        //in the getUnenlistedResource method in the if check after
        //matchManagedConnections or in the internalGetResource method
        //If we were to call remove directly here there is always the danger
        //of a ConcurrentModificationExceptionbeing thrown when we return
        //
        //In case of this method being called asynchronously, since
        //the resource has been marked as "errorOccured", it will get
        //removed in the next iteration of getUnenlistedResource
        //or internalGetResource
        ds.removeResource(h);
    }

    private void doFailAllConnectionsProcessing() {
        logFine("doFailAllConnectionsProcessing entered");
        cancelResizerTask();
        if (poolLifeCycleListener != null) {
            poolLifeCycleListener.connectionValidationFailed(ds.getResourcesSize());
        }
       
        emptyPool();
        try {
            createResources(allocator, steadyPoolSize);
        } catch (PoolingException pe) {
            //Ignore and hope the resizer does its stuff
            logFine("in doFailAllConnectionsProcessing couldn't create steady resources");
        }
        scheduleResizerTask();
        logFine("doFailAllConnectionsProcessing done - created new resources");

    }

    /**
     * this method is called when a resource is enlisted in
     *
     * @param tran     Transaction
     * @param resource ResourceHandle
     */
    public void resourceEnlisted(Transaction tran, ResourceHandle resource)
            throws IllegalStateException {
            poolTxHelper.resourceEnlisted(tran, resource);
    }

    /**
     * this method is called when transaction tran is completed
     *
     * @param tran   Transaction
     * @param status status of transaction
     */
    public void transactionCompleted(Transaction tran, int status) throws IllegalStateException {
        List<ResourceHandle> delistedResources = poolTxHelper.transactionCompleted(tran, status, poolInfo);
        for (ResourceHandle resource : delistedResources) {
            // Application might not have closed the connection.
            if (isResourceUnused(resource)) {
                freeResource(resource);
            }
        }
    }

    protected boolean isResourceUnused(ResourceHandle h) {
        return h.getResourceState().isFree();
    }


    public ResourceHandle createResource(ResourceAllocator alloc) throws PoolingException {
        //NOTE : Pool should not call this method directly, it should be called only by pool-datastructure
        ResourceHandle result = createSingleResource(alloc);

        ResourceState state = new ResourceState();
        state.setBusy(false);
        state.setEnlisted(false);
        result.setResourceState(state);

        if (poolLifeCycleListener != null) {
            poolLifeCycleListener.connectionCreated();
        }
        return result;
    }

    public void createResourceAndAddToPool() throws PoolingException {
        createResourceAndAddToPool(allocator);
    }

    public Set getInvalidConnections(Set connections) throws ResourceException {
        return allocator.getInvalidConnections(connections);
    }

    public void invalidConnectionDetected(ResourceHandle h) {
        incrementNumConnFailedValidation();
    }

    public void resizePool(boolean forced) {
        resizerTask.resizePool(forced);
    }


    protected void notifyWaitingThreads() {
        // notify the first thread in the waitqueue
        Object waitMonitor = null;
        synchronized (waitQueue) {
            if (waitQueue.getQueueLength() > 0) {
                waitMonitor = waitQueue.remove();
                if(poolLifeCycleListener != null) {
                    poolLifeCycleListener.connectionRequestDequeued();
                }
            }
        }
        if (waitMonitor != null) {
            synchronized (waitMonitor) {
                if (_logger.isLoggable(Level.FINE)) {
                    _logger.log(Level.FINE, "Notifying wait monitor : " + waitMonitor.toString());
                }
                waitMonitor.notify();
            }
        } else {
            logFine(" Wait monitor is null");
        }
    }

    private void incrementNumConnFailedValidation() {
        if (poolLifeCycleListener != null) {
            poolLifeCycleListener.connectionValidationFailed(1);
        }
    }

    private ResourceHandle getNewResource(ResourceAllocator alloc) throws PoolingException {
        ds.addResource(alloc, 1);
        return ds.getResource();
    }


    private ResourceState getResourceState(ResourceHandle h) {
        return h.getResourceState();
    }

    public void emptyPool() {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "EmptyPool: Name = " + poolInfo);
        }
        ds.removeAll();
    }

    public void emptyFreeConnectionsInPool() {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Emptying free connections in pool : " + poolInfo);
        }

        ResourceHandle h;
        while ((h = ds.getResource()) != null) {
            ds.removeResource(h);
        }
    }

    public String toString() {
        StringBuffer sb = new StringBuffer("Pool [");
        sb.append(poolInfo);
        sb.append("] PoolSize=");
        sb.append(ds.getResourcesSize());
        sb.append("  FreeResources=");
        sb.append(ds.getFreeListSize());
        sb.append("  QueueSize=");
        sb.append(waitQueue.getQueueLength());
        sb.append(" matching=");
        sb.append((matchConnections ? "on" : "off"));
        sb.append(" validation=");
        sb.append((validation ? "on" : "off"));
        return sb.toString();
    }

    /**
     * @inheritDoc
     */
    public void blockRequests(long waitTimeout){
        blocked = true;
        this.reconfigWaitTime = waitTimeout;
    }

    /**
     * @inheritDoc
     */
    public PoolWaitQueue getPoolWaitQueue(){
        return waitQueue;
    }

    /**
     * @inheritDoc
     */
    public PoolWaitQueue getReconfigWaitQueue(){
        return reconfigWaitQueue;
    }


    public long getReconfigWaitTime() {
        return reconfigWaitTime;
    }

    /**
     * Reinitialize connections established in the connection pool and
     * bring the pool to steady pool size.
     *
     * @throws com.sun.appserv.connectors.internal.api.PoolingException
     */
    public synchronized boolean flushConnectionPool() throws PoolingException {

        logFine("Flush Connection Pool entered");       
       
        if(!poolInitialized) {
            _logger.log(Level.WARNING, "poolmgr.flush_noop_pool_not_initialized", getPoolInfo());
            throw new PoolingException("Flush Connection Pool failed for " +
                    getPoolInfo() +
                    ". Please see server.log for more details.");
        }
       
        try {       
            killExtraResources(ds.getResourcesSize());
            increaseSteadyPoolSize(steadyPoolSize);
        } catch(PoolingException ex) {
            _logger.log(Level.WARNING, "pool.flush_pool_failure",
                    new Object[] {getPoolInfo(), ex.getMessage()});
            throw ex;
        }
        logFine("Flush Connection Pool done");       
           
        return true;
    }
   
    /**
     * Reconfigure the Pool's properties. The reconfigConnectorConnectionPool
     * method in the ConnectorRuntime will use this method (through PoolManager)
     * if it needs to just change pool properties and not recreate the pool
     *
     * @param poolResource - the ConnectorConnectionPool JavaBean that holds
     *                     the new pool properties
     * @throws PoolingException if the pool resizing fails
     */
    public synchronized void reconfigurePool(ConnectorConnectionPool poolResource)
            throws PoolingException {
        int _idleTime = Integer.parseInt(poolResource.getIdleTimeoutInSeconds())
                * 1000;
        if (poolInitialized) {
            if (_idleTime != idletime && _idleTime != 0) {
                scheduleResizerTask();
            }
            if (_idleTime == 0) {
                //resizerTask.cancel();
                cancelResizerTask();
            }
        }
        idletime = _idleTime;

        resizeQuantity = Integer.parseInt(poolResource.getPoolResizeQuantity());

        maxWaitTime = Integer.parseInt(poolResource.getMaxWaitTimeInMillis());
        //Make sure it's not negative.
        if (maxWaitTime < 0) {
            maxWaitTime = 0;
        }

        validation = poolResource.isIsConnectionValidationRequired();
        failAllConnections = poolResource.isFailAllConnections();
        setAdvancedPoolConfiguration(poolResource);

        //Self managed quantities. These are ignored if self management
        //is on
        if (!isSelfManaged()) {
            int _maxPoolSize = Integer.parseInt(poolResource.getMaxPoolSize());
            int oldMaxPoolSize = maxPoolSize;

            if (_maxPoolSize < steadyPoolSize) {
                //should not happen, admin must throw exception when this condition happens.
                //as a precaution set max pool size to steady pool size
                maxPoolSize = steadyPoolSize;
            } else {
                maxPoolSize = _maxPoolSize;
            }

            if (oldMaxPoolSize != maxPoolSize) {
                ds.setMaxSize(maxPoolSize);
            }
            int _steadyPoolSize = Integer.parseInt(poolResource.getSteadyPoolSize());
            int oldSteadyPoolSize = steadyPoolSize;

            if (_steadyPoolSize > maxPoolSize) {
                //should not happen, admin must throw exception when this condition happens.
                //as a precaution set steady pool size to max pool size
                steadyPoolSize = maxPoolSize;
            } else {
                steadyPoolSize = _steadyPoolSize;
            }

            if (poolInitialized) {
                //In this case we need to kill extra connections in the pool
                //For the case where the value is increased, we need not
                //do anything
                //num resources to kill is decided by the resources in the pool.
                //if we have less than current maxPoolSize resources, we need to
                //kill less.
                int toKill = ds.getResourcesSize() - maxPoolSize;

                if (toKill > 0)
                    killExtraResources(toKill);
            }

            if (oldSteadyPoolSize != steadyPoolSize) {
                if (poolInitialized) {
                    if (oldSteadyPoolSize < steadyPoolSize) {
                        increaseSteadyPoolSize(_steadyPoolSize);
                        if (poolLifeCycleListener != null) {
                            poolLifeCycleListener.connectionsFreed(steadyPoolSize);
                        }
                    }
                }
            }
        }
    }

    /**
     * sets advanced pool properties<br>
     * used during pool configuration (initialization) and re-configuration<br>
     *
     * @param poolResource Connector Connection Pool
     */
    private void setAdvancedPoolConfiguration(ConnectorConnectionPool poolResource) {
        matchConnections = poolResource.matchConnections();
        preferValidateOverRecreate = poolResource.isPreferValidateOverRecreate();
        maxConnectionUsage_ = Integer.parseInt(poolResource.getMaxConnectionUsage());
        connectionCreationRetryAttempts_ = Integer.parseInt
                (poolResource.getConCreationRetryAttempts());
        //Converting seconds to milliseconds as TimerTask will take input in milliseconds
        conCreationRetryInterval_ =
                Integer.parseInt(poolResource.getConCreationRetryInterval()) * 1000L;
        connectionCreationRetry_ = connectionCreationRetryAttempts_ > 0;

        validateAtmostPeriodInMilliSeconds_ =
                Integer.parseInt(poolResource.getValidateAtmostOncePeriod()) * 1000L;
        boolean connectionLeakReclaim_ = poolResource.isConnectionReclaim();
        long connectionLeakTimeoutInMilliSeconds_ = Integer.parseInt(
                poolResource.getConnectionLeakTracingTimeout()) * 1000L;

        boolean connectionLeakTracing_ = connectionLeakTimeoutInMilliSeconds_ > 0;
        if (leakDetector == null) {
            leakDetector = new ConnectionLeakDetector(poolInfo, connectionLeakTracing_,
                    connectionLeakTimeoutInMilliSeconds_, connectionLeakReclaim_);
        } else {
            leakDetector.reset(connectionLeakTracing_,
                    connectionLeakTimeoutInMilliSeconds_, connectionLeakReclaim_);
        }
    }

    /**
     * Kill the extra resources.<br>
     * The maxPoolSize being reduced causes this method to
     * be called
     */
    private void killExtraResources(int numToKill) {
        cancelResizerTask();

        ResourceHandle h;
        for (int i = 0; i < numToKill && ((h = ds.getResource()) != null); i++) {
            ds.removeResource(h);
        }
        scheduleResizerTask();
    }

    /*
    * Increase the number of steady resources in the pool
    * if we detect that the steadyPoolSize has been increased
    */
    private void increaseSteadyPoolSize(int newSteadyPoolSize)
            throws PoolingException {
        cancelResizerTask();
        for (int i = ds.getResourcesSize(); i < newSteadyPoolSize; i++) {
            createResourceAndAddToPool(allocator);
        }
        scheduleResizerTask();
    }

    /**
     * @param alloc ResourceAllocator
     * @throws PoolingException when unable to create a resource
     */
    private void createResourceAndAddToPool(ResourceAllocator alloc) throws PoolingException {
        addResource(alloc);
    }

    /**
     * Switch on matching of connections in the pool.
     */
    public void switchOnMatching() {
        matchConnections = true;
    }

    /**
     * query the name of this pool. Required by monitoring
     *
     * @return the name of this pool
     */
    public PoolInfo getPoolInfo() {
        return poolInfo;
    }

    public synchronized void cancelResizerTask() {

        logFine("Cancelling resizer");
        if (resizerTask != null) {
            resizerTask.cancel();
        }
        resizerTask = null;

        if (timer != null) {
            timer.purge();
        }
    }


    /**
     * This method can be used for debugging purposes
     */
    public synchronized void dumpPoolStatus() {
        _logger.log(Level.INFO, "Name of pool :" + poolInfo);
        _logger.log(Level.INFO, "Free connections :" + ds.getFreeListSize());
        _logger.log(Level.INFO, "Total connections :" + ds.getResourcesSize());
        _logger.log(Level.INFO, "Pool's matching is :" + matchConnections);
    }


    private void logFine(String msg) {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine(msg);
        }
    }

    //Self management methods
    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public int getResizeQuantity() {
        return resizeQuantity;
    }

    public long getIdleTimeout() {
        return idletime;
    }

    public int getWaitQueueLength() {
        return waitQueue.getQueueLength();
    }

    public int getSteadyPoolSize() {
        return steadyPoolSize;
    }


    public void setMaxPoolSize(int size) {
        if (size < ds.getResourcesSize()) {
            synchronized (this) {
                int toKill = ds.getResourcesSize() - size;
                if (toKill > 0) {
                    try {
                        killExtraResources(toKill);
                    } catch (Exception re) {
                        //ignore for now
                        if (_logger.isLoggable(Level.FINE)) {
                            _logger.log(Level.FINE, "setMaxPoolSize:: killExtraResources " +
                                    "throws exception: " + re.getMessage());
                        }
                    }
                }
            }
        }
        maxPoolSize = size;
    }

    public void setSteadyPoolSize(int size) {
        steadyPoolSize = size;
    }

    public void setSelfManaged(boolean selfManaged) {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "Setting selfManaged to : " + selfManaged + " in pool : " + poolInfo);
        }
        selfManaged_ = selfManaged;
    }

    protected boolean isSelfManaged() {
        return selfManaged_;
    }

    public void potentialConnectionLeakFound() {
        if (poolLifeCycleListener != null)
            poolLifeCycleListener.foundPotentialConnectionLeak();
    }

    public void printConnectionLeakTrace(StringBuffer stackTrace) {
        if (poolLifeCycleListener != null) {
            String msg = localStrings.getStringWithDefault("monitoring.statistics", "Monitoring Statistics :");
            stackTrace.append("\n");
            stackTrace.append(msg);
            stackTrace.append("\n");
            //TODO : change toString() to a more specific method name
            poolLifeCycleListener.toString(stackTrace);
        }
    }

    public void reclaimConnection(ResourceHandle handle) {
        //all reclaimed connections must be killed instead of returning them
        //to the pool
        //Entity beans when used in bean managed transaction will face an issue
        //since connections are destroyed during reclaim. Stateful session beans
        // will work fine.
        String msg = localStrings.getString("reclaim.leaked.connection", poolInfo);
        _logger.log(Level.INFO, msg);
        ds.removeResource(handle);
        notifyWaitingThreads();

    }

    /**
     * Get Connection Pool status by computing the free/used values of the
     * connections in the pool. Computations are based on whether the pool
     * is initialized or not when this method is invoked.

     * @return PoolStatus object
     */
    public PoolStatus getPoolStatus() {
        PoolStatus poolStatus = new PoolStatus(this.poolInfo);
        int numFree = (this.poolInitialized) ? ds.getFreeListSize() : 0;
        int numUsed = (this.poolInitialized) ? ds.getResourcesSize()-ds.getFreeListSize() : 0;
        poolStatus.setNumConnFree(numFree);
        poolStatus.setNumConnUsed(numUsed);
        return poolStatus;
    }
}
TOP

Related Classes of com.sun.enterprise.resource.pool.ConnectionPool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.