Coverage Report - org.codehaus.plexus.util.SweeperPool
 
Classes in this File Line Coverage Branch Coverage Complexity
SweeperPool
88%
47/53
73%
25/34
2.526
SweeperPool$Sweeper
96%
32/33
60%
6/10
2.526
 
 1  
 package org.codehaus.plexus.util;
 2  
 
 3  
 /*
 4  
  * Copyright The Codehaus Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 import java.util.ArrayList;
 20  
 
 21  
 /**
 22  
  * Pools a bunch of objects . Runs a sweeper periodically to keep it down to size. The objects in the pool first get
 23  
  * disposed first.
 24  
  *
 25  
  * @author <a href="mailto:bert@tuaworks.co.nz">Bert van Brakel</a>
 26  
  * @version $Id$
 27  
  */
 28  
 public class SweeperPool
 29  
 {
 30  
     /***/
 31  
     private static final boolean DEBUG = false;
 32  
 
 33  
     /** Sweeps the pool periodically to trim it's size */
 34  
     private transient Sweeper sweeper;
 35  
 
 36  
     /** Absolute maximum size of the pool. */
 37  
     private transient int maxSize;
 38  
 
 39  
     /** The size the pool gets trimmed down to */
 40  
     private transient int minSize;
 41  
 
 42  
     /**
 43  
      * When the sweeper runs and the pool is over this size, then the pool is trimmed
 44  
      */
 45  
     private int triggerSize;
 46  
 
 47  
     /** Holds the pooled objects */
 48  
     private ArrayList<Object> pooledObjects;
 49  
 
 50  
     /** Flag indicating this pool is shuting down */
 51  2
     private boolean shuttingDown = false;
 52  
 
 53  
     // private Vector used;
 54  
 
 55  
     /**
 56  
      * <p>
 57  
      * There are a number of settings to control how the pool operates.
 58  
      * <ul>
 59  
      * <li><code>minSize</code> - this is the size the pool is trimmed to</li>
 60  
      * <li><code>triggerSize</code> - this determines if the pool is trimmed when the sweeper runs. If the pool size is
 61  
      * greater or equal than this value then the pool is trimmed to <code>minSize</code>.</lie>
 62  
      * <li><code>maxSize</code> - if the pool has reached this size, any objects added are immediately disposed. If the
 63  
      * pool is this size when the sweeper runs, then the pool is also trimmed to <code>minSize</code> irrespective of
 64  
      * the triggerSize.</li>
 65  
      * <li><code>sweepInterval</code> - how often the sweeper runs. Is actually the time since the sweeper last finished
 66  
      * a pass. 0 if the sweeper should not run.</li>
 67  
      * </ul>
 68  
      * </p>
 69  
      * <p>
 70  
      * Any value less than 0 is automatically converted to 0
 71  
      * </p>
 72  
      */
 73  
     public SweeperPool( int maxSize, int minSize, int intialCapacity, int sweepInterval, int triggerSize )
 74  
     {
 75  2
         super();
 76  2
         this.maxSize = saneConvert( maxSize );
 77  2
         this.minSize = saneConvert( minSize );
 78  2
         this.triggerSize = saneConvert( triggerSize );
 79  2
         pooledObjects = new ArrayList( intialCapacity );
 80  
 
 81  
         // only run a sweeper if sweep interval is positive
 82  2
         if ( sweepInterval > 0 )
 83  
         {
 84  1
             sweeper = new Sweeper( this, sweepInterval );
 85  1
             sweeper.start();
 86  
         }
 87  2
     }
 88  
 
 89  
     private int saneConvert( int value )
 90  
     {
 91  6
         if ( value < 0 )
 92  
         {
 93  0
             return 0;
 94  
         }
 95  
         else
 96  
         {
 97  6
             return value;
 98  
         }
 99  
     }
 100  
 
 101  
     /**
 102  
      * Return the pooled object
 103  
      */
 104  
     public synchronized Object get()
 105  
     {
 106  4
         if ( ( pooledObjects.size() == 0 ) || shuttingDown )
 107  
         {
 108  1
             return null;
 109  
         }
 110  
         else
 111  
         {
 112  3
             Object obj = pooledObjects.remove( 0 );
 113  3
             objectRetrieved( obj );
 114  
 
 115  
             // used.add(obj);
 116  3
             return obj;
 117  
         }
 118  
     }
 119  
 
 120  
     /**
 121  
      * Add an object to the pool
 122  
      *
 123  
      * @param obj the object to pool. Can be null.
 124  
      * @return true if the object was added to the pool, false if it was disposed or null
 125  
      */
 126  
     public synchronized boolean put( Object obj )
 127  
     {
 128  8
         objectAdded( obj );
 129  
 
 130  8
         if ( ( obj != null ) && ( pooledObjects.size() < maxSize ) && ( shuttingDown == false ) )
 131  
         {
 132  7
             pooledObjects.add( obj );
 133  
 
 134  7
             return true;
 135  
         }
 136  1
         else if ( obj != null )
 137  
         {
 138  
             // no longer need the object, so dispose it
 139  1
             objectDisposed( obj );
 140  
         }
 141  
 
 142  1
         return false;
 143  
     }
 144  
 
 145  
     /**
 146  
      * Return the number of pooled objects. This is never greater than t maximum size of the pool
 147  
      *
 148  
      * @return the number of pooled objects
 149  
      */
 150  
     public synchronized int getSize()
 151  
     {
 152  6
         return pooledObjects.size();
 153  
     }
 154  
 
 155  
     /**
 156  
      * Dispose of this pool. Stops the sweeper and disposes each object in the pool
 157  
      */
 158  
     public void dispose()
 159  
     {
 160  2
         shuttingDown = true;
 161  
 
 162  2
         if ( sweeper != null )
 163  
         {
 164  1
             sweeper.stop();
 165  
             try
 166  
             {
 167  1
                 sweeper.join();
 168  
             }
 169  0
             catch ( InterruptedException e )
 170  
             {
 171  0
                 System.err.println( "Unexpected exception occurred: " );
 172  0
                 e.printStackTrace();
 173  1
             }
 174  
         }
 175  
 
 176  2
         synchronized ( this )
 177  
         {
 178  
             // use an array here as objects may still be being put back in the pool
 179  
             // and we don't want to throw a ConcurrentModificationException
 180  2
             Object[] objects = pooledObjects.toArray();
 181  
 
 182  3
             for ( Object object : objects )
 183  
             {
 184  1
                 objectDisposed( object );
 185  
             }
 186  
 
 187  2
             pooledObjects.clear();
 188  2
         }
 189  2
     }
 190  
 
 191  
     /**
 192  
      * A pool has been disposed if has been shutdown and the sweeper has completed running.
 193  
      *
 194  
      * @return true if the pool has been disposed, false otherwise
 195  
      */
 196  
     boolean isDisposed()
 197  
     {
 198  2
         if ( !shuttingDown )
 199  
         {
 200  0
             return false;
 201  
         }
 202  
 
 203  
         // A null sweeper means one was never started.
 204  2
         if ( sweeper == null )
 205  
         {
 206  1
             return true;
 207  
         }
 208  
 
 209  1
         return sweeper.hasStopped();
 210  
     }
 211  
 
 212  
     /**
 213  
      * Trim the pool down to min size
 214  
      */
 215  
     public synchronized void trim()
 216  
     {
 217  2
         if ( ( ( triggerSize > 0 ) && ( pooledObjects.size() >= triggerSize ) )
 218  
             || ( ( maxSize > 0 ) && ( pooledObjects.size() >= maxSize ) ) )
 219  
         {
 220  4
             while ( pooledObjects.size() > minSize )
 221  
             {
 222  3
                 objectDisposed( pooledObjects.remove( 0 ) );
 223  
             }
 224  
         }
 225  2
     }
 226  
 
 227  
     /**
 228  
      * Override this to be notified of object disposal. Called after the object has been removed. Occurs when the pool
 229  
      * is trimmed.
 230  
      *
 231  
      * @param obj
 232  
      */
 233  
     public void objectDisposed( Object obj )
 234  
     {
 235  0
     }
 236  
 
 237  
     /**
 238  
      * Override this to be notified of object addition. Called before object is to be added.
 239  
      *
 240  
      * @param obj
 241  
      */
 242  
     public void objectAdded( Object obj )
 243  
     {
 244  8
     }
 245  
 
 246  
     /**
 247  
      * Override this to be notified of object retrieval. Called after object removed from the pool, but before returned
 248  
      * to the client.
 249  
      *
 250  
      * @param obj
 251  
      */
 252  
     public void objectRetrieved( Object obj )
 253  
     {
 254  3
     }
 255  
 
 256  
     /**
 257  
      * Periodically at <code>sweepInterval</code> goes through and tests if the pool should be trimmed.
 258  
      *
 259  
      * @author bert
 260  
      */
 261  
     private static class Sweeper
 262  
         implements Runnable
 263  
     {
 264  
         private final transient SweeperPool pool;
 265  
 
 266  1
         private transient boolean service = false;
 267  
 
 268  
         private final transient int sweepInterval;
 269  
 
 270  1
         private transient Thread t = null;
 271  
 
 272  
         /**
 273  
          *
 274  
          */
 275  
         public Sweeper( SweeperPool pool, int sweepInterval )
 276  
         {
 277  1
             super();
 278  1
             this.sweepInterval = sweepInterval;
 279  1
             this.pool = pool;
 280  1
         }
 281  
 
 282  
         /**
 283  
          * Run the sweeper.
 284  
          *
 285  
          * @see java.lang.Runnable#run()
 286  
          */
 287  
         public void run()
 288  
         {
 289  1
             debug( "started" );
 290  
 
 291  1
             if ( sweepInterval > 0 )
 292  
             {
 293  1
                 synchronized ( this )
 294  
                 {
 295  3
                     while ( service )
 296  
                     {
 297  
                         try
 298  
                         {
 299  
                             // wait specified number of seconds
 300  
                             // before running next sweep
 301  2
                             wait( sweepInterval * 1000 );
 302  
                         }
 303  0
                         catch ( InterruptedException e )
 304  
                         {
 305  2
                         }
 306  2
                         runSweep();
 307  
                     }
 308  1
                 }
 309  
             }
 310  
 
 311  1
             debug( "stopped" );
 312  1
         }
 313  
 
 314  
         public void start()
 315  
         {
 316  1
             if ( !service )
 317  
             {
 318  1
                 service = true;
 319  1
                 t = new Thread( this );
 320  1
                 t.setName( "Sweeper" );
 321  1
                 t.start();
 322  
             }
 323  1
         }
 324  
 
 325  
         public synchronized void stop()
 326  
         {
 327  1
             service = false;
 328  1
             notifyAll();
 329  1
         }
 330  
 
 331  
         void join()
 332  
             throws InterruptedException
 333  
         {
 334  1
             t.join();
 335  1
         }
 336  
 
 337  
         boolean hasStopped()
 338  
         {
 339  1
             return !service && !t.isAlive();
 340  
         }
 341  
 
 342  
         private final void debug( String msg )
 343  
         {
 344  
             if ( DEBUG )
 345  
             {
 346  
                 System.err.println( this + ":" + msg );
 347  
             }
 348  4
         }
 349  
 
 350  
         private void runSweep()
 351  
         {
 352  2
             debug( "runningSweep. time=" + System.currentTimeMillis() );
 353  2
             pool.trim();
 354  2
         }
 355  
     }
 356  
 }