001: package com.quadcap.sql.file;
002:
003: /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.IOException;
042: import java.io.PrintWriter;
043:
044: import java.util.Enumeration;
045: import java.util.Iterator;
046: import java.util.Hashtable;
047:
048: import com.quadcap.util.Debug;
049: import com.quadcap.util.DList;
050: import com.quadcap.util.DListItem;
051: import com.quadcap.util.ListException;
052:
053: import com.quadcap.util.collections.LongMap;
054:
055: /**
056: * This class manages a number of buffers on an underlying store.
057: * <p>
058: *
059: * What about write-through policies?
060: *
061: * @author Stan Bailes
062: */
063: public abstract class Cache {
064: Object store;
065: Object lock;
066: int size;
067: boolean readOnly;
068:
069: /**
070: * Map long -> Cacheable
071: */
072: LongMap t;
073:
074: /**
075: * LRU list of free cache items
076: */
077: DList lru = new DList();
078:
079: /**
080: * Initialize the cache.
081: *
082: * @param s the underlying store
083: * @param size the size of the cache
084: */
085: public void init(Object store, int size) {
086: this .store = store;
087: this .size = size;
088: if (this .lock == null)
089: this .lock = this ;
090: lru.resize(size);
091: t = new LongMap(size);
092: }
093:
094: /**
095: * Am I read only?
096: */
097: public boolean isReadOnly() {
098: return readOnly;
099: }
100:
101: /**
102: * Set the 'read only' flag
103: */
104: public void setReadOnly(boolean v) {
105: this .readOnly = v;
106: }
107:
108: /**
109: * Specify the lock-object to be used to synchronize access to the
110: * cache. If the lock isn't specified, it defaults to the cache
111: * object itself.
112: *
113: * @param lock the lock object
114: */
115: public void setLock(Object lock) {
116: this .lock = lock;
117: }
118:
119: /**
120: * Search the cache for the specified key and return the associated
121: * cache item if one was found. Otherwise, find a free cache item
122: * and initialize it from the underlying store.
123: *
124: * @param key the search key
125: * @return the cache item associated with the specified key.
126: */
127: public Cacheable getCacheable(long key) throws IOException {
128: synchronized (lock) {
129: // ---- check the cache first.
130: Cacheable c = (Cacheable) t.get(key);
131: if (c == null) {
132: // ---- if not in the cache, find a free cache slot and
133: // ---- fill it with this object.
134: c = getCacheable();
135: c.init(store, key);
136: c.setReadOnly(readOnly);
137: t.put(key, c);
138: }
139:
140: // ---- move this object to the front of the LRU list.
141: try {
142: lru.moveFront(c.getDListItem());
143: } catch (ListException e) {
144: //#ifdef DEBUG
145: Debug.print(e);
146: //#endif
147: }
148: c.incrRefCount();
149: if (lru.size() != size)
150: throw new RuntimeException("cache size!");
151: //checkCache();
152: return c;
153: }
154: }
155:
156: /**
157: * Get the specified object from the cache, if available; from the
158: * underlying store if not. In any case, the object (if found)
159: * should be in the cache after this call.<p>
160: *
161: * If the object isn't in the cache <b>OR</b> the underlying store,
162: * this method should return null, and the contents of both the cache
163: * and the underlying store should remain unchanged.<p>
164: *
165: * <b>This implementation doesn't change the underlying store, but
166: * the cache ends up holding a CacheItem with a null data component.</b>
167: * @param key the search key
168: * @param return the data associated with the key
169: */
170: public Object get(int key) throws IOException {
171: Object data = null;
172: synchronized (lock) {
173: Cacheable c = getCacheable(key);
174: try {
175: data = c.getData();
176: } finally {
177: c.decrRefCount();
178: }
179: }
180: return data;
181: }
182:
183: /**
184: * Store the object in the cache. The object will be marked <i>dirty</i>,
185: * and will eventually be written back to the underlying store.
186: */
187: public void put(int key, Object val) throws IOException {
188: synchronized (lock) {
189: Cacheable c = getCacheable(key);
190: try {
191: c.setData(val);
192: } finally {
193: c.decrRefCount();
194: }
195: }
196: }
197:
198: /**
199: * Factory to create an empty cache slot. Only a fixed number of
200: * cache slots should be created, and they should then be recycled,
201: * never destroyed.
202: */
203: abstract public Cacheable makeCacheable();
204:
205: /**
206: * Find a free cache slot. If necessary, seize an existing cache
207: * slot, with preference for the "least recently used" item.
208: * This function should only be called if the lock on the cache is
209: * held by this thread.
210: */
211: private final Cacheable getCacheable() throws IOException {
212: Cacheable c = null;
213: DListItem d = lru.tail();
214:
215: while (d != null) {
216: c = (Cacheable) d.obj;
217: if (c != null && c.getRefCount() > 0) {
218: if (d == lru.head()) {
219: throw new RuntimeException(
220: "no free cache item: cache size = "
221: + lru.size() + "(" + size + ")");
222: }
223: d = d.prev;
224: } else {
225: break; // 'c' contains the Cacheable we're looking for
226: }
227: }
228:
229: if (c == null) {
230: d.obj = c = makeCacheable();
231: c.setDListItem(d);
232: } else {
233: long key = c.getKey();
234: t.remove(key);
235: if (c.isDirty())
236: c.flush();
237: }
238: return c;
239: }
240:
241: /**
242: * Flush all modified items back to the underlying store.
243: */
244: public void flush() throws IOException {
245: synchronized (lock) {
246: boolean started = false;
247: for (DListItem d = lru.head(); !started || d != lru.head(); d = d.next) {
248: started = true;
249: Cacheable c = (Cacheable) d.obj;
250: if (c != null) {
251: c.flush();
252: }
253: }
254: }
255: }
256:
257: public void revert() {
258: synchronized (lock) {
259: lru = new DList();
260: init(store, size);
261: }
262: }
263:
264: public void show(PrintWriter os) {
265: synchronized (lock) {
266: lru.show(os, "\n");
267: }
268: }
269: }
|