001: /*
002: * CoadunationLib: The coaduntion implementation library.
003: * Copyright (C) 2006 Rift IT Contracting
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
018: *
019: * KeySyncCache.java
020: *
021: * This object is responsible for returning synchronization objects based on a
022: * key passed in.
023: */
024:
025: // package path
026: package com.rift.coad.lib.cache;
027:
028: // java imports
029: import java.util.Date;
030: import java.util.Map;
031: import java.util.HashMap;
032: import java.util.Iterator;
033:
034: // logging import
035: import org.apache.log4j.Logger;
036:
037: // coadunation imports
038: import com.rift.coad.lib.configuration.ConfigurationFactory;
039: import com.rift.coad.lib.configuration.Configuration;
040: import com.rift.coad.lib.thread.ThreadStateMonitor;
041:
042: /**
043: * This object is responsible for returning synchronization objects based on a
044: * key passed in.
045: *
046: * @author Brett Chaldecott
047: */
048: public class KeySyncCache implements Cache {
049:
050: /**
051: * This object is used to synchronize access to a specific key value.
052: */
053: public static class KeySync {
054: // private
055: private Date touchTime = null;
056:
057: /**
058: * The constructor of the key sync object.
059: */
060: public KeySync() {
061: touch();
062: }
063:
064: /**
065: * This object returns true if this object is expiried.
066: *
067: * @return TRUE if expired FALSE if not.
068: * @param expiryDate The date the time is older than for expiry to
069: * succeed.
070: */
071: public synchronized boolean isExpired(Date expiryDate) {
072: return (touchTime.getTime() < expiryDate.getTime());
073: }
074:
075: /**
076: * This method updates the last touch time for this object.
077: */
078: public synchronized void touch() {
079: touchTime = new Date();
080: }
081: }
082:
083: // class constants
084: private final static String CACHE_EXPIRY_TIME = "key_sync_cache_expiry";
085: private final static long CACHE_EXPIRY_TIME_DEFAULT = 30 * 60 * 1000;
086:
087: // private member variables
088: protected static Logger log = Logger.getLogger(KeySyncCache.class
089: .getName());
090:
091: // private member variable
092: private ThreadStateMonitor status = new ThreadStateMonitor();
093: private Map keySyncMap = new HashMap();
094: private Configuration config = null;
095: private long cacheExpiryTime = 0;
096:
097: /**
098: * Creates a new instance of KeySyncCache
099: *
100: * @exception CacheException
101: */
102: public KeySyncCache() throws CacheException {
103: try {
104: Configuration config = ConfigurationFactory.getInstance()
105: .getConfig(KeySyncCache.class);
106: cacheExpiryTime = config.getLong(CACHE_EXPIRY_TIME,
107: CACHE_EXPIRY_TIME_DEFAULT);
108: } catch (Exception ex) {
109: log.error(
110: "Failed to start the KeySyncCache object because : "
111: + ex.getMessage(), ex);
112: throw new CacheException(
113: "Failed to start the KeySyncCache object because : "
114: + ex.getMessage(), ex);
115: }
116: }
117:
118: /**
119: * This method returns the key sync object for the requested key.
120: *
121: * @return The reference to the key stink object to return.
122: * @param key The key to retrieve.
123: */
124: public KeySync getKeySync(Object key) throws CacheException {
125: checkStatus();
126: synchronized (keySyncMap) {
127: if (!keySyncMap.containsKey(key)) {
128: keySyncMap.put(key, new KeySync());
129: }
130: KeySync keySync = (KeySync) keySyncMap.get(key);
131: keySync.touch();
132: return keySync;
133: }
134: }
135:
136: /**
137: * This method is called to perform garbage collection on the cache entries.
138: */
139: public void garbageCollect() {
140: Map keySyncMap = new HashMap();
141: synchronized (this .keySyncMap) {
142: keySyncMap.putAll(this .keySyncMap);
143: }
144: Date expiryDate = new Date(new Date().getTime()
145: - cacheExpiryTime);
146: for (Iterator iter = keySyncMap.keySet().iterator(); iter
147: .hasNext();) {
148: Object key = iter.next();
149: KeySync keySync = (KeySync) keySyncMap.get(key);
150: synchronized (this .keySyncMap) {
151: if (keySync.isExpired(expiryDate)) {
152: this .keySyncMap.remove(key);
153: }
154: }
155: }
156: }
157:
158: /**
159: * This method is called to forcibly remove everything from the cache.
160: */
161: public void clear() {
162: status.terminate(false);
163: synchronized (this .keySyncMap) {
164: this .keySyncMap.clear();
165: }
166: }
167:
168: /**
169: * This mehtod returns true if the cache contains the checked entry.
170: *
171: * @return TRUE if the cache contains the checked entry.
172: * @param cacheEntry The entry to perform the check for.
173: */
174: public boolean contains(Object cacheEntry) {
175: synchronized (keySyncMap) {
176: return keySyncMap.containsKey(cacheEntry);
177: }
178: }
179:
180: /**
181: * This method checks the status of this cache and throws if it has been
182: * terminated.
183: *
184: * @exception CacheException
185: */
186: private void checkStatus() throws CacheException {
187: if (status.isTerminated()) {
188: throw new CacheException("The cache has been terminated.");
189: }
190: }
191:
192: }
|