001: package org.objectweb.celtix.common.util;
002:
003: import java.lang.ref.SoftReference;
004: import java.util.Iterator;
005: import java.util.LinkedList;
006: import java.util.Queue;
007:
008: /**
009: * This class pools objects, for efficiency accross a lightweight
010: * fixed-size primary cache and a variable-size secondary cache - the
011: * latter uses soft references to allow the polled object be GCed if
012: * necessary.
013: * <p>
014: * To use the cache, a subclass is defined which provides an implementation
015: * of the abstract get() method - this may be conveniently achieved via
016: * an anonymous subclass. The cache is then populated by calling the
017: * populate_cache() method - the reason a two-stage process is used is
018: * to avoid problems with the inner class create() method accessing outer
019: * class data members from the inner class ctor (before its reference to
020: * the outer class is initialized).
021: * <p>
022: *
023: * @author Eoghan Glynn
024: */
025: public abstract class AbstractTwoStageCache<E> {
026: private Object mutex;
027: private int preallocation;
028: private int primaryCacheSize;
029: private int secondaryCacheHighWaterMark;
030:
031: private Queue<E> primaryCache;
032: private Queue<SoftReference<E>> secondaryCache;
033:
034: /**
035: * Constructor.
036: *
037: * @param pCacheSize primary cache size
038: * @param secondary_cache_max secondary cache high water mark
039: * @param preallocation the number of object to preallocation when the
040: * cache is created
041: */
042: public AbstractTwoStageCache(int pCacheSize, int highWaterMark,
043: int prealloc) {
044: this (pCacheSize, highWaterMark, prealloc, null);
045: }
046:
047: /**
048: * Constructor.
049: *
050: * @param pCacheSize primary cache size
051: * @param secondary_cache_max secondary cache high water mark
052: * @param preallocation the number of object to preallocation when the
053: * cache is created
054: * @param mutex object to use as a monitor
055: */
056: public AbstractTwoStageCache(int pCacheSize, int highWaterMark,
057: int prealloc, Object mutexParam) {
058: this .primaryCacheSize = Math.min(pCacheSize, highWaterMark);
059: this .secondaryCacheHighWaterMark = highWaterMark - pCacheSize;
060: this .preallocation = prealloc > highWaterMark ? highWaterMark
061: : prealloc;
062: this .mutex = mutexParam != null ? mutexParam : this ;
063: }
064:
065: public String toString() {
066: return "AbstractTwoStageCache";
067: }
068:
069: /**
070: * Over-ride this method to create objects to populate the pool
071: *
072: * @return newly created object
073: */
074: protected abstract E create() throws Exception;
075:
076: /**
077: * Populate the cache
078: */
079: public void populateCache() throws Exception {
080: // create cache
081: primaryCache = new LinkedList<E>();
082: secondaryCache = new LinkedList<SoftReference<E>>();
083:
084: // preallocate objects into primary cache
085: int primaryCachePreallocation = (preallocation > primaryCacheSize) ? primaryCacheSize
086: : preallocation;
087: for (int i = 0; i < primaryCachePreallocation; i++) {
088: primaryCache.offer(create());
089: }
090:
091: // preallocate objects into secondary cache
092: int secondaryCachePreallocation = preallocation
093: - primaryCachePreallocation;
094: for (int i = 0; i < secondaryCachePreallocation; i++) {
095: secondaryCache.offer(new SoftReference<E>(create()));
096: }
097: }
098:
099: /**
100: * Return a cached or newly created object
101: *
102: * @return an object
103: */
104: public E get() throws Exception {
105: E ret = poll();
106:
107: if (ret == null) {
108: ret = create();
109: }
110:
111: return ret;
112: }
113:
114: /**
115: * Return a cached object if one is available
116: *
117: * @return an object
118: */
119: public E poll() {
120: E ret = null;
121:
122: synchronized (mutex) {
123: if (primaryCache != null) {
124: ret = primaryCache.poll();
125: if (ret == null) {
126: SoftReference<E> sr = secondaryCache.poll();
127: while (ret == null && sr != null) {
128: if (sr != null) {
129: ret = sr.get();
130: }
131: if (ret == null) {
132: sr = secondaryCache.poll();
133: }
134: }
135: }
136: }
137: }
138:
139: return ret;
140: }
141:
142: /**
143: * Recycle an old Object.
144: *
145: * @param oldObject the object to recycle
146: * @return true iff the object can be accomodated in the cache
147: */
148: public boolean recycle(E oldObject) {
149: boolean cached = false;
150:
151: synchronized (mutex) {
152: if (primaryCache != null) {
153: if (primaryCache.size() < primaryCacheSize) {
154: cached = primaryCache.offer(oldObject);
155: }
156:
157: if (!cached
158: && (secondaryCache.size() >= secondaryCacheHighWaterMark)) {
159: // check for nulls in secondary cache and remove them to create room
160: Iterator<SoftReference<E>> it = secondaryCache
161: .iterator();
162: while (it.hasNext()) {
163: SoftReference<E> sr = it.next();
164: if (sr.get() == null) {
165: it.remove();
166: }
167: }
168: }
169:
170: if (!cached
171: && (secondaryCache.size() < secondaryCacheHighWaterMark)) {
172: cached = secondaryCache.offer(new SoftReference<E>(
173: oldObject));
174: }
175: }
176: }
177:
178: return cached;
179: }
180: }
|