001: /*
002: * Copyright (c) 2002-2007 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.oscache.plugins.diskpersistence;
006:
007: import com.opensymphony.oscache.base.Config;
008: import com.opensymphony.oscache.base.persistence.PersistenceListener;
009:
010: import java.io.File;
011: import java.security.MessageDigest;
012: import java.security.NoSuchAlgorithmException;
013:
014: /**
015: * Persists cache data to disk. Provides a hash of the standard key name as the file name.
016: *
017: * A configurable hash algorithm is used to create a digest of the cache key for the
018: * disk filename. This is to allow for more sane filenames for objects which dont generate
019: * friendly cache keys.
020: *
021: * @author <a href="mailto:jparrott@soe.sony.com">Jason Parrott</a>
022: */
023: public class HashDiskPersistenceListener extends
024: AbstractDiskPersistenceListener {
025:
026: private static final int DIR_LEVELS = 3;
027:
028: public final static String HASH_ALGORITHM_KEY = "cache.persistence.disk.hash.algorithm";
029: public final static String DEFAULT_HASH_ALGORITHM = "MD5";
030: protected MessageDigest md = null;
031:
032: /**
033: * Initializes the <tt>HashDiskPersistenceListener</tt>. Namely this involves only setting up the
034: * message digester to hash the key values.
035: * @see com.opensymphony.oscache.base.persistence.PersistenceListener#configure(com.opensymphony.oscache.base.Config)
036: */
037: public PersistenceListener configure(Config config) {
038: try {
039: if (config
040: .getProperty(HashDiskPersistenceListener.HASH_ALGORITHM_KEY) != null) {
041: try {
042: md = MessageDigest
043: .getInstance(config
044: .getProperty(HashDiskPersistenceListener.HASH_ALGORITHM_KEY));
045: } catch (NoSuchAlgorithmException e) {
046: md = MessageDigest
047: .getInstance(HashDiskPersistenceListener.DEFAULT_HASH_ALGORITHM);
048: }
049: } else {
050: md = MessageDigest
051: .getInstance(HashDiskPersistenceListener.DEFAULT_HASH_ALGORITHM);
052: }
053: } catch (NoSuchAlgorithmException e) {
054: e.printStackTrace();
055: throw new RuntimeException(
056: "No hash algorithm available for disk persistence",
057: e);
058: }
059:
060: return super .configure(config);
061: }
062:
063: /**
064: * Generates a file name for the given cache key. In this case the file name is attempted to be
065: * generated from the hash of the standard key name. Cache algorithm is configured via the
066: * <em>cache.persistence.disk.hash.algorithm</em> configuration variable.
067: * @param key cache entry key
068: * @return char[] file name
069: */
070: protected synchronized char[] getCacheFileName(String key) {
071: if ((key == null) || (key.length() == 0)) {
072: throw new IllegalArgumentException("Invalid key '" + key
073: + "' specified to getCacheFile.");
074: }
075:
076: String hexDigest = byteArrayToHexString(md.digest(key
077: .getBytes()));
078:
079: // CACHE-249: Performance improvement for large disk persistence usage
080: StringBuffer filename = new StringBuffer(hexDigest.length() + 2
081: * DIR_LEVELS);
082: for (int i = 0; i < DIR_LEVELS; i++) {
083: filename.append(hexDigest.charAt(i)).append(File.separator);
084: }
085: filename.append(hexDigest);
086:
087: return filename.toString().toCharArray();
088: }
089:
090: /**
091: * Nibble conversion. Thanks to our friends at:
092: * http://www.devx.com/tips/Tip/13540
093: * @param in the byte array to convert
094: * @return a java.lang.String based version of they byte array
095: */
096: static String byteArrayToHexString(byte[] in) {
097: if ((in == null) || (in.length <= 0)) {
098: return null;
099: }
100:
101: StringBuffer out = new StringBuffer(in.length * 2);
102:
103: for (int i = 0; i < in.length; i++) {
104: byte ch = (byte) (in[i] & 0xF0); // Strip off high nibble
105: ch = (byte) (ch >>> 4);
106:
107: // shift the bits down
108: ch = (byte) (ch & 0x0F);
109:
110: // must do this is high order bit is on!
111: out.append(PSEUDO[(int) ch]); // convert the nibble to a String Character
112: ch = (byte) (in[i] & 0x0F); // Strip off low nibble
113: out.append(PSEUDO[(int) ch]); // convert the nibble to a String Character
114: }
115:
116: return out.toString();
117: }
118:
119: static final String[] PSEUDO = { "0", "1", "2", "3", "4", "5", "6",
120: "7", "8", "9", "A", "B", "C", "D", "E", "F" };
121:
122: }
|