Source Code Cross Referenced for LRUCache.java in  » Database-ORM » XORM » org » xorm » cache » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » XORM » org.xorm.cache 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package org.xorm.cache;
002:
003:        import org.xorm.InterfaceManagerFactory;
004:        import org.xorm.datastore.Row;
005:        import org.xorm.datastore.Table;
006:        import java.lang.ref.ReferenceQueue;
007:        import java.lang.ref.SoftReference;
008:        import java.util.Collection;
009:        import java.util.Iterator;
010:        import java.util.LinkedHashMap;
011:        import java.util.Map;
012:        import java.util.Properties;
013:        import java.util.logging.Logger;
014:
015:        /**
016:         * An LRU Cache with a hard and soft reference limit.  Objects that exceed the
017:         * hard limit are stored as soft references, and objects that exceed the soft
018:         * limit are discarded from the cache.  The hard limit and soft limit are
019:         * additive, in that hard limit is the number of objects to store with hard
020:         * references, and the soft limit is the number of objects to store with soft
021:         * references, exclusive of the hard limit.  Hence, a hard limit of 10, and soft
022:         * limit of 20 would create a (possible) cache size of 30.  Since items stored
023:         * as soft references are subject to collection by the Garbage collector, the
024:         * soft reference cache will often be smaller than the limit set on it.  It is
025:         * possible to also configure the cache to behave as only a soft or hard cache
026:         * by simply configuring the hard and soft limit appropriately.  See the
027:         * additional documentation below about how to configure this.
028:         * This class uses a referenceQueue to insure that values removed from the 
029:         * This class can also be configured to log its statistics to its logger
030:         * (defined as Logger.getLogger("org.xorm.cache.LRUCache"))
031:         *
032:         * Normally, this class would be used by adding the following properties to the properties files given to xorm at startup:
033:         * <ul><li>org.xorm.cache.DataCacheClass=org.xorm.cache.LRUCache</li></ul>
034:         * The following properties will override the default values in this class:
035:         * <ul><li>org.xorm.cache.LRUCache.hardSize=&lt;non-negative intValue&gt;</li>
036:         * <li>org.xorm.cache.LRUCache.softSize=&lt;intValue&gt;</li>
037:         * <li>org.xorm.cache.LRUCache.logInterval=&lt;intValue&gt;</li></ul>
038:         * See setProperties for a description of the meaning of these properties.
039:         * 
040:         * @author Harry Evans
041:         */
042:        public class LRUCache implements  DataCache {
043:
044:            public static final int DEFAULT_HARD_SIZE = 500;
045:            public static final int DEFAULT_SOFT_SIZE = -1;
046:            public static final long DEFAULT_LOG_INTERVAL = -1;
047:
048:            private Map hardMap;
049:            private Map softMap;
050:            private int hardSize;
051:            private int softSize;
052:            private RowCacheKeyFactory ckFactory;
053:            private ReferenceQueue referenceQueue;
054:            private static final Logger logger = Logger.getLogger("LRUCache");
055:            private long lastLogTime;
056:            private long logInterval;
057:            private int hits;
058:            private int misses;
059:
060:            private final Object semaphore;
061:
062:            /**
063:             * Construct a LRUCache.  This method is provided primarily for use when
064:             * programatically assigning the cache that XORM will use.  The default
065:             * constructor is usually the one that gets used.
066:             * @param hardSize The number of objects to keep hard references to.  If
067:             * this number is greater than 0, keep references to that number of objects
068:             * with hard references.  Objects that are discarded from the hard cache
069:             * will be put into soft cache, if soft cache does not have a size of 0.
070:             * If this value is 0, do not create a hard cache.  An exception is thrown
071:             * if this value is less than 0.
072:             * @param softSize The number of objects to keep soft references to.  If
073:             * this number is greater than 0, keep references to that number of objects
074:             * with soft references.  Objects that are discarded from the soft cache,
075:             * either through lack of space, or through garbage collection, are
076:             * discarded completely from the cache.  If this value is 0, do not create a
077:             * soft cache.  If this value is less than 0, the number of references
078:             * stored will be limited to the amount of memory in the system, and how
079:             * aggressive the garbage collector is.
080:             * @param aLogInterval the amount of time in milliseconds to log usage
081:             * statistics to the logger ("org.xorm.cache.LRUCache").  If this value is
082:             * 0, always log.  If this value is a negative number, never log.  If this
083:             * value is positive, log whenever that number of milliseconds has elapsed
084:             * and the cache is accessed.  Logging is done on a best effort basis, and
085:             * does not use a background thread.  Therefore, if the cache is not
086:             * accessed for time greater than the log interval, no values will be
087:             * logged.
088:             * @throws RuntimeException if the hardSize is less than 0, or the
089:             * hardSize and softSize are both 0
090:             */
091:            public LRUCache(int hardSize, int softSize, long aLogInterval) {
092:                semaphore = new Object();
093:                init(hardSize, softSize, aLogInterval);
094:            }
095:
096:            /**
097:             * Construct an LRUCache using DEFAULT_HARD_SIZE, DEFAULT_SOFT_SIZE, and
098:             * DEFAULT_LOG_INTERVAL.  This constructor is provided to enable reflective
099:             * instantiation of the cache.  It is normally used in combination with a
100:             * call to setProperties, which will result in the cache extracting
101:             * (possibly) different values than the default with which to run.
102:             */
103:            public LRUCache() {
104:                semaphore = new Object();
105:                init(DEFAULT_HARD_SIZE, DEFAULT_SOFT_SIZE, DEFAULT_LOG_INTERVAL);
106:            }
107:
108:            /**
109:             * method used to acutally set the values for the size of the caches, and
110:             * the log interval.  Called by the constructors, and by setProperties. 
111:             * Note, that whenever this method is called with good parameters, the
112:             * entire cache is dumped.
113:             */
114:            private void init(int hardSize, int softSize, long aLogInterval) {
115:                if (hardSize <= 0 && softSize == 0) {
116:                    String message = "HardSize and SoftSize cannot both 0 or less in size: hardSize: "
117:                            + hardSize + " softSize: " + softSize;
118:                    logger.severe(message);
119:                    throw new RuntimeException(message);
120:                }
121:                synchronized (semaphore) {
122:                    ckFactory = new RowCacheKeyFactory();
123:                    if (softSize != 0) {
124:                        softMap = new LRUMap(softSize, this );
125:                    }
126:                    if (hardSize > 0) {
127:                        if (softMap != null) {
128:                            hardMap = new LRUMap(hardSize, this );
129:                        } else {
130:                            hardMap = new LRUMap(hardSize, this );
131:                        }
132:                    }
133:                    referenceQueue = new ReferenceQueue();
134:                    logInterval = aLogInterval;
135:                    lastLogTime = System.currentTimeMillis();
136:                    this .hardSize = hardSize;
137:                    this .softSize = softSize;
138:                    hits = 0;
139:                    misses = 0;
140:                }
141:            }
142:
143:            /**
144:             * This method is called by XORM after reflectively instantiating the class.
145:             * This method looks for the following properties:
146:             * <ul><li>org.xorm.cache.LRUCache.hardSize</li>
147:             * <li>org.xorm.cache.LRUCache.softSize</li>
148:             * <li>org.xorm.cache.LRUCache.logInterval</li></ul>
149:             * Any property that isn't found will have the class default value assigned
150:             * to it.
151:             * @param props The Properties object (possibly) containing values to use
152:             * for hardSize, softSize, and logInterval.
153:             */
154:            public void setProperties(Properties props) {
155:                String hardSize = props.getProperty(
156:                        "org.xorm.cache.LRUCache.hardSize", String
157:                                .valueOf(DEFAULT_HARD_SIZE));
158:                String softSize = props.getProperty(
159:                        "org.xorm.cache.LRUCache.softSize", String
160:                                .valueOf(DEFAULT_SOFT_SIZE));
161:                String logInterval = props.getProperty(
162:                        "org.xorm.cache.LRUCache.logInterval", String
163:                                .valueOf(LRUCache.DEFAULT_LOG_INTERVAL));
164:                //ensureCache(cacheKey, hardSize, softSize);
165:                int hardValue = DEFAULT_HARD_SIZE;
166:                int softValue = DEFAULT_SOFT_SIZE;
167:                long logValue = DEFAULT_LOG_INTERVAL;
168:                StringBuffer message = new StringBuffer();
169:                try {
170:                    hardValue = Integer.parseInt(hardSize);
171:                } catch (NumberFormatException e) {
172:                    message
173:                            .append(
174:                                    "Property name: org.xorm.cache.LRUCache.hardSize\nProperty value: ")
175:                            .append(hardSize).append("\n");
176:                }
177:                try {
178:                    softValue = Integer.parseInt(softSize);
179:                } catch (NumberFormatException e) {
180:                    message
181:                            .append(
182:                                    "Property name: org.xorm.cache.LRUCache.softSize\nProperty value: ")
183:                            .append(softSize).append("\n");
184:                }
185:                try {
186:                    logValue = Long.parseLong(logInterval);
187:                } catch (NumberFormatException e) {
188:                    message
189:                            .append(
190:                                    "Property name: org.xorm.cache.LRUCache.logInterval\nProperty value: ")
191:                            .append(logInterval).append("\n");
192:                }
193:                if (message.length() > 0) {
194:                    throw new RuntimeException(
195:                            "The following properties require numeric values, but were not parsable:\n"
196:                                    + message.toString()
197:                                    + "Cache is usable, but is using only default values.");
198:                } else {
199:                    init(hardValue, softValue, logValue);
200:                }
201:            }
202:
203:            public void setFactory(InterfaceManagerFactory factory) {
204:                // not needed
205:            }
206:
207:            public void add(Row row) {
208:                addInternal(row);
209:                serviceStaleEntries();
210:            }
211:
212:            /**
213:             * Internal method to handle just adding a row to the cache, without any
214:             * stale entry servicing done by the public methods.
215:             * @param row The Row object to add.
216:             */
217:            private void addInternal(Row row) {
218:                Object key = ckFactory.getCacheKey(row);
219:                synchronized (semaphore) {
220:                    if (hardMap != null) {
221:                        hardMap.put(key, row);
222:                    } else {
223:                        softMap.put(key, wrapValue(key, row));
224:                    }
225:                }
226:                // We need to flag that this instance of the Row is cached.
227:                // That way if the row needs to be modified, the caller will
228:                // know that it should be cloned first.
229:                row.setCached(true);
230:            }
231:
232:            /**
233:             * Optional method to add a collection of objects to the cache all at once.
234:             * Not currently used by xorm or defined by the xorm interface.
235:             * @param c A Collection of rows to be added to the cache.  The Rows will be
236:             * added in Iterator order.
237:             */
238:            public void addAll(Collection c) {
239:                for (Iterator it = c.iterator(); it.hasNext();) {
240:                    //don't make it service stale entries every add, just after all the adds
241:                    addInternal((Row) it.next());
242:                }
243:                serviceStaleEntries();
244:            }
245:
246:            /* methods for possible future use
247:             public boolean containsKey(Object key) {
248:             serviceStaleEntries();
249:             if(hardMap != null && hardMap.containsKey(key)) {
250:             return true;
251:             }
252:             return (softMap != null && softMap.containsKey(key));
253:             }
254:             public boolean containsValue(Object o) {
255:             serviceStaleEntries();
256:             //TODO maybe figure out how to fix this?
257:             throw new UnsupportedOperationException();
258:             }
259:             public boolean invalidate(Object key) {
260:             serviceStaleEntries();
261:             if(hardMap != null && hardMap.containsKey(key)) {
262:             hardMap.remove(key);
263:             return true;
264:             } else if(softMap != null && softMap.containsKey(key)) {
265:             softMap.remove(key);
266:             return true;
267:             }
268:             return false;
269:             }
270:
271:             */
272:            /**
273:             * Retrieves a cloned copy of the Row from the cache with the
274:             * matching primary key.
275:             */
276:            public Row get(Table table, Object aPrimaryKey) {
277:                serviceStaleEntries();
278:                Object realKey = ckFactory
279:                        .makeRowPrimaryKey(table, aPrimaryKey);
280:                if (realKey != null) {
281:                    Object value = get(realKey);
282:                    if (value != null) {
283:                        // We're no longer going to clone cached rows.
284:                        // Instead we should have setCached(true) already.
285:                        // The caller will use that to determine if a
286:                        // clone is necessary.
287:                        //return (Row)((Row)value).clone();
288:                        return (Row) value;
289:                    }
290:                }
291:                return null;
292:            }
293:
294:            /**
295:             * Retrieves an object from the cache.  Used internally to retrieve real
296:             * values instead of clones.
297:             */
298:            private Object get(Object key) {
299:                Object value = null;
300:                synchronized (semaphore) {
301:                    if (hardMap != null) {
302:                        value = hardMap.get(key);
303:                    }
304:                    if (value == null && softMap != null) {
305:                        Object wrappedValue = softMap.get(key);
306:                        if (wrappedValue != null) {
307:                            value = unwrapValue(wrappedValue);
308:                            if (hardMap != null) {
309:                                softMap.remove(key);
310:                                hardMap.put(key, value);
311:                            }
312:                        }
313:                    }
314:                }
315:                if (value == null) {
316:                    misses++;
317:                } else {
318:                    hits++;
319:                }
320:                log(false);
321:                return value;
322:            }
323:
324:            /**
325:             * Optional method for use by external classes to force logging of the cache
326:             * usage statistics regardless of the value of logInterval.
327:             */
328:            public void log() {
329:                log(true);
330:            }
331:
332:            /**
333:             * Internal method to handle when usgae statictics should actually be
334:             * logged.
335:             * @param force If true, log regardless of other considerations.
336:             */
337:            private void log(boolean force) {
338:                if (force
339:                        || logInterval == 0
340:                        || System.currentTimeMillis() < (lastLogTime + logInterval)) {
341:                    String msg = new StringBuffer("[LRUCache] hardSize: ")
342:                            .append(hardSize).append(" softSize: ").append(
343:                                    softSize).append(" hits: ").append(hits)
344:                            .append(" misses: ").append(" misses: ").append(
345:                                    " logInterval: ").append(logInterval)
346:                            .toString();
347:                    logger.fine(msg);
348:                    lastLogTime = System.currentTimeMillis();
349:                }
350:            }
351:
352:            /**
353:             * Optional method to remove a value from the cache.  Since all objects removed are Rows, this method should not be called directly in the near future.
354:             */
355:            public Object removeValue(Object value) {
356:                Object key = ckFactory.getCacheKey(value);
357:                return removeKey(key);
358:            }
359:
360:            /**
361:             * Method that removes a record from the cache by key.
362:             */
363:            private Object removeKey(Object key) {
364:                serviceStaleEntries();
365:                synchronized (semaphore) {
366:                    if (hardMap != null && hardMap.containsKey(key)) {
367:                        return hardMap.remove(key);
368:                    } else if (softMap != null && softMap.containsKey(key)) {
369:                        return unwrapValue(softMap.remove(key));
370:                    }
371:                }
372:                return null;
373:            }
374:
375:            public void remove(Row row) {
376:                removeValue(row);
377:                // I don't know if this will make a difference, but once
378:                // a row leaves the cache we should flip the cached flag
379:                // back to false.
380:                row.setCached(false);
381:            }
382:
383:            /**
384:             * Method called by LRUMap when a value is removed from a cache, to
385:             * (possibly) place it in a new cache.  This only occurrs (currently) when
386:             * the hard map and soft map are both defined, and the hard map must bump a
387:             * value into the soft map.  Dropping the value from the old map is handled
388:             * by the calling map implementation.
389:             * @param aMap the map the value is currently contained in.
390:             * @param eldestEntry the value that is being bumped out, due to space
391:             * limitations.
392:             */
393:            private void dumpMapValue(LRUMap aMap, Map.Entry eldestEntry) {
394:                if (aMap == hardMap && softMap != null) {
395:                    synchronized (semaphore) {
396:                        Object key = eldestEntry.getKey();
397:                        Object value = eldestEntry.getValue();
398:                        Object ref = wrapValue(key, value);
399:                        softMap.put(key, ref);
400:                    }
401:                }
402:            }
403:
404:            /**
405:             * Method to wrap values in soft references for use in the soft map.
406:             */
407:            private Object wrapValue(Object key, Object value) {
408:                return new KeyedSoftReference(value, referenceQueue, key);
409:            }
410:
411:            /**
412:             * Method used to extract the Row value from a keyed soft reference, when it
413:             * is being extracted from the softMap.
414:             */
415:            private Object unwrapValue(Object value) {
416:                if (value == null) {
417:                    return value;
418:                }
419:                if (value instanceof  KeyedSoftReference) {
420:                    KeyedSoftReference ksr = (KeyedSoftReference) value;
421:                    return ksr.get();
422:                } else {
423:                    return value;
424:                }
425:            }
426:
427:            /**
428:             * Method to clean up soft referneces that have been flagged for collection
429:             * and removal.  This avoids the memory leaks that are often found in soft
430:             * and weak reference implementations, such as ThreadLocal variables.
431:             */
432:            private void serviceStaleEntries() {
433:                Object stale = null;
434:                synchronized (semaphore) {
435:                    while ((stale = referenceQueue.poll()) != null) {
436:                        KeyedSoftReference ksr = (KeyedSoftReference) stale;
437:                        Object key = ksr.getKey();
438:                        softMap.remove(key);
439:                    }
440:                }
441:            }
442:
443:            /**
444:             * A class to take advantage of the LRU properties of LinkedHashMap.  This
445:             * implemeation will service the removal of old entries, once the maxSize
446:             * value equals the number of contained values, by calling the dumpMapValue
447:             * method of the LRUCache parent provided at construction.
448:             * @author Harry Evans
449:             */
450:            static class LRUMap extends java.util.LinkedHashMap {
451:                private int maxSize;
452:                private LRUCache parent;
453:
454:                /**
455:                 * Construct a new LRUMap.
456:                 * @param aMaxSize the maximum number of values to hold onto before
457:                 * calling parent.dumpMapValue().  If aMaxSize is greater than 0, than a
458:                 * cache size over this value will result in the eldest entry being
459:                 * removed, and dumpMapValue() being called.  If 0 or less, dumpMapValue
460:                 * will never be called.
461:                 * @param aParent the LRUCache parent of this map to which it dumps
462:                 * values if full.
463:                 */
464:                public LRUMap(int aMaxSize, LRUCache aParent) {
465:                    super (16, 0.75f, true);
466:                    maxSize = aMaxSize;
467:                    parent = aParent;
468:                }
469:
470:                /**
471:                 * Override the default( noop) behaviour of this method.  Checks if
472:                 * maxSize is greater than 0 and size() is greater than maxSize, and if
473:                 * so, calls parent.dumpMapValue, and returns true, so that the value is
474:                 * removed.
475:                 */
476:                protected boolean removeEldestEntry(
477:                        java.util.Map.Entry eldestEntry) {
478:                    boolean remove = ((maxSize > 0) ? (size() > maxSize)
479:                            : false);
480:                    if (remove) {
481:                        parent.dumpMapValue(this, eldestEntry);
482:                    }
483:                    return remove;
484:                }
485:            }
486:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.