com.sun.enterprise.resource.pool.ConnectionPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of connectors-runtime Show documentation
Show all versions of connectors-runtime Show documentation
Java EE Connector Architecture Runtime for GlassFish
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.enterprise.resource.pool;
import static com.sun.appserv.connectors.internal.spi.BadConnectionEventListener.POOL_RECONFIGURED_ERROR_CODE;
import static com.sun.enterprise.connectors.service.ConnectorAdminServiceUtils.getReservePrefixedJNDINameForPool;
import static java.util.logging.Level.FINE;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.NamingException;
import org.glassfish.resourcebase.resources.api.PoolInfo;
import com.sun.appserv.connectors.internal.api.PoolingException;
import com.sun.enterprise.connectors.ConnectorConnectionPool;
import com.sun.enterprise.connectors.ConnectorRuntime;
import com.sun.enterprise.resource.ResourceHandle;
import com.sun.enterprise.resource.ResourceSpec;
import com.sun.enterprise.resource.ResourceState;
import com.sun.enterprise.resource.allocator.ResourceAllocator;
import com.sun.enterprise.resource.listener.PoolLifeCycleListener;
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.transaction.api.JavaEETransaction;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.logging.LogDomains;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.ManagedConnection;
import jakarta.resource.spi.RetryableUnavailableException;
import jakarta.transaction.Transaction;
/**
* Connection Pool for Connector & JDBC resources
*
* @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 dataStructure;
protected String dataStructureType;
protected String dataStructureParameters;
protected PoolWaitQueue waitQueue;
protected PoolWaitQueue reconfigWaitQueue;
private long reconfigWaitTime;
protected String poolWaitQueueClass;
protected final PoolInfo poolInfo; // poolName
private final 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;
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);
_logger.log(FINE, () -> "Connection Pool : " + this.poolInfo);
}
protected void initializePoolWaitQueue() throws PoolingException {
waitQueue = PoolWaitQueueFactory.createPoolWaitQueue(poolWaitQueueClass);
reconfigWaitQueue = PoolWaitQueueFactory.createPoolWaitQueue(poolWaitQueueClass);
}
protected void initializePoolDataStructure() throws PoolingException {
dataStructure =
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 {
try {
return (ConnectorConnectionPool)
ConnectorRuntime.getRuntime()
.getResourceNamingService()
.lookup(
poolInfo,
getReservePrefixedJNDINameForPool(poolInfo),
env);
} catch (NamingException ex) {
throw new PoolingException(ex);
}
}
// 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 - dataStructure.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);
_logger.finest("scheduled resizer task");
}
protected Resizer initializeResizer() {
return new Resizer(poolInfo, dataStructure, 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 = dataStructure.addResource(alloc, 1);
if (numResCreated > 0) {
for (int i = 0; i < numResCreated; i++) {
if (poolLifeCycleListener != null) {
poolLifeCycleListener.incrementNumConnFree(false, steadyPoolSize);
}
}
}
_logger.log(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.
*/
@Override
public ResourceHandle getResource(ResourceSpec spec, ResourceAllocator alloc, Transaction transaction) 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 javaEETransaction = ((JavaEETransaction) transaction);
Set resourcesSet = null;
if (javaEETransaction != null) {
resourcesSet = javaEETransaction.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, transaction);
} 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(FINE)) {
_logger.log(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();
}
throw new PoolingException(localStrings.getStringWithDefault("poolmgr.no.available.resource",
"No available resource. Wait-time expired."));
}
}
if (!blocked) {
// add to wait-queue
Object waitMonitor = new Object();
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionRequestQueued();
}
synchronized (waitMonitor) {
waitQueue.addToQueue(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 don't expect the queue to grow to great numbers
// so the overhead for removing inexistant objects is low.
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "removing wait monitor from queue: " + waitMonitor);
}
if (waitQueue.removeFromQueue(waitMonitor)) {
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionRequestDequeued();
}
}
}
} else {
// Add to reconfig-wait-queue
Object reconfigWaitMonitor = new Object();
synchronized (reconfigWaitMonitor) {
reconfigWaitQueue.addToQueue(reconfigWaitMonitor);
try {
if (reconfigWaitTime > 0) {
_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);
_logger.log(Level.FINEST, "[DRC] throwing Retryable-Unavailable-Exception");
RetryableUnavailableException rue = new RetryableUnavailableException(
"Pool Reconfigured, " + "Connection Factory can retry the lookup");
rue.setErrorCode(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 resourceSpec, ResourceAllocator resourceAllocator, Transaction transaction) throws PoolingException {
if (!poolInitialized) {
initPool(resourceAllocator);
}
ResourceHandle resourceHandle = getResourceFromTransaction(transaction, resourceAllocator, resourceSpec);
if (resourceHandle != null) {
return resourceHandle;
}
resourceHandle = prefetch(resourceSpec, resourceAllocator, transaction);
if (resourceHandle != null) {
return resourceHandle;
}
// We didnt get a connection that is already enlisted in the current transaction (if any).
resourceHandle = getUnenlistedResource(resourceSpec, resourceAllocator, transaction);
if (resourceHandle != null) {
if (maxConnectionUsage_ > 0) {
resourceHandle.incrementUsageCount();
}
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionUsed(resourceHandle.getId());
// Decrement numConnFree
poolLifeCycleListener.decrementNumConnFree();
}
}
return resourceHandle;
}
/**
* Try to get a resource from current transaction if it is shareable
*
* @param transaction Current Transaction
* @param resourceAllocator ResourceAllocator
* @param resourceSpec ResourceSpec
* @return result ResourceHandle
*/
private ResourceHandle getResourceFromTransaction(Transaction transaction, ResourceAllocator resourceAllocator, ResourceSpec resourceSpec) {
ResourceHandle resourceFromTransaction = null;
try {
// comment-1: sharing is possible only if caller is marked
// shareable, so abort right here if that's not the case
if (transaction != null && resourceAllocator.shareableWithinComponent()) {
// TODO should be handled by PoolTxHelper
JavaEETransaction javaEETransaction = (JavaEETransaction) transaction;
// case 1. look for free and enlisted in same tx
Set set = javaEETransaction.getResources(poolInfo);
if (set != null) {
Iterator iter = set.iterator();
while (iter.hasNext()) {
ResourceHandle resourceHandle = (ResourceHandle) iter.next();
if (resourceHandle.hasConnectionErrorOccurred()) {
iter.remove();
continue;
}
ResourceState state = resourceHandle.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 (resourceHandle.getResourceAllocator().shareableWithinComponent()) {
if (resourceSpec.isXA() || poolTxHelper.isNonXAResourceAndFree(javaEETransaction, resourceHandle)) {
if (matchConnections) {
if (!resourceAllocator.matchConnection(resourceHandle)) {
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionNotMatched();
}
continue;
}
if (resourceHandle.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
resourceFromTransaction = null;
break;
}
iter.remove();
continue;
}
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionMatched();
}
}
if (state.isFree()) {
setResourceStateToBusy(resourceHandle);
}
resourceFromTransaction = resourceHandle;
break;
}
}
}
}
}
} catch (ClassCastException e) {
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "Pool: getResource : " + "transaction is not JavaEETransaction but a " + transaction.getClass().getName(),
e);
}
}
return resourceFromTransaction;
}
/**
* To provide an unenlisted, valid, matched resource from pool.
*
* @param resourceSpec ResourceSpec
* @param resourceAllocator ResourceAllocator
* @param transaction Transaction
* @return ResourceHandle resource from pool
* @throws PoolingException Exception while getting resource from pool
*/
protected ResourceHandle getUnenlistedResource(ResourceSpec resourceSpec, ResourceAllocator resourceAllocator, Transaction transaction) throws PoolingException {
return getResourceFromPool(resourceAllocator, resourceSpec);
}
/**
* Check whether the connection is valid
*
* @param resourceHandle Resource to be validated
* @param resourceAllocator Allocator to validate the resource
* @return boolean representing validation result
*/
protected boolean isConnectionValid(ResourceHandle resourceHandle, ResourceAllocator resourceAllocator) {
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 = resourceHandle.getLastValidated();
if (validationPeriod > 0) {
currentTime = System.currentTimeMillis();
long timeSinceValidation = currentTime - resourceHandle.getLastValidated();
if (timeSinceValidation < validationPeriod) {
validationRequired = false;
}
}
if (validationRequired) {
if (!resourceAllocator.isConnectionValid(resourceHandle)) {
connectionValid = false;
incrementNumConnFailedValidation();
} else {
resourceHandle.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
* return a new resource. returns null if the pool new resources cannot be created.
*
* @param resourceAllocator ResourceAllocator
* @return ResourceHandle resource from pool
* @throws PoolingException if unable to create a new resource
*/
protected ResourceHandle getResourceFromPool(ResourceAllocator resourceAllocator, ResourceSpec resourceSpec) 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 resourceFromPool = null;
ResourceHandle resourceHandle;
List freeResources = new ArrayList<>();
try {
while ((resourceHandle = dataStructure.getResource()) != null) {
if (resourceHandle.hasConnectionErrorOccurred()) {
dataStructure.removeResource(resourceHandle);
continue;
}
if (matchConnection(resourceHandle, resourceAllocator)) {
boolean isValid = isConnectionValid(resourceHandle, resourceAllocator);
if (resourceHandle.hasConnectionErrorOccurred() || !isValid) {
if (failAllConnections) {
resourceFromPool = createSingleResourceAndAdjustPool(resourceAllocator, resourceSpec);
// No need to match since the resource is created with the allocator of caller.
break;
} else {
dataStructure.removeResource(resourceHandle);
// Resource is invalid, continue iteration.
continue;
}
}
if (resourceHandle.isShareable() == resourceAllocator.shareableWithinComponent()) {
// Got a matched, valid resource
resourceFromPool = resourceHandle;
break;
} else {
freeResources.add(resourceHandle);
}
} else {
freeResources.add(resourceHandle);
}
}
} finally {
// Return all unmatched, free resources
for (ResourceHandle freeResource : freeResources) {
dataStructure.returnResource(freeResource);
}
freeResources.clear();
}
if (resourceFromPool != null) {
// Set correct state
setResourceStateToBusy(resourceFromPool);
} else {
resourceFromPool = resizePoolAndGetNewResource(resourceAllocator);
}
return resourceFromPool;
}
/**
* Scale-up the pool to serve the new request.
* If pool is at max-pool-size and free resources are found, purge unmatched
* resources, create new connections and serve the request.
*
* @param resourceAllocator ResourceAllocator used to create new resources
* @return ResourceHandle newly created resource
* @throws PoolingException when not able to create resources
*/
private ResourceHandle resizePoolAndGetNewResource(ResourceAllocator resourceAllocator) throws PoolingException {
// Must be called from the thread holding the lock to this pool.
ResourceHandle newResource = null;
int numOfConnsToCreate = 0;
if (dataStructure.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 - dataStructure.getResourcesSize();
} else if (dataStructure.getResourcesSize() + resizeQuantity <= maxPoolSize) {
// Create and add resources of quantity "resizeQuantity"
numOfConnsToCreate = resizeQuantity;
} else if (dataStructure.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 - dataStructure.getResourcesSize();
}
if (numOfConnsToCreate > 0) {
createResources(resourceAllocator, numOfConnsToCreate);
newResource = getMatchedResourceFromPool(resourceAllocator);
} else if (dataStructure.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) {
newResource = resizePoolAndGetNewResource(resourceAllocator);
}
}
return newResource;
}
// TODO can't this be replaced by getResourceFromPool ?
private ResourceHandle getMatchedResourceFromPool(ResourceAllocator alloc) {
ResourceHandle matchedResourceFromPool = null;
List activeResources = new ArrayList<>();
try {
ResourceHandle handle;
while ((handle = dataStructure.getResource()) != null) {
if (matchConnection(handle, alloc)) {
matchedResourceFromPool = handle;
setResourceStateToBusy(matchedResourceFromPool);
break;
} else {
activeResources.add(handle);
}
}
} finally {
// Return unmatched resources
for (ResourceHandle activeResource : activeResources) {
dataStructure.returnResource(activeResource);
}
activeResources.clear();
}
return matchedResourceFromPool;
}
/**
* Try to purge resources by size <= quantity
*
* @param quantity maximum no. of resources to remove.
* @return resourceCount No. of resources actually removed.
*/
private int purgeResources(int quantity) {
// Must be called from the thread holding the lock to this pool.
int totalResourcesRemoved = 0;
int freeResourcesCount = dataStructure.getFreeListSize();
int resourcesCount = (freeResourcesCount >= quantity) ? quantity : freeResourcesCount;
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "Purging resources of size : " + resourcesCount);
}
for (int i = resourcesCount - 1; i >= 0; i--) {
ResourceHandle resource = dataStructure.getResource();
if (resource != null) {
dataStructure.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 resourceAllocator ResourceAllocator to create resource
* @param resourceSpec ResourceSpec
* @return newly created resource
* @throws PoolingException when unable to create a resource
*/
protected ResourceHandle createSingleResourceAndAdjustPool(ResourceAllocator resourceAllocator, ResourceSpec resourceSpec) throws PoolingException {
ResourceHandle handle = dataStructure.getResource();
if (handle != null) {
dataStructure.removeResource(handle);
}
return getNewResource(resourceAllocator);
}
/**
* 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(FINE)) {
_logger.log(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(FINE)) {
_logger.log(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);
}
}
@Override
public void setPoolLifeCycleListener(PoolLifeCycleListener listener) {
this.poolLifeCycleListener = listener;
}
@Override
public void removePoolLifeCycleListener() {
poolLifeCycleListener = null;
}
@Override
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(FINE)) {
_logger.log(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
*/
@Override
public void resourceClosed(ResourceHandle h) throws IllegalStateException {
if (_logger.isLoggable(FINE)) {
_logger.log(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 && !h.getDestroyByLeakTimeOut()) {
poolLifeCycleListener.connectionReleased(h.getId());
}
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "Pool: resourceFreed: " + h);
}
}
/**
* If the resource is used for maxConnectionUsage times, destroy and create one
*
* @param handle Resource to be checked
*/
protected void performMaxConnectionUsageOperation(ResourceHandle handle) {
dataStructure.removeResource(handle);
_logger.log(Level.INFO, "resource_pool.remove_max_used_conn", new Object[] { handle.getId(), handle.getUsageCount() });
if (poolLifeCycleListener != null) {
poolLifeCycleListener.decrementConnectionUsed(handle.getId());
}
// compensate with a new resource only when the pool-size is less than steady-pool-size
if (dataStructure.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)) {
// Only when resource handle usage count is more than maxConnUsage
if (maxConnectionUsage_ > 0 && resourceHandle.getUsageCount() >= maxConnectionUsage_) {
performMaxConnectionUsageOperation(resourceHandle);
} else {
// Put it back to the free collection.
dataStructure.returnResource(resourceHandle);
// update the monitoring data
if (poolLifeCycleListener != null && !resourceHandle.getDestroyByLeakTimeOut()) {
poolLifeCycleListener.decrementConnectionUsed(resourceHandle.getId());
poolLifeCycleListener.incrementNumConnFree(false, steadyPoolSize);
}
}
// 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;
}
@Override
public void resourceErrorOccurred(ResourceHandle h) throws IllegalStateException {
if (_logger.isLoggable(FINE)) {
_logger.log(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
dataStructure.removeResource(h);
}
private void doFailAllConnectionsProcessing() {
logFine("doFailAllConnectionsProcessing entered");
cancelResizerTask();
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionValidationFailed(dataStructure.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
*/
@Override
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
*/
@Override
public void transactionCompleted(Transaction tran, int status) throws IllegalStateException {
List 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();
}
@Override
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;
}
@Override
public void createResourceAndAddToPool() throws PoolingException {
createResourceAndAddToPool(allocator);
}
@Override
public Set getInvalidConnections(Set connections) throws ResourceException {
return allocator.getInvalidConnections(connections);
}
@Override
public void invalidConnectionDetected(ResourceHandle h) {
incrementNumConnFailedValidation();
}
@Override
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(FINE)) {
_logger.log(FINE, "Notifying wait monitor : " + waitMonitor.toString());
}
waitMonitor.notifyAll();
}
} else {
logFine(" Wait monitor is null");
}
}
private void incrementNumConnFailedValidation() {
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionValidationFailed(1);
}
}
private ResourceHandle getNewResource(ResourceAllocator alloc) throws PoolingException {
addResource(alloc);
return dataStructure.getResource();
}
private ResourceState getResourceState(ResourceHandle h) {
return h.getResourceState();
}
@Override
public void emptyPool() {
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "EmptyPool: Name = " + poolInfo);
}
dataStructure.removeAll();
}
@Override
public void emptyFreeConnectionsInPool() {
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "Emptying free connections in pool : " + poolInfo);
}
ResourceHandle h;
while ((h = dataStructure.getResource()) != null) {
dataStructure.removeResource(h);
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("Pool [");
sb.append(poolInfo);
sb.append("] PoolSize=");
sb.append(dataStructure.getResourcesSize());
sb.append(" FreeResources=");
sb.append(dataStructure.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
*/
@Override
public void blockRequests(long waitTimeout) {
blocked = true;
this.reconfigWaitTime = waitTimeout;
}
/**
* @inheritDoc
*/
@Override
public PoolWaitQueue getPoolWaitQueue() {
return waitQueue;
}
/**
* @inheritDoc
*/
@Override
public PoolWaitQueue getReconfigWaitQueue() {
return reconfigWaitQueue;
}
@Override
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
*/
@Override
public synchronized boolean flushConnectionPool() throws PoolingException {
logFine("Flush Connection Pool entered");
if (!poolInitialized) {
_logger.log(Level.WARNING, "poolmgr.flush_noop_pool_not_initialized", getPoolInfo());
String exString = localStrings.getString("poolmgr.flush_noop_pool_not_initialized", poolInfo.toString());
throw new PoolingException(exString);
}
try {
cancelResizerTask();
dataStructure.removeAll();
scheduleResizerTask();
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
*/
@Override
public synchronized void reconfigurePool(ConnectorConnectionPool poolResource) throws PoolingException {
int _idleTime = Integer.parseInt(poolResource.getIdleTimeoutInSeconds()) * 1000;
if (poolInitialized) {
if (_idleTime != idletime && _idleTime != 0) {
idletime = _idleTime;
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) {
dataStructure.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 = dataStructure.getResourcesSize() - maxPoolSize;
if (toKill > 0) {
killExtraResources(toKill);
}
}
reconfigureSteadyPoolSize(oldSteadyPoolSize, _steadyPoolSize);
}
}
protected void reconfigureSteadyPoolSize(int oldSteadyPoolSize, int newSteadyPoolSize) throws PoolingException {
if (oldSteadyPoolSize != steadyPoolSize) {
if (poolInitialized) {
if (oldSteadyPoolSize < steadyPoolSize) {
increaseSteadyPoolSize(newSteadyPoolSize);
if (poolLifeCycleListener != null) {
poolLifeCycleListener.connectionsFreed(steadyPoolSize);
}
}
}
}
}
/**
* sets advanced pool properties
* used during pool configuration (initialization) and re-configuration
*
* @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.
* 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 = dataStructure.getResource()) != null); i++) {
dataStructure.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 = dataStructure.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.
*/
@Override
public void switchOnMatching() {
matchConnections = true;
}
/**
* query the name of this pool. Required by monitoring
*
* @return the name of this pool
*/
@Override
public PoolInfo getPoolInfo() {
return poolInfo;
}
@Override
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 :" + dataStructure.getFreeListSize());
_logger.log(Level.INFO, "Total connections :" + dataStructure.getResourcesSize());
_logger.log(Level.INFO, "Pool's matching is :" + matchConnections);
}
private void logFine(String msg) {
if (_logger.isLoggable(FINE)) {
_logger.fine(msg);
}
}
// Self management methods
@Override
public int getMaxPoolSize() {
return maxPoolSize;
}
@Override
public int getResizeQuantity() {
return resizeQuantity;
}
@Override
public long getIdleTimeout() {
return idletime;
}
@Override
public int getWaitQueueLength() {
return waitQueue.getQueueLength();
}
@Override
public int getSteadyPoolSize() {
return steadyPoolSize;
}
@Override
public void setMaxPoolSize(int size) {
if (size < dataStructure.getResourcesSize()) {
synchronized (this) {
int toKill = dataStructure.getResourcesSize() - size;
if (toKill > 0) {
try {
killExtraResources(toKill);
} catch (Exception re) {
// ignore for now
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "setMaxPoolSize:: killExtraResources " + "throws exception: " + re.getMessage());
}
}
}
}
}
maxPoolSize = size;
}
@Override
public void setSteadyPoolSize(int size) {
steadyPoolSize = size;
}
@Override
public void setSelfManaged(boolean selfManaged) {
if (_logger.isLoggable(FINE)) {
_logger.log(FINE, "Setting selfManaged to : " + selfManaged + " in pool : " + poolInfo);
}
selfManaged_ = selfManaged;
}
protected boolean isSelfManaged() {
return selfManaged_;
}
@Override
public void potentialConnectionLeakFound() {
if (poolLifeCycleListener != null) {
poolLifeCycleListener.foundPotentialConnectionLeak();
}
}
@Override
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);
}
}
@Override
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);
dataStructure.removeResource(handle);
handle.setDestroyByLeakTimeOut(true);
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
*/
@Override
public PoolStatus getPoolStatus() {
PoolStatus poolStatus = new PoolStatus(this.poolInfo);
int numFree = (this.poolInitialized) ? dataStructure.getFreeListSize() : 0;
int numUsed = (this.poolInitialized) ? dataStructure.getResourcesSize() - dataStructure.getFreeListSize() : 0;
poolStatus.setNumConnFree(numFree);
poolStatus.setNumConnUsed(numUsed);
return poolStatus;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy