Coverage report

  %line %branch
org.apache.jcs.engine.memory.mru.MRUMemoryCache
68% 
76% 

 1  
 package org.apache.jcs.engine.memory.mru;
 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.Serializable;
 24  
 import java.util.ArrayList;
 25  
 import java.util.Iterator;
 26  
 import java.util.LinkedList;
 27  
 import java.util.ListIterator;
 28  
 import java.util.Map;
 29  
 
 30  
 import org.apache.commons.logging.Log;
 31  
 import org.apache.commons.logging.LogFactory;
 32  
 import org.apache.jcs.engine.CacheConstants;
 33  
 import org.apache.jcs.engine.behavior.ICacheElement;
 34  
 import org.apache.jcs.engine.control.CompositeCache;
 35  
 import org.apache.jcs.engine.control.group.GroupAttrName;
 36  
 import org.apache.jcs.engine.control.group.GroupId;
 37  
 import org.apache.jcs.engine.memory.AbstractMemoryCache;
 38  
 import org.apache.jcs.engine.stats.StatElement;
 39  
 import org.apache.jcs.engine.stats.Stats;
 40  
 import org.apache.jcs.engine.stats.behavior.IStatElement;
 41  
 import org.apache.jcs.engine.stats.behavior.IStats;
 42  
 
 43  
 /**
 44  
  * A SLOW reference management system. The most recently used items move to the
 45  
  * front of the list and get spooled to disk if the cache hub is configured to
 46  
  * use a disk cache.
 47  
  * <p>
 48  
  * This class is mainly for testing the hub. It also shows that use of the
 49  
  * Collection LinkedList is far slower than JCS' own double linked list.
 50  
  */
 51  64
 public class MRUMemoryCache
 52  
     extends AbstractMemoryCache
 53  
 {
 54  
     private static final long serialVersionUID = 5013101678192336129L;
 55  
 
 56  15
     private final static Log log = LogFactory.getLog( MRUMemoryCache.class );
 57  
 
 58  56
     private int hitCnt = 0;
 59  
 
 60  56
     private int missCnt = 0;
 61  
 
 62  56
     private int putCnt = 0;
 63  
 
 64  
     /**
 65  
      * Object to lock on the Field
 66  
      */
 67  56
     private int[] lockMe = new class="keyword">int[0];
 68  
 
 69  
     /**
 70  
      * MRU list.
 71  
      */
 72  56
     private LinkedList mrulist = new LinkedList();
 73  
 
 74  
     /**
 75  
      * For post reflection creation initialization
 76  
      * @param hub
 77  
      */
 78  
     public synchronized void initialize( CompositeCache hub )
 79  
     {
 80  56
         super.initialize( hub );
 81  56
         log.info( "Initialized MRUMemoryCache for " + cacheName );
 82  56
     }
 83  
 
 84  
     /**
 85  
      * Puts an item to the cache.
 86  
      * @param ce
 87  
      * @exception IOException
 88  
      */
 89  
     public void update( ICacheElement ce )
 90  
         throws IOException
 91  
     {
 92  104008
         putCnt++;
 93  
 
 94  104008
         Serializable key = ce.getKey();
 95  104008
         ce.getElementAttributes().setLastAccessTimeNow();
 96  
 
 97  
         // need a more fine grained locking here
 98  104008
         boolean replace = false;
 99  104008
         if ( map.containsKey( key ) )
 100  
         {
 101  0
             replace = true;
 102  
         }
 103  117009
         synchronized ( lockMe )
 104  
         {
 105  104008
             map.put( key, ce );
 106  104008
             if ( replace )
 107  
             {
 108  
                 // the slowest method I've ever seen
 109  0
                 mrulist.remove( key );
 110  
             }
 111  104008
             mrulist.addFirst( key );
 112  91007
         }
 113  
 
 114  
         // save a microsecond on the second call.
 115  104008
         int size = map.size();
 116  
         // need to spool at a certain percentage synchronously
 117  104008
         if ( size < this.cattr.getMaxObjects() )
 118  
         {
 119  65984
             return;
 120  
         }
 121  
         // SPOOL LAST -- need to make this a grouping in a queue
 122  
 
 123  38024
         if ( log.isDebugEnabled() )
 124  
         {
 125  0
             log.debug( "In RAM overflow" );
 126  
         }
 127  
 
 128  
         // write the last item to disk.
 129  
         try
 130  
         {
 131  
             // PUSH more than 1 TO DISK TO MINIMIZE THE TYPICAL spool at each
 132  
             // put.
 133  38024
             int chunkSizeCorrected = Math.min( size, chunkSize );
 134  
 
 135  38024
             if ( log.isDebugEnabled() )
 136  
             {
 137  0
                 log.debug( "update: About to spool to disk cache, map.size() = " + size
 138  
                     + ", this.cattr.getMaxObjects() = " + this.cattr.getMaxObjects() + ", chunkSizeCorrected = "
 139  
                     + chunkSizeCorrected );
 140  
             }
 141  
 
 142  
             // The spool will put them in a disk event queue, so there is no
 143  
             // need to pre-queue the queuing. This would be a bit wasteful
 144  
             // and wouldn't save much time in this synchronous call.
 145  114072
             for ( int i = 0; i < chunkSizeCorrected; i++ )
 146  
             {
 147  
                 // remove the last
 148  76048
                 spoolLastElement();
 149  
             }
 150  
 
 151  38024
             if ( log.isDebugEnabled() )
 152  
             {
 153  0
                 log.debug( "update: After spool,  map.size() = " + size + ", this.cattr.getMaxObjects() = "
 154  
                     + this.cattr.getMaxObjects() + ", chunkSizeCorrected = " + chunkSizeCorrected );
 155  
             }
 156  
 
 157  
         }
 158  0
         catch ( Exception ex )
 159  
         {
 160  
             // impossible case.
 161  0
             log.error( "Problem updating MRU.", ex );
 162  0
             throw new IllegalStateException( ex.getMessage() );
 163  33271
         }
 164  38024
     }
 165  
 
 166  
     /**
 167  
      * This removes the last elemement in the list.
 168  
      * <p>
 169  
      * @return ICacheElement if there was a last element, else null.
 170  
      */
 171  
     protected ICacheElement spoolLastElement()
 172  
     {
 173  76048
         ICacheElement toSpool = null;
 174  
 
 175  
         // need a more fine grained locking here
 176  85554
         synchronized ( lockMe )
 177  
         {
 178  76048
             Serializable last = (Serializable) mrulist.removeLast();
 179  76048
             if ( last != null )
 180  
             {
 181  76048
                 toSpool = (ICacheElement) map.get( last );
 182  76048
                 map.remove( last );
 183  
             }
 184  66542
         }
 185  
         // Might want to rename this "overflow" incase the hub
 186  
         // wants to do something else.
 187  76048
         if ( toSpool != null )
 188  
         {
 189  76048
             cache.spoolToDisk( toSpool );
 190  
         }
 191  
 
 192  76048
         return toSpool;
 193  
     }
 194  
 
 195  
     /**
 196  
      * This instructs the memory cache to remove the <i>numberToFree</i>
 197  
      * according to its eviction policy. For example, the LRUMemoryCache will
 198  
      * remove the <i>numberToFree</i> least recently used items. These will be
 199  
      * spooled to disk if a disk auxiliary is available.
 200  
      * <p>
 201  
      * @param numberToFree
 202  
      * @return the number that were removed. if you ask to free 5, but there are
 203  
      *         only 3, you will get 3.
 204  
      * @throws IOException
 205  
      */
 206  
     public int freeElements( class="keyword">int numberToFree )
 207  
         throws IOException
 208  
     {
 209  0
         int freed = 0;
 210  0
         for ( ; freed < numberToFree; freed++ )
 211  
         {
 212  0
             ICacheElement element = spoolLastElement();
 213  0
             if ( element == null )
 214  
             {
 215  0
                 break;
 216  
             }
 217  
         }
 218  0
         return freed;
 219  
     }
 220  
 
 221  
     /**
 222  
      * Get an item from the cache without affecting its last access time or
 223  
      * position.
 224  
      * @return Element matching key if found, or null
 225  
      * @param key
 226  
      *            Identifies item to find
 227  
      * @exception IOException
 228  
      */
 229  
     public ICacheElement getQuiet( Serializable key )
 230  
         throws IOException
 231  
     {
 232  13280
         ICacheElement ce = null;
 233  
 
 234  
         try
 235  
         {
 236  13280
             ce = (ICacheElement) map.get( key );
 237  13280
             if ( ce != null )
 238  
             {
 239  11136
                 if ( log.isDebugEnabled() )
 240  
                 {
 241  0
                     log.debug( cacheName + ": MRUMemoryCache quiet hit for " + key );
 242  0
                 }
 243  
             }
 244  
             else
 245  
             {
 246  2144
                 log.debug( cacheName + ": MRUMemoryCache quiet miss for " + key );
 247  
             }
 248  
         }
 249  0
         catch ( Exception e )
 250  
         {
 251  0
             log.error( "Problem getting quietly from MRU.", e );
 252  12281
         }
 253  
 
 254  13280
         return ce;
 255  
     }
 256  
 
 257  
     /**
 258  
      * Gets an item out of the map. If it finds an item, it is removed from the
 259  
      * list and then added to the first position in the linked list.
 260  
      * @return
 261  
      * @param key
 262  
      * @exception IOException
 263  
      */
 264  
     public ICacheElement get( Serializable key )
 265  
         throws IOException
 266  
     {
 267  56008
         ICacheElement ce = null;
 268  56008
         boolean found = false;
 269  
 
 270  
         try
 271  
         {
 272  56008
             if ( log.isDebugEnabled() )
 273  
             {
 274  0
                 log.debug( "get> key=" + key );
 275  0
                 log.debug( "get> key=" + key.toString() );
 276  
             }
 277  
 
 278  56008
             ce = (ICacheElement) map.get( key );
 279  56008
             if ( log.isDebugEnabled() )
 280  
             {
 281  0
                 log.debug( "ce =" + ce );
 282  
             }
 283  
 
 284  56008
             if ( ce != null )
 285  
             {
 286  19976
                 found = true;
 287  19976
                 ce.getElementAttributes().setLastAccessTimeNow();
 288  19976
                 hitCnt++;
 289  19976
                 if ( log.isDebugEnabled() )
 290  
                 {
 291  0
                     log.debug( cacheName + " -- RAM-HIT for " + key );
 292  
                 }
 293  
             }
 294  
         }
 295  0
         catch ( Exception e )
 296  
         {
 297  0
             log.error( "Problem getting element.", e );
 298  49007
         }
 299  
 
 300  
         try
 301  
         {
 302  56008
             if ( !found )
 303  
             {
 304  
                 // Item not found in cache.
 305  36032
                 missCnt++;
 306  36032
                 if ( log.isDebugEnabled() )
 307  
                 {
 308  0
                     log.debug( cacheName + " -- MISS for " + key );
 309  
                 }
 310  36032
                 return null;
 311  
             }
 312  
         }
 313  0
         catch ( Exception e )
 314  
         {
 315  0
             log.error( "Error handling miss", e );
 316  0
             return null;
 317  17479
         }
 318  
 
 319  
         try
 320  
         {
 321  22473
             synchronized ( lockMe )
 322  
             {
 323  19976
                 mrulist.remove( ce.getKey() );
 324  19976
                 mrulist.addFirst( ce.getKey() );
 325  17479
             }
 326  
         }
 327  0
         catch ( Exception e )
 328  
         {
 329  0
             log.error( "Error making first", e );
 330  0
             return null;
 331  17479
         }
 332  
 
 333  19976
         return ce;
 334  
     }
 335  
 
 336  
     /**
 337  
      * Removes an item from the cache.
 338  
      * @return
 339  
      * @param key
 340  
      * @exception IOException
 341  
      */
 342  
     public boolean remove( Serializable key )
 343  
         throws IOException
 344  
     {
 345  16008
         if ( log.isDebugEnabled() )
 346  
         {
 347  0
             log.debug( "remove> key=" + key );
 348  
         }
 349  
 
 350  16008
         boolean removed = false;
 351  
 
 352  
         // handle partial removal
 353  16008
         if ( key instanceof String && key.toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
 354  
         {
 355  
             // remove all keys of the same name hierarchy.
 356  9
             synchronized ( lockMe )
 357  
             {
 358  1008
                 for ( Iterator itr = map.entrySet().iterator(); itr.hasNext(); )
 359  
                 {
 360  7992
                     Map.Entry entry = (Map.Entry) itr.next();
 361  7992
                     Object k = entry.getKey();
 362  7992
                     if ( k instanceof String && k.toString().startsWith( key.toString() ) )
 363  
                     {
 364  4000
                         itr.remove();
 365  4000
                         Serializable keyR = (Serializable) entry.getKey();
 366  
                         // map.remove( keyR );
 367  4000
                         mrulist.remove( keyR );
 368  4000
                         removed = true;
 369  
                     }
 370  6993
                 }
 371  7
             }
 372  7
         }
 373  16000
         else if ( key instanceof GroupId )
 374  
         {
 375  
             // remove all keys of the same name hierarchy.
 376  0
             synchronized ( lockMe )
 377  
             {
 378  0
                 for ( Iterator itr = map.entrySet().iterator(); itr.hasNext(); )
 379  
                 {
 380  0
                     Map.Entry entry = (Map.Entry) itr.next();
 381  0
                     Object k = entry.getKey();
 382  
 
 383  0
                     if ( k instanceof GroupAttrName && ( (GroupAttrName) k ).groupId.equals( key ) )
 384  
                     {
 385  0
                         itr.remove();
 386  0
                         mrulist.remove( k );
 387  0
                         removed = true;
 388  
                     }
 389  0
                 }
 390  0
             }
 391  0
         }
 392  
         else
 393  
         {
 394  
             // remove single item.
 395  16000
             if ( map.containsKey( key ) )
 396  
             {
 397  8982
                 synchronized ( lockMe )
 398  
                 {
 399  7984
                     map.remove( key );
 400  7984
                     mrulist.remove( key );
 401  6986
                 }
 402  7984
                 removed = true;
 403  
             }
 404  
         }
 405  
         // end else not hierarchical removal
 406  16008
         return removed;
 407  
     }
 408  
 
 409  
     /**
 410  
      * Get an Array of the keys for all elements in the memory cache
 411  
      * @return Object[]
 412  
      */
 413  
     public Object[] getKeyArray()
 414  
     {
 415  
         // need to lock to map here?
 416  38
         synchronized ( lockMe )
 417  
         {
 418  38
             return map.keySet().toArray();
 419  0
         }
 420  
     }
 421  
 
 422  
     /**
 423  
      * Dump the cache map for debugging.
 424  
      */
 425  
     public void dumpMap()
 426  
     {
 427  0
         log.debug( "dumpingMap" );
 428  0
         for ( Iterator itr = map.entrySet().iterator(); itr.hasNext(); )
 429  
         {
 430  
             // for ( Iterator itr = memCache.getIterator(); itr.hasNext();) {
 431  0
             Map.Entry e = (Map.Entry) itr.next();
 432  0
             ICacheElement ce = (ICacheElement) e.getValue();
 433  0
             log.debug( "dumpMap> key=" + e.getKey() + ", val=" + ce.getVal() );
 434  0
         }
 435  0
     }
 436  
 
 437  
     /**
 438  
      * Dump the cache entries from first to list for debugging.
 439  
      */
 440  
     public void dumpCacheEntries()
 441  
     {
 442  0
         log.debug( "dumpingCacheEntries" );
 443  0
         ListIterator li = mrulist.listIterator();
 444  0
         while ( li.hasNext() )
 445  
         {
 446  0
             Serializable key = (Serializable) li.next();
 447  0
             log.debug( "dumpCacheEntries> key=" + key + ", val=" + ( (ICacheElement) map.get( key ) ).getVal() );
 448  0
         }
 449  0
     }
 450  
 
 451  
     /*
 452  
      * (non-Javadoc)
 453  
      * @see org.apache.jcs.engine.memory.MemoryCache#getStatistics()
 454  
      */
 455  
     public IStats getStatistics()
 456  
     {
 457  8
         IStats stats = new Stats();
 458  8
         stats.setTypeName( "MRU Memory Cache" );
 459  
 
 460  8
         ArrayList elems = new ArrayList();
 461  
 
 462  8
         IStatElement se = null;
 463  
 
 464  8
         se = new StatElement();
 465  8
         se.setName( "List Size" );
 466  8
         se.setData( "" + mrulist.size() );
 467  8
         elems.add( se );
 468  
 
 469  8
         se = new StatElement();
 470  8
         se.setName( "Map Size" );
 471  8
         se.setData( "" + map.size() );
 472  8
         elems.add( se );
 473  
 
 474  8
         se = new StatElement();
 475  8
         se.setName( "Put Count" );
 476  8
         se.setData( "" + putCnt );
 477  8
         elems.add( se );
 478  
 
 479  8
         se = new StatElement();
 480  8
         se.setName( "Hit Count" );
 481  8
         se.setData( "" + hitCnt );
 482  8
         elems.add( se );
 483  
 
 484  8
         se = new StatElement();
 485  8
         se.setName( "Miss Count" );
 486  8
         se.setData( "" + missCnt );
 487  8
         elems.add( se );
 488  
 
 489  
         // get an array and put them in the Stats object
 490  8
         IStatElement[] ses = (IStatElement[]) elems.toArray( new StatElement[0] );
 491  8
         stats.setStatElements( ses );
 492  
 
 493  8
         return stats;
 494  
     }
 495  
 }

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