1
2 /* Open Source Java Caching Service
3 * Copyright (C) 2002 Frank Karlstrøm
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * The author can be contacted by email: fjankk@users.sourceforge.net
19 */
20 package org.fjank.jcache;
21
22 import java.lang.ref.ReferenceQueue;
23 import java.util.Iterator;
24 import javax.util.jcache.Attributes;
25 import javax.util.jcache.CacheAttributes;
26 import javax.util.jcache.CacheException;
27 import javax.util.jcache.CacheNotAvailableException;
28 import javax.util.jcache.NullObjectException;
29
30
31
32
33 /**
34 * A Runnable class for Sweeping the cache for expired objects. Either they are
35 * serialized to disk, or they are removed from the cache depending on the
36 * attributes of the object.
37 *
38 * @author Frank Karlstrøm
39 */
40 public class CacheSweeper implements Runnable {
41 /** the singleton sweeper */
42 private static CacheSweeper _singleton;
43 /** a boolean indication wether this sweeper is active or not. */
44 //2004-09-FB
45 private boolean active = false;
46
47 /**
48 * Creates new CacheSweeper
49 */
50 private CacheSweeper() {
51 }
52
53 /**
54 * sweeps the cache and removes invalid objects, waits for cleanInterval
55 * seconds, then sweeps again etc.
56 */
57 public void run() {
58 CacheImpl cache = CacheImpl.getCache(true);
59 CacheAttributes attribs = cache.getAttributes();
60
61 while (active) {
62 try {
63 Thread.sleep(attribs.getCleanInterval() * 1000);
64 } catch (InterruptedException e) {
65 //normal
66 }
67 sweepCache();
68 }
69 }
70
71 /**
72 * checks the size of the objects in the cache, and if the memory limit
73 * is nearing its capacity, somoe objects are saved to disk. The
74 * algorithm for choosing these is in the method writeToDisk(). Then the
75 * ReferenceQueue is checked, and all objects in this queue have been
76 * swept by the GC. if they have expired, they are deleted from the cache.
77 * If not, they are retained to the next cachesweep.
78 */
79 private void sweepCache() {
80 CacheImpl cache = null;
81 try {
82 cache = CacheImpl.getCache(true);
83
84 int maxSize = cache.getAttributes().getMemoryCacheSize();
85 long maxBytes = maxSize * 1024 * 1024;
86 if (getMemoryCacheSize() >= maxBytes) {
87 writeToDisk();
88 }
89 /**sweeps the entire cache, and de-references the expired objects.
90 * This will add them to the appropiate ReferenceQueue
91 * when their referenceCount is zero. If no one uses the objects,
92 * this will happen immediantely.
93 * We also attempt to stimulate the gc to run. (Not neccessary perhaps?)
94 *
95 */
96
97 CacheRegion reg = cache.getRegion();
98 sweepGroup(reg);
99 Iterator iter = cache.userRegionNames();
100 while(iter.hasNext()) {
101 Object key = iter.next();
102 CacheRegion userReg = cache.getRegion(key);
103 sweepGroup(userReg);
104 }
105 for(int i=0; i<3; i++) {
106 System.runFinalization();
107 System.gc();
108 try {
109 Thread.sleep(500);
110 } catch (InterruptedException e1) {;}
111 }
112
113 /**now its up to the referencequeue to remove the
114 * appropiate obects.
115 */
116 CacheObject cacheObj = null;
117 ReferenceQueue q = cache.getReferenceQueue();
118 while ((cacheObj = (CacheObject) q.poll()) != null) {
119 cacheObj.resetRefCount();
120 tryRemoval(cacheObj);
121 }
122
123 } catch (CacheException e) {
124 e.printStackTrace();
125 stopSweeper();
126 } catch (java.lang.IncompatibleClassChangeError e) {
127 //the cache has probably been reloaded/redeployed.
128 //restart ourselves.
129 if (cache != null) {
130 cache.close();
131 try {
132 cache.open();
133 } catch (CacheNotAvailableException ee) {
134 stopSweeper();
135 }
136 }
137 }
138 }
139 /**
140 * Will sweep a group in search for an object which has expired.
141 * any groups encountered will use this method as a recursive method
142 * to further search for expired objects.
143 * @todo there's no failsafe for groups containing themselves.
144 * @param group the group to sweep.
145 * @throws NullObjectException if a nullobject is encountered.
146 */
147 private void sweepGroup(CacheGroup group) throws NullObjectException {
148 Iterator iter = group.weakReferenceObjects.keySet().iterator();
149 while(iter.hasNext()) {
150 Object key = iter.next();
151 Object obj = group.weakReferenceObjects.get(key);
152 if(obj instanceof CacheObject) {
153 if(hasExpired((CacheObject) obj)) {
154 group.removeObjectReference(key);
155 }
156 }else if(obj instanceof CacheGroup) {
157 sweepGroup((CacheGroup) obj);
158 }else {
159 System.out.println("An unknown object ("+obj.getClass().getName()+") was discovered in the cache.");
160 break;
161 }
162 }
163 }
164
165 /**
166 * the actual remove routine. if the object has been alive longer than the
167 * timeToLive , its invalidated.
168 * @todo Some of the code here is duplicated in tryRemoveHardReference.
169 * @todo return value is never used.
170 * @param cacheObj the CacheObject to try to remove.
171 * @return an boolean indication wether the object was removed or not.
172 */
173
174 private boolean tryRemoval(final CacheObject cacheObj) throws NullObjectException {
175 if (hasExpired(cacheObj)) {
176 cacheObj.invalidate();
177 return true;
178 }
179 return false;
180 }
181 private boolean hasExpired(CacheObject obj) throws NullObjectException {
182 Attributes attributes = obj.getAttributes();
183 if(attributes==null) throw new NullObjectException("A null attributes was detected.");
184 /*-1 is the default for attributes, and the default is
185 * non invalidation, so if the ttl is -1, the objects remains in the cache.
186 */
187
188 if(attributes.getTimeToLive()==-1) {
189 return false;
190 }
191 if(attributes.getLoader()!=null) return false;
192 long now = System.currentTimeMillis();
193 long timealive = (now - attributes.getCreateTime()) / 1000;
194
195 //only ttl is support for now. the rest of the params can be implemented later.
196 if (timealive >= attributes.getTimeToLive()) {
197 return true;
198 }
199 return false;
200 }
201 /**
202 * Finds all objects wich are not in use, and determines wich one of them
203 * is the best to flush to disk. LFU is a caching strategy that stands for
204 * "least frequently used". In this strategy, elements are evicted based
205 * when they were added, when they were last used, and how many times they
206 * have been used. Other strategies may be implemented later. Gets the
207 * diskFile, fins an available position in the file, converts the
208 * CacheObject to a CacheObjectDisk, and writes it to the disk file.
209 *
210 *@todo NOT IMPLEMENTED.
211 */
212 private void writeToDisk() {
213 }
214
215 /**
216 * Gets the current size of all objects in the cache. Return the size of
217 * the actuall object, not regarding the size of the attributes, or the
218 * wrapper objects.
219 *
220 * @return long the current size of all objects in the cache.
221 *
222 *@todo NOT IMPLEMENTED.
223 */
224 private long getMemoryCacheSize() {
225 return 0;
226 }
227
228 /**
229 * activates this sweeper.
230 */
231 synchronized void startSweeper() {
232 //2004/09-FB
233 if (!_singleton.active){
234 active=true;
235 CacheThreadFactory fact = CacheThreadFactory.getInstance();
236 fact.setName("Fjanks FKache - CacheSweeper");
237 fact.setDaemon(true);
238 fact.newThread(this).start();
239
240 }
241 }
242
243 /**
244 * deactivates this sweeper.
245 */
246 //2004/09-FB
247 synchronized void stopSweeper() {
248 active = false;
249
250 }
251
252 /**
253 * create this sweeper instance.
254 */
255 public static synchronized CacheSweeper getInstance() {
256 if (_singleton == null) {
257 _singleton = new CacheSweeper();
258 //2004/09-FB
259 //_singleton.startSweeper();
260
261 }
262 return _singleton;
263 }
264
265 //2004/09-FB
266 /**
267 * remove this sweeper instance.
268 */
269 static synchronized void removeInstance() {
270 if (_singleton != null) {
271 if (_singleton.active){
272 _singleton.stopSweeper();
273 }
274 _singleton = null;
275
276 }
277 }
278 }