edu.vt.middleware.ldap.pool.AbstractLdapPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vt-ldap Show documentation
Show all versions of vt-ldap Show documentation
Library for performing common LDAP operations
/*
$Id: AbstractLdapPool.java 1330 2010-05-23 22:10:53Z dfisher $
Copyright (C) 2003-2010 Virginia Tech.
All rights reserved.
SEE LICENSE FOR MORE INFORMATION
Author: Middleware Services
Email: [email protected]
Version: $Revision: 1330 $
Updated: $Date: 2010-05-23 18:10:53 -0400 (Sun, 23 May 2010) $
*/
package edu.vt.middleware.ldap.pool;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Timer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import edu.vt.middleware.ldap.BaseLdap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* AbstractLdapPool
contains the basic implementation for pooling
* ldap objects. The main design objective for the supplied pooling
* implementations is to provide a pool that does not block on object creation
* or destruction. This is what accounts for the multiple locks available on
* this class. The pool is backed by two queues, one for available objects and
* one for active objects. Objects that are available for {@link #checkOut()}
* exist in the available queue. Objects that are actively in use exist in the
* active queue. Note that depending on the implementation an object can exist
* in both queues at the same time.
*
* @param type of ldap object
*
* @author Middleware Services
* @version $Revision: 1330 $ $Date: 2010-05-23 18:10:53 -0400 (Sun, 23 May 2010) $
*/
public abstract class AbstractLdapPool
implements LdapPool
{
/** Lock for the entire pool. */
protected final ReentrantLock poolLock = new ReentrantLock();
/** Condition for notifying threads that an object was returned. */
protected final Condition poolNotEmpty = poolLock.newCondition();
/** Lock for check ins. */
protected final ReentrantLock checkInLock = new ReentrantLock();
/** Lock for check outs. */
protected final ReentrantLock checkOutLock = new ReentrantLock();
/** Log for this class. */
protected final Log logger = LogFactory.getLog(this.getClass());
/** List of available ldap objects in the pool. */
protected Queue> available = new LinkedList>();
/** List of ldap objects in use. */
protected Queue> active = new LinkedList>();
/** Ldap pool config. */
protected LdapPoolConfig poolConfig;
/** Factory to create ldap objects. */
protected LdapFactory ldapFactory;
/** Timer for scheduling pool tasks. */
private Timer poolTimer = new Timer(true);
/**
* Creates a new pool with the supplied pool configuration and ldap factory.
* The pool configuration will be marked as immutable by this pool.
*
* @param lpc LdapPoolConfig
* @param lf LdapFactory
*/
public AbstractLdapPool(final LdapPoolConfig lpc, final LdapFactory lf)
{
this.poolConfig = lpc;
this.poolConfig.makeImmutable();
this.ldapFactory = lf;
}
/** {@inheritDoc} */
public LdapPoolConfig getLdapPoolConfig()
{
return this.poolConfig;
}
/** {@inheritDoc} */
public void setPoolTimer(final Timer t)
{
this.poolTimer = t;
}
/** {@inheritDoc} */
public void initialize()
{
if (this.logger.isDebugEnabled()) {
this.logger.debug("beginning pool initialization");
}
this.poolTimer.scheduleAtFixedRate(
new PrunePoolTask(this),
this.poolConfig.getPruneTimerPeriod(),
this.poolConfig.getPruneTimerPeriod());
if (this.logger.isDebugEnabled()) {
this.logger.debug("prune pool task scheduled");
}
this.poolTimer.scheduleAtFixedRate(
new ValidatePoolTask(this),
this.poolConfig.getValidateTimerPeriod(),
this.poolConfig.getValidateTimerPeriod());
if (this.logger.isDebugEnabled()) {
this.logger.debug("validate pool task scheduled");
}
this.initializePool();
if (this.logger.isDebugEnabled()) {
this.logger.debug("pool initialized to size " + this.available.size());
}
}
/** Attempts to fill the pool to its minimum size. */
private void initializePool()
{
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"checking ldap pool size >= " + this.poolConfig.getMinPoolSize());
}
int count = 0;
this.poolLock.lock();
try {
while (
this.available.size() < this.poolConfig.getMinPoolSize() &&
count < this.poolConfig.getMinPoolSize() * 2) {
final T t = this.createAvailable();
if (this.poolConfig.isValidateOnCheckIn()) {
if (this.ldapFactory.validate(t)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(
"ldap object passed initialize validation: " + t);
}
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn(
"ldap object failed initialize validation: " + t);
}
this.removeAvailable(t);
}
}
count++;
}
} finally {
this.poolLock.unlock();
}
}
/** {@inheritDoc} */
public void close()
{
this.poolLock.lock();
try {
while (this.available.size() > 0) {
final PooledLdap pl = this.available.remove();
this.ldapFactory.destroy(pl.getLdap());
}
while (this.active.size() > 0) {
final PooledLdap pl = this.active.remove();
this.ldapFactory.destroy(pl.getLdap());
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("pool closed");
}
} finally {
this.poolLock.unlock();
}
this.poolTimer.cancel();
}
/**
* Create a new ldap object and place it in the available pool.
*
* @return ldap object that was placed in the available pool
*/
protected T createAvailable()
{
final T t = this.ldapFactory.create();
if (t != null) {
final PooledLdap pl = new PooledLdap(t);
this.poolLock.lock();
try {
this.available.add(pl);
} finally {
this.poolLock.unlock();
}
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn("unable to create available ldap object");
}
}
return t;
}
/**
* Create a new ldap object and place it in the active pool.
*
* @return ldap object that was placed in the active pool
*/
protected T createActive()
{
final T t = this.ldapFactory.create();
if (t != null) {
final PooledLdap pl = new PooledLdap(t);
this.poolLock.lock();
try {
this.active.add(pl);
} finally {
this.poolLock.unlock();
}
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn("unable to create active ldap object");
}
}
return t;
}
/**
* Create a new ldap object and place it in both the available and active
* pools.
*
* @return ldap object that was placed in the available and active pools
*/
protected T createAvailableAndActive()
{
final T t = this.ldapFactory.create();
if (t != null) {
final PooledLdap pl = new PooledLdap(t);
this.poolLock.lock();
try {
this.available.add(pl);
this.active.add(pl);
} finally {
this.poolLock.unlock();
}
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn("unable to create available and active ldap object");
}
}
return t;
}
/**
* Remove an ldap object from the available pool.
*
* @param t ldap object that exists in the available pool
*/
protected void removeAvailable(final T t)
{
boolean destroy = false;
final PooledLdap pl = new PooledLdap(t);
this.poolLock.lock();
try {
if (this.available.remove(pl)) {
destroy = true;
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn(
"attempt to remove unknown available ldap object: " + t);
}
}
} finally {
this.poolLock.unlock();
}
if (destroy) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("removing available ldap object: " + t);
}
this.ldapFactory.destroy(t);
}
}
/**
* Remove an ldap object from the active pool.
*
* @param t ldap object that exists in the active pool
*/
protected void removeActive(final T t)
{
boolean destroy = false;
final PooledLdap pl = new PooledLdap(t);
this.poolLock.lock();
try {
if (this.active.remove(pl)) {
destroy = true;
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn(
"attempt to remove unknown active ldap object: " + t);
}
}
} finally {
this.poolLock.unlock();
}
if (destroy) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("removing active ldap object: " + t);
}
this.ldapFactory.destroy(t);
}
}
/**
* Remove an ldap object from both the available and active pools.
*
* @param t ldap object that exists in the both the available and active
* pools
*/
protected void removeAvailableAndActive(final T t)
{
boolean destroy = false;
final PooledLdap pl = new PooledLdap(t);
this.poolLock.lock();
try {
if (this.available.remove(pl)) {
destroy = true;
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"attempt to remove unknown available ldap object: " + t);
}
}
if (this.active.remove(pl)) {
destroy = true;
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"attempt to remove unknown active ldap object: " + t);
}
}
} finally {
this.poolLock.unlock();
}
if (destroy) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("removing active ldap object: " + t);
}
this.ldapFactory.destroy(t);
}
}
/**
* Attempts to activate and validate an ldap object. Performed before an
* object is returned from {@link LdapPool#checkOut()}.
*
* @param t ldap object
*
* @throws LdapPoolException if this method fais
* @throws LdapActivationException if the ldap object cannot be activated
* @throws LdapValidateException if the ldap object cannot be validated
*/
protected void activateAndValidate(final T t)
throws LdapPoolException
{
if (!this.ldapFactory.activate(t)) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("ldap object failed activation: " + t);
}
this.removeAvailableAndActive(t);
throw new LdapActivationException("Activation of ldap object failed");
}
if (
this.poolConfig.isValidateOnCheckOut() &&
!this.ldapFactory.validate(t)) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("ldap object failed check out validation: " + t);
}
this.removeAvailableAndActive(t);
throw new LdapValidationException("Validation of ldap object failed");
}
}
/**
* Attempts to validate and passivate an ldap object. Performed when an object
* is given to {@link LdapPool#checkIn}.
*
* @param t ldap object
*
* @return whether both validate and passivation succeeded
*/
protected boolean validateAndPassivate(final T t)
{
boolean valid = false;
if (this.poolConfig.isValidateOnCheckIn()) {
if (!this.ldapFactory.validate(t)) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("ldap object failed check in validation: " + t);
}
} else {
valid = true;
}
} else {
valid = true;
}
if (valid && !this.ldapFactory.passivate(t)) {
valid = false;
if (this.logger.isWarnEnabled()) {
this.logger.warn("ldap object failed activation: " + t);
}
}
return valid;
}
/** {@inheritDoc} */
public void prune()
{
if (this.logger.isTraceEnabled()) {
this.logger.trace(
"waiting for pool lock to prune " + this.poolLock.getQueueLength());
}
this.poolLock.lock();
try {
if (this.active.size() == 0) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("pruning pool of size " + this.available.size());
}
while (this.available.size() > this.poolConfig.getMinPoolSize()) {
PooledLdap pl = this.available.peek();
final long time = System.currentTimeMillis() - pl.getCreatedTime();
if (time > this.poolConfig.getExpirationTime()) {
pl = this.available.remove();
if (this.logger.isTraceEnabled()) {
this.logger.trace(
"removing " + pl.getLdap() + " in the pool for " + time + "ms");
}
this.ldapFactory.destroy(pl.getLdap());
} else {
break;
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("pool size pruned to " + this.available.size());
}
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("pool is currently active, no objects pruned");
}
}
} finally {
this.poolLock.unlock();
}
}
/** {@inheritDoc} */
public void validate()
{
this.poolLock.lock();
try {
if (this.active.size() == 0) {
if (this.poolConfig.isValidatePeriodically()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"validate for pool of size " + this.available.size());
}
final Queue> remove = new LinkedList>();
for (PooledLdap pl : this.available) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("validating " + pl.getLdap());
}
if (this.ldapFactory.validate(pl.getLdap())) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(
"ldap object passed validation: " + pl.getLdap());
}
} else {
if (this.logger.isWarnEnabled()) {
this.logger.warn(
"ldap object failed validation: " + pl.getLdap());
}
remove.add(pl);
}
}
for (PooledLdap pl : remove) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("removing " + pl.getLdap() + " from the pool");
}
this.available.remove(pl);
this.ldapFactory.destroy(pl.getLdap());
}
}
this.initializePool();
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"pool size after validation is " + this.available.size());
}
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"pool is currently active, " +
"no validation performed");
}
}
} finally {
this.poolLock.unlock();
}
}
/** {@inheritDoc} */
public int availableCount()
{
return this.available.size();
}
/** {@inheritDoc} */
public int activeCount()
{
return this.active.size();
}
/**
* Called by the garbage collector on an object when garbage collection
* determines that there are no more references to the object.
*
* @throws Throwable if an exception is thrown by this method
*/
protected void finalize()
throws Throwable
{
try {
this.close();
} finally {
super.finalize();
}
}
/**
* PooledLdap
contains an ldap object that is participating in a
* pool. Used to track how long an ldap object has been in either the
* available or active queues.
*
* @param type of ldap object
*/
static protected class PooledLdap
{
/** hash code seed. */
protected static final int HASH_CODE_SEED = 89;
/** Underlying ldap object. */
private T ldap;
/** Time this object was created. */
private long createdTime;
/**
* Creates a new PooledLdap
with the supplied ldap object.
*
* @param t ldap object
*/
public PooledLdap(final T t)
{
this.ldap = t;
this.createdTime = System.currentTimeMillis();
}
/**
* Returns the ldap object.
*
* @return underlying ldap object
*/
public T getLdap()
{
return this.ldap;
}
/**
* Returns the time this object was created.
*
* @return creation time
*/
public long getCreatedTime()
{
return this.createdTime;
}
/**
* Returns whether the supplied Object
contains the same data
* as this bean.
*
* @param o Object
*
* @return boolean
*/
public boolean equals(final Object o)
{
if (o == null) {
return false;
}
return
o == this ||
(this.getClass() == o.getClass() &&
o.hashCode() == this.hashCode());
}
/**
* This returns the hash code for this object.
*
* @return int
*/
public int hashCode()
{
int hc = HASH_CODE_SEED;
if (this.ldap != null) {
hc += this.ldap.hashCode();
}
return hc;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy