Clover coverage report -
Coverage timestamp: Sa Jul 7 2007 09:11:40 CEST
file stats: LOC: 113   Methods: 3
NCLOC: 60   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
HashDiskPersistenceListener.java 80% 82,1% 100% 82,9%
coverage coverage
 1    /*
 2    * Copyright (c) 2002-2007 by OpenSymphony
 3    * All rights reserved.
 4    */
 5    package com.opensymphony.oscache.plugins.diskpersistence;
 6   
 7    import com.opensymphony.oscache.base.Config;
 8    import com.opensymphony.oscache.base.persistence.PersistenceListener;
 9   
 10    import java.io.File;
 11    import java.security.MessageDigest;
 12    import java.security.NoSuchAlgorithmException;
 13   
 14    /**
 15    * Persists cache data to disk. Provides a hash of the standard key name as the file name.
 16    *
 17    * A configurable hash algorithm is used to create a digest of the cache key for the
 18    * disk filename. This is to allow for more sane filenames for objects which dont generate
 19    * friendly cache keys.
 20    *
 21    * @author <a href="mailto:jparrott@soe.sony.com">Jason Parrott</a>
 22    */
 23    public class HashDiskPersistenceListener extends AbstractDiskPersistenceListener {
 24   
 25    private static final int DIR_LEVELS = 3;
 26   
 27    public final static String HASH_ALGORITHM_KEY = "cache.persistence.disk.hash.algorithm";
 28    public final static String DEFAULT_HASH_ALGORITHM = "MD5";
 29    protected MessageDigest md = null;
 30   
 31    /**
 32    * Initializes the <tt>HashDiskPersistenceListener</tt>. Namely this involves only setting up the
 33    * message digester to hash the key values.
 34    * @see com.opensymphony.oscache.base.persistence.PersistenceListener#configure(com.opensymphony.oscache.base.Config)
 35    */
 36  59 public PersistenceListener configure(Config config) {
 37  59 try {
 38  59 if (config.getProperty(HashDiskPersistenceListener.HASH_ALGORITHM_KEY) != null) {
 39  30 try {
 40  30 md = MessageDigest.getInstance(config.getProperty(HashDiskPersistenceListener.HASH_ALGORITHM_KEY));
 41    } catch (NoSuchAlgorithmException e) {
 42  0 md = MessageDigest.getInstance(HashDiskPersistenceListener.DEFAULT_HASH_ALGORITHM);
 43    }
 44    } else {
 45  29 md = MessageDigest.getInstance(HashDiskPersistenceListener.DEFAULT_HASH_ALGORITHM);
 46    }
 47    } catch (NoSuchAlgorithmException e) {
 48  0 e.printStackTrace();
 49  0 throw new RuntimeException("No hash algorithm available for disk persistence", e);
 50    }
 51   
 52  59 return super.configure(config);
 53    }
 54   
 55    /**
 56    * Generates a file name for the given cache key. In this case the file name is attempted to be
 57    * generated from the hash of the standard key name. Cache algorithm is configured via the
 58    * <em>cache.persistence.disk.hash.algorithm</em> configuration variable.
 59    * @param key cache entry key
 60    * @return char[] file name
 61    */
 62  482 protected synchronized char[] getCacheFileName(String key) {
 63  482 if ((key == null) || (key.length() == 0)) {
 64  0 throw new IllegalArgumentException("Invalid key '" + key + "' specified to getCacheFile.");
 65    }
 66   
 67  482 String hexDigest = byteArrayToHexString(md.digest(key.getBytes()));
 68   
 69    // CACHE-249: Performance improvement for large disk persistence usage
 70  482 StringBuffer filename = new StringBuffer(hexDigest.length() + 2 * DIR_LEVELS);
 71  482 for (int i=0; i < DIR_LEVELS; i++) {
 72  1446 filename.append(hexDigest.charAt(i)).append(File.separator);
 73    }
 74  482 filename.append(hexDigest);
 75   
 76  482 return filename.toString().toCharArray();
 77    }
 78   
 79    /**
 80    * Nibble conversion. Thanks to our friends at:
 81    * http://www.devx.com/tips/Tip/13540
 82    * @param in the byte array to convert
 83    * @return a java.lang.String based version of they byte array
 84    */
 85  522 static String byteArrayToHexString(byte[] in) {
 86  522 if ((in == null) || (in.length <= 0)) {
 87  0 return null;
 88    }
 89   
 90  522 StringBuffer out = new StringBuffer(in.length * 2);
 91   
 92  522 for (int i = 0; i < in.length; i++) {
 93  7767 byte ch = (byte) (in[i] & 0xF0); // Strip off high nibble
 94  7767 ch = (byte) (ch >>> 4);
 95   
 96    // shift the bits down
 97  7767 ch = (byte) (ch & 0x0F);
 98   
 99    // must do this is high order bit is on!
 100  7767 out.append(PSEUDO[(int) ch]); // convert the nibble to a String Character
 101  7767 ch = (byte) (in[i] & 0x0F); // Strip off low nibble
 102  7767 out.append(PSEUDO[(int) ch]); // convert the nibble to a String Character
 103    }
 104   
 105  522 return out.toString();
 106    }
 107   
 108    static final String[] PSEUDO = {
 109    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
 110    "E", "F"
 111    };
 112   
 113    }