KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > groboutils > util > datastruct > v1 > ObjectCache


1 /*
2  * @(#)ObjectCache.java 0.9.0 04/11/2001 - 13:21:11
3  *
4  * Copyright (C) 2001-2003 Matt Albrecht
5  * groboclown@users.sourceforge.net
6  * http://groboutils.sourceforge.net
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */

26
27 package net.sourceforge.groboutils.util.datastruct.v1;
28
29
30 /**
31  * An object cache which allows for objects to be added and removed.
32  * If the cache is empty when an object is requested, the object type
33  * is created and returned. There can be a maximum size specified for
34  * the pending object list - if an object is retrieved by the cache,
35  * and the list is beyond its size, then the object is thrown away.
36  * By default, the maximum size is unlimited.
37  * <P>
38  * If the cache should not create objects, then a ObjectCreator should
39  * not be given to the cache.
40  * <P>
41  * Alternatively, you can specify that the cache not create objects and
42  * instead wait for the objects to be retrieved.
43  * <P>
44  * Care has been taken to keep this synchronized across threads.
45  *
46  * @author Matt Albrecht <a HREF="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
47  * @since April 11, 2001 (0.9.0 Alpha)
48  * @version $Date: 2003/05/23 20:55:43 $
49  */

50 public class ObjectCache
51 {
52     /**
53      * The size to use when you want to specify an unlimited cache size.
54      */

55     public static final int UNLIMITED_SIZE = -1;
56     
57     
58     /**
59      * An interface which needs to be implemented and given to the
60      * cache in order to create new instances.
61      */

62     public static interface ObjectCreator
63     {
64         /**
65          * Called when a new object needs to be created.
66          *
67          * @return the newly created object
68          */

69         public Object JavaDoc createObject();
70     }
71     
72     
73     /**
74      * A default object creator - given a Class object, it attempts
75      * to create a new Object using the default constructor.
76      */

77     public static class DefaultObjectCreator implements ObjectCreator
78     {
79         private Class JavaDoc clazz;
80         
81         /**
82          * Defines the class to create an instance in the creation method;
83          * the class <i>must</i> have a no-argument constructor for this
84          * class to work.
85          */

86         public DefaultObjectCreator( Class JavaDoc clazz )
87         {
88             if (clazz == null)
89             {
90                 throw new IllegalArgumentException JavaDoc( "No null args" );
91             }
92             this.clazz = clazz;
93         }
94         
95         public Object JavaDoc createObject()
96         {
97             try
98             {
99                 return clazz.newInstance();
100             }
101             catch (Exception JavaDoc e)
102             {
103                 return null;
104             }
105         }
106     }
107     
108     
109     
110     /**
111      * We use a synch queue to optimize the store of objects
112      */

113     private SynchQueue cache = new SynchQueue();
114     
115     /**
116      * We also need to keep the object created.
117      */

118     private ObjectCreator creator;
119     
120     /**
121      * Maximum size of the cache.
122      */

123     private int maxSize = UNLIMITED_SIZE;
124
125     
126     /**
127      * Records the number of overflows - for performance tuning.
128      */

129     private int overflowCount = 0;
130
131     
132     /**
133      * Records the number of underflows - for performance tuning.
134      */

135     private int underflowCount = 0;
136     
137     
138     //-----------------------------------------------------
139
// Constructors
140

141     
142     /**
143      * Create a new ObjectCache without an object creator.
144      */

145     public ObjectCache()
146     {
147         // do nothing
148
}
149     
150     
151     /**
152      * Create a new ObjectCache without an object creator, and
153      * sets the maximum number of objects to keep waiting in the cache.
154      */

155     public ObjectCache( int maxSize )
156     {
157         setMaxSize( maxSize );
158     }
159     
160     
161     /**
162      * Create a new ObjectCache. This uses the given creator to
163      * create new objects for the cache.
164      */

165     public ObjectCache( ObjectCreator creator )
166     {
167         setObjectCreator( creator );
168     }
169     
170     
171     /**
172      * Create a new ObjectCache. This uses the given Class to
173      * create new objects for the cache, using its default constructor.
174      */

175     public ObjectCache( Class JavaDoc creator )
176     {
177         setClassCreator( creator );
178     }
179     
180     
181     /**
182      * Create a new ObjectCache. This uses the given creator to
183      * create new objects for the cache, and sets the internal maximum
184      * number of elements to keep waiting.
185      */

186     public ObjectCache( ObjectCreator creator, int maxSize )
187     {
188         setMaxSize( maxSize );
189         setObjectCreator( creator );
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     public ObjectCache( Class JavaDoc creator, int maxSize )
200     {
201         setMaxSize( maxSize );
202         setClassCreator( creator );
203     }
204     
205     
206     /**
207      * Create a new ObjectCache. This uses the given creator to
208      * create new objects for the cache, and sets the internal maximum
209      * number of elements to keep waiting.
210      *
211      * @param fill <tt>true</tt> if the cache should be filled at
212      * construction time, or <tt>false</tt> if it should be empty
213      * initially.
214      */

215     public ObjectCache( ObjectCreator creator, int maxSize, boolean fill )
216     {
217         setMaxSize( maxSize );
218         setObjectCreator( creator );
219         if (fill)
220         {
221             fillCache();
222         }
223     }
224     
225     
226     /**
227      * Create a new ObjectCache. This uses the given Class to
228      * create new objects for the cache, using its default constructor,
229      * and sets the internal maximum
230      * number of elements to keep waiting.
231      *
232      * @param fill <tt>true</tt> if the cache should be filled at
233      * construction time, or <tt>false</tt> if it should be empty
234      * initially.
235      */

236     public ObjectCache( Class JavaDoc creator, int maxSize, boolean fill )
237     {
238         setMaxSize( maxSize );
239         setClassCreator( creator );
240         if (fill)
241         {
242             fillCache();
243         }
244     }
245     
246     
247
248     
249     //-----------------------------------------------------
250
// Public methods
251

252     
253     /**
254      * Adds an element to the end of the queue. If the list is empty,
255      * then the next waiting thread is woken up. If the list has one or
256      * fewer elements, this this method will block any access to the queue,
257      * otherwise this only blocks access to adding to the list.
258      *
259      * @param o the object to place at the end of the list.
260      */

261     public void putBack( Object JavaDoc o )
262     {
263         if (o == null)
264         {
265             // throw an exception - can't insert a null
266
throw new IllegalArgumentException JavaDoc(
267                 "Null objects cannot be added into the cache" );
268         }
269         if (this.maxSize > 0)
270         {
271             if (this.cache.size() >= this.maxSize)
272             {
273 // System.out.println("ObjectCache.putBack: caused overflow");
274
// ignore the object - we're full
275
this.overflowCount++;
276                 return;
277             }
278         }
279         this.cache.enqueue( o );
280     }
281
282     /**
283      * Retrieves a cached element. If the cache is empty, and no
284      * creator is known, then <tt>null</tt> is returned. If the cache is
285      * empty, and a creator is known, then a new object is created and
286      * returned.
287      * <P>
288      * Synchronized so that the time between the isEmpty check and the
289      * pull does not have another thread pulling out an instance. Only
290      * the get needs to be synchronized, so as to not mess with the
291      * checks.
292      */

293     public Object JavaDoc get()
294     {
295         // only synchronize the necesary code.
296
synchronized( this )
297         {
298             if (!this.cache.isEmpty())
299             {
300                 try
301                 {
302 // System.out.println("ObjectCache.get(): not empty, dequeueing");
303
return this.cache.dequeue();
304                 }
305                 catch (InterruptedException JavaDoc ie)
306                 {
307                     // should never happen - but it might!
308
throw new IllegalStateException JavaDoc("encountered an interrupt: "+ie);
309                 }
310             }
311         }
312         // an underflow
313
// doesn't need to be synchronized
314
//System.err.println("ObjectCache.get(): underflow, calling createObject");
315
//(new Throwable()).printStackTrace();
316
this.underflowCount++;
317         return createObject();
318     }
319     
320     
321     /**
322      * Retrieves a cached element. If the cache is empty, then one of several
323      * things can happen, based on the time passed in:
324      * <UL>
325      * <LI><B>&lt; 0:</B> <tt>null</tt> is immediately returned. This is
326      * the identical behavior to calling {@link #get()} with
327      * a <tt>null</tt> creator.
328      * <LI><B>0:</B> the routine will wait indefinitely until
329      * an object is {@link #putBack( Object )} into the cache.
330      * <LI><B>&gt; 0:</B> the routine will wait up to the given number
331      * of milliseconds for another object to be
332      * {@link #putBack( Object )}. If by that time the cache is still
333      * empty, then <tt>null</tt> is returned.
334      * </UL>
335      * <P>
336      * Important parts of the code are synchronized.
337      */

338     public Object JavaDoc get( long millisWaitTime )
339             throws InterruptedException JavaDoc
340     {
341         // only synchronize the necesary code.
342
synchronized( this )
343         {
344             // normal behavior
345
if (!this.cache.isEmpty())
346             {
347                 try
348                 {
349 // System.out.println("ObjectCache.get( "+millisWaitTime+" ): not empty, dequeueing");
350
return this.cache.dequeue();
351                 }
352                 catch (InterruptedException JavaDoc ie)
353                 {
354                     // should never happen - but it might!
355
throw new IllegalStateException JavaDoc("encountered an interrupt: "+ie);
356                 }
357             }
358         }
359         // an underflow
360
// doesn't need to be synchronized
361
//System.out.println("ObjectCache.get( "+millisWaitTime+" ): underflow...");
362
//(new Throwable()).printStackTrace();
363
this.underflowCount++;
364         
365         return this.cache.dequeue( millisWaitTime );
366     }
367     
368     
369     /**
370      * Retrieves the number of "overflows" encountered. An overflow occurs
371      * when the cache is full and a {@link #putBack( Object )} is called.
372      */

373     public int getOverflows()
374     {
375         return this.overflowCount;
376     }
377     
378     
379     /**
380      * Retrieves the number of "underflows" encountered. An underflow occurs
381      * when the cache is empty and a {@link #get()} is called.
382      */

383     public int getUnderflows()
384     {
385         return this.underflowCount;
386     }
387     
388     
389     /**
390      * Resets the internal maximum number of objects that the cache can
391      * hold. Note that it does not immediately clear out the extra objects -
392      * that is naturally cleared by the {@link #putBack( Object )} ignoring
393      * overflows.
394      */

395     public void setMaxSize( int size )
396     {
397         this.maxSize = size;
398     }
399     
400     
401     /**
402      *
403      */

404     public int getMaxSize()
405     {
406         return this.maxSize;
407     }
408     
409     
410     /**
411      * Sets the internal cache-underflow Object creator.
412      */

413     public void setObjectCreator( ObjectCreator creator )
414     {
415         this.creator = creator;
416     }
417     
418     
419     /**
420      * Creates a new DefaultObjectCreator based on the given class.
421      */

422     public void setClassCreator( Class JavaDoc creator )
423     {
424         ObjectCreator oc = null;
425         if (creator != null)
426         {
427             oc = new DefaultObjectCreator( creator );
428         }
429         setObjectCreator( oc );
430     }
431     
432     
433     /**
434      * Create a new object and put it into the cache. This follows the
435      * rules of {@link #putBack( Object )}.
436      */

437     public void addObject()
438     {
439         Object JavaDoc o = createObject();
440         if (o != null)
441         {
442             putBack( o );
443         }
444     }
445     
446     
447     /**
448      * Fills the cache to its maximum. If there is no maximum or there
449      * is no creator, then nothing is done.
450      */

451     public void fillCache()
452     {
453         if (this.creator != null)
454         {
455             // even if creation doesn't work, go ahead and increment the count
456
// - this prevents an infinite loop
457
for (int i = this.cache.size(); i < this.maxSize; i++)
458             {
459                 addObject();
460             }
461         }
462     }
463     
464     //-----------------------------------------------------
465
// Protected methods
466

467     
468     /**
469      * Generates an Object for the cache. May return null.
470      */

471     protected Object JavaDoc createObject()
472     {
473         Object JavaDoc o = null;
474         if (this.creator != null)
475         {
476 // System.out.println("ObjectCache.createObject(): calling creator's createObject");
477
o = this.creator.createObject();
478         }
479         return o;
480     }
481 }
482
483
Popular Tags