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: * BeanCache.java
020: *
021: * This object is responsible for mantaining a cache of beans. It is used by
022: * both the RMI tie classes and the Local proxy objects when a method that
023: * contains, find|get and remove|delete is found.
024: */
025:
026: // package path
027: package com.rift.coad.lib.bean;
028:
029: // java imports
030: import java.util.Date;
031: import java.util.Iterator;
032: import java.util.Map;
033: import java.util.HashMap;
034: import javax.rmi.PortableRemoteObject;
035:
036: // logging import
037: import org.apache.log4j.Logger;
038:
039: // coadunation imports
040: import com.rift.coad.lib.cache.Cache;
041: import com.rift.coad.lib.cache.CacheEntry;
042: import com.rift.coad.lib.configuration.ConfigurationFactory;
043: import com.rift.coad.lib.configuration.Configuration;
044: import com.rift.coad.lib.thread.ThreadStateMonitor;
045:
046: /**
047: * This object is responsible for mantaining a cache of beans. It is used by
048: * both the RMI tie classes and the Local proxy objects when a method that
049: * contains, find|get and remove|delete is found.
050: *
051: * @author Brett Chaldecott
052: */
053: public class BeanCache implements Cache {
054:
055: // class constants
056: private final static String CACHE_EXPIRY_TIME = "bean_cache_expiry";
057: private final static long CACHE_EXPIRY_TIME_DEFAULT = 30 * 60 * 1000;
058:
059: // private member variables
060: protected static Logger log = Logger.getLogger(BeanCache.class
061: .getName());
062:
063: // private member variables
064: private Map entries = new HashMap();
065: private long defaultCacheExpiryTime = 0;
066: private ThreadStateMonitor status = new ThreadStateMonitor();
067:
068: /**
069: * Creates a new instance of BeanCache
070: *
071: * @exception BeanException
072: */
073: public BeanCache() throws BeanException {
074: try {
075: Configuration config = ConfigurationFactory.getInstance()
076: .getConfig(BeanCache.class);
077: defaultCacheExpiryTime = config.getLong(CACHE_EXPIRY_TIME,
078: CACHE_EXPIRY_TIME_DEFAULT);
079: } catch (Exception ex) {
080: log.error("Failed to start the BeanCache object because : "
081: + ex.getMessage(), ex);
082: throw new BeanException(
083: "Failed to start the BeanCache object because : "
084: + ex.getMessage(), ex);
085: }
086: }
087:
088: /**
089: * This method is called to perform garbage collection on the cache entries.
090: */
091: public void garbageCollect() {
092: // copy the entries map
093: Map entries = new HashMap();
094: synchronized (this .entries) {
095: entries.putAll(this .entries);
096: }
097:
098: // loop through the entires and remove the expired ones
099: Date expiryDate = new Date();
100: for (Iterator iter = entries.keySet().iterator(); iter
101: .hasNext();) {
102: Object cacheKey = iter.next();
103: BeanCacheEntry beanCacheEntry = (BeanCacheEntry) entries
104: .get(cacheKey);
105: if (beanCacheEntry.isExpired(expiryDate)) {
106: synchronized (this .entries) {
107: if (beanCacheEntry.getCacheEntry() != null) {
108: try {
109: PortableRemoteObject
110: .unexportObject((java.rmi.Remote) beanCacheEntry
111: .getCacheEntry());
112: // remove from cache
113: this .entries.remove(cacheKey);
114: beanCacheEntry.cacheRelease();
115: } catch (java.rmi.NoSuchObjectException ex) {
116: log.warn("The object was never exported : "
117: + ex.getMessage(), ex);
118: // remove from cache
119: this .entries.remove(cacheKey);
120: beanCacheEntry.cacheRelease();
121: } catch (Exception ex) {
122: log.error(
123: "Failed to un-export this object : "
124: + ex.getMessage(), ex);
125: }
126: } else {
127: // if this object has not cache entry as in no rmi tie
128: // class than just remove it.
129: this .entries.remove(cacheKey);
130: beanCacheEntry.cacheRelease();
131: }
132: }
133: }
134: }
135: }
136:
137: /**
138: * This method is called to forcibly remove everything from the cache.
139: */
140: public void clear() {
141: // copy the entries map
142: Map entries = new HashMap();
143: synchronized (this .entries) {
144: entries.putAll(this .entries);
145: this .entries.clear();
146: status.terminate(false);
147: }
148:
149: // loop through the entires and remove the expired ones
150: for (Iterator iter = entries.keySet().iterator(); iter
151: .hasNext();) {
152: Object cacheKey = iter.next();
153: BeanCacheEntry beanCacheEntry = (BeanCacheEntry) entries
154: .get(cacheKey);
155: if (beanCacheEntry.getCacheEntry() != null) {
156: try {
157: PortableRemoteObject
158: .unexportObject((java.rmi.Remote) beanCacheEntry
159: .getCacheEntry());
160: } catch (java.rmi.NoSuchObjectException ex) {
161: log.warn("The cache object was not bound : "
162: + ex.getMessage(), ex);
163: } catch (Exception ex) {
164: log.error(
165: "Failed to un-export the cached object : "
166: + ex.getMessage(), ex);
167: }
168: }
169: beanCacheEntry.cacheRelease();
170: }
171: }
172:
173: /**
174: * This mehtod returns true if the cache contains the checked entry.
175: *
176: * @return TRUE if the cache contains the checked entry.
177: * @param cacheKey The entry to perform the check for.
178: */
179: public boolean contains(Object cacheKey) {
180: synchronized (entries) {
181: return entries.containsKey(cacheKey);
182: }
183: }
184:
185: /**
186: * This method returns the bean cache entry for manipulation by the caller.
187: *
188: * @return A reference to the bean cache object.
189: * @param cacheEntry The cache entry to retrieve.
190: */
191: public BeanCacheEntry getEntry(Object cacheKey)
192: throws BeanException {
193: synchronized (entries) {
194: checkStatus();
195: BeanCacheEntry beanCacheEntry = (BeanCacheEntry) entries
196: .get(cacheKey);
197: if (beanCacheEntry != null) {
198: beanCacheEntry.touch();
199: }
200: return beanCacheEntry;
201: }
202: }
203:
204: /**
205: * This method adds the entry to the cache.
206: *
207: * @param cacheKey The key to identify this entry by.
208: * @param wrappedObject The object wrapped by the cache entry.
209: * @param entry An entry in the cache
210: */
211: public void addCacheEntry(long timeout, Object cacheKey,
212: Object wrappedObject, CacheEntry entry)
213: throws BeanException {
214: synchronized (entries) {
215: checkStatus();
216: long cacheTimeout = timeout;
217: if (timeout == -1) {
218: cacheTimeout = defaultCacheExpiryTime;
219: }
220: // remove the original entry if there is one.
221: BeanCacheEntry beanCacheEntry = getEntry(cacheKey);
222: if (beanCacheEntry != null) {
223: try {
224: PortableRemoteObject
225: .unexportObject((java.rmi.Remote) beanCacheEntry
226: .getCacheEntry());
227: } catch (java.rmi.NoSuchObjectException ex) {
228: log.warn("The cache object was not bound : "
229: + ex.getMessage(), ex);
230: }
231: }
232: entries.put(cacheKey, new BeanCacheEntry(cacheTimeout,
233: cacheKey, wrappedObject, entry));
234: }
235: }
236:
237: /**
238: * This method adds a new entry to the cache.
239: *
240: * @param cacheKey The key to identify this entry by.
241: * @param wrappedObject The object wrapped by the proxy.
242: * @param proxy The proxy to add.
243: * @param handle The handler for the bean proxy object.
244: */
245: public void addCacheEntry(long timeout, Object cacheKey,
246: Object wrappedObject, Object proxy, CacheEntry handle)
247: throws BeanException {
248: synchronized (entries) {
249: checkStatus();
250: long cacheTimeout = timeout;
251: if (timeout == -1) {
252: cacheTimeout = defaultCacheExpiryTime;
253: }
254: // remove the original entry if there is one.
255: BeanCacheEntry beanCacheEntry = getEntry(cacheKey);
256: if (beanCacheEntry != null) {
257: try {
258: PortableRemoteObject
259: .unexportObject((java.rmi.Remote) beanCacheEntry
260: .getCacheEntry());
261: } catch (java.rmi.NoSuchObjectException ex) {
262: log.warn("The cache object was not bound : "
263: + ex.getMessage(), ex);
264: }
265: }
266: entries.put(cacheKey, new BeanCacheEntry(cacheTimeout,
267: cacheKey, wrappedObject, proxy, handle));
268: }
269: }
270:
271: /**
272: * This method removes the entry from the cache based on the key passed in.
273: *
274: * @param cacheKey The key in the cache to remove.
275: */
276: public void removeCacheEntry(Object cacheKey) throws BeanException {
277: synchronized (entries) {
278: checkStatus();
279: entries.remove(cacheKey);
280: }
281: }
282:
283: /**
284: * This method check the bean cache status.
285: */
286: private void checkStatus() throws BeanException {
287: if (status.isTerminated()) {
288: throw new BeanException("Bean cache has been terminated.");
289: }
290: }
291: }
|