001 package echopoint.util.collections;
002 /*
003 * This file is part of the Echo Point Project. This project is a collection
004 * of Components that have extended the Echo Web Application Framework.
005 *
006 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
007 *
008 * The contents of this file are subject to the Mozilla Public License Version
009 * 1.1 (the "License"); you may not use this file except in compliance with
010 * the License. You may obtain a copy of the License at
011 * http://www.mozilla.org/MPL/
012 *
013 * Software distributed under the License is distributed on an "AS IS" basis,
014 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
015 * for the specific language governing rights and limitations under the
016 * License.
017 *
018 * Alternatively, the contents of this file may be used under the terms of
019 * either the GNU General Public License Version 2 or later (the "GPL"), or
020 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
021 * in which case the provisions of the GPL or the LGPL are applicable instead
022 * of those above. If you wish to allow use of your version of this file only
023 * under the terms of either the GPL or the LGPL, and not to allow others to
024 * use your version of this file under the terms of the MPL, indicate your
025 * decision by deleting the provisions above and replace them with the notice
026 * and other provisions required by the GPL or the LGPL. If you do not delete
027 * the provisions above, a recipient may use your version of this file under
028 * the terms of any one of the MPL, the GPL or the LGPL.
029 */
030
031 import java.lang.ref.WeakReference;
032 import java.util.ArrayList;
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.Set;
036 import java.util.Timer;
037 import java.util.TimerTask;
038
039
040 /**
041 * <code>TimerExpiryCache</code> is an a implementation
042 * of <code>ExpiryCache</code> that can contain
043 * objects that "expire". A <b>shared</b> background task will
044 * periodically "reap" objects that have "expired".
045 * <p>
046 * By default, soft references are used to the cached data so that they can
047 * be reclaimed in low memory conditions regardless of whether they
048 * have expired or not.
049 * <p>
050 * The time-to-live and access-timeout is used to decide when an object
051 * has expired and needs to be removed from the cache.
052 * <p>
053 * Time-to-live is simple. Once the specified period elapses, the object
054 * is removed from the cache, regardless of how many times its been
055 * accessed.
056 * <p>
057 * Access-timeout is a little more complicated. Each time the object
058 * is taken from the cache, its lastAccessTime is tracked. If the
059 * access-timeout has expired (since its last access) then the object
060 * is taken from the cache.
061 * <p>
062 * If both the time-to-live and access-timeout is -1, then the object
063 * will never expire from the cache.
064 */
065 public class TimerExpiryCache extends ExpiryCache {
066
067 /**
068 * The reaper interval is 2 minutes. Therefore the cached object
069 * life span granulairty is +/- 1 minute.
070 */
071 public static final long DEFAULT_REAPER_INTERVAL = 2 * 60 * 1000;
072
073 /** This is an array of all ExpiryCaches that are to be reaped */
074 private static List allExpiryCaches;
075
076 /**
077 * The TimerExpiryCache reaper runs as a TimerTask under this static Timer.
078 * All TimerExpiryCache instances share this common reaper.
079 */
080 private static Timer reaperTimer;
081 static {
082 allExpiryCaches = new ArrayList();
083 reaperTimer = new Timer(true);
084 reaperTimer.schedule(new ReaperTimerTask(), DEFAULT_REAPER_INTERVAL, DEFAULT_REAPER_INTERVAL);
085 }
086
087 /**
088 * Constructs a default <code>TimerExpiryCache</code>
089 */
090 public TimerExpiryCache() {
091 this(DEFAULT_TIME_TO_LIVE,DEFAULT_ACCESS_TIMEOUT,true);
092 }
093
094 /**
095 * Constructs a <code>TimerExpiryCache</code>
096 *
097 * @param timeToLive - the default time-to-live for a cache entry
098 * @param accessTimeout - the default access timeout for a cache entry
099 */
100 public TimerExpiryCache(long timeToLive, long accessTimeout) {
101 this(timeToLive,accessTimeout,true);
102 }
103
104 /**
105 * Constructs a <code>TimerExpiryCache</code> with all the parameters
106 *
107 * @param timeToLive - the default time-to-live for a cache entry
108 * @param accessTimeout - the default access timeout for a cache entry
109 * @param softReferences - whether soft refernces are used to cached data
110 */
111 public TimerExpiryCache(long timeToLive, long accessTimeout, boolean softReferences) {
112 super(timeToLive,accessTimeout,softReferences);
113 // add ourselves to the queue of TimerExpiryCaches for the reaper
114 synchronized (allExpiryCaches) {
115 allExpiryCaches.add(new WeakReference(this));
116 }
117 }
118
119 /** A shared TimrTask that reapers all of the various ExpiryCaches */
120 private static class ReaperTimerTask extends TimerTask {
121
122 public void run() {
123 //System.err.println("Cleanup");
124 Thread currentThread = Thread.currentThread();
125 if (currentThread.getPriority() != Thread.MIN_PRIORITY)
126 currentThread.setPriority(Thread.MIN_PRIORITY);
127
128 synchronized (allExpiryCaches) {
129 for (Iterator iter = allExpiryCaches.iterator(); iter.hasNext();) {
130 WeakReference weakRef = (WeakReference) iter.next();
131 TimerExpiryCache expiryCache = (TimerExpiryCache) (weakRef == null ? null : weakRef.get());
132 //
133 // our global list is weakly referenced to the ExpiryCaches. If
134 // its gone, then no one else was interested in the cache
135 // and neither are we
136 //
137 if (expiryCache == null) {
138 iter.remove();
139 continue;
140 }
141
142 List list = new ArrayList();
143 synchronized (expiryCache) {
144 long now = System.currentTimeMillis();
145 Set keys = expiryCache.keySet();
146 Iterator itr = keys.iterator(); // Must be in synchronized block
147 while (itr.hasNext()) {
148 Object key = itr.next();
149 if (expiryCache.hasExpired(key,now)) {
150 list.add(key);
151 }
152 }
153 for (Iterator iter2 = list.iterator(); iter2.hasNext();) {
154 //System.err.println("Removing :" + key);
155 Object key = iter2.next();
156 expiryCache.put(key,null);
157 }
158 }
159 }
160 }
161 }
162 };
163 }