File: ObjectCache.java

package info (click to toggle)
libgroboutils-java 5-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 8,496 kB
  • sloc: java: 59,880; xml: 12,762; sh: 377; perl: 104; makefile: 20
file content (482 lines) | stat: -rw-r--r-- 14,101 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/*
 * @(#)ObjectCache.java      0.9.0 04/11/2001 - 13:21:11
 *
 * Copyright (C) 2001-2003 Matt Albrecht
 * groboclown@users.sourceforge.net
 * http://groboutils.sourceforge.net
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 */

package net.sourceforge.groboutils.util.datastruct.v1;


/**
 * An object cache which allows for objects to be added and removed.
 * If the cache is empty when an object is requested, the object type
 * is created and returned.  There can be a maximum size specified for
 * the pending object list - if an object is retrieved by the cache,
 * and the list is beyond its size, then the object is thrown away.
 * By default, the maximum size is unlimited.
 * <P>
 * If the cache should not create objects, then a ObjectCreator should
 * not be given to the cache.
 * <P>
 * Alternatively, you can specify that the cache not create objects and
 * instead wait for the objects to be retrieved.
 * <P>
 * Care has been taken to keep this synchronized across threads.
 *
 * @author     Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @since      April 11, 2001 (0.9.0 Alpha)
 * @version    $Date: 2003/05/23 20:55:43 $
 */
public class ObjectCache
{
    /**
     * The size to use when you want to specify an unlimited cache size.
     */
    public static final int UNLIMITED_SIZE = -1;
    
    
    /**
     * An interface which needs to be implemented and given to the
     * cache in order to create new instances.
     */
    public static interface ObjectCreator
    {
        /**
         * Called when a new object needs to be created.
         *
         * @return the newly created object
         */
        public Object createObject();
    }
    
    
    /**
     * A default object creator - given a Class object, it attempts
     * to create a new Object using the default constructor.
     */
    public static class DefaultObjectCreator implements ObjectCreator
    {
        private Class clazz;
        
        /**
         * Defines the class to create an instance in the creation method;
         * the class <i>must</i> have a no-argument constructor for this
         * class to work.
         */
        public DefaultObjectCreator( Class clazz )
        {
            if (clazz == null)
            {
                throw new IllegalArgumentException( "No null args" );
            }
            this.clazz = clazz;
        }
        
        public Object createObject()
        {
            try
            {
                return clazz.newInstance();
            }
            catch (Exception e)
            {
                return null;
            }
        }
    }
    
    
    
    /**
     * We use a synch queue to optimize the store of objects
     */
    private SynchQueue cache = new SynchQueue();
    
    /**
     * We also need to keep the object created.
     */
    private ObjectCreator creator;
    
    /**
     * Maximum size of the cache.
     */
    private int maxSize = UNLIMITED_SIZE;

    
    /**
     * Records the number of overflows - for performance tuning.
     */
    private int overflowCount = 0;

    
    /**
     * Records the number of underflows - for performance tuning.
     */
    private int underflowCount = 0;
    
    
    //-----------------------------------------------------
    // Constructors
    
    
    /**
     * Create a new ObjectCache without an object creator.
     */
    public ObjectCache()
    {
        // do nothing
    }
    
    
    /**
     * Create a new ObjectCache without an object creator, and
     * sets the maximum number of objects to keep waiting in the cache.
     */
    public ObjectCache( int maxSize )
    {
        setMaxSize( maxSize );
    }
    
    
    /**
     * Create a new ObjectCache. This uses the given creator to
     * create new objects for the cache.
     */
    public ObjectCache( ObjectCreator creator )
    {
        setObjectCreator( creator );
    }
    
    
    /**
     * Create a new ObjectCache. This uses the given Class to
     * create new objects for the cache, using its default constructor.
     */
    public ObjectCache( Class creator )
    {
        setClassCreator( creator );
    }
    
    
    /**
     * Create a new ObjectCache. This uses the given creator to
     * create new objects for the cache, and sets the internal maximum
     * number of elements to keep waiting.
     */
    public ObjectCache( ObjectCreator creator, int maxSize )
    {
        setMaxSize( maxSize );
        setObjectCreator( creator );
    }
    
    
    /**
     * Create a new ObjectCache. This uses the given Class to
     * create new objects for the cache, using its default constructor,
     * and sets the internal maximum
     * number of elements to keep waiting.
     */
    public ObjectCache( Class creator, int maxSize )
    {
        setMaxSize( maxSize );
        setClassCreator( creator );
    }
    
    
    /**
     * Create a new ObjectCache. This uses the given creator to
     * create new objects for the cache, and sets the internal maximum
     * number of elements to keep waiting.
     *
     * @param fill <tt>true</tt> if the cache should be filled at
     *      construction time, or <tt>false</tt> if it should be empty
     *      initially.
     */
    public ObjectCache( ObjectCreator creator, int maxSize, boolean fill )
    {
        setMaxSize( maxSize );
        setObjectCreator( creator );
        if (fill)
        {
            fillCache();
        }
    }
    
    
    /**
     * Create a new ObjectCache. This uses the given Class to
     * create new objects for the cache, using its default constructor,
     * and sets the internal maximum
     * number of elements to keep waiting.
     *
     * @param fill <tt>true</tt> if the cache should be filled at
     *      construction time, or <tt>false</tt> if it should be empty
     *      initially.
     */
    public ObjectCache( Class creator, int maxSize, boolean fill )
    {
        setMaxSize( maxSize );
        setClassCreator( creator );
        if (fill)
        {
            fillCache();
        }
    }
    
    

    
    //-----------------------------------------------------
    // Public methods
    
    
    /**
     * Adds an element to the end of the queue.  If the list is empty,
     * then the next waiting thread is woken up.  If the list has one or
     * fewer elements, this this method will block any access to the queue,
     * otherwise this only blocks access to adding to the list.
     *
     * @param o the object to place at the end of the list.
     */
    public void putBack( Object o )
    {
        if (o == null)
        {
            // throw an exception - can't insert a null
            throw new IllegalArgumentException(
                "Null objects cannot be added into the cache" );
        }
        if (this.maxSize > 0)
        {
            if (this.cache.size() >= this.maxSize)
            {
// System.out.println("ObjectCache.putBack: caused overflow");
                // ignore the object - we're full
                this.overflowCount++;
                return;
            }
        }
        this.cache.enqueue( o );
    }

    /**
     * Retrieves a cached element. If the cache is empty, and no
     * creator is known, then <tt>null</tt> is returned. If the cache is
     * empty, and a creator is known, then a new object is created and
     * returned.
     * <P>
     * Synchronized so that the time between the isEmpty check and the
     * pull does not have another thread pulling out an instance. Only
     * the get needs to be synchronized, so as to not mess with the
     * checks.
     */
    public Object get()
    {
        // only synchronize the necesary code.
        synchronized( this )
        {
            if (!this.cache.isEmpty())
            {
                try
                {
// System.out.println("ObjectCache.get(): not empty, dequeueing");
                    return this.cache.dequeue();
                }
                catch (InterruptedException ie)
                {
                    // should never happen - but it might!
                    throw new IllegalStateException("encountered an interrupt: "+ie);
                }
            }
        }
        // an underflow
        // doesn't need to be synchronized
//System.err.println("ObjectCache.get(): underflow, calling createObject");
//(new Throwable()).printStackTrace();
        this.underflowCount++;
        return createObject();
    }
    
    
    /**
     * Retrieves a cached element. If the cache is empty, then one of several
     * things can happen, based on the time passed in:
     *  <UL>
     *      <LI><B>&lt; 0:</B> <tt>null</tt> is immediately returned. This is
     *          the identical behavior to calling {@link #get()} with
     *          a <tt>null</tt> creator.
     *      <LI><B>0:</B> the routine will wait indefinitely until
     *          an object is {@link #putBack( Object )} into the cache.
     *      <LI><B>&gt; 0:</B> the routine will wait up to the given number
     *          of milliseconds for another object to be
     *          {@link #putBack( Object )}. If by that time the cache is still
     *          empty, then <tt>null</tt> is returned.
     *  </UL>
     * <P>
     * Important parts of the code are synchronized.
     */
    public Object get( long millisWaitTime )
            throws InterruptedException
    {
        // only synchronize the necesary code.
        synchronized( this )
        {
            // normal behavior
            if (!this.cache.isEmpty())
            {
                try
                {
// System.out.println("ObjectCache.get( "+millisWaitTime+" ): not empty, dequeueing");
                    return this.cache.dequeue();
                }
                catch (InterruptedException ie)
                {
                    // should never happen - but it might!
                    throw new IllegalStateException("encountered an interrupt: "+ie);
                }
            }
        }
        // an underflow
        // doesn't need to be synchronized
//System.out.println("ObjectCache.get( "+millisWaitTime+" ): underflow...");
//(new Throwable()).printStackTrace();
        this.underflowCount++;
        
        return this.cache.dequeue( millisWaitTime );
    }
    
    
    /**
     * Retrieves the number of "overflows" encountered. An overflow occurs
     * when the cache is full and a {@link #putBack( Object )} is called.
     */
    public int getOverflows()
    {
        return this.overflowCount;
    }
    
    
    /**
     * Retrieves the number of "underflows" encountered. An underflow occurs
     * when the cache is empty and a {@link #get()} is called.
     */
    public int getUnderflows()
    {
        return this.underflowCount;
    }
    
    
    /**
     * Resets the internal maximum number of objects that the cache can
     * hold. Note that it does not immediately clear out the extra objects -
     * that is naturally cleared by the {@link #putBack( Object )} ignoring
     * overflows.
     */
    public void setMaxSize( int size )
    {
        this.maxSize = size;
    }
    
    
    /**
     * 
     */
    public int getMaxSize()
    {
        return this.maxSize;
    }
    
    
    /**
     * Sets the internal cache-underflow Object creator.
     */
    public void setObjectCreator( ObjectCreator creator )
    {
        this.creator = creator;
    }
    
    
    /**
     * Creates a new DefaultObjectCreator based on the given class.
     */
    public void setClassCreator( Class creator )
    {
        ObjectCreator oc = null;
        if (creator != null)
        {
            oc = new DefaultObjectCreator( creator );
        }
        setObjectCreator( oc );
    }
    
    
    /**
     * Create a new object and put it into the cache.  This follows the
     * rules of {@link #putBack( Object )}.
     */
    public void addObject()
    {
        Object o = createObject();
        if (o != null)
        {
            putBack( o );
        }
    }
    
    
    /**
     * Fills the cache to its maximum.  If there is no maximum or there
     * is no creator, then nothing is done.
     */
    public void fillCache()
    {
        if (this.creator != null)
        {
            // even if creation doesn't work, go ahead and increment the count
            // - this prevents an infinite loop
            for (int i = this.cache.size(); i < this.maxSize; i++)
            {
                addObject();
            }
        }
    }
    
    //-----------------------------------------------------
    // Protected methods
    
    
    /**
     * Generates an Object for the cache.  May return null.
     */
    protected Object createObject()
    {
        Object o = null;
        if (this.creator != null)
        {
// System.out.println("ObjectCache.createObject(): calling creator's createObject");
            o = this.creator.createObject();
        }
        return o;
    }
}