Coverage report

  %line %branch
org.apache.jcs.engine.control.CompositeCacheManager
64% 
87% 

 1  
 package org.apache.jcs.engine.control;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.io.Serializable;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Enumeration;
 27  
 import java.util.HashSet;
 28  
 import java.util.Hashtable;
 29  
 import java.util.Iterator;
 30  
 import java.util.Properties;
 31  
 import java.util.Set;
 32  
 
 33  
 import org.apache.commons.logging.Log;
 34  
 import org.apache.commons.logging.LogFactory;
 35  
 import org.apache.jcs.auxiliary.AuxiliaryCacheAttributes;
 36  
 import org.apache.jcs.auxiliary.AuxiliaryCacheFactory;
 37  
 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
 38  
 import org.apache.jcs.engine.CacheConstants;
 39  
 import org.apache.jcs.engine.CompositeCacheAttributes;
 40  
 import org.apache.jcs.engine.ElementAttributes;
 41  
 import org.apache.jcs.engine.behavior.ICacheType;
 42  
 import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
 43  
 import org.apache.jcs.engine.behavior.ICompositeCacheManager;
 44  
 import org.apache.jcs.engine.behavior.IElementAttributes;
 45  
 import org.apache.jcs.engine.behavior.IShutdownObservable;
 46  
 import org.apache.jcs.engine.behavior.IShutdownObserver;
 47  
 import org.apache.jcs.engine.stats.CacheStats;
 48  
 import org.apache.jcs.engine.stats.behavior.ICacheStats;
 49  
 import org.apache.jcs.utils.threadpool.ThreadPoolManager;
 50  
 
 51  
 /**
 52  
  * Manages a composite cache. This provides access to caches and is the primary
 53  
  * way to shutdown the caching system as a whole.
 54  
  * <p>
 55  
  * The composite cache manager is responsible for creating / configuring cache
 56  
  * regions. It serves as a factory for the ComositeCache class. The
 57  
  * CompositeCache is the core of JCS, the hub for various auxiliaries.
 58  
  * <p>
 59  
  * It is recommended that you use the JCS convenience class for all cache
 60  
  * access.
 61  
  *
 62  
  */
 63  213
 public class CompositeCacheManager
 64  
     implements IRemoteCacheConstants, Serializable, ICompositeCacheManager, IShutdownObservable
 65  
 {
 66  
     private static final long serialVersionUID = 7598584393134401756L;
 67  
 
 68  396
     private final static Log log = LogFactory.getLog( CompositeCacheManager.class );
 69  
 
 70  
     /** Caches managed by this cache manager */
 71  201
     protected Hashtable caches = new Hashtable();
 72  
 
 73  
     /** Internal system caches for this cache manager */
 74  201
     protected Hashtable systemCaches = new Hashtable();
 75  
 
 76  
     /** Number of clients accessing this cache manager */
 77  
     private int clients;
 78  
 
 79  
     /** Default cache attributes for this cache manager */
 80  201
     protected ICompositeCacheAttributes defaultCacheAttr = new CompositeCacheAttributes();
 81  
 
 82  
     /** Default elemeent attributes for this cache manager */
 83  201
     protected IElementAttributes defaultElementAttr = new ElementAttributes();
 84  
 
 85  
     /** Used to keep track of configured auxiliaries */
 86  201
     protected Hashtable auxFacs = new Hashtable( 11 );
 87  
 
 88  
     /** ??? */
 89  201
     protected Hashtable auxAttrs = new Hashtable( 11 );
 90  
 
 91  
     /** Properties with which this manager was configured */
 92  
     protected Properties props;
 93  
 
 94  
     /** The default auxiliary caches to be used if not preconfigured */
 95  
     protected String defaultAuxValues;
 96  
 
 97  
     /** The Singleton Instance */
 98  
     protected static CompositeCacheManager instance;
 99  
 
 100  
     private static final String SYSTEM_PROPERTY_KEY_PREFIX = "jcs";
 101  
 
 102  
     private static final boolean DEFAULT_USE_SYSTEM_PROPERTIES = true;
 103  
 
 104  201
     private Set shutdownObservers = new HashSet();
 105  
 
 106  
     /**
 107  
      * Gets the CacheHub instance. For backward compatibility, if this creates
 108  
      * the instance it will attempt to configure it with the default
 109  
      * configuration. If you want to configure from your own source, use
 110  
      * {@link #getUnconfiguredInstance}and then call {@link #configure}
 111  
      *
 112  
      * @return
 113  
      */
 114  
     public static synchronized CompositeCacheManager getInstance()
 115  
     {
 116  78
         if ( instance == null )
 117  
         {
 118  39
             log.debug( "Instance is null, creating with default config" );
 119  
 
 120  39
             instance = createInstance();
 121  
 
 122  39
             instance.configure();
 123  
         }
 124  
 
 125  78
         instance.incrementClients();
 126  
 
 127  78
         return instance;
 128  
     }
 129  
 
 130  
     /**
 131  
      * Initializes the cache manager using the props file for the given name.
 132  
      *
 133  
      * @param propsFilename
 134  
      * @return CompositeCacheManager configured from the give propsFileName
 135  
      */
 136  
     public static synchronized CompositeCacheManager getInstance( String propsFilename )
 137  
     {
 138  0
         if ( instance == null )
 139  
         {
 140  0
             if ( log.isInfoEnabled() )
 141  
             {
 142  0
                 log.info( "Instance is null, creating with default config [" + propsFilename + "]" );
 143  
             }
 144  
 
 145  0
             instance = createInstance();
 146  
 
 147  0
             instance.configure( propsFilename );
 148  
         }
 149  
 
 150  0
         instance.incrementClients();
 151  
 
 152  0
         return instance;
 153  
     }
 154  
 
 155  
     /**
 156  
      * Get a CacheHub instance which is not configured. If an instance already
 157  
      * exists, it will be returned.
 158  
      *
 159  
      * @return
 160  
      */
 161  
     public static synchronized CompositeCacheManager getUnconfiguredInstance()
 162  
     {
 163  228
         if ( instance == null )
 164  
         {
 165  162
             if ( log.isInfoEnabled() )
 166  
             {
 167  162
                 log.info( "Instance is null, creating with provided config" );
 168  
             }
 169  
 
 170  162
             instance = createInstance();
 171  
         }
 172  
 
 173  228
         instance.incrementClients();
 174  
 
 175  228
         return instance;
 176  
     }
 177  
 
 178  
     /**
 179  
      * Simple factory method, must override in subclasses so getInstance creates /
 180  
      * returns the correct object.
 181  
      *
 182  
      * @return CompositeCacheManager
 183  
      */
 184  
     protected static CompositeCacheManager createInstance()
 185  
     {
 186  201
         return new CompositeCacheManager();
 187  
     }
 188  
 
 189  
     /**
 190  
      * Configure with default properties file
 191  
      */
 192  
     public void configure()
 193  
     {
 194  39
         configure( CacheConstants.DEFAULT_CONFIG );
 195  39
     }
 196  
 
 197  
     /**
 198  
      * Configure from specific properties file.
 199  
      *
 200  
      * @param propFile
 201  
      *            Path <u>within classpath </u> to load configuration from
 202  
      */
 203  
     public void configure( String propFile )
 204  
     {
 205  259
         log.info( "Creating cache manager from config file: " + propFile );
 206  
 
 207  259
         Properties props = new Properties();
 208  
 
 209  259
         InputStream is = getClass().getResourceAsStream( propFile );
 210  
 
 211  259
         if ( is != null )
 212  
         {
 213  
             try
 214  
             {
 215  259
                 props.load( is );
 216  
 
 217  259
                 if ( log.isDebugEnabled() )
 218  
                 {
 219  0
                     log.debug( "File [" + propFile + "] contained " + props.size() + " properties" );
 220  
                 }
 221  
             }
 222  0
             catch ( IOException ex )
 223  
             {
 224  0
                 log.error( "Failed to load properties for name [" + propFile + "]", ex );
 225  0
                 throw new IllegalStateException( ex.getMessage() );
 226  
             }
 227  
             finally
 228  
             {
 229  0
                 try
 230  
                 {
 231  259
                     is.close();
 232  
                 }
 233  0
                 catch ( Exception ignore )
 234  
                 {
 235  
                     // Ignored
 236  252
                 }
 237  0
             }
 238  252
         }
 239  
         else
 240  
         {
 241  0
             log.error( "Failed to load properties for name [" + propFile + "]" );
 242  0
             throw new IllegalStateException( "Failed to load properties for name [" + propFile + "]" );
 243  
         }
 244  
 
 245  259
         configure( props );
 246  258
     }
 247  
 
 248  
     /**
 249  
      * Configure from properties object.
 250  
      * <p>
 251  
      * This method will call confiure, instructing it to use ssytem properties
 252  
      * as a default.
 253  
      *
 254  
      * @param props
 255  
      */
 256  
     public void configure( Properties props )
 257  
     {
 258  259
         configure( props, DEFAULT_USE_SYSTEM_PROPERTIES );
 259  258
     }
 260  
 
 261  
     /**
 262  
      * Configure from properties object, overriding with values from the system
 263  
      * properteis if instructed.
 264  
      * <p>
 265  
      * You can override a specif value by passing in a ssytem property:
 266  
      * <p>
 267  
      * For example, you could override this value in the cache.ccf file by
 268  
      * starting up your program with the argument:
 269  
      * -Djcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
 270  
      *
 271  
      *
 272  
      * @param props
 273  
      * @param useSystemProperties --
 274  
      *            if true, values starting with jcs will be put into the props
 275  
      *            file prior to configuring the cache.
 276  
      */
 277  
     public void configure( Properties props, boolean useSystemProperties )
 278  
     {
 279  267
         if ( props != null )
 280  
         {
 281  
 
 282  267
             if ( useSystemProperties )
 283  
             {
 284  
                 // override any setting with values from the system properties.
 285  259
                 Properties sysProps = System.getProperties();
 286  259
                 Set keys = sysProps.keySet();
 287  259
                 Iterator keyIt = keys.iterator();
 288  14327
                 while ( keyIt.hasNext() )
 289  
                 {
 290  14061
                     String key = (String) keyIt.next();
 291  14061
                     if ( key.startsWith( SYSTEM_PROPERTY_KEY_PREFIX ) )
 292  
                     {
 293  8
                         if ( log.isInfoEnabled() )
 294  
                         {
 295  8
                             log.info( "Using system property [[" + key + "] [" + sysProps.getProperty( key ) + "]]" );
 296  
                         }
 297  8
                         props.put( key, sysProps.getProperty( key ) );
 298  
                     }
 299  13682
                 }
 300  
             }
 301  
 
 302  
             // set the props value and then configure the ThreadPoolManager
 303  267
             ThreadPoolManager.setProps( props );
 304  267
             ThreadPoolManager poolMgr = ThreadPoolManager.getInstance();
 305  
 
 306  267
             if ( log.isDebugEnabled() )
 307  
             {
 308  0
                 log.debug( "ThreadPoolManager = " + poolMgr );
 309  
             }
 310  
 
 311  
             // configure the cache
 312  267
             CompositeCacheConfigurator configurator = new CompositeCacheConfigurator( this );
 313  
 
 314  267
             configurator.doConfigure( props );
 315  
 
 316  266
             this.props = props;
 317  258
         }
 318  
         else
 319  
         {
 320  0
             log.error( "No properties found.  Please configure the cache correctly." );
 321  
         }
 322  266
     }
 323  
 
 324  
     /**
 325  
      * Gets the defaultCacheAttributes attribute of the CacheHub object
 326  
      *
 327  
      * @return The defaultCacheAttributes value
 328  
      */
 329  
     public ICompositeCacheAttributes getDefaultCacheAttributes()
 330  
     {
 331  0
         return this.defaultCacheAttr.copy();
 332  
     }
 333  
 
 334  
     /**
 335  
      * Sets the defaultCacheAttributes attribute of the CacheHub object
 336  
      *
 337  
      * @param icca
 338  
      *            The new defaultCacheAttributes value
 339  
      */
 340  
     public void setDefaultCacheAttributes( ICompositeCacheAttributes icca )
 341  
     {
 342  267
         this.defaultCacheAttr = icca;
 343  267
     }
 344  
 
 345  
     /**
 346  
      * Sets the defaultElementAttributes attribute of the CacheHub object
 347  
      *
 348  
      * @param iea
 349  
      *            The new defaultElementAttributes value
 350  
      */
 351  
     public void setDefaultElementAttributes( IElementAttributes iea )
 352  
     {
 353  267
         this.defaultElementAttr = iea;
 354  267
     }
 355  
 
 356  
     /**
 357  
      * Gets the defaultElementAttributes attribute of the CacheHub object
 358  
      *
 359  
      * @return The defaultElementAttributes value
 360  
      */
 361  
     public IElementAttributes getDefaultElementAttributes()
 362  
     {
 363  820
         return this.defaultElementAttr.copy();
 364  
     }
 365  
 
 366  
     /**
 367  
      * Gets the cache attribute of the CacheHub object
 368  
      *
 369  
      * @param cacheName
 370  
      * @return CompositeCache -- the cache region controller
 371  
      */
 372  
     public CompositeCache getCache( String cacheName )
 373  
     {
 374  3726
         return getCache( cacheName, this.defaultCacheAttr.copy() );
 375  
     }
 376  
 
 377  
     /**
 378  
      * Gets the cache attribute of the CacheHub object
 379  
      *
 380  
      * @param cacheName
 381  
      * @param cattr
 382  
      * @return
 383  
      */
 384  
     public CompositeCache getCache( String cacheName, ICompositeCacheAttributes cattr )
 385  
     {
 386  3734
         cattr.setCacheName( cacheName );
 387  3734
         return getCache( cattr, this.defaultElementAttr );
 388  
     }
 389  
 
 390  
     /**
 391  
      * Gets the cache attribute of the CacheHub object
 392  
      *
 393  
      * @param cacheName
 394  
      * @param cattr
 395  
      * @param attr
 396  
      * @return
 397  
      */
 398  
     public CompositeCache getCache( String cacheName, ICompositeCacheAttributes cattr, IElementAttributes attr )
 399  
     {
 400  8
         cattr.setCacheName( cacheName );
 401  8
         return getCache( cattr, attr );
 402  
     }
 403  
 
 404  
     /**
 405  
      * Gets the cache attribute of the CacheHub object
 406  
      *
 407  
      * @param cattr
 408  
      * @return
 409  
      */
 410  
     public CompositeCache getCache( ICompositeCacheAttributes cattr )
 411  
     {
 412  0
         return getCache( cattr, this.defaultElementAttr );
 413  
     }
 414  
 
 415  
     /**
 416  
      * If the cache has already been created, then the CacheAttributes and the
 417  
      * element Attributes will be ignored. Currently there is no overiding the
 418  
      * CacheAttributes once it is set up. You can change the default
 419  
      * ElementAttributes for a region later.
 420  
      * <p>
 421  
      * Overriding the default elemental atributes will require changing the way
 422  
      * the atributes are assigned to elements. Get cache creates a cache with
 423  
      * defaults if none are specified. We might want to create separate method
 424  
      * for creating/getting. . .
 425  
      *
 426  
      * @param cattr
 427  
      * @param attr
 428  
      * @return CompositeCache
 429  
      */
 430  
     public CompositeCache getCache( ICompositeCacheAttributes cattr, IElementAttributes attr )
 431  
     {
 432  
         CompositeCache cache;
 433  
 
 434  3742
         if ( log.isDebugEnabled() )
 435  
         {
 436  0
             log.debug( "attr = " + attr );
 437  
         }
 438  
 
 439  3758
         synchronized ( caches )
 440  
         {
 441  3742
             cache = (CompositeCache) caches.get( cattr.getCacheName() );
 442  3742
             if ( cache == null )
 443  
             {
 444  203
                 cattr.setCacheName( cattr.getCacheName() );
 445  
 
 446  203
                 CompositeCacheConfigurator configurator = new CompositeCacheConfigurator( this );
 447  
 
 448  203
                 cache = configurator.parseRegion( this.props, cattr.getCacheName(), class="keyword">this.defaultAuxValues, cattr );
 449  
 
 450  203
                 caches.put( cattr.getCacheName(), cache );
 451  
             }
 452  3726
         }
 453  
 
 454  3742
         return cache;
 455  
     }
 456  
 
 457  
     /**
 458  
      * @param name
 459  
      */
 460  
     public void freeCache( String name )
 461  
     {
 462  0
         freeCache( name, false );
 463  0
     }
 464  
 
 465  
     /**
 466  
      * @param name
 467  
      * @param fromRemote
 468  
      */
 469  
     public void freeCache( String name, boolean fromRemote )
 470  
     {
 471  0
         CompositeCache cache = (CompositeCache) caches.remove( name );
 472  
 
 473  0
         if ( cache != null )
 474  
         {
 475  0
             cache.dispose( fromRemote );
 476  
         }
 477  0
     }
 478  
 
 479  
     /**
 480  
      * Calls freeCache on all regions
 481  
      */
 482  
     public void shutDown()
 483  
     {
 484  
         // notify any observers
 485  0
         synchronized ( shutdownObservers )
 486  
         {
 487  
             // We don't need to worry about lcoking the set.
 488  
             // since this is a shutdown command, nor do we need
 489  
             // to queue these up.
 490  0
             Iterator it = shutdownObservers.iterator();
 491  0
             while ( it.hasNext() )
 492  
             {
 493  0
                 IShutdownObserver observer = (IShutdownObserver) it.next();
 494  0
                 observer.shutdown();
 495  0
             }
 496  0
         }
 497  
 
 498  
         // do the traditional shutdown of the regions.
 499  0
         String[] names = getCacheNames();
 500  0
         int len = names.length;
 501  0
         for ( int i = 0; i < len; i++ )
 502  
         {
 503  0
             String name = names[i];
 504  0
             freeCache( name );
 505  
         }
 506  0
     }
 507  
 
 508  
     /** */
 509  
     protected void incrementClients()
 510  
     {
 511  306
         clients++;
 512  306
     }
 513  
 
 514  
     /** */
 515  
     public void release()
 516  
     {
 517  0
         release( false );
 518  0
     }
 519  
 
 520  
     /**
 521  
      * @param fromRemote
 522  
      */
 523  
     private void release( boolean fromRemote )
 524  
     {
 525  0
         synchronized ( CompositeCacheManager.class )
 526  
         {
 527  
             // Wait until called by the last client
 528  0
             if ( --clients > 0 )
 529  
             {
 530  0
                 if ( log.isDebugEnabled() )
 531  
                 {
 532  0
                     log.debug( "Release called, but " + clients + " remain" );
 533  0
                     return;
 534  
                 }
 535  
             }
 536  
 
 537  0
             if ( log.isDebugEnabled() )
 538  
             {
 539  0
                 log.debug( "Last client called release. There are " + caches.size() + " caches which will be disposed" );
 540  
             }
 541  
 
 542  0
             Enumeration allCaches = caches.elements();
 543  
 
 544  0
             while ( allCaches.hasMoreElements() )
 545  
             {
 546  0
                 CompositeCache cache = (CompositeCache) allCaches.nextElement();
 547  
 
 548  0
                 if ( cache != null )
 549  
                 {
 550  0
                     cache.dispose( fromRemote );
 551  
                 }
 552  0
             }
 553  0
         }
 554  0
     }
 555  
 
 556  
     /**
 557  
      * Returns a list of the current cache names.
 558  
      *
 559  
      * @return String[]
 560  
      */
 561  
     public String[] getCacheNames()
 562  
     {
 563  72
         String[] list = new String[caches.size()];
 564  72
         int i = 0;
 565  72
         for ( Iterator itr = caches.keySet().iterator(); itr.hasNext(); )
 566  
         {
 567  123
             list[i++] = (String) itr.next();
 568  123
         }
 569  72
         return list;
 570  
     }
 571  
 
 572  
     /**
 573  
      * @return
 574  
      */
 575  
     public int getCacheType()
 576  
     {
 577  0
         return ICacheType.CACHE_HUB;
 578  
     }
 579  
 
 580  
     /**
 581  
      * @return ICompositeCacheAttributes
 582  
      */
 583  
     public ICompositeCacheAttributes getDefaultCattr()
 584  
     {
 585  0
         return this.defaultCacheAttr;
 586  
     }
 587  
 
 588  
     /**
 589  
      * @param auxFac
 590  
      */
 591  
     void registryFacPut( AuxiliaryCacheFactory auxFac )
 592  
     {
 593  145
         auxFacs.put( auxFac.getName(), auxFac );
 594  145
     }
 595  
 
 596  
     /**
 597  
      * @param name
 598  
      * @return AuxiliaryCacheFactory
 599  
      */
 600  
     AuxiliaryCacheFactory registryFacGet( String name )
 601  
     {
 602  374
         return (AuxiliaryCacheFactory) auxFacs.get( name );
 603  
     }
 604  
 
 605  
     /**
 606  
      * @param auxAttr
 607  
      */
 608  
     void registryAttrPut( AuxiliaryCacheAttributes auxAttr )
 609  
     {
 610  145
         auxAttrs.put( auxAttr.getName(), auxAttr );
 611  145
     }
 612  
 
 613  
     /**
 614  
      * @param name
 615  
      * @return AuxiliaryCacheAttributes
 616  
      */
 617  
     AuxiliaryCacheAttributes registryAttrGet( String name )
 618  
     {
 619  374
         return (AuxiliaryCacheAttributes) auxAttrs.get( name );
 620  
     }
 621  
 
 622  
     /**
 623  
      * Gets stats for debugging. This calls gets statistics and then puts all
 624  
      * the results in a string. This returns data for all regions.
 625  
      *
 626  
      * @return String
 627  
      */
 628  
     public String getStats()
 629  
     {
 630  35
         ICacheStats[] stats = getStatistics();
 631  35
         if ( stats == null )
 632  
         {
 633  0
             return "NONE";
 634  
         }
 635  
 
 636  
         // force the array elements into a string.
 637  35
         StringBuffer buf = new StringBuffer();
 638  35
         int statsLen = stats.length;
 639  105
         for ( int i = 0; i < statsLen; i++ )
 640  
         {
 641  70
             buf.append( "\n---------------------------\n" );
 642  70
             buf.append( stats[i] );
 643  
         }
 644  35
         return buf.toString();
 645  
     }
 646  
 
 647  
     /**
 648  
      * This returns data gathered for all regions and all the auxiliaries they
 649  
      * currently uses.
 650  
      *
 651  
      * @return
 652  
      */
 653  
     public ICacheStats[] getStatistics()
 654  
     {
 655  42
         ArrayList cacheStats = new ArrayList();
 656  42
         Enumeration allCaches = caches.elements();
 657  119
         while ( allCaches.hasMoreElements() )
 658  
         {
 659  77
             CompositeCache cache = (CompositeCache) allCaches.nextElement();
 660  77
             if ( cache != null )
 661  
             {
 662  77
                 cacheStats.add( cache.getStatistics() );
 663  
             }
 664  77
         }
 665  42
         ICacheStats[] stats = (ICacheStats[]) cacheStats.toArray( new CacheStats[0] );
 666  42
         return stats;
 667  
     }
 668  
 
 669  
     /**
 670  
      * Perhaps the composite cache itself should be the observable object. It
 671  
      * doesn't make much of a difference. There are some problems with region by
 672  
      * region shutdown. Some auxiliaries are global. They will need to track
 673  
      * when every region has shutdown before doing things like closing the
 674  
      * socket with a lateral.
 675  
      * <p>
 676  
      * @param observer
 677  
      */
 678  
     public void registerShutdownObserver( IShutdownObserver observer )
 679  
     {
 680  
         // synchronized to take care of iteration safety
 681  
         // during shutdown.
 682  13
         synchronized ( shutdownObservers )
 683  
         {
 684  
             // the set will take care of duplication protection
 685  13
             shutdownObservers.add( observer );
 686  13
         }
 687  13
     }
 688  
 
 689  
     /*
 690  
      * (non-Javadoc)
 691  
      *
 692  
      * @see org.apache.jcs.engine.behavior.ShutdownObservable#deregisterShutdownObserver(org.apache.jcs.engine.behavior.ShutdownObserver)
 693  
      */
 694  
     public void deregisterShutdownObserver( IShutdownObserver observer )
 695  
     {
 696  0
         synchronized ( shutdownObservers )
 697  
         {
 698  0
             shutdownObservers.remove( observer );
 699  0
         }
 700  0
     }
 701  
 }

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