com.sun.ejb.containers.util.pool.NonBlockingPool Maven / Gradle / Ivy
/*
* Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 1997, 2018 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
*/
/**
*
$Source: /cvs/glassfish/appserv-core/src/java/com/sun/ejb/containers/util/pool/NonBlockingPool.java,v $
* @author $Author: cf126330 $
* @version $Revision: 1.4 $ $Date: 2007/03/30 19:10:26 $
*/
package com.sun.ejb.containers.util.pool;
import static com.sun.enterprise.util.Utility.setContextClassLoader;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.WARNING;
import com.sun.ejb.containers.EJBContextImpl;
import com.sun.ejb.containers.EjbContainerUtilImpl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimerTask;
/**
*
* NonBlockingPool pool provides the basic implementation of an object pool.
*
*
* The implementation uses a linked list to maintain a list of (available) objects. If the pool is empty it simply creates one
* using the ObjectFactory instance.
*
*
* Subclasses can change this behaviour by overriding getObject(...) and returnObject(....) methods. This class provides
* basic support for synchronization, event notification, pool shutdown and pool object recycling. It also does some
* very basic bookkeeping like the number of objects created, number of threads waiting for object.
*
*
* Subclasses can make use of these book-keeping data to provide complex pooling mechanism like LRU / MRU / Random.
* Also, note that AbstractPool does not have a notion of pool limit. It is upto to the derived classes to implement
* these features.
*
*/
public class NonBlockingPool extends AbstractPool {
protected boolean addedResizeTask;
volatile protected boolean addedIdleBeanWork;
protected boolean inResizing;
private String poolName;
private TimerTask poolTimerTask;
private boolean maintainSteadySize;
/**
* If glassfish-ejb-jar.xml singleton-bean-pool is true, steadyPoolSize is 1, and
* maxPoolSize is 1, then this field is set to true, and only 1 bean instance is created. The pool size at any given
* time may be 0 or 1. Both PoolResizeTimerTask and ReSizeWork are skipped.
*/
private boolean singletonBeanPool;
// Set to true after close(). Prevents race condition
// of async resize task kicking in after close().
private boolean poolClosed;
protected NonBlockingPool() {
}
public NonBlockingPool(long beanId, String poolName, ObjectFactory factory, int steadyPoolSize, int resizeQuantity, int maxPoolSize,
int idleTimeoutInSeconds, ClassLoader loader) {
this(beanId, poolName, factory, steadyPoolSize, resizeQuantity, maxPoolSize, idleTimeoutInSeconds, loader, false);
}
public NonBlockingPool(long beanId, String poolName, ObjectFactory factory, int steadyPoolSize, int resizeQuantity, int maxPoolSize,
int idleTimeoutInSeconds, ClassLoader loader, boolean singletonBeanPool) {
this.poolName = poolName;
this.beanId = beanId;
this.singletonBeanPool = singletonBeanPool && (steadyPoolSize == 1) && (maxPoolSize == 1);
initializePool(factory, steadyPoolSize, resizeQuantity, maxPoolSize, idleTimeoutInSeconds, loader);
}
protected void initializePool(ObjectFactory factory, int steadyPoolSize, int resizeQuantity, int maxPoolSize, int idleTimeoutInSeconds, ClassLoader loader) {
pooledObjects = new ArrayList<>();
this.pooledObjectFactory = factory;
this.steadyPoolSize = steadyPoolSize <= 0 ? 0 : steadyPoolSize;
this.resizeQuantity = resizeQuantity <= 0 ? 0 : resizeQuantity;
this.maxPoolSize = maxPoolSize <= 0 ? Integer.MAX_VALUE : maxPoolSize;
this.steadyPoolSize = this.steadyPoolSize > this.maxPoolSize ? this.maxPoolSize : this.steadyPoolSize;
this.idleTimeoutInSeconds = idleTimeoutInSeconds <= 0 || this.singletonBeanPool ? 0 : idleTimeoutInSeconds;
this.containerClassLoader = loader;
this.maintainSteadySize = this.singletonBeanPool ? false : this.steadyPoolSize > 0;
if (this.idleTimeoutInSeconds > 0 && this.resizeQuantity > 0) {
try {
this.poolTimerTask = new PoolResizeTimerTask();
EjbContainerUtilImpl.getInstance()
.getTimer()
.scheduleAtFixedRate(
poolTimerTask, idleTimeoutInSeconds * 1000L, idleTimeoutInSeconds * 1000L);
_logger.log(FINE, () -> "[Pool-" + poolName + "]: Added PoolResizeTimerTask...");
} catch (Throwable th) {
_logger.log(WARNING, "[Pool-" + poolName + "]: Could not add" + " PoolTimerTask. Continuing anyway...", th);
}
}
}
@Override
public void setContainerClassLoader(ClassLoader loader) {
this.containerClassLoader = loader;
}
@Override
public Object getObject(Object param) {
boolean toAddResizeTask = false;
Object pooledObject = null;
synchronized (pooledObjects) {
int size = pooledObjects.size();
if (size > steadyPoolSize) {
poolSuccess++;
return pooledObjects.remove(size - 1);
}
if (size > 0) {
poolSuccess++;
if ((maintainSteadySize) && (addedResizeTask == false)) {
toAddResizeTask = addedResizeTask = true;
pooledObject = pooledObjects.remove(size - 1);
} else {
return pooledObjects.remove(size - 1);
}
} else if (!singletonBeanPool) {
if ((maintainSteadySize) && (addedResizeTask == false)) {
toAddResizeTask = addedResizeTask = true;
}
poolProbeNotifier.ejbObjectAddedEvent(beanId, appName, modName, ejbName);
createdCount++; // hope that everything will be OK.
}
}
if (toAddResizeTask) {
addResizeTaskForImmediateExecution();
}
if (pooledObject != null) {
return pooledObject;
}
if (singletonBeanPool) {
synchronized (pooledObjects) {
while (pooledObjects.isEmpty() && (createdCount - destroyedCount) > 0) {
try {
pooledObjects.wait();
} catch (InterruptedException ex) { // ignore
}
}
if (!pooledObjects.isEmpty()) {
return pooledObjects.remove(0);
}
try {
pooledObject = pooledObjectFactory.create(param);
createdCount++;
return pooledObject;
} catch (RuntimeException th) {
poolProbeNotifier.ejbObjectAddFailedEvent(beanId, appName, modName, ejbName);
throw th;
}
}
} else {
try {
return pooledObjectFactory.create(param);
} catch (RuntimeException th) {
synchronized (pooledObjects) {
poolProbeNotifier.ejbObjectAddFailedEvent(beanId, appName, modName, ejbName);
createdCount--;
}
throw th;
}
}
}
private void addResizeTaskForImmediateExecution() {
try {
ReSizeWork work = new ReSizeWork();
EjbContainerUtilImpl.getInstance().addWork(work);
_logger.log(FINE, () -> "[Pool-" + poolName + "]: Added PoolResizeTimerTask...");
} catch (Exception ex) {
synchronized (pooledObjects) {
addedResizeTask = false;
}
_logger.log(WARNING, ex, () -> "[Pool-" + poolName + "]: Cannot perform " + " pool resize task");
}
}
/**
* Return an object back to the pool. An object that is obtained through getObject() must always be returned back to the
* pool using either returnObject(obj) or through destroyObject(obj).
*/
@Override
public void returnObject(Object object) {
synchronized (pooledObjects) {
if (pooledObjects.size() < maxPoolSize) {
pooledObjects.add(object);
if (singletonBeanPool) {
pooledObjects.notify();
}
return;
}
poolProbeNotifier.ejbObjectDestroyedEvent(beanId, appName, modName, ejbName);
destroyedCount++;
}
try {
pooledObjectFactory.destroy(object);
} catch (Exception ex) {
_logger.log(FINE, "exception in returnObj", ex);
}
}
/**
* Destroys an Object. Note that applications should not ignore the reference to the object that they got from
* getObject(). An object that is obtained through getObject() must always be returned back to the pool using either
* returnObject(obj) or through destroyObject(obj). This method tells that the object should be destroyed and cannot be
* reused.
*/
@Override
public void destroyObject(Object object) {
synchronized (pooledObjects) {
poolProbeNotifier.ejbObjectDestroyedEvent(beanId, appName, modName, ejbName);
destroyedCount++;
if (singletonBeanPool) {
pooledObjects.notify();
}
}
try {
pooledObjectFactory.destroy(object);
} catch (Exception ex) {
_logger.log(FINE, "exception in destroyObject", ex);
}
}
/**
* Prepopulate the pool with objects.
*
* @param count the number of objects to be added.
*/
public void prepopulate(int count) {
steadyPoolSize = count <= 0 ? 0 : count;
steadyPoolSize = steadyPoolSize > maxPoolSize ? maxPoolSize : steadyPoolSize;
if (steadyPoolSize > 0) {
preload(steadyPoolSize);
}
}
/**
* Preload the pool with objects.
*
* @param count the number of objects to be added.
*/
@Override
protected void preload(int count) {
List