001: /*
002: * @(#)ObjectCache.java 0.9.0 04/11/2001 - 13:21:11
003: *
004: * Copyright (C) 2001-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.util.datastruct.v1;
028:
029: /**
030: * An object cache which allows for objects to be added and removed.
031: * If the cache is empty when an object is requested, the object type
032: * is created and returned. There can be a maximum size specified for
033: * the pending object list - if an object is retrieved by the cache,
034: * and the list is beyond its size, then the object is thrown away.
035: * By default, the maximum size is unlimited.
036: * <P>
037: * If the cache should not create objects, then a ObjectCreator should
038: * not be given to the cache.
039: * <P>
040: * Alternatively, you can specify that the cache not create objects and
041: * instead wait for the objects to be retrieved.
042: * <P>
043: * Care has been taken to keep this synchronized across threads.
044: *
045: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
046: * @since April 11, 2001 (0.9.0 Alpha)
047: * @version $Date: 2003/05/23 20:55:43 $
048: */
049: public class ObjectCache {
050: /**
051: * The size to use when you want to specify an unlimited cache size.
052: */
053: public static final int UNLIMITED_SIZE = -1;
054:
055: /**
056: * An interface which needs to be implemented and given to the
057: * cache in order to create new instances.
058: */
059: public static interface ObjectCreator {
060: /**
061: * Called when a new object needs to be created.
062: *
063: * @return the newly created object
064: */
065: public Object createObject();
066: }
067:
068: /**
069: * A default object creator - given a Class object, it attempts
070: * to create a new Object using the default constructor.
071: */
072: public static class DefaultObjectCreator implements ObjectCreator {
073: private Class clazz;
074:
075: /**
076: * Defines the class to create an instance in the creation method;
077: * the class <i>must</i> have a no-argument constructor for this
078: * class to work.
079: */
080: public DefaultObjectCreator(Class clazz) {
081: if (clazz == null) {
082: throw new IllegalArgumentException("No null args");
083: }
084: this .clazz = clazz;
085: }
086:
087: public Object createObject() {
088: try {
089: return clazz.newInstance();
090: } catch (Exception e) {
091: return null;
092: }
093: }
094: }
095:
096: /**
097: * We use a synch queue to optimize the store of objects
098: */
099: private SynchQueue cache = new SynchQueue();
100:
101: /**
102: * We also need to keep the object created.
103: */
104: private ObjectCreator creator;
105:
106: /**
107: * Maximum size of the cache.
108: */
109: private int maxSize = UNLIMITED_SIZE;
110:
111: /**
112: * Records the number of overflows - for performance tuning.
113: */
114: private int overflowCount = 0;
115:
116: /**
117: * Records the number of underflows - for performance tuning.
118: */
119: private int underflowCount = 0;
120:
121: //-----------------------------------------------------
122: // Constructors
123:
124: /**
125: * Create a new ObjectCache without an object creator.
126: */
127: public ObjectCache() {
128: // do nothing
129: }
130:
131: /**
132: * Create a new ObjectCache without an object creator, and
133: * sets the maximum number of objects to keep waiting in the cache.
134: */
135: public ObjectCache(int maxSize) {
136: setMaxSize(maxSize);
137: }
138:
139: /**
140: * Create a new ObjectCache. This uses the given creator to
141: * create new objects for the cache.
142: */
143: public ObjectCache(ObjectCreator creator) {
144: setObjectCreator(creator);
145: }
146:
147: /**
148: * Create a new ObjectCache. This uses the given Class to
149: * create new objects for the cache, using its default constructor.
150: */
151: public ObjectCache(Class creator) {
152: setClassCreator(creator);
153: }
154:
155: /**
156: * Create a new ObjectCache. This uses the given creator to
157: * create new objects for the cache, and sets the internal maximum
158: * number of elements to keep waiting.
159: */
160: public ObjectCache(ObjectCreator creator, int maxSize) {
161: setMaxSize(maxSize);
162: setObjectCreator(creator);
163: }
164:
165: /**
166: * Create a new ObjectCache. This uses the given Class to
167: * create new objects for the cache, using its default constructor,
168: * and sets the internal maximum
169: * number of elements to keep waiting.
170: */
171: public ObjectCache(Class creator, int maxSize) {
172: setMaxSize(maxSize);
173: setClassCreator(creator);
174: }
175:
176: /**
177: * Create a new ObjectCache. This uses the given creator to
178: * create new objects for the cache, and sets the internal maximum
179: * number of elements to keep waiting.
180: *
181: * @param fill <tt>true</tt> if the cache should be filled at
182: * construction time, or <tt>false</tt> if it should be empty
183: * initially.
184: */
185: public ObjectCache(ObjectCreator creator, int maxSize, boolean fill) {
186: setMaxSize(maxSize);
187: setObjectCreator(creator);
188: if (fill) {
189: fillCache();
190: }
191: }
192:
193: /**
194: * Create a new ObjectCache. This uses the given Class to
195: * create new objects for the cache, using its default constructor,
196: * and sets the internal maximum
197: * number of elements to keep waiting.
198: *
199: * @param fill <tt>true</tt> if the cache should be filled at
200: * construction time, or <tt>false</tt> if it should be empty
201: * initially.
202: */
203: public ObjectCache(Class creator, int maxSize, boolean fill) {
204: setMaxSize(maxSize);
205: setClassCreator(creator);
206: if (fill) {
207: fillCache();
208: }
209: }
210:
211: //-----------------------------------------------------
212: // Public methods
213:
214: /**
215: * Adds an element to the end of the queue. If the list is empty,
216: * then the next waiting thread is woken up. If the list has one or
217: * fewer elements, this this method will block any access to the queue,
218: * otherwise this only blocks access to adding to the list.
219: *
220: * @param o the object to place at the end of the list.
221: */
222: public void putBack(Object o) {
223: if (o == null) {
224: // throw an exception - can't insert a null
225: throw new IllegalArgumentException(
226: "Null objects cannot be added into the cache");
227: }
228: if (this .maxSize > 0) {
229: if (this .cache.size() >= this .maxSize) {
230: // System.out.println("ObjectCache.putBack: caused overflow");
231: // ignore the object - we're full
232: this .overflowCount++;
233: return;
234: }
235: }
236: this .cache.enqueue(o);
237: }
238:
239: /**
240: * Retrieves a cached element. If the cache is empty, and no
241: * creator is known, then <tt>null</tt> is returned. If the cache is
242: * empty, and a creator is known, then a new object is created and
243: * returned.
244: * <P>
245: * Synchronized so that the time between the isEmpty check and the
246: * pull does not have another thread pulling out an instance. Only
247: * the get needs to be synchronized, so as to not mess with the
248: * checks.
249: */
250: public Object get() {
251: // only synchronize the necesary code.
252: synchronized (this ) {
253: if (!this .cache.isEmpty()) {
254: try {
255: // System.out.println("ObjectCache.get(): not empty, dequeueing");
256: return this .cache.dequeue();
257: } catch (InterruptedException ie) {
258: // should never happen - but it might!
259: throw new IllegalStateException(
260: "encountered an interrupt: " + ie);
261: }
262: }
263: }
264: // an underflow
265: // doesn't need to be synchronized
266: //System.err.println("ObjectCache.get(): underflow, calling createObject");
267: //(new Throwable()).printStackTrace();
268: this .underflowCount++;
269: return createObject();
270: }
271:
272: /**
273: * Retrieves a cached element. If the cache is empty, then one of several
274: * things can happen, based on the time passed in:
275: * <UL>
276: * <LI><B>< 0:</B> <tt>null</tt> is immediately returned. This is
277: * the identical behavior to calling {@link #get()} with
278: * a <tt>null</tt> creator.
279: * <LI><B>0:</B> the routine will wait indefinitely until
280: * an object is {@link #putBack( Object )} into the cache.
281: * <LI><B>> 0:</B> the routine will wait up to the given number
282: * of milliseconds for another object to be
283: * {@link #putBack( Object )}. If by that time the cache is still
284: * empty, then <tt>null</tt> is returned.
285: * </UL>
286: * <P>
287: * Important parts of the code are synchronized.
288: */
289: public Object get(long millisWaitTime) throws InterruptedException {
290: // only synchronize the necesary code.
291: synchronized (this ) {
292: // normal behavior
293: if (!this .cache.isEmpty()) {
294: try {
295: // System.out.println("ObjectCache.get( "+millisWaitTime+" ): not empty, dequeueing");
296: return this .cache.dequeue();
297: } catch (InterruptedException ie) {
298: // should never happen - but it might!
299: throw new IllegalStateException(
300: "encountered an interrupt: " + ie);
301: }
302: }
303: }
304: // an underflow
305: // doesn't need to be synchronized
306: //System.out.println("ObjectCache.get( "+millisWaitTime+" ): underflow...");
307: //(new Throwable()).printStackTrace();
308: this .underflowCount++;
309:
310: return this .cache.dequeue(millisWaitTime);
311: }
312:
313: /**
314: * Retrieves the number of "overflows" encountered. An overflow occurs
315: * when the cache is full and a {@link #putBack( Object )} is called.
316: */
317: public int getOverflows() {
318: return this .overflowCount;
319: }
320:
321: /**
322: * Retrieves the number of "underflows" encountered. An underflow occurs
323: * when the cache is empty and a {@link #get()} is called.
324: */
325: public int getUnderflows() {
326: return this .underflowCount;
327: }
328:
329: /**
330: * Resets the internal maximum number of objects that the cache can
331: * hold. Note that it does not immediately clear out the extra objects -
332: * that is naturally cleared by the {@link #putBack( Object )} ignoring
333: * overflows.
334: */
335: public void setMaxSize(int size) {
336: this .maxSize = size;
337: }
338:
339: /**
340: *
341: */
342: public int getMaxSize() {
343: return this .maxSize;
344: }
345:
346: /**
347: * Sets the internal cache-underflow Object creator.
348: */
349: public void setObjectCreator(ObjectCreator creator) {
350: this .creator = creator;
351: }
352:
353: /**
354: * Creates a new DefaultObjectCreator based on the given class.
355: */
356: public void setClassCreator(Class creator) {
357: ObjectCreator oc = null;
358: if (creator != null) {
359: oc = new DefaultObjectCreator(creator);
360: }
361: setObjectCreator(oc);
362: }
363:
364: /**
365: * Create a new object and put it into the cache. This follows the
366: * rules of {@link #putBack( Object )}.
367: */
368: public void addObject() {
369: Object o = createObject();
370: if (o != null) {
371: putBack(o);
372: }
373: }
374:
375: /**
376: * Fills the cache to its maximum. If there is no maximum or there
377: * is no creator, then nothing is done.
378: */
379: public void fillCache() {
380: if (this .creator != null) {
381: // even if creation doesn't work, go ahead and increment the count
382: // - this prevents an infinite loop
383: for (int i = this .cache.size(); i < this .maxSize; i++) {
384: addObject();
385: }
386: }
387: }
388:
389: //-----------------------------------------------------
390: // Protected methods
391:
392: /**
393: * Generates an Object for the cache. May return null.
394: */
395: protected Object createObject() {
396: Object o = null;
397: if (this .creator != null) {
398: // System.out.println("ObjectCache.createObject(): calling creator's createObject");
399: o = this.creator.createObject();
400: }
401: return o;
402: }
403: }
|