leif 02/01/23 18:55:54
Added: src/scratchpad/org/apache/avalon/excalibur/pool
ResourceLimitingPool.java Validatable.java
ValidatedResourceLimitingPool.java
Log:
Add new ResourceLimiting Pools.
Revision Changes Path
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/pool/ResourceLimitingPool.java
Index: ResourceLimitingPool.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.pool;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.avalon.excalibur.pool.ObjectFactory;
import org.apache.avalon.excalibur.pool.Pool;
import org.apache.avalon.excalibur.pool.Poolable;
import org.apache.avalon.excalibur.pool.PoolController;
import org.apache.avalon.excalibur.pool.Recyclable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* General Pool implementation which supports; weak and strong pool size
limits,
* optional blocking gets when poolables are not available, and automatic
pool
* trimming of unused poolables.
*
* Whenever get() is called, the pool tests to see whether it is time to trim
old
* poolables from the pool. If any old poolables exist then they are
removed at
* this time. This means that old poolables will not be removed if get() is
never
* called. Applications can optionally call trim() to force old objects to
be
* trimmed. See the trim() method for details of how trimming works.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/01/24 02:55:54 $
* @since 4.0
*/
public class ResourceLimitingPool
extends AbstractLogEnabled
implements Pool, LogEnabled, Disposable, ThreadSafe
{
/*---------------------------------------------------------------
* Protected Fields
*-------------------------------------------------------------*/
/**
* Object used to synchronize access to the get and put methods
*/
protected final Object m_semaphore = new Object();
/*---------------------------------------------------------------
* Private Fields
*-------------------------------------------------------------*/
/**
* Keeps track of whether or not the Pool has been disposed.
*/
private boolean m_disposed = false;
/**
* The Object Factory used to generate new Poolable instances for the
pool.
*/
private final ObjectFactory m_factory;
/**
* The maximum size of the pool.
*/
private final int m_max;
/**
* Whether or not the pool allows for the creation of objects beyond the
maximum pool size.
*/
private final boolean m_maxStrict;
/**
* Whether or not the pool should cause threads requesting a Poolable to
block when m_maxStrict
* is true, the pool size is equal to m_max and there are no Poolable
instances available.
*/
private final boolean m_blocking;
/**
* The maximum amount of time in milliseconds that the pool will block.
If 0, blocking will
* wait indeffinately.
*/
private final long m_blockTimeout;
/**
* The minimum interval with which old unused poolables will be removed
from the pool.
*/
private final long m_trimInterval;
/**
* The last time that the pool was trimmed.
*/
private long m_lastTrim;
/**
* List of the Poolable instances which are available for use.
*/
private LinkedList m_ready;
/**
* Store the size of the ready list to optimize operations which require
this value.
*/
private int m_readySize;
/**
* List of the Poolable instance which are available for use but have
been idle for a while.
*/
private LinkedList m_oldReady;
/**
* Store the size of the old ready list to optimize operations which
require this value.
*/
private int m_oldReadySize;
/**
* Total number of Poolable instances in the pool
*/
private int m_size;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
/**
* Creates a new ResourceLimitingPool
*
* @param factory The ObjectFactory which will be used to create new
Poolables as needed by
* the pool.
* @param max Maximum number of Poolables which can be stored in the
pool, 0 implies no limit.
* @param maxStrict true if the pool should never allow more than max
Poolable to be created.
* Will cause an exception to be thrown if more than max Poolables are
requested and blocking
* is false.
* @param blocking true if the pool should cause a thread calling get()
to block when Poolables
* are not currently available in the pool.
* @param blockTimeout The maximum amount of time, in milliseconds, that
a call to get() will
* block before an exception is thrown. A value of 0 implies an
indefinate wait.
* @param trimInterval The minimum interval with which old unused
poolables will be removed
* from the pool. A value of 0 will cause the pool to never trim
poolables.
*/
public ResourceLimitingPool( final ObjectFactory factory,
int max,
boolean maxStrict,
boolean blocking,
long blockTimeout,
long trimInterval )
{
m_factory = factory;
m_max = (max <= 0 ? Integer.MAX_VALUE : max);
m_maxStrict = maxStrict;
m_blocking = blocking;
m_blockTimeout = blockTimeout;
m_trimInterval = trimInterval;
// Create the pool lists.
m_ready = new LinkedList();
if ( m_trimInterval > 0 )
{
m_oldReady = new LinkedList();
}
}
/*---------------------------------------------------------------
* Pool Methods
*-------------------------------------------------------------*/
/**
* Gets a Poolable from the pool. If there is room in the pool, a new
Poolable will be
* created. Depending on the parameters to the constructor, the method
may block or throw
* an exception if a Poolable is not available on the pool.
*
* @returns Always returns a Poolable. Contract requires that put must
always be called with
* the Poolable returned.
* @throws Exception An exception may be thrown as described above or if
there is an exception
* thrown by the ObjectFactory's newInstance() method.
*/
public Poolable get() throws Exception
{
if ( m_disposed ) throw new IllegalStateException("Already Disposed");
synchronized(m_semaphore)
{
// If trimming is enabled then trim if it is time
if ( ( m_oldReady != null) &&
( System.currentTimeMillis() - m_lastTrim >= m_trimInterval )
)
{
trimInner();
}
Poolable poolable;
// Look for a Poolable at the end of the m_ready list
if ( m_readySize > 0 )
{
// A poolable is ready and waiting in the pool
poolable = (Poolable)m_ready.removeLast();
m_readySize--;
}
else if ( m_oldReadySize > 0 )
{
// An old poolable is ready and waiting in the pool
poolable = (Poolable)m_oldReady.removeLast();
m_oldReadySize--;
}
else
{
// Are we allowed to create a new poolable here?
if ( ( m_size >= m_max ) && m_maxStrict )
{
// The pool has as many active Poolables as it is allowed
and
// we are not allowed to create any more.
// Are we allowed to wait for a Poolable to become
available?
if ( m_blocking )
{
long blockStart = System.currentTimeMillis();
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Blocking until a Poolable is
available. "
+ "Thread: " +
Thread.currentThread().getName() );
}
if ( m_blockTimeout > 0 )
{
// Wait for a limited amount of time for a
poolable is made
// available.
// Other threads may grab a connection before
this thread gets the
// semaphore, so be careful.
long blockWait = m_blockTimeout;
do
{
if ( blockWait > 0 )
{
try
{
m_semaphore.wait( blockWait );
}
catch ( InterruptedException e ) {}
// The dispose() method might have woken
us up.
if ( m_disposed )
{
throw new IllegalStateException(
"Already Disposed" );
}
if ( m_readySize == 0 )
{
// Not available yet, calculate how
much longer to wait.
long now = System.currentTimeMillis();
blockWait = m_blockTimeout - ( now -
blockStart );
}
}
else
{
// We timed out waiting.
long now = System.currentTimeMillis();
if ( getLogger().isDebugEnabled() )
{
getLogger().debug(
"Timed out waiting for a Poolable
to become "
+ "available. Blocked for " + (
now - blockStart )
+ "ms. Thread: " +
Thread.currentThread().getName() );
}
throw new Exception
( "Could not create enough Components
to service your "
+ "request (Timed out)." );
}
}
while ( m_readySize == 0 );
}
else
{
// Wait until we get a poolable no matter how
long it takes.
// Other threads may grab a connection before
this thread gets the
// semaphore, so be careful.
do
{
try
{
m_semaphore.wait();
}
catch ( InterruptedException e ) {}
// The dispose() method might have woken us
up.
if ( m_disposed )
{
throw new IllegalStateException( "Already
Disposed" );
}
}
while ( m_readySize == 0 );
}
// A poolable is ready and waiting in the pool
poolable = (Poolable)m_ready.removeLast();
m_readySize--;
if ( getLogger().isDebugEnabled() )
{
long now = System.currentTimeMillis();
getLogger().debug( "Blocked for " + (now -
blockStart) + "ms "
+ "waiting for a Poolable to become
available. "
+ "Thread: " +
Thread.currentThread().getName() );
}
}
else
{
// We must fail.
throw new Exception
( "Could not create enough Components to service
your request." );
}
}
else
{
// Create a new poolable
poolable = newPoolable();
m_size++;
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Created a new " +
poolable.getClass().getName()
+ " from the object factory." );
}
}
}
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Got a " + poolable.getClass().getName() +
" from the pool." );
}
return poolable;
}
}
/**
* Returns a poolable to the pool and notifies any thread blocking.
*
* @param poolable Poolable to return to the pool.
*/
public void put( Poolable poolable )
{
// Handle Recyclable objects
if ( poolable instanceof Recyclable )
{
((Recyclable)poolable).recycle();
}
synchronized(m_semaphore)
{
if ( m_size <= m_max )
{
if ( m_disposed )
{
// The pool has already been disposed.
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Put called for a " +
poolable.getClass().getName()
+ " after the pool was disposed." );
}
permanentlyRemovePoolable( poolable );
}
else
{
// There is room in the pool to keep this poolable.
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Put a " +
poolable.getClass().getName()
+ " back into the pool." );
}
m_ready.addLast( poolable );
m_readySize++;
// Let any waiting threads know that a poolable has
become available.
if ( m_blocking )
{
m_semaphore.notify();
}
}
}
else
{
// More Poolables were created than can be held in the pool,
so remove.
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "No room to put a " +
poolable.getClass().getName()
+ " back into the pool, so remove it." );
}
permanentlyRemovePoolable( poolable );
}
}
}
/*---------------------------------------------------------------
* Disposable Methods
*-------------------------------------------------------------*/
/**
* The dispose operation is called at the end of a components lifecycle.
* This method will be called after Startable.stop() method (if
implemented
* by component). Components use this method to release and destroy any
* resources that the Component owns.
*/
public void dispose()
{
m_disposed = true;
// Any Poolables in the m_ready list need to be disposed of
synchronized(m_semaphore)
{
// Remove objects in the ready list.
for ( Iterator iter = m_ready.iterator(); iter.hasNext(); )
{
Poolable poolable = (Poolable)iter.next();
iter.remove();
m_readySize--;
permanentlyRemovePoolable( poolable );
}
// Remove objects in the old ready list.
if ( m_oldReady != null )
{
for ( Iterator iter = m_oldReady.iterator(); iter.hasNext(); )
{
Poolable poolable = (Poolable)iter.next();
iter.remove();
m_oldReadySize--;
permanentlyRemovePoolable( poolable );
}
}
// Notify any threads currently waiting for objects so they can
abort
if ( m_blocking )
{
m_semaphore.notifyAll();
}
if ( ( m_size > 0 ) && getLogger().isDebugEnabled() )
{
getLogger().debug( "There were " + m_size
+ " outstanding objects when the pool was disposed." );
}
}
}
/*---------------------------------------------------------------
* Methods
*-------------------------------------------------------------*/
/**
* Permanently removes a poolable from the pool's active list and
* destroys it so that it will not ever be reused.
* This method is only called by threads that have m_semaphore locked.
*/
protected void permanentlyRemovePoolable( Poolable poolable )
{
m_size--;
removePoolable( poolable );
}
/**
* Returns the total number of Poolables created by the pool. Includes
active and ready.
*/
public int getSize()
{
return m_size;
}
/**
* Returns the number of available Poolables waiting in the pool.
*/
public int getReadySize()
{
synchronized(m_semaphore)
{
return m_readySize + m_oldReadySize;
}
}
/**
* Create a new poolable instance by by calling the newInstance method
* on the pool's ObjectFactory.
* This is the method to override when you need to enforce creational
* policies.
* This method is only called by threads that have m_semaphore locked.
*/
protected Poolable newPoolable() throws Exception
{
Object obj = m_factory.newInstance();
return (Poolable)obj;
}
/**
* Called when an object is being removed permanently from the pool.
* This is the method to override when you need to enforce destructional
* policies.
*
* This method is only called by threads that have m_semaphore locked.
*
* @param poolable Poolable to be completely removed from the pool.
*/
protected void removePoolable(Poolable poolable)
{
try
{
m_factory.decommission( poolable );
}
catch ( Exception e )
{
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Error decommissioning object", e );
}
}
}
/**
* Forces the pool to trim, remove, old Poolables from the pool. If the
Pool
* was created with a non-zero value for trimInterval, then this method
will
* be called at that interval when get() is called. If get() is not
called
* for long periods of time then if may be necessary to call this method
* manually.
*
* Trimming is done by maintaing two lists of objects. The first is a
ready list
* of new poolables. The second is a list of old poolables. Each time
trim() is
* called, the contents of the old list are removed from the pool. Then
the
* contents of the new list is moved into the old list.
*
* Each time get() is called on the pool, the new list is checked first,
then the
* old list is checked, finally a new poolable may be created if both
lists are
* empty. Then whenever put() is called, the poolables are always
returned to
* the new list. In this way, the need for maining time stamps for each
poolable
* can be avoided while at the same time avoiding unnecessary removal
and creation
* on poolables.
*
* This works out to a poolable having a maximum idle time of two calls
to trim() or
* twice the value of trimInterval.
*
* NOTE - The trimming feature does not harm performance because pools
with high
* load will not have old poolables to be trimmed, and the benefits to
system
* resources from not keeping around unused poolables makes up for any
hit.
*
* @returns the number of Poolables that were trimmed.
*/
public int trim()
{
if ( m_oldReady != null )
{
synchronized(m_semaphore)
{
return trimInner();
}
}
else
{
throw new IllegalStateException( "This pool is not configured to
do trimming." );
}
}
/**
* See trim() for details.
*
* This method is only called by threads that have m_semaphore locked.
*/
private int trimInner()
{
int trimCount = 0;
// Remove any poolables in the m_oldReady list.
if ( m_oldReadySize > 0 )
{
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Trimming " + m_oldReadySize + " idle
objects from pool." );
}
trimCount = m_oldReadySize;
for ( Iterator iter = m_oldReady.iterator(); iter.hasNext(); )
{
Poolable poolable = (Poolable)iter.next();
iter.remove();
m_oldReadySize--;
permanentlyRemovePoolable( poolable );
}
}
// Move the poolables in m_ready into m_oldReady (swap lists)
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Marking " + m_readySize + " objects as old in
pool." );
}
LinkedList tempList = m_oldReady;
m_oldReady = m_ready;
m_oldReadySize = m_readySize;
m_ready = tempList;
m_readySize = 0;
m_lastTrim = System.currentTimeMillis();
return trimCount;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/pool/Validatable.java
Index: Validatable.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.pool;
import org.apache.avalon.excalibur.pool.Poolable;
/**
* Used to define an object which can be validated by a
ValidatedResourceLimitingPool.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/01/24 02:55:54 $
* @since 4.0
*/
public interface Validatable
extends Poolable
{
/**
* Called when an object is retrieved from a
ValidatedResourceLimitingPool for reuse.
*
* @returns true if the object is ok. false will cause the object to be
discarded.
*/
boolean validate();
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/pool/ValidatedResourceLimitingPool.java
Index: ValidatedResourceLimitingPool.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.pool;
import org.apache.avalon.excalibur.pool.ObjectFactory;
import org.apache.avalon.excalibur.pool.Poolable;
/**
* A ResourceLimitingPool which validates reused poolables before they are
* returned with a call get().
*
* @author <a href="mailto:[EMAIL PROTECTED]">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/01/24 02:55:54 $
* @since 4.0
*/
public class ValidatedResourceLimitingPool
extends ResourceLimitingPool
{
/*---------------------------------------------------------------
* Private Fields
*-------------------------------------------------------------*/
/**
* Used for communication between the get() and newPoolable() methods,
only valid
* within a single synchronized block.
*/
private boolean m_needsValidation;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
/**
* Creates a new ValidatedResourceLimitingPool
*
* @param factory The ObjectFactory which will be used to create new
Poolables as needed by
* the pool.
* @param max Maximum number of Poolables which can be stored in the
pool, 0 implies no limit.
* @param maxStrict true if the pool should never allow more than max
Poolable to be created.
* Will cause an exception to be thrown if more than max Poolables are
requested and blocking
* is false.
* @param blocking true if the pool should cause a thread calling get()
to block when Poolables
* are not currently available on the pool.
* @param blockTimeout The maximum amount of time, in milliseconds, that
a call to get() will
* block before an exception is thrown. A value of 0 implies an
indefinate wait.
* @param trimInterval The minimum interval with which old unused
poolables will be removed
* from the pool. A value of 0 will cause the pool to never trim
poolables.
*/
public ValidatedResourceLimitingPool( final ObjectFactory factory,
int max,
boolean maxStrict,
boolean blocking,
long blockTimeout,
long trimInterval )
{
super( factory, max, maxStrict, blocking, blockTimeout, trimInterval
);
}
/*---------------------------------------------------------------
* Pool Methods
*-------------------------------------------------------------*/
/**
* Gets a Poolable from the pool. If there is room in the pool, a new
Poolable will be
* created. Depending on the parameters to the constructor, the method
may block or throw
* an exception if a Poolable is not available on the pool.
*
* @returns Always returns a Poolable. Contract requires that put must
always be called with
* the Poolable returned.
* @throws Exception An exception may be thrown as described above or if
there is an exception
* thrown by the ObjectFactory's newInstance() method.
*/
public Poolable get() throws Exception
{
Poolable poolable;
boolean needsValidation;
// If an obtained Poolable is invalid, then we will want to obtain
another one requiring
// that we loop.
do
{
synchronized(m_semaphore)
{
// Set the needs validation flag to false. The super.get()
method will call the
// newPoolable() method causing the flag to be set to false
if called.
m_needsValidation = true;
poolable = super.get();
// Store the validation flag in a local variable so that we
can continue to use it
// after we release the semaphore.
needsValidation = m_needsValidation;
}
// If necessay, validate the poolable now that this thread owns
it.
if ( needsValidation )
{
// Call the validation method for the obtained poolable.
if ( !validatePoolable( poolable ) )
{
// The poolable is no longer valid. We need to
resynchronize to remove the bad
// poolable and prepare to get another one.
synchronized(m_semaphore)
{
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Removing a " +
poolable.getClass().getName()
+ " from the pool because it failed
validation." );
}
permanentlyRemovePoolable( poolable );
poolable = null;
}
}
}
}
while ( poolable == null );
return poolable;
}
/*---------------------------------------------------------------
* ResourceLimitingPool Methods
*-------------------------------------------------------------*/
/**
* Create a new poolable instance by by calling the newInstance method
* on the pool's ObjectFactory.
* This is the method to override when you need to enforce creational
* policies.
* This method is only called by threads that have m_semaphore locked.
*/
protected Poolable newPoolable() throws Exception
{
// Set the validation flag to false. See the exclamation in the
get() method.
m_needsValidation = false;
return super.newPoolable();
}
/*---------------------------------------------------------------
* Public Methods
*-------------------------------------------------------------*/
/**
* If the poolable implements Validatable, then its validate() method
will be called to give
* the poolable a chance to validate itself.
* Different functionality can be achieved by overriding this method.
* This method is only called by threads that have m_semaphore locked.
*
* @param poolable The Poolable to be validated
* @returns true if the Poolable is valid, false if it should be removed
from the pool.
*/
protected boolean validatePoolable(Poolable poolable) throws Exception
{
if ( poolable instanceof Validatable )
{
return ((Validatable)poolable).validate();
}
else
{
return true;
}
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>