CVS: plexus-container/src/java/org/apache/plexus/util SweeperPool.java,NONE,1.1 ThreadSafeMap.java,NONE,1.1 Tracer.java,NONE,1.1
Jason van Zyl <[email protected]> Tue, 5 Aug 2003 15:45:31 -0500
| Newsgroups | gmane.comp.java.plexus.devel |
|---|---|
| Message-ID | <[email protected]> |
Update of /cvsroot/plexus/plexus-container/src/java/org/apache/plexus/util In directory hogshead.codehaus.org:/tmp/cvs-serv29227/src/java/org/apache/plexus/util Added Files: SweeperPool.java ThreadSafeMap.java Tracer.java Log Message: o Adding some new files. --- NEW FILE: SweeperPool.java --- package org.apache.plexus.util; import java.util.ArrayList; /** * Pools a bunch of objects . Runs a sweeper periodically to * keep it down to size. The objects in the pool first get disposed first. * * * @author <a href="mailto:[email protected]">Bert van Brakel</a> */ public class SweeperPool { /***/ private static final boolean DEBUG = false; /** Sweeps the pool periodically to trim it's size */ private transient Sweeper sweeper; /** Absolute maxiumuim size of the pool.*/ private transient int maxSize; /** The size the pool gets trimmed down to */ private transient int minSize; /** When the sweeper runs * and the pool is over this size, then the pool is trimmed */ private int triggerSize; /** Holds the pooled objects */ private ArrayList pooledObjects; /** Flag indicating this pool is shuting down */ private boolean shuttingDown = false; //private Vector used; /** * * <p>There are a number of settings to control how the pool operates. * <ul> * <li><code>minSize</code> - this is the size the pool is trimmed to</li> * <li><code>triggerSize</code> - this determines if the pool is trimmed when * the sweeper runs. If the pool size is greater or equal than this value then * the pool is trimmed to <code>minSize</code>.</lie> * <li><code>maxSize</code> - if the pool has reached this size, any objects added * are immediatley disposed. If the pool is this size when the sweeper runs, then * the pool is also trimmed to <code>minSize</code> irrespective of the triggerSize. * </li> * <li><code>sweepInterval</code> - how often the sweeper runs. Is actually the * time since the sweeper last finished a pass. 0 if the sweeper should not run. * </li> * </ul> * </p> * * <p>Any value less than 0 is automatically converted to 0</p> */ public SweeperPool( int maxSize, int minSize, int intialCapacity, int sweepInterval, int triggerSize ) { super( ); this.maxSize = saneConvert( maxSize ); this.minSize = saneConvert( minSize ); this.triggerSize = saneConvert( triggerSize ); pooledObjects = new ArrayList( intialCapacity ); if( sweepInterval > 0 ) { sweeper = new Sweeper( this, sweepInterval ); sweeper.start( ); } } private int saneConvert( int value ) { if( value < 0 ) { return 0; } else { return value; } } /** * Return the pooled object */ public synchronized Object get( ) { if( ( pooledObjects.size( ) == 0 ) || shuttingDown ) { return null; } else { Object obj = pooledObjects.remove( 0 ); objectRetrieved( obj ); //used.add(obj); return obj; } } /** * Add an object to the pool * * @param obj the object to pool. Can be null. * * @return true if the object was added to the pool, false if it was disposed or null * */ public synchronized boolean put( Object obj ) { objectAdded( obj ); if( ( obj != null ) && ( pooledObjects.size( ) < maxSize ) && ( shuttingDown == false ) ) { pooledObjects.add( obj ); return true; } else if( obj != null ) { //no longer need the object, so dispose it objectDisposed( obj ); } return false; } /** * Return the number of pooled objects. This is never * greater than t maximuim size of the pool * * @return the number of pooled objects */ public int getSize( ) { return pooledObjects.size( ); } /** * Dispose of this pool. Stops the sweeper and disposes each object in the pool * */ public synchronized void dispose( ) { shuttingDown = true; if( sweeper != null ) { sweeper.stop( ); } //use an array here as objects may still be being put back in the pool //and we don't want to throw a ConcurrentModificationException Object[] objects = pooledObjects.toArray( ); for( int i = 0; i < objects.length; i++ ) { objectDisposed( objects[i] ); } pooledObjects.clear( ); } /** * Trim the pool down to min size * */ public void trim( ) { if( ( ( triggerSize > 0 ) && ( pooledObjects.size( ) >= triggerSize ) ) || ( ( maxSize > 0 ) && ( pooledObjects.size( ) >= maxSize ) ) ) { while( pooledObjects.size( ) > minSize ) { objectDisposed( pooledObjects.remove( 0 ) ); } } } /** * Override this to be notified of object disposal. Called * after the object has been removed. Occurs when the pool * is trimmed. * * @param obj */ public void objectDisposed( Object obj ) { } /** * Override this to be notified of object addition. * Called before object is to be added. * * @param obj */ public void objectAdded( Object obj ) { } /** * Override this to be notified of object retrieval. * Called after object removed from the pool, but * before returned to the client. * * @param obj */ public void objectRetrieved( Object obj ) { } /** * Periodically at <code>sweepInterval</code> goes through * and tests if the pool should be trimmed. * * @author bert * */ class Sweeper implements Runnable { private final transient SweeperPool pool; private transient boolean service = false; private final transient int sweepInterval; /** * */ public Sweeper( SweeperPool pool, int sweepInterval ) { super( ); this.sweepInterval = sweepInterval; this.pool = pool; } /** * Run the seeper. * * @see java.lang.Runnable#run() */ public void run( ) { debug( "started" ); synchronized( this ) { while( service ) { if( sweepInterval > 0 ) { try { wait( sweepInterval * 1000 ); } catch( InterruptedException e ) { } runSweep( ); } } } debug( "stopped" ); } public void start( ) { if( ! service ) { Thread t = new Thread( this ); t.start( ); service = true; } } public synchronized void stop( ) { service = false; notifyAll( ); } private final void debug( String msg ) { if( DEBUG ) { System.err.println( this + ":" + msg ); } } private void runSweep( ) { debug( "runningSweep. time=" + System.currentTimeMillis( ) ); pool.trim( ); } } } --- NEW FILE: ThreadSafeMap.java --- package org.apache.plexus.util; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Concurrent reads, but synchronized writes. Wraps a <code>HashMap</code> * * <p>Writes will block new reads, and current reads will block new writes<p> * * <p>Due to the extra overhead of locks, this map should only be used if there are * far more reads then writes. If the ratio is about equal then just creating a * synchronized Map would be faster</p> * * <p>The iterators returned by all of this class's "collection view methods" are * fail-fast: if the map is structurally modified at any time after the iterator is * created, in any way except through the iterator's own remove or add methods, * the iterator will throw a ConcurrentModificationException.</p> * * <p>Created on 17/06/2003</p> * * * @author <a href="mailto:[email protected]">Bert van Brakel</a> * @version $Revision: 1.1 $ */ public class ThreadSafeMap implements Map { /** * The Map holding the entries. */ private Map objects; /** * How many readers are currently reading the Map */ private long readers = 0; /** * Whether a write lock, or write request is active */ private boolean writeLock = false; /** * The object synchronized against. Seperate object used and not this * Map instance because we don't know if external code is going to * synchronize on this Map and stuff things up. */ private Object lock = new Object( ); private ThreadSafeSet keySet = null; private ThreadSafeSet entrySet = null; private ThreadSafeCollection values = null; /** * Constructor * * */ public ThreadSafeMap( ) { super( ); objects = new HashMap( ); } public Set entrySet( ) { if( entrySet == null ) { entrySet = new ThreadSafeSet( this, objects.entrySet( ) ); } return entrySet; } public Set keySet( ) { if( keySet == null ) { keySet = new ThreadSafeSet( this, objects.keySet( ) ); } return keySet; } /** * Constructor * * */ public ThreadSafeMap( int initializeSize ) { super( ); objects = new HashMap( initializeSize ); } /** * Return the object which we lock against. Useful * if we want to call multiple methods on this * object and we want to have exclusive access during * that time. * * <p>NOTE: not yet implementetd!!</p> * * @return */ public Object getWriteLock( ) { return lock; } /** * If a write is currently underway, or is waiting for readers to finish, * this method will block until the write has completed. * */ private void enteringRead( ) { if( writeLock ) { synchronized( lock ) { while( writeLock ) { try { lock.wait( ); } catch( InterruptedException e ) { } } } } readers++; } /** * Called when a read has finished. If it is the last reader, notify the * waiting writer. * */ private void exitingRead( ) { readers--; if( readers == 0 ) { //notify waiting write lock... synchronized( lock ) { lock.notifyAll( ); } } } public Object[] getValues( ) { enteringRead( ); Object[] values = objects.values( ).toArray( ); exitingRead( ); return values; } public Object get( Object key ) { if( key == null ) { return null; } enteringRead( ); Object obj = objects.get( key ); exitingRead( ); return obj; } public Object put( Object key, Object obj ) { if( key == null ) { return null; } obtainWriteLock( ); Object prev = objects.put( key, obj ); releaseWriteLock( ); return prev; } public Object remove( Object key ) { if( key == null ) { return null; } obtainWriteLock( ); Object obj = objects.remove( key ); releaseWriteLock( ); return obj; } public void removeAll( Object[] keys ) { obtainWriteLock( ); for( int i = 0; i < keys.length; i++ ) { objects.remove( keys[i] ); } releaseWriteLock( ); } /** * @see java.util.Map#containsKey(java.lang.Object) */ public boolean containsKey( Object key ) { enteringRead( ); boolean ret = objects.containsKey( key ); exitingRead( ); return ret; } /** * @see java.util.Map#containsValue(java.lang.Object) */ public boolean containsValue( Object value ) { enteringRead( ); boolean ret = objects.containsValue( value ); exitingRead( ); return ret; } /** * @see java.util.Map#isEmpty() */ public boolean isEmpty( ) { if( objects.size( ) == 0 ) { return true; } else { return false; } } /** * @see java.util.Map#putAll(java.util.Map) */ public void putAll( Map map ) { if( map == null ) { return; } obtainWriteLock( ); //put this in a try block as the underlying map may //throw an exception (for example if the given Map is modified, //contains erroneuous values), and we want to ensure the //writelock is released try { objects.putAll( map ); } finally { releaseWriteLock( ); } } /** * @see java.util.Map#size() */ public int size( ) { return objects.size( ); } /** * @todo : return a non modifiable or locking Collection?? * @see java.util.Map#values() */ public Collection values( ) { if( values == null ) { values = new ThreadSafeCollection( this, objects.values( ) ); } return values; } public void clear( ) { obtainWriteLock( ); objects.clear( ); releaseWriteLock( ); } /** * Obtain a writelock. This method will block until all current readers * have finished. Any attempted reads once this lock is obtained will * also block until this write has completed. * */ private void obtainWriteLock( ) { synchronized( lock ) { while( writeLock ) { //wait till the current writer has finished try { lock.wait( ); } catch( InterruptedException e ) { } } //only thread which got this far, //notify intention to all to do //a write writeLock = true; //wait till all the readers have finished. //Need to do this as some reads may take a long //time if the Map is large. while( readers > 0 ) { try { lock.wait( ); } catch( InterruptedException e ) { } } } } /** * Release the write lock and notify and waiting readers to continue. * */ private synchronized void releaseWriteLock( ) { //only the current thread which holds //the lock can do this synchronized( lock ) { writeLock = false; //and notify waiting readers and //writers. lock.notifyAll( ); } } /** * Takes part in the read/write locking of the ThreadedMap * * <p>Created on 17/06/2003</p> * * @author <a href="mailto:[email protected]">Bert van Brakel</a> * * @version $Revision: 1.1 $ */ class ThreadSafeSet implements Set { private final Set set; private final ThreadSafeMap map; ThreadSafeSet( ThreadSafeMap parent, Set set ) { this.set = set; this.map = parent; } /** * @param o * @return */ public boolean add( Object o ) { map.obtainWriteLock( ); boolean ret = set.add( o ); map.releaseWriteLock( ); return ret; } /** * @param c * @return */ public boolean addAll( Collection c ) { map.obtainWriteLock( ); boolean ret = set.addAll( c ); map.releaseWriteLock( ); return ret; } /** * */ public void clear( ) { map.obtainWriteLock( ); set.clear( ); map.releaseWriteLock( ); } /** * @param o * @return */ public boolean contains( Object o ) { map.enteringRead( ); boolean result = set.contains( o ); map.exitingRead( ); return result; } /** * @param c * @return */ public boolean containsAll( Collection c ) { map.enteringRead( ); boolean result = set.containsAll( c ); map.exitingRead( ); return result; } /** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals( Object obj ) { map.enteringRead( ); boolean result = set.equals( obj ); map.exitingRead( ); return result; } /** * @see java.lang.Object#hashCode() */ public int hashCode( ) { map.enteringRead( ); int ret = set.hashCode( ); map.exitingRead( ); return ret; } /** * @return */ public boolean isEmpty( ) { return set.isEmpty( ); } /** * @return */ public Iterator iterator( ) { return new ThreadSafeIterator( map, set.iterator( ) ); } /** * @param o * @return */ public boolean remove( Object o ) { map.obtainWriteLock( ); boolean ret = set.remove( o ); map.releaseWriteLock( ); return ret; } /** * @param c * @return */ public boolean removeAll( Collection c ) { map.obtainWriteLock( ); boolean ret = set.removeAll( c ); map.releaseWriteLock( ); return ret; } /** * @param c * @return */ public boolean retainAll( Collection c ) { map.obtainWriteLock( ); boolean ret = set.retainAll( c ); map.releaseWriteLock( ); return ret; } /** * @return */ public int size( ) { return set.size( ); } /** * @return */ public Object[] toArray( ) { return set.toArray( ); } /** * @param a * @return */ public Object[] toArray( Object[] a ) { return set.toArray( a ); } /** * @see java.lang.Object#toString() */ public String toString( ) { return set.toString( ); } } class ThreadSafeIterator implements Iterator { private final Iterator iter; private final ThreadSafeMap map; /** * Constructor * * */ public ThreadSafeIterator( ThreadSafeMap lockMap, Iterator delegate ) { super( ); this.iter = delegate; this.map = lockMap; } /** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals( Object obj ) { return iter.equals( obj ); } /** * @see java.lang.Object#hashCode() */ public int hashCode( ) { return iter.hashCode( ); } /** * @return */ public boolean hasNext( ) { return iter.hasNext( ); } /** * @return */ public Object next( ) { map.enteringRead( ); Object ret = null; try { //within try incase something gets modified and it //throws a wobbly, though shouldn't happen with //the locks ret = iter.next( ); } finally { map.exitingRead( ); } return ret; } /** * */ public void remove( ) { try { map.obtainWriteLock( ); iter.remove( ); } finally { map.releaseWriteLock( ); } } /** * @see java.lang.Object#toString() */ public String toString( ) { return iter.toString( ); } } class ThreadSafeCollection implements Collection { private final ThreadSafeMap map; private Collection col; ThreadSafeCollection( ThreadSafeMap map, Collection col ) { this.col = col; this.map = map; } /** * @see java.util.Collection#add(java.lang.Object) */ public boolean add( Object o ) { // not supported return false; } /** * @see java.util.Collection#addAll(java.util.Collection) */ public boolean addAll( Collection c ) { // not supported return false; } /** * @see java.util.Collection#clear() */ public void clear( ) { map.clear( ); } /** * @see java.util.Collection#contains(java.lang.Object) */ public boolean contains( Object o ) { return col.contains( o ); } /** * @see java.util.Collection#containsAll(java.util.Collection) */ public boolean containsAll( Collection c ) { map.enteringRead( ); boolean ret = col.containsAll( c ); map.exitingRead( ); return ret; } /** * @see java.util.Collection#isEmpty() */ public boolean isEmpty( ) { if( map.size( ) == 0 ) { return true; } else { return false; } } /** * @see java.util.Collection#iterator() */ public Iterator iterator( ) { map.enteringRead( ); Iterator iter = new ThreadSafeIterator( map, col.iterator( ) ); map.exitingRead( ); return iter; } /** * @see java.util.Collection#remove(java.lang.Object) */ public boolean remove( Object o ) { map.obtainWriteLock( ); boolean ret = col.remove( o ); map.releaseWriteLock( ); return ret; } /** * @see java.util.Collection#removeAll(java.util.Collection) */ public boolean removeAll( Collection c ) { map.obtainWriteLock( ); boolean ret = col.removeAll( c ); map.releaseWriteLock( ); return ret; } /** * @see java.util.Collection#retainAll(java.util.Collection) */ public boolean retainAll( Collection c ) { map.obtainWriteLock( ); boolean ret = col.retainAll( c ); map.releaseWriteLock( ); return ret; } /** * @see java.util.Collection#size() */ public int size( ) { return map.size( ); } /** * @see java.util.Collection#toArray() */ public Object[] toArray( ) { map.enteringRead( ); Object[] ret = col.toArray( ); map.exitingRead( ); return ret; } /** * @see java.util.Collection#toArray(java.lang.Object[]) */ public Object[] toArray( Object[] a ) { map.enteringRead( ); Object[] ret = col.toArray( a ); map.exitingRead( ); return ret; } } } --- NEW FILE: Tracer.java --- package org.apache.plexus.util; import java.io.PrintWriter; import java.io.StringWriter; /** * Convenience class to handle throwable stacktraces * * <p>Created on 18/06/2003</p> * * @author <a href="mailto:[email protected]">Bert van Brakel</a> * @version $Revision: 1.1 $ */ public class Tracer { /** * Constructor * * */ private Tracer() { super(); } /** * Return the throwable stack trace as a string * */ public static String traceToString(Throwable t) { if( t== null) { return null; } StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); return sw.toString(); } }