001: /*
002: Copyright (C) 2003 Know Gate S.L. All rights reserved.
003: C/Oña, 107 1º2 28050 Madrid (Spain)
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions
007: are met:
008:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011:
012: 2. The end-user documentation included with the redistribution,
013: if any, must include the following acknowledgment:
014: "This product includes software parts from hipergate
015: (http://www.hipergate.org/)."
016: Alternately, this acknowledgment may appear in the software itself,
017: if and wherever such third-party acknowledgments normally appear.
018:
019: 3. The name hipergate must not be used to endorse or promote products
020: derived from this software without prior written permission.
021: Products derived from this software may not be called hipergate,
022: nor may hipergate appear in their name, without prior written
023: permission.
024:
025: This library is distributed in the hope that it will be useful,
026: but WITHOUT ANY WARRANTY; without even the implied warranty of
027: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
028:
029: You should have received a copy of hipergate License with this code;
030: if not, visit http://www.hipergate.org or mail to info@hipergate.org
031: */
032:
033: package com.knowgate.dataobjs;
034:
035: import java.lang.System;
036: import java.util.TreeMap;
037: import java.util.Iterator;
038: import java.lang.Thread;
039:
040: final class DBSubsetCacheReaper extends Thread {
041:
042: private DBSubsetCache oDBCache;
043:
044: DBSubsetCacheReaper(DBSubsetCache objDBCache) {
045: oDBCache = objDBCache;
046: }
047:
048: private void reapEntries() {
049: // Position of last recently used entry to discard
050: int iLRU = oDBCache.iTopIndex - oDBCache.iUsed;
051: // Count of entries to discard (20% of cache capacity)
052: int iDiscardCount = (oDBCache.capacity() * 2) / 10;
053: int iComma; // Delimiter position "Table,Key"
054: String sEntry; // Full entry descriptor "Table,Key"
055: String sTable; // Entry related table
056: String sKey; // Entry key
057:
058: // Discard bottom 20% entries
059: for (int i = iLRU; i < iLRU + iDiscardCount; i++) {
060: sEntry = oDBCache.getKey(iLRU);
061: if (null != sEntry) {
062: iComma = sEntry.indexOf(',');
063: sTable = sEntry.substring(0, iComma);
064: sKey = sEntry.substring(iComma + 1);
065: oDBCache.expire(sKey);
066: } // fi(LRUList[])
067: } // next (i)
068:
069: oDBCache.iUsed -= iDiscardCount; // Decrement used slots count
070: } // reapEntries()
071:
072: public void run() {
073: reapEntries();
074: }
075: } // DBSubsetCache
076:
077: // ============================================================
078:
079: /**
080: *
081: * <p>Local Cache for DBSubset Objects</p>
082: * @version 3.0
083: */
084:
085: public final class DBSubsetCache {
086:
087: /**
088: * <p>Default constructor</p>
089: * Cache capacity is set to 100
090: */
091: public DBSubsetCache() {
092: iUsed = 0;
093: iTopIndex = 0;
094: iCacheCapacity = 100;
095: LRUList = new String[iCacheCapacity];
096: for (int s = 0; s < iCacheCapacity; s++)
097: LRUList[s] = null;
098: oCache = new TreeMap();
099: }
100:
101: /**
102: *
103: * @param iCapacity Maximum number of entries that cache can hold
104: */
105:
106: public DBSubsetCache(int iCapacity) {
107: iUsed = 0;
108: iTopIndex = 0;
109: iCacheCapacity = iCapacity;
110: LRUList = new String[iCacheCapacity];
111: for (int s = 0; s < iCacheCapacity; s++)
112: LRUList[s] = null;
113: oCache = new TreeMap();
114: }
115:
116: // ----------------------------------------------------------
117:
118: /**
119: * Get Maximum number of entries that cache can hold
120: */
121:
122: public int capacity() {
123: return iCacheCapacity;
124: }
125:
126: // ----------------------------------------------------------
127:
128: /**
129: * Add new entry to cache
130: * @param sTableName Associated table (optional)
131: * @param sKey Unique key for cache entry
132: * @param oDBSS Stored DBSubset
133: */
134: public synchronized void put(String sTableName, String sKey,
135: DBSubset oDBSS) {
136: int iIndex = iTopIndex % iCacheCapacity;
137: iTopIndex++;
138: iUsed++;
139: DBCacheEntry oEntry = new DBCacheEntry(oDBSS, sTableName,
140: iIndex);
141: DBSubsetCacheReaper oReaper;
142:
143: if (null == sTableName)
144: sTableName = "none";
145:
146: oCache.put(sKey, oEntry);
147: LRUList[iIndex] = sTableName + "," + sKey;
148:
149: if (iUsed >= iCacheCapacity - 1) {
150: oReaper = new DBSubsetCacheReaper(this );
151: oReaper.run();
152: } // fi (iUsed>=iCacheCapacity-1)
153: } // put()
154:
155: // ----------------------------------------------------------
156:
157: /**
158: * Remove a cache entry
159: * @param sKey Unique key for cache entry
160: * @return <b>true</b> if cache already contained an entry with given key, <b>false</b> if no entry was removed from cache.
161: */
162:
163: public synchronized boolean expire(String sKey) {
164: Object objEntry = oCache.get(sKey);
165:
166: if (null != objEntry) {
167: setKey(null, ((DBCacheEntry) objEntry).iIndex);
168: oCache.remove(sKey);
169: }
170: return null != objEntry ? true : false;
171: } // remove()
172:
173: // ----------------------------------------------------------
174:
175: /**
176: * Replace a cache entry
177: * @param sTableName Associated table (optional)
178: * @param sKey Unique key for cache entry
179: * @param oDBSS New DBSubset to be stored
180: */
181:
182: public void replace(String sTableName, String sKey, DBSubset oDBSS) {
183:
184: expire(sKey);
185: put(sTableName, sKey, oDBSS);
186: } // replace()
187:
188: // ----------------------------------------------------------
189:
190: /**
191: * Clear cache
192: */
193:
194: public synchronized void clear() {
195: oCache.clear();
196: for (int s = 0; s < iCacheCapacity; s++)
197: LRUList[s] = null;
198: iTopIndex = 0;
199: iUsed = 0;
200: } // clear()
201:
202: // ----------------------------------------------------------
203:
204: /**
205: * Remove all entries from cache that are registers from a given table
206: * @param sTable Table Name
207: */
208:
209: public synchronized void clear(String sTable) {
210: Iterator oIter = oCache.keySet().iterator();
211: String sKey;
212: DBCacheEntry oEntry;
213:
214: if (sTable == null)
215: sTable = "none";
216:
217: while (oIter.hasNext()) {
218: sKey = (String) oIter.next();
219: oEntry = (DBCacheEntry) oCache.get(sKey);
220: if (sTable.equals(oEntry.sTable))
221: expire(sKey);
222: } // wend
223: } // clear()
224:
225: // ----------------------------------------------------------
226:
227: /**
228: * Get DBSubset from cache
229: * @param sKey Unique key for cache entry
230: * @return DBSubset reference or <b>null</if no DBSubset with such key was found
231: */
232:
233: public DBSubset get(String sKey) {
234: Object oObj = oCache.get(sKey);
235: DBCacheEntry oEntry;
236:
237: if (oObj != null) {
238: oEntry = (DBCacheEntry) oObj;
239: oEntry.iTimesUsed++;
240: oEntry.lastUsed = System.currentTimeMillis();
241: return oEntry.oDBSubset;
242: } else
243: return null;
244: } // get()
245:
246: // ----------------------------------------------------------
247:
248: /**
249: * Get DBCacheEntry from cache
250: * @param sKey Unique key for cache entry
251: * @return DBCacheEntry reference or <b>null</if no DBCacheEntry with such key was found
252: */
253:
254: public DBCacheEntry getEntry(String sKey) {
255: Object oObj = oCache.get(sKey);
256: DBCacheEntry oEntry;
257:
258: if (oObj != null) {
259: oEntry = (DBCacheEntry) oObj;
260: oEntry.iTimesUsed++;
261: oEntry.lastUsed = System.currentTimeMillis();
262: return oEntry;
263: } else
264: return null;
265: } // getEntry()
266:
267: // ----------------------------------------------------------
268:
269: public String getKey(int iEntryIndex) {
270: return LRUList[iEntryIndex % iCacheCapacity];
271: }
272:
273: // ----------------------------------------------------------
274:
275: public void setKey(String sKey, int iEntryIndex) {
276: LRUList[iEntryIndex % iCacheCapacity] = sKey;
277: }
278:
279: // ----------------------------------------------------------
280:
281: public class DBCacheEntry {
282: public long lastModified;
283: public long lastUsed;
284: public int iTimesUsed;
285: public int iIndex;
286: public String sTable;
287: public DBSubset oDBSubset;
288:
289: DBCacheEntry(DBSubset oDBSS, String sTbl, int iIdx) {
290: sTable = sTbl;
291: iIndex = iIdx;
292: iTimesUsed = 0;
293: lastUsed = lastModified = System.currentTimeMillis();
294: oDBSubset = oDBSS;
295: }
296: } // DBCacheEntry
297:
298: private int iCacheCapacity; // Número máximo de entradas en el cache
299: private String LRUList[]; // Slots usados por el algoritmo de limpieza Least Recently Used
300: private TreeMap oCache; // B-Tree con las entradas del cache
301:
302: public int iTopIndex; // Máximo índice en el cache (siempre de accede módulo la capacidad)
303: public int iUsed; // Contador de entradas actualmente en uso
304: } // DBSubsetCache
|