Quadcap Embeddable Server

com/quadcap/util/collections/Cache.java

Go to the documentation of this file.
00001 package com.quadcap.util.collections; 00002 00003 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.IOException; 00042 import java.io.PrintWriter; 00043 00044 import java.util.Enumeration; 00045 import java.util.Hashtable; 00046 00047 import com.quadcap.util.Debug; 00048 import com.quadcap.util.DList; 00049 import com.quadcap.util.DListItem; 00050 import com.quadcap.util.ListException; 00051 00052 /** 00053 * This class manages a number of buffers on an underlying store. 00054 * <p> 00055 * 00056 * What about write-through policies? 00057 * 00058 * @author Stan Bailes 00059 */ 00060 public abstract class Cache { 00061 Object store; 00062 Object lock; 00063 int size; 00064 00065 Hashtable t; 00066 DList lru = new DList(); 00067 00068 /** 00069 * Initialize the cache. 00070 * 00071 * @param s the underlying store 00072 * @param size the size of the cache 00073 */ 00074 public void init(Object store, int size) { 00075 this.store = store; 00076 this.size = size; 00077 this.lock = this; 00078 lru.resize(size); 00079 t = new Hashtable(); 00080 } 00081 00082 /** 00083 * Specify the lock-object to be used to synchronize access to the 00084 * cache. If the lock isn't specified, it defaults to the cache 00085 * object itself. 00086 * 00087 * @param lock the lock object 00088 */ 00089 public void setLock(Object lock) { 00090 this.lock = lock; 00091 } 00092 00093 /** 00094 * Search the cache for the specified key and return the associated 00095 * cache item if one was found. Otherwise, find a free cache item 00096 * and initialize it from the underlying store. 00097 * 00098 * @param key the search key 00099 * @return the cache item associated with the specified key. 00100 */ 00101 public Cacheable getCacheable(Object key) throws IOException { 00102 //Debug.println("getCacheable(" + key + ")"); 00103 synchronized (lock) { 00104 // ---- check the cache first. 00105 Cacheable c = (Cacheable)t.get(key); 00106 if (c == null) { 00107 // ---- if not in the cache, find a free cache slot and 00108 // ---- fill it with this object. 00109 c = getCacheable(); 00110 c.init(store, key); 00111 t.put(key, c); 00112 } 00113 00114 // ---- move this object to the front of the LRU list. 00115 try { 00116 lru.moveFront(c.getDListItem()); 00117 } catch (ListException e) { 00118 Debug.print(e); 00119 } 00120 c.incrRefCount(); 00121 //checkCache(); 00122 return c; 00123 } 00124 } 00125 00126 void checkCache() { 00127 synchronized (lock) { 00128 Hashtable s = new Hashtable(); 00129 // check that each item in the lru is in the hashtable 00130 try { 00131 boolean started = false; 00132 for (DListItem d = lru.head(); !started || d != lru.head(); 00133 d = d.next) { 00134 started = true; 00135 Cacheable c = (Cacheable)d.obj; 00136 if (c == null) continue; 00137 Object key = c.getKey(); 00138 Cacheable c1 = (Cacheable)t.get(key); 00139 if (c1 != c) { 00140 System.out.println("lru item not in hashtable: " + 00141 key); 00142 } 00143 s.put(key, c); 00144 } 00145 } catch (ListException e) { 00146 Debug.print(e); 00147 } 00148 00149 // check that each item in the hashtable is actually in the lru 00150 for (Enumeration e = t.keys(); e.hasMoreElements(); ) { 00151 Object key = e.nextElement(); 00152 if (s.get(key) == null) { 00153 System.out.println("hashtable item not in lru: " + key); 00154 } 00155 } 00156 } 00157 } 00158 00159 /** 00160 * Get the specified object from the cache, if available; from the 00161 * underlying store if not. In any case, the object (if found) 00162 * should be in the cache after this call.<p> 00163 * 00164 * If the object isn't in the cache <b>OR</b> the underlying store, 00165 * this method should return null, and the contents of both the cache 00166 * and the underlying store should remain unchanged.<p> 00167 * 00168 * <b>This implementation doesn't change the underlying store, but 00169 * the cache ends up holding a CacheItem with a null data component.</b> 00170 * @param key the search key 00171 * @param return the data associated with the key 00172 */ 00173 public Object get(Object key) throws IOException { 00174 Object data = null; 00175 synchronized (lock) { 00176 Cacheable c = getCacheable(key); 00177 try { 00178 data = c.getData(); 00179 } finally { 00180 c.decrRefCount(); 00181 } 00182 } 00183 return data; 00184 } 00185 00186 /** 00187 * Store the object in the cache. The object will be marked <i>dirty</i>, 00188 * and will eventually be written back to the underlying store. 00189 */ 00190 public void put(Object key, Object val) throws IOException { 00191 synchronized (lock) { 00192 Cacheable c = getCacheable(key); 00193 try { 00194 c.setData(val); 00195 } finally { 00196 c.decrRefCount(); 00197 } 00198 } 00199 } 00200 00201 /** 00202 * Factory to create an empty cache slot. Only a fixed number of 00203 * cache slots should be created, and they should then be recycled, 00204 * never destroyed. 00205 */ 00206 abstract public Cacheable makeCacheable(); 00207 00208 /** 00209 * Find a free cache slot. If necessary, seize an existing cache 00210 * slot, with preference for the "least recently used" item. 00211 * This function should only be called if the lock on the cache is 00212 * held by this thread. 00213 */ 00214 Cacheable getCacheable() throws IOException { 00215 Cacheable c = null; 00216 DListItem d = lru.tail(); 00217 00218 while (d != null) { 00219 c = (Cacheable)d.obj; 00220 if (c != null && c.getRefCount() > 0) { 00221 if (d == lru.head()) { 00222 show(new PrintWriter(Debug.debugStream)); 00223 throw new RuntimeException("no free cache item"); 00224 } 00225 d = d.prev; 00226 } else { 00227 break; // 'c' contains the Cacheable we're looking for 00228 } 00229 } 00230 00231 if (c == null) { 00232 d.obj = c = makeCacheable(); 00233 c.setDListItem(d); 00234 } else { 00235 Object key = c.getKey(); 00236 if (key != null) { 00237 //Debug.println("Cache: remove(" + key + ")"); 00238 t.remove(key); 00239 } 00240 if (c.isDirty()) c.flush(); 00241 } 00242 return c; 00243 } 00244 00245 /** 00246 * Flush all modified items back to the underlying store. 00247 */ 00248 public void flush() throws IOException { 00249 synchronized (lock) { 00250 boolean started = false; 00251 for (DListItem d = lru.head(); !started || d != lru.head(); 00252 d = d.next) { 00253 started = true; 00254 Cacheable c = (Cacheable)d.obj; 00255 if (c != null) { 00256 // XXX I'm not sure I should be synchronizing on this 00257 // XXX abstract object here. Maybe let the implementing 00258 // XXX class take care of it's own mutual exclusion. 00259 // synchronized (c) 00260 c.flush(); 00261 } 00262 } 00263 } 00264 } 00265 00266 public void show(PrintWriter os) { 00267 synchronized (lock) { 00268 lru.show(os, "\n"); 00269 } 00270 } 00271 }