Coverage report

  %line %branch
org.apache.turbine.services.pool.TurbinePoolService
24% 
84% 

 1  
 package org.apache.turbine.services.pool;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2005 The Apache Software 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.lang.reflect.Method;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 
 25  
 import org.apache.commons.configuration.Configuration;
 26  
 
 27  
 import org.apache.commons.logging.Log;
 28  
 import org.apache.commons.logging.LogFactory;
 29  
 
 30  
 import org.apache.turbine.services.InitializationException;
 31  
 import org.apache.turbine.services.TurbineBaseService;
 32  
 import org.apache.turbine.services.factory.FactoryService;
 33  
 import org.apache.turbine.services.factory.TurbineFactory;
 34  
 import org.apache.turbine.util.TurbineException;
 35  
 import org.apache.turbine.util.pool.ArrayCtorRecyclable;
 36  
 import org.apache.turbine.util.pool.BoundedBuffer;
 37  
 import org.apache.turbine.util.pool.Recyclable;
 38  
 
 39  
 /**
 40  
  * The Pool Service extends the Factory Service by adding support
 41  
  * for pooling instantiated objects. When a new instance is
 42  
  * requested, the service first checks its pool if one is available.
 43  
  * If the the pool is empty, a new instance will be requested
 44  
  * from the FactoryService.
 45  
  *
 46  
  * <p>For objects implementing the Recyclable interface, a recycle
 47  
  * method will be called, when they taken from the pool, and
 48  
  * a dispose method, when they are returned to the pool.
 49  
  *
 50  
  * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
 51  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 52  
  * @version $Id: TurbinePoolService.java 264148 2005-08-29 14:21:04Z henning $
 53  
  */
 54  21
 public class TurbinePoolService
 55  
         extends TurbineBaseService
 56  
         implements PoolService
 57  
 {
 58  
     /** Are we currently debugging the pool recycling? */
 59  42
     private boolean debugPool = false;
 60  
 
 61  
     /** Internal Reference to the Factory */
 62  
     private FactoryService factoryService;
 63  
 
 64  
     /** Logging */
 65  63
     private static Log log = LogFactory.getLog(TurbinePoolService.class);
 66  
 
 67  
     /**
 68  
      * An inner class for class specific pools.
 69  
      */
 70  
     private class PoolBuffer
 71  
     {
 72  
         /**
 73  
          * An inner class for cached recycle methods.
 74  
          */
 75  
         private class Recycler
 76  
         {
 77  
             /** The recycle method. */
 78  
             private final Method recycle;
 79  
 
 80  
             /** The signature. */
 81  
             private final String[] signature;
 82  
 
 83  
             /**
 84  
              * Constructs a new recycler.
 85  
              *
 86  
              * @param rec the recycle method.
 87  
              * @param sign the signature.
 88  
              */
 89  
             public Recycler(Method rec, String[] sign)
 90  
             {
 91  
                 recycle = rec;
 92  
                 signature = ((sign != null) && (sign.length > 0))
 93  
                         ? sign : null;
 94  
             }
 95  
 
 96  
             /**
 97  
              * Matches the given signature against
 98  
              * that of the recycle method of this recycler.
 99  
              *
 100  
              * @param sign the signature.
 101  
              * @return the matching recycle method or null.
 102  
              */
 103  
             public Method match(String[] sign)
 104  
             {
 105  
                 if ((sign != null) && (sign.length > 0))
 106  
                 {
 107  
                     if ((signature != null)
 108  
                             && (sign.length == signature.length))
 109  
                     {
 110  
                         for (int i = 0; i < signature.length; i++)
 111  
                         {
 112  
                             if (!signature[i].equals(sign[i]))
 113  
                             {
 114  
                                 return null;
 115  
                             }
 116  
                         }
 117  
                         return recycle;
 118  
                     }
 119  
                     else
 120  
                     {
 121  
                         return null;
 122  
                     }
 123  
                 }
 124  
                 else if (signature == null)
 125  
                 {
 126  
                     return recycle;
 127  
                 }
 128  
                 else
 129  
                 {
 130  
                     return null;
 131  
                 }
 132  
             }
 133  
         }
 134  
 
 135  
         /** A buffer for class instances. */
 136  
         private BoundedBuffer pool;
 137  
 
 138  
         /** A flag to determine if a more efficient recycler is implemented. */
 139  
         private boolean arrayCtorRecyclable;
 140  
 
 141  
         /** A cache for recycling methods. */
 142  
         private ArrayList recyclers;
 143  
 
 144  
         /**
 145  
          * Constructs a new pool buffer with a specific capacity.
 146  
          *
 147  
          * @param capacity a capacity.
 148  
          */
 149  
         public PoolBuffer(int capacity)
 150  
         {
 151  
             pool = new BoundedBuffer(capacity);
 152  
         }
 153  
 
 154  
         /**
 155  
          * Tells pool that it contains objects which can be
 156  
          * initialized using an Object array.
 157  
          *
 158  
          * @param isArrayCtor a <code>boolean</code> value
 159  
          */
 160  
         public void setArrayCtorRecyclable(boolean isArrayCtor)
 161  
         {
 162  
             arrayCtorRecyclable = isArrayCtor;
 163  
         }
 164  
 
 165  
         /**
 166  
          * Polls for an instance from the pool.
 167  
          *
 168  
          * @return an instance or null.
 169  
          */
 170  
         public Object poll(Object[] params, String[] signature)
 171  
                 throws TurbineException
 172  
         {
 173  
             // If we're debugging the recycling code, we want different
 174  
             // objects to be used when pulling from the pool. Ensure that
 175  
             // each pool fills up at least to half its capacity.
 176  
             if (debugPool && (pool.size() < (pool.capacity() / 2)))
 177  
             {
 178  
                 log.debug("Size: " + pool.size()
 179  
                         + ", capacity: " + pool.capacity());
 180  
                 return null;
 181  
             }
 182  
 
 183  
             Object instance = pool.poll();
 184  
             if (instance != null)
 185  
             {
 186  
                 if (arrayCtorRecyclable)
 187  
                 {
 188  
                     ((ArrayCtorRecyclable) instance).recycle(params);
 189  
                 }
 190  
                 else if (instance instanceof Recyclable)
 191  
                 {
 192  
                     try
 193  
                     {
 194  
                         if ((signature != null) && (signature.length > 0))
 195  
                         {
 196  
                             /* Get the recycle method from the cache. */
 197  
                             Method recycle = getRecycle(signature);
 198  
                             if (recycle == null)
 199  
                             {
 200  
                                 synchronized (this)
 201  
                                 {
 202  
                                     /* Make a synchronized recheck. */
 203  
                                     recycle = getRecycle(signature);
 204  
                                     if (recycle == null)
 205  
                                     {
 206  
                                         Class clazz = instance.getClass();
 207  
                                         recycle = clazz.getMethod("recycle",
 208  
                                                 factoryService.getSignature(
 209  
                                                         clazz, params, signature));
 210  
                                         ArrayList cache = recyclers != null
 211  
                                                 ? (ArrayList) recyclers.clone()
 212  
                                                 : new ArrayList();
 213  
                                         cache.add(
 214  
                                                 new Recycler(recycle, signature));
 215  
                                         recyclers = cache;
 216  
                                     }
 217  
                                 }
 218  
                             }
 219  
                             recycle.invoke(instance, params);
 220  
                         }
 221  
                         else
 222  
                         {
 223  
                             ((Recyclable) instance).recycle();
 224  
                         }
 225  
                     }
 226  
                     catch (Exception x)
 227  
                     {
 228  
                         throw new TurbineException(
 229  
                                 "Recycling failed for " + instance.getClass().getName(), x);
 230  
                     }
 231  
                 }
 232  
             }
 233  
             return instance;
 234  
         }
 235  
 
 236  
         /**
 237  
          * Offers an instance to the pool.
 238  
          *
 239  
          * @param instance an instance.
 240  
          */
 241  
         public boolean offer(Object instance)
 242  
         {
 243  
             if (instance instanceof Recyclable)
 244  
             {
 245  
                 try
 246  
                 {
 247  
                     ((Recyclable) instance).dispose();
 248  
                 }
 249  
                 catch (Exception x)
 250  
                 {
 251  
                     return false;
 252  
                 }
 253  
             }
 254  
             return pool.offer(instance);
 255  
         }
 256  
 
 257  
         /**
 258  
          * Returns the capacity of the pool.
 259  
          *
 260  
          * @return the capacity.
 261  
          */
 262  
         public int capacity()
 263  
         {
 264  
             return pool.capacity();
 265  
         }
 266  
 
 267  
         /**
 268  
          * Returns the size of the pool.
 269  
          *
 270  
          * @return the size.
 271  
          */
 272  
         public int size()
 273  
         {
 274  
             return pool.size();
 275  
         }
 276  
 
 277  
         /**
 278  
          * Returns a cached recycle method
 279  
          * corresponding to the given signature.
 280  
          *
 281  
          * @param signature the signature.
 282  
          * @return the recycle method or null.
 283  
          */
 284  
         private Method getRecycle(String[] signature)
 285  
         {
 286  
             ArrayList cache = recyclers;
 287  
             if (cache != null)
 288  
             {
 289  
                 Method recycle;
 290  
                 for (Iterator i = cache.iterator(); i.hasNext();)
 291  
                 {
 292  
                     recycle = ((Recycler) i.next()).match(signature);
 293  
                     if (recycle != null)
 294  
                     {
 295  
                         return recycle;
 296  
                     }
 297  
                 }
 298  
             }
 299  
             return null;
 300  
         }
 301  
     }
 302  
 
 303  
     /**
 304  
      * The default capacity of pools.
 305  
      */
 306  42
     private int poolCapacity = DEFAULT_POOL_CAPACITY;
 307  
 
 308  
     /**
 309  
      * The pool repository, one pool for each class.
 310  
      */
 311  42
     private HashMap poolRepository = new HashMap();
 312  
 
 313  
     /**
 314  
      * Constructs a Pool Service.
 315  
      */
 316  
     public TurbinePoolService()
 317  42
     {
 318  42
     }
 319  
 
 320  
     /**
 321  
      * Initializes the service by setting the pool capacity.
 322  
      *
 323  
      * @param config initialization configuration.
 324  
      * @throws InitializationException if initialization fails.
 325  
      */
 326  
     public void init()
 327  
             throws InitializationException
 328  
     {
 329  44
         Configuration conf = getConfiguration();
 330  
 
 331  44
         int capacity = conf.getInt(POOL_CAPACITY_KEY,
 332  
                 DEFAULT_POOL_CAPACITY);
 333  
 
 334  44
         if (capacity <= 0)
 335  
         {
 336  0
             throw new IllegalArgumentException("Capacity must be >0");
 337  
         }
 338  44
         poolCapacity = capacity;
 339  
 
 340  44
         debugPool = conf.getBoolean(POOL_DEBUG_KEY,
 341  
                 POOL_DEBUG_DEFAULT);
 342  
 
 343  44
         if (debugPool)
 344  
         {
 345  0
             log.info("Activated Pool Debugging!");
 346  
         }
 347  
 
 348  44
         factoryService = TurbineFactory.getService();
 349  
 
 350  44
         if (factoryService == null)
 351  
         {
 352  0
             throw new InitializationException("Factory Service is not configured"
 353  
                     + " but required for the Pool Service!");
 354  
         }
 355  
 
 356  44
         setInit(true);
 357  44
     }
 358  
 
 359  
     /**
 360  
      * Gets an instance of a named class either from the pool
 361  
      * or by calling the Factory Service if the pool is empty.
 362  
      *
 363  
      * @param className the name of the class.
 364  
      * @return the instance.
 365  
      * @throws TurbineException if recycling fails.
 366  
      */
 367  
     public Object getInstance(String className)
 368  
             throws TurbineException
 369  
     {
 370  0
         Object instance = pollInstance(className, null, class="keyword">null);
 371  0
         return (instance == null)
 372  
                 ? factoryService.getInstance(className)
 373  
                 : instance;
 374  
     }
 375  
 
 376  
     /**
 377  
      * Gets an instance of a named class either from the pool
 378  
      * or by calling the Factory Service if the pool is empty.
 379  
      * The specified class loader will be passed to the Factory Service.
 380  
      *
 381  
      * @param className the name of the class.
 382  
      * @param loader the class loader.
 383  
      * @return the instance.
 384  
      * @throws TurbineException if recycling fails.
 385  
      */
 386  
     public Object getInstance(String className,
 387  
                               ClassLoader loader)
 388  
             throws TurbineException
 389  
     {
 390  0
         Object instance = pollInstance(className, null, class="keyword">null);
 391  0
         return (instance == null)
 392  
                 ? factoryService.getInstance(className, loader)
 393  
                 : instance;
 394  
     }
 395  
 
 396  
     /**
 397  
      * Gets an instance of a named class either from the pool
 398  
      * or by calling the Factory Service if the pool is empty.
 399  
      * Parameters for its constructor are given as an array of objects,
 400  
      * primitive types must be wrapped with a corresponding class.
 401  
      *
 402  
      * @param className the name of the class.
 403  
      * @param loader the class loader.
 404  
      * @param params an array containing the parameters of the constructor.
 405  
      * @param signature an array containing the signature of the constructor.
 406  
      * @return the instance.
 407  
      * @throws TurbineException if recycling fails.
 408  
      */
 409  
     public Object getInstance(String className,
 410  
                               Object[] params,
 411  
                               String[] signature)
 412  
             throws TurbineException
 413  
     {
 414  0
         Object instance = pollInstance(className, params, signature);
 415  0
         return (instance == null)
 416  
                 ? factoryService.getInstance(className, params, signature)
 417  
                 : instance;
 418  
     }
 419  
 
 420  
     /**
 421  
      * Gets an instance of a named class either from the pool
 422  
      * or by calling the Factory Service if the pool is empty.
 423  
      * Parameters for its constructor are given as an array of objects,
 424  
      * primitive types must be wrapped with a corresponding class.
 425  
      * The specified class loader will be passed to the Factory Service.
 426  
      *
 427  
      * @param className the name of the class.
 428  
      * @param loader the class loader.
 429  
      * @param params an array containing the parameters of the constructor.
 430  
      * @param signature an array containing the signature of the constructor.
 431  
      * @return the instance.
 432  
      * @throws TurbineException if recycling fails.
 433  
      */
 434  
     public Object getInstance(String className,
 435  
                               ClassLoader loader,
 436  
                               Object[] params,
 437  
                               String[] signature)
 438  
             throws TurbineException
 439  
     {
 440  0
         Object instance = pollInstance(className, params, signature);
 441  0
         return (instance == null)
 442  
                 ? factoryService.getInstance(className, loader, params, signature)
 443  
                 : instance;
 444  
     }
 445  
 
 446  
     /**
 447  
      * Tests if specified class loaders are supported for a named class.
 448  
      *
 449  
      * @param className the name of the class.
 450  
      * @return true if class loaders are supported, false otherwise.
 451  
      * @throws TurbineException if test fails.
 452  
      * @deprecated Use TurbineFactory.isLoaderSupported(className);
 453  
      */
 454  
     public boolean isLoaderSupported(String className)
 455  
             throws TurbineException
 456  
     {
 457  0
         return factoryService.isLoaderSupported(className);
 458  
     }
 459  
 
 460  
     /**
 461  
      * Gets an instance of a specified class either from the pool
 462  
      * or by instatiating from the class if the pool is empty.
 463  
      *
 464  
      * @param clazz the class.
 465  
      * @return the instance.
 466  
      * @throws TurbineException if recycling fails.
 467  
      */
 468  
     public Object getInstance(Class clazz)
 469  
             throws TurbineException
 470  
     {
 471  0
         Object instance = pollInstance(clazz.getName(), null, class="keyword">null);
 472  0
         return (instance == null)
 473  
                 ? factoryService.getInstance(clazz.getName())
 474  
                 : instance;
 475  
     }
 476  
 
 477  
     /**
 478  
      * Gets an instance of a specified class either from the pool
 479  
      * or by instatiating from the class if the pool is empty.
 480  
      *
 481  
      * @param clazz the class.
 482  
      * @param params an array containing the parameters of the constructor.
 483  
      * @param signature an array containing the signature of the constructor.
 484  
      * @return the instance.
 485  
      * @throws TurbineException if recycling fails.
 486  
      */
 487  
     public Object getInstance(Class clazz,
 488  
                               Object params[],
 489  
                               String signature[])
 490  
             throws TurbineException
 491  
     {
 492  0
         Object instance = pollInstance(clazz.getName(), params, signature);
 493  0
         return (instance == null)
 494  
                 ? factoryService.getInstance(clazz.getName(), params, signature)
 495  
                 : instance;
 496  
     }
 497  
 
 498  
     /**
 499  
      * Puts a used object back to the pool. Objects implementing
 500  
      * the Recyclable interface can provide a recycle method to
 501  
      * be called when they are reused and a dispose method to be
 502  
      * called when they are returned to the pool.
 503  
      *
 504  
      * @param instance the object instance to recycle.
 505  
      * @return true if the instance was accepted.
 506  
      */
 507  
     public boolean putInstance(Object instance)
 508  
     {
 509  0
         if (instance != null)
 510  
         {
 511  0
             HashMap repository = poolRepository;
 512  0
             String className = instance.getClass().getName();
 513  0
             PoolBuffer pool = (PoolBuffer) repository.get(className);
 514  0
             if (pool == null)
 515  
             {
 516  0
                 pool = new PoolBuffer(getCapacity(className));
 517  0
                 repository = (HashMap) repository.clone();
 518  0
                 repository.put(className, pool);
 519  0
                 poolRepository = repository;
 520  
 
 521  0
                 if (instance instanceof ArrayCtorRecyclable)
 522  
                 {
 523  0
                     pool.setArrayCtorRecyclable(true);
 524  
                 }
 525  
             }
 526  0
             return pool.offer(instance);
 527  
         }
 528  
         else
 529  
         {
 530  0
             return false;
 531  
         }
 532  
     }
 533  
 
 534  
     /**
 535  
      * Gets the capacity of the pool for a named class.
 536  
      *
 537  
      * @param className the name of the class.
 538  
      */
 539  
     public int getCapacity(String className)
 540  
     {
 541  0
         PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
 542  0
         if (pool == null)
 543  
         {
 544  
             /* Check class specific capacity. */
 545  
             int capacity;
 546  
 
 547  0
             Configuration conf = getConfiguration();
 548  0
             capacity = conf.getInt(
 549  
                     POOL_CAPACITY_KEY + '.' + className,
 550  
                     poolCapacity);
 551  0
             capacity = (capacity <= 0) ? poolCapacity : capacity;
 552  0
             return capacity;
 553  
         }
 554  
         else
 555  
         {
 556  0
             return pool.capacity();
 557  
         }
 558  
     }
 559  
 
 560  
     /**
 561  
      * Sets the capacity of the pool for a named class.
 562  
      * Note that the pool will be cleared after the change.
 563  
      *
 564  
      * @param className the name of the class.
 565  
      * @param capacity the new capacity.
 566  
      */
 567  
     public void setCapacity(String className,
 568  
                             int capacity)
 569  
     {
 570  0
         HashMap repository = poolRepository;
 571  0
         repository = (repository != null)
 572  
                 ? (HashMap) repository.clone() : new HashMap();
 573  
 
 574  0
         capacity = (capacity <= 0) ? poolCapacity : capacity;
 575  
 
 576  0
         repository.put(className, new PoolBuffer(capacity));
 577  0
         poolRepository = repository;
 578  0
     }
 579  
 
 580  
     /**
 581  
      * Gets the current size of the pool for a named class.
 582  
      *
 583  
      * @param className the name of the class.
 584  
      */
 585  
     public int getSize(String className)
 586  
     {
 587  0
         PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
 588  0
         return (pool != null) ? pool.size() : 0;
 589  
     }
 590  
 
 591  
     /**
 592  
      * Clears instances of a named class from the pool.
 593  
      *
 594  
      * @param className the name of the class.
 595  
      */
 596  
     public void clearPool(String className)
 597  
     {
 598  0
         HashMap repository = poolRepository;
 599  0
         if (repository.get(className) != null)
 600  
         {
 601  0
             repository = (HashMap) repository.clone();
 602  0
             repository.remove(className);
 603  0
             poolRepository = repository;
 604  
         }
 605  0
     }
 606  
 
 607  
     /**
 608  
      * Clears all instances from the pool.
 609  
      */
 610  
     public void clearPool()
 611  
     {
 612  0
         poolRepository = new HashMap();
 613  0
     }
 614  
 
 615  
     /**
 616  
      * Polls and recycles an object of the named class from the pool.
 617  
      *
 618  
      * @param className the name of the class.
 619  
      * @param params an array containing the parameters of the constructor.
 620  
      * @param signature an array containing the signature of the constructor.
 621  
      * @return the object or null.
 622  
      * @throws TurbineException if recycling fails.
 623  
      */
 624  
     private Object pollInstance(String className,
 625  
                                 Object[] params,
 626  
                                 String[] signature)
 627  
             throws TurbineException
 628  
     {
 629  0
         PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
 630  0
         return (pool != null) ? pool.poll(params, signature) : class="keyword">null;
 631  
     }
 632  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.