001: /* Copyright 2002 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.concurrency.caching;
007:
008: import java.util.Map;
009:
010: import org.jasig.portal.EntityTypes;
011: import org.jasig.portal.IBasicEntity;
012: import org.jasig.portal.PortalSessionManager;
013: import org.jasig.portal.concurrency.CachingException;
014: import org.jasig.portal.concurrency.IEntityCache;
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017:
018: /**
019: * Reference implementation of IEntityCache. Each cache holds entities of
020: * a single type in an LRUCache, a kind of HashMap. Synchronization for
021: * get(), add() and remove() is handled by the LRUCache. At intervals,
022: * the cleanupThread kicks off a sweep of the cache to trim it down to
023: * its maximum size.
024: *
025: * @author Dan Ellentuck
026: * @version $Revision: 36731 $
027: * @see org.jasig.portal.concurrency.caching.LRUCache
028: */
029: public class ReferenceEntityCache implements IEntityCache {
030: private static final Log LOG = LogFactory
031: .getLog(ReferenceEntityCache.class);
032: protected Map cache;
033: protected Class entityType;
034: protected String simpleTypeName;
035: protected static int threadID = 0;
036:
037: // The interval between cache updates; defaults to 60 seconds.
038: protected int sweepIntervalMillis = 60 * 1000;
039:
040: // An alarm clock to kick off the cache refresh.
041: protected Thread cleanupThread = null;
042:
043: protected class CacheSweeper implements Runnable {
044: protected CacheSweeper() {
045: super ();
046: }
047:
048: public void run() {
049: for (;;) {
050: try {
051: Thread.sleep(sweepIntervalMillis);
052: } catch (InterruptedException e) {
053: }
054: cleanupCache();
055: }
056: }
057: }
058:
059: /**
060: * ReferenceEntityCache constructor comment.
061: */
062: public ReferenceEntityCache(Class type, int maxSize,
063: int maxUnusedTime, int sweepInterval)
064: throws CachingException {
065: super ();
066: initializeEntityType(type);
067: entityType = type;
068: sweepIntervalMillis = sweepInterval;
069: setCache(new LRUCache(maxSize, maxUnusedTime));
070: String threadName = "uPortal ReferenceEntityCache sweeper thread #"
071: + ++threadID;
072: cleanupThread = new Thread(PortalSessionManager
073: .getThreadGroup(), new CacheSweeper(), threadName);
074: cleanupThread.setDaemon(true);
075: cleanupThread.start();
076: }
077:
078: /**
079: * Checks that <code>entity</code> is the same type as, i.e., could be cast
080: * to, the cache type.
081: *
082: * @param entity the entity to be added to the cache.
083: */
084: public void add(IBasicEntity entity) throws CachingException {
085: if (!this .getEntityType().isAssignableFrom(
086: entity.getEntityIdentifier().getType())) {
087: throw new CachingException("Problem adding " + entity
088: + ": entity type is incompatible with cache.");
089: }
090:
091: getCache().put(entity.getEntityIdentifier().getKey(), entity);
092: }
093:
094: /**
095: *
096: */
097: private void initializeEntityType(Class type)
098: throws CachingException {
099: try {
100: EntityTypes.addIfNecessary(type,
101: "Added by ReferenceEntityCache");
102: } catch (Exception ex) {
103: throw new CachingException("Problem adding entity type "
104: + type, ex);
105: }
106: }
107:
108: /**
109: * Remove stale entries from the cache.
110: */
111: public void cleanupCache() {
112: int before = size();
113: debug("ENTERING ReferenceEntityCache.cleanupCache() for "
114: + getSimpleTypeName() + " : number of entries: "
115: + before);
116: ((LRUCache) getCache()).sweepCache();
117: debug("LEAVING ReferenceEntityCache.cleanupCache() for "
118: + getSimpleTypeName() + " : removed "
119: + (before - size()) + " cache entries.");
120: }
121:
122: /**
123: * Remove all entries from the cache.
124: */
125: public void clearCache() {
126: getCache().clear();
127: }
128:
129: /**
130: */
131: void debug(String msg) {
132: if (LOG.isDebugEnabled()) {
133: java.sql.Timestamp ts = new java.sql.Timestamp(System
134: .currentTimeMillis());
135: LOG.debug(ts + " : " + msg);
136: }
137: }
138:
139: /**
140: * @param key the key of the entity.
141: * @return org.jasig.portal.concurrency.IBasicEntity
142: */
143: public IBasicEntity get(String key) {
144: return (IBasicEntity) getCache().get(key);
145: }
146:
147: /**
148: * @return java.util.Map
149: */
150: protected java.util.Map getCache() {
151: return cache;
152: }
153:
154: /**
155: * @return java.lang.Class
156: */
157: public final java.lang.Class getEntityType() {
158: return entityType;
159: }
160:
161: /**
162: * @param key the key of the entity to be un-cached.
163: */
164: public void remove(String key) throws CachingException {
165: cache.remove(key);
166: }
167:
168: /**
169: * @param newCache java.util.Map
170: */
171: protected void setCache(java.util.Map newCache) {
172: cache = newCache;
173: }
174:
175: /**
176: * @return int
177: */
178: public int size() {
179: return getCache().size();
180: }
181:
182: /**
183: * Returns a String that represents the value of this object.
184: * @return a string representation of the receiver
185: */
186: public String toString() {
187: return "ReferenceEntityCache for " + getSimpleTypeName();
188: }
189:
190: private String getSimpleTypeName() {
191: if (simpleTypeName == null) {
192: String name = getEntityType().getName();
193: while (name.indexOf('.') >= 0) {
194: name = name.substring(name.indexOf('.') + 1);
195: }
196: simpleTypeName = name;
197: }
198: return simpleTypeName;
199: }
200:
201: /**
202: * @param entity the entity to be updated in the cache.
203: */
204: public void update(IBasicEntity entity) throws CachingException {
205: add(entity);
206: }
207:
208: }
|