Coverage report

  %line %branch
org.apache.jcs.auxiliary.disk.block.BlockDiskKeyStore$MyThreadFactory
100% 
100% 

 1  
 package org.apache.jcs.auxiliary.disk.block;
 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.BufferedInputStream;
 23  
 import java.io.BufferedOutputStream;
 24  
 import java.io.EOFException;
 25  
 import java.io.File;
 26  
 import java.io.FileInputStream;
 27  
 import java.io.FileOutputStream;
 28  
 import java.io.ObjectInputStream;
 29  
 import java.io.ObjectOutputStream;
 30  
 import java.io.Serializable;
 31  
 import java.util.HashMap;
 32  
 import java.util.Iterator;
 33  
 import java.util.Map;
 34  
 import java.util.Set;
 35  
 
 36  
 import org.apache.commons.logging.Log;
 37  
 import org.apache.commons.logging.LogFactory;
 38  
 import org.apache.jcs.auxiliary.disk.LRUMapJCS;
 39  
 import org.apache.jcs.utils.timing.ElapsedTimer;
 40  
 
 41  
 import EDU.oswego.cs.dl.util.concurrent.ClockDaemon;
 42  
 import EDU.oswego.cs.dl.util.concurrent.ThreadFactory;
 43  
 
 44  
 /**
 45  
  * This is responsible for storing the keys.
 46  
  * <p>
 47  
  * @author Aaron Smuts
 48  
  */
 49  
 public class BlockDiskKeyStore
 50  
 {
 51  
     /** The logger */
 52  
     private static final Log log = LogFactory.getLog( BlockDiskKeyStore.class );
 53  
 
 54  
     /** Attributes governing the behavior of the block disk cache. */
 55  
     private BlockDiskCacheAttributes blockDiskCacheAttributes;
 56  
 
 57  
     /** The key to block map */
 58  
     private Map keyHash;
 59  
 
 60  
     /** The file where we persist the keys */
 61  
     private File keyFile;
 62  
 
 63  
     /** The name to prefix log messages with. */
 64  
     private final String logCacheName;
 65  
 
 66  
     /** Name of the file where we persist the keys */
 67  
     private String fileName;
 68  
 
 69  
     /** The maximum number of keys to store in memory */
 70  
     private int maxKeySize;
 71  
 
 72  
     /** we need this so we can communicate free blocks to the data store when keys fall off the LRU */
 73  
     private BlockDiskCache blockDiskCache;
 74  
 
 75  
     /** The root directory in which the keyFile lives */
 76  
     private File rootDirectory;
 77  
 
 78  
     /**
 79  
      * The background key persister, one for all regions.
 80  
      */
 81  
     private static ClockDaemon persistenceDaemon;
 82  
 
 83  
     /**
 84  
      * Set the configuration options.
 85  
      * <p>
 86  
      * @param cacheAttributes
 87  
      * @param blockDiskCache used for freeing
 88  
      * @throws Exception
 89  
      */
 90  
     public BlockDiskKeyStore( BlockDiskCacheAttributes cacheAttributes, BlockDiskCache blockDiskCache )
 91  
         throws Exception
 92  
     {
 93  
         this.blockDiskCacheAttributes = cacheAttributes;
 94  
         this.logCacheName = "Region [" + this.blockDiskCacheAttributes.getCacheName() + "] ";
 95  
         this.fileName = class="keyword">this.blockDiskCacheAttributes.getCacheName();
 96  
         this.maxKeySize = cacheAttributes.getMaxKeySize();
 97  
         this.blockDiskCache = blockDiskCache;
 98  
 
 99  
         String rootDirName = cacheAttributes.getDiskPath();
 100  
         this.rootDirectory = new File( rootDirName );
 101  
         this.rootDirectory.mkdirs();
 102  
 
 103  
         if ( log.isInfoEnabled() )
 104  
         {
 105  
             log.info( logCacheName + "Cache file root directory [" + rootDirName + "]" );
 106  
         }
 107  
 
 108  
         this.keyFile = new File( rootDirectory, fileName + ".key" );
 109  
 
 110  
         if ( log.isInfoEnabled() )
 111  
         {
 112  
             log.info( logCacheName + "Key File [" + this.keyFile.getAbsolutePath() + "]" );
 113  
         }
 114  
 
 115  
         if ( keyFile.length() > 0 )
 116  
         {
 117  
             loadKeys();
 118  
             // TODO verify somehow
 119  
         }
 120  
         else
 121  
         {
 122  
             initKeyMap();
 123  
         }
 124  
 
 125  
         // add this region to the persistence thread.
 126  
         // TODO we might need to stagger this a bit.
 127  
         if ( this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds() > 0 )
 128  
         {
 129  
             if ( persistenceDaemon == null )
 130  
             {
 131  
                 persistenceDaemon = new ClockDaemon();
 132  
                 persistenceDaemon.setThreadFactory( new MyThreadFactory() );
 133  
             }
 134  
             persistenceDaemon
 135  
                 .executePeriodically( this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds() * 1000,
 136  
                                       new Runnable()
 137  
                                       {
 138  
                                           public void run()
 139  
                                           {
 140  
                                               saveKeys();
 141  
                                           }
 142  
                                       }, false );
 143  
         }
 144  
     }
 145  
 
 146  
     /**
 147  
      * Saves key file to disk. This gets the LRUMap entry set and write the entries out one by one
 148  
      * after putting them in a wrapper.
 149  
      */
 150  
     protected void saveKeys()
 151  
     {
 152  
         try
 153  
         {
 154  
             ElapsedTimer timer = new ElapsedTimer();
 155  
             int numKeys = keyHash.size();
 156  
             if ( log.isInfoEnabled() )
 157  
             {
 158  
                 log.info( logCacheName + "Saving keys to [" + this.keyFile.getAbsolutePath() + "], key count ["
 159  
                     + numKeys + "]" );
 160  
             }
 161  
 
 162  
             keyFile.delete();
 163  
 
 164  
             keyFile = new File( rootDirectory, fileName + ".key" );
 165  
             FileOutputStream fos = new FileOutputStream( keyFile );
 166  
             BufferedOutputStream bos = new BufferedOutputStream( fos, 1024 );
 167  
             ObjectOutputStream oos = new ObjectOutputStream( bos );
 168  
             try
 169  
             {
 170  
                 // don't need to synchronize, since the underlying collection makes a copy
 171  
                 Iterator keyIt = keyHash.entrySet().iterator();
 172  
                 while ( keyIt.hasNext() )
 173  
                 {
 174  
                     Map.Entry entry = (Map.Entry) keyIt.next();
 175  
                     BlockDiskElementDescriptor descriptor = new BlockDiskElementDescriptor();
 176  
                     descriptor.setKey( (Serializable) entry.getKey() );
 177  
                     descriptor.setBlocks( (int[]) entry.getValue() );
 178  
                     // stream these out in the loop.
 179  
                     oos.writeObject( descriptor );
 180  
                 }
 181  
             }
 182  
             finally
 183  
             {
 184  
                 oos.flush();
 185  
                 oos.close();
 186  
             }
 187  
 
 188  
             if ( log.isInfoEnabled() )
 189  
             {
 190  
                 log.info( logCacheName + "Finished saving keys. It took " + timer.getElapsedTimeString() + " to store "
 191  
                     + numKeys + " keys.  Key file length [" + keyFile.length() + "]" );
 192  
             }
 193  
         }
 194  
         catch ( Exception e )
 195  
         {
 196  
             log.error( logCacheName + "Problem storing keys.", e );
 197  
         }
 198  
     }
 199  
 
 200  
     /**
 201  
      * Resets the file and creates a new key map.
 202  
      */
 203  
     protected void reset()
 204  
     {
 205  
         File keyFileTemp = new File( this.rootDirectory, fileName + ".key" );
 206  
         keyFileTemp.delete();
 207  
 
 208  
         keyFile = new File( this.rootDirectory, fileName + ".key" );
 209  
 
 210  
         initKeyMap();
 211  
     }
 212  
 
 213  
     /**
 214  
      * This is mainly used for testing. It leave the disk in tact, and just clears memory.
 215  
      */
 216  
     protected void clearMemoryMap()
 217  
     {
 218  
         this.keyHash.clear();
 219  
     }
 220  
 
 221  
     /**
 222  
      * Create the map for keys that contain the index position on disk.
 223  
      */
 224  
     private void initKeyMap()
 225  
     {
 226  
         keyHash = null;
 227  
         if ( maxKeySize >= 0 )
 228  
         {
 229  
             keyHash = new LRUMap( maxKeySize );
 230  
             if ( log.isInfoEnabled() )
 231  
             {
 232  
                 log.info( logCacheName + "Set maxKeySize to: '" + maxKeySize + "'" );
 233  
             }
 234  
         }
 235  
         else
 236  
         {
 237  
             // If no max size, use a plain map for memory and processing efficiency.
 238  
             keyHash = new HashMap();
 239  
             // keyHash = Collections.synchronizedMap( new HashMap() );
 240  
             if ( log.isInfoEnabled() )
 241  
             {
 242  
                 log.info( logCacheName + "Set maxKeySize to unlimited'" );
 243  
             }
 244  
         }
 245  
     }
 246  
 
 247  
     /**
 248  
      * Loads the keys from the .key file. The keys are stored individually on disk. They are added
 249  
      * one by one to an LRUMap..
 250  
      * <p>
 251  
      * @throws InterruptedException
 252  
      */
 253  
     protected void loadKeys()
 254  
         throws InterruptedException
 255  
     {
 256  
         if ( log.isInfoEnabled() )
 257  
         {
 258  
             log.info( logCacheName + "Loading keys for " + keyFile.toString() );
 259  
         }
 260  
 
 261  
         try
 262  
         {
 263  
             // create a key map to use.
 264  
             initKeyMap();
 265  
 
 266  
             HashMap keys = new HashMap();
 267  
 
 268  
             FileInputStream fis = new FileInputStream( keyFile );
 269  
             BufferedInputStream bis = new BufferedInputStream( fis );
 270  
             ObjectInputStream ois = new ObjectInputStream( bis );
 271  
             try
 272  
             {
 273  
                 while ( true )
 274  
                 {
 275  
                     BlockDiskElementDescriptor descriptor = (BlockDiskElementDescriptor) ois.readObject();
 276  
                     if ( descriptor != null )
 277  
                     {
 278  
                         keys.put( descriptor.getKey(), descriptor.getBlocks() );
 279  
                     }
 280  
                 }
 281  
             }
 282  
             catch ( EOFException eof )
 283  
             {
 284  
                 // nothing
 285  
             }
 286  
             finally
 287  
             {
 288  
                 ois.close();
 289  
             }
 290  
 
 291  
             if ( !keys.isEmpty() )
 292  
             {
 293  
                 if ( log.isDebugEnabled() )
 294  
                 {
 295  
                     log.debug( logCacheName + "Found " + keys.size() + " in keys file." );
 296  
                 }
 297  
 
 298  
                 keyHash.putAll( keys );
 299  
 
 300  
                 if ( log.isInfoEnabled() )
 301  
                 {
 302  
                     log.info( logCacheName + "Loaded keys from [" + fileName + "], key count: " + keyHash.size()
 303  
                         + "; up to " + maxKeySize + " will be available." );
 304  
                 }
 305  
             }
 306  
         }
 307  
         catch ( Exception e )
 308  
         {
 309  
             log.error( logCacheName + "Problem loading keys for file " + fileName, e );
 310  
         }
 311  
     }
 312  
 
 313  
     /**
 314  
      * Gets the entry set.
 315  
      * <p>
 316  
      * @return entry set.
 317  
      */
 318  
     public Set entrySet()
 319  
     {
 320  
         return this.keyHash.entrySet();
 321  
     }
 322  
 
 323  
     /**
 324  
      * Gets the key set.
 325  
      * <p>
 326  
      * @return key set.
 327  
      */
 328  
     public Set keySet()
 329  
     {
 330  
         return this.keyHash.keySet();
 331  
     }
 332  
 
 333  
     /**
 334  
      * Gets the size of the key hash.
 335  
      * <p>
 336  
      * @return the number of keys.
 337  
      */
 338  
     public int size()
 339  
     {
 340  
         return this.keyHash.size();
 341  
     }
 342  
 
 343  
     /**
 344  
      * gets the object for the key.
 345  
      * <p>
 346  
      * @param key
 347  
      * @return Object
 348  
      */
 349  
     public int[] get( Object key )
 350  
     {
 351  
         return (int[]) this.keyHash.get( key );
 352  
     }
 353  
 
 354  
     /**
 355  
      * Puts a int[] in the keyStore.
 356  
      * <p>
 357  
      * @param key
 358  
      * @param value
 359  
      */
 360  
     public void put( Object key, int[] value )
 361  
     {
 362  
         this.keyHash.put( key, value );
 363  
     }
 364  
 
 365  
     /**
 366  
      * Remove by key.
 367  
      * <p>
 368  
      * @param key
 369  
      * @return BlockDiskElementDescriptor if it was present, else null
 370  
      */
 371  
     public int[] remove( Object key )
 372  
     {
 373  
         return (int[]) this.keyHash.remove( key );
 374  
     }
 375  
 
 376  
     /**
 377  
      * Class for recylcing and lru. This implments the LRU overflow callback, so we can mark the
 378  
      * blocks as free.
 379  
      */
 380  
     public class LRUMap
 381  
         extends LRUMapJCS
 382  
     {
 383  
         /** Don't change */
 384  
         private static final long serialVersionUID = 4955079991472142198L;
 385  
 
 386  
         /**
 387  
          * <code>tag</code> tells us which map we are working on.
 388  
          */
 389  
         public String tag = "orig";
 390  
 
 391  
         /**
 392  
          * Default
 393  
          */
 394  
         public LRUMap()
 395  
         {
 396  
             super();
 397  
         }
 398  
 
 399  
         /**
 400  
          * @param maxKeySize
 401  
          */
 402  
         public LRUMap( int maxKeySize )
 403  
         {
 404  
             super( maxKeySize );
 405  
         }
 406  
 
 407  
         /**
 408  
          * This is called when the may key size is reaced. The least recently used item will be
 409  
          * passed here. We will store the position and size of the spot on disk in the recycle bin.
 410  
          * <p>
 411  
          * @param key
 412  
          * @param value
 413  
          */
 414  
         protected void processRemovedLRU( Object key, Object value )
 415  
         {
 416  
             blockDiskCache.freeBlocks( (int[]) value );
 417  
             if ( log.isDebugEnabled() )
 418  
             {
 419  
                 log.debug( logCacheName + "Removing key: [" + key + "] from key store." );
 420  
                 log.debug( logCacheName + "Key store size: [" + this.size() + "]." );
 421  
             }
 422  
         }
 423  
     }
 424  
 
 425  
     /**
 426  
      * Allows us to set the daemon status on the clockdaemon
 427  
      * @author aaronsm
 428  
      */
 429  24
     class MyThreadFactory
 430  
         implements ThreadFactory
 431  
     {
 432  
 
 433  
         /**
 434  
          * Ensures that we create daemon threads.
 435  
          * <p>
 436  
          * (non-Javadoc)
 437  
          * @see EDU.oswego.cs.dl.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
 438  
          */
 439  
         public Thread newThread( Runnable runner )
 440  
         {
 441  24
             Thread t = new Thread( runner );
 442  24
             t.setDaemon( true );
 443  24
             t.setPriority( Thread.MIN_PRIORITY );
 444  24
             return t;
 445  
         }
 446  
     }
 447  
 }

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