KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jdo > spi > persistence > support > sqlstore > impl > VersionConsistencyCacheImpl


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.jdo.spi.persistence.support.sqlstore.impl;
25
26 import java.util.Collections JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Properties JavaDoc;
31 import java.util.ResourceBundle JavaDoc;
32
33 import com.sun.appserv.util.cache.Cache;
34 import com.sun.appserv.util.cache.CacheListener;
35 import com.sun.appserv.util.cache.LruCache;
36
37 import com.sun.jdo.spi.persistence.support.sqlstore.LogHelperPersistenceManager;
38 import com.sun.jdo.spi.persistence.support.sqlstore.StateManager;
39 import com.sun.jdo.spi.persistence.support.sqlstore.VersionConsistencyCache;
40
41 import com.sun.jdo.spi.persistence.utility.BucketizedHashtable;
42 import com.sun.jdo.spi.persistence.utility.I18NHelper;
43 import com.sun.jdo.spi.persistence.utility.logging.Logger;
44
45
46 /**
47  * A 2-level cache of StateManager instances (i.e., a map of maps). The inner
48  * map is a BucketizedHashtable or a LRU cache, depending on parameter given at
49  * construction.
50  *
51  * @author Dave Bristor
52  */

53 public class VersionConsistencyCacheImpl implements VersionConsistencyCache {
54     /** The outermost map of the two-level cache. */
55     private final Map JavaDoc pcTypeMap = new HashMap JavaDoc();
56
57     /** Used to create different kinds of caches. */
58     // Not final, so that we can create different kinds of caches for testing.
59
private static CacheFactory cacheFactory;
60
61     private final static ResourceBundle JavaDoc messages =
62         I18NHelper.loadBundle(VersionConsistencyCacheImpl.class);
63
64     /** Use the PersistenceManager's logger. */
65     private static Logger logger = LogHelperPersistenceManager.getLogger();
66
67     /** Name of implementation class of LRU cache. */
68     private static final String JavaDoc LRU_CACHE_CLASSNAME =
69       "com.sun.appserv.util.cache.LruCache"; // NOI18aN
70

71
72     //
73
// Cache configuration controls
74
//
75

76     /** Prefix of each property name of configuration item. */
77     private static final String JavaDoc PROPERTY_PREFIX =
78         "com.sun.jdo.spi.persistence.support.sqlstore.impl.VersionConsistency."; // NOI18N
79

80
81     /** Determines whether to use LruCache or the default. */
82     private static boolean lruCache = false;
83
84     /** Name of property to choose LRU or basic cache. */
85     private static final String JavaDoc LRU_CACHE_PROPERTY = PROPERTY_PREFIX + "LruCache"; // NOI18N
86

87
88     /** For both LruCache and BucketizedHashtable. */
89     private static float loadFactor = 0.75F;
90
91     /** Name of property for specifying loadFactor. */
92     private static final String JavaDoc LOAD_FACTOR_PROPERTY =
93         PROPERTY_PREFIX + "loadFactor"; // NOI18N
94

95
96     /** For BucketizedHashtable only. */
97     private static int bucketSize = 13;
98
99     /** Name of property for specifying bucketSize. */
100     private static final String JavaDoc BUCKET_SIZE_PROPERTY =
101         PROPERTY_PREFIX + "bucketSize"; // NOI18N
102

103
104     /** For BucketizedHashtable only. */
105     private static int initialCapacity = 131;
106
107     /** Name of property for specifying initialCapacity. */
108     private static final String JavaDoc INITIAL_CAPACITY_PROPERTY =
109         PROPERTY_PREFIX + "initialCapacity"; // NOI18N
110

111
112     /** For LruCache only. */
113     private static int maxEntries = 131;
114
115     /** Name of property for specifying maxEntries. */
116     private static final String JavaDoc MAX_ENTRIES_PROPERTY =
117         PROPERTY_PREFIX + "maxEntries"; // NOI18N
118

119
120      /** LruCache only, 10 minute timeout */
121     private static long timeout = 1000L * 60 * 10;
122     
123     /** Name of property for specifying timeout. */
124     private static final String JavaDoc TIMEOUT_PROPERTY =
125         PROPERTY_PREFIX + "timeout"; // NOI18N
126

127     // Create the cache factory
128
static {
129         cacheFactory = createCacheFactory();
130     }
131     
132
133     /** Empty default constructor. */
134     private VersionConsistencyCacheImpl() {
135     }
136
137     /** Creates a cache with desired performance. This constructor is
138      * expected to be used for unit testing ONLY.
139      * @param highPerf If true, use LruCache, else use BucketizedHashtable.
140      */

141     protected VersionConsistencyCacheImpl(boolean highPerf) {
142         if (highPerf) {
143             cacheFactory = new LruCacheFactory();
144         } else {
145             cacheFactory = new BasicCacheFactory();
146         }
147     }
148
149     /**
150      * Create a cache. The performance characteristics of the cache depends
151      * on the setting of the runtime properties. If the flag
152      * <code>com.sun.jdo.spi.persistence.support.sqlstore.impl.VersionConsistency.LruCache</code>
153      * is true, then the LruCache cache is used. If it has some other value,
154      * the BucketizedHashtable cache is used. If not set, but we can load
155      * the LruCache class, the LruCache cache is used. Otherwise, we use
156      * the BucketizedHashtable cache. Other properties control particulars
157      * of those two caches.
158      */

159     static VersionConsistencyCache create() {
160         return new VersionConsistencyCacheImpl();
161     }
162
163     /**
164      * Create a CacheFactory. Uses system properties to determine what kind of
165      * cache will be returned by the factory.
166      */

167     static CacheFactory createCacheFactory() {
168         CacheFactory rc = null;
169         
170         loadFactor = getFloatValue(LOAD_FACTOR_PROPERTY, loadFactor);
171
172         bucketSize = getIntValue(BUCKET_SIZE_PROPERTY, bucketSize);
173
174         initialCapacity = getIntValue(INITIAL_CAPACITY_PROPERTY, initialCapacity);
175
176         maxEntries = getIntValue(MAX_ENTRIES_PROPERTY, maxEntries);
177
178         timeout = getLongValue(TIMEOUT_PROPERTY, timeout);
179
180         // Determine whether to use LRU cache or not.
181
boolean lruCache = false;
182         try {
183             
184             // Don't use Boolean.getBoolean, because we want to know if the
185
// flag is given or not.
186
String JavaDoc s = System.getProperty(LRU_CACHE_PROPERTY);
187             if (s != null) {
188                 lruCache = Boolean.valueOf(s).booleanValue();
189                 if (lruCache) {
190                     
191                     // If user specifies lruCache, but it is not available,
192
// log a WARNING and use the basic cache.
193
try {
194                         Class.forName(LRU_CACHE_CLASSNAME);
195                     } catch (Exception JavaDoc ex) {
196                         logger.warning(
197                             I18NHelper.getMessage(
198                                 messages,
199                                 "jdo.versionconsistencycacheimpl.lrucachenotfound")); // NOI18N
200
lruCache = false;
201                     }
202                 }
203                 
204             } else {
205                 // No flag given: Try to load LRU cache
206
try {
207                     Class.forName(LRU_CACHE_CLASSNAME);
208                     lruCache = true;
209                 } catch (Exception JavaDoc ex) {
210                     // LRU cache not found, so use default
211
}
212             }
213         } catch (Exception JavaDoc ex) {
214             
215             // This probably should not happen, but fallback to the
216
// default cache just in case.
217
lruCache = false;
218             logger.warning(
219                 I18NHelper.getMessage(
220                     messages,
221                     "jdo.versionconsistencycacheimpl.unexpectedduringcreate", ex));// NOI18N
222
}
223
224         if (lruCache) {
225             rc = new LruCacheFactory();
226         } else {
227             rc = new BasicCacheFactory();
228         }
229
230         if (logger.isLoggable(Logger.FINER)) {
231             String JavaDoc values =
232                 "\nloadFactor= " + loadFactor // NOI18N
233
+ "\nbucketSize= " + bucketSize // NOI18N
234
+ "\ninitialCapacity=" + initialCapacity // NOI18N
235
+ "\nmaxEntries=" + maxEntries // NOI18N
236
+ "\ntimeout=" + timeout // NOI18N
237
+ "\nlruCache=" + lruCache; // NOI18N
238
logger.finer(
239                 I18NHelper.getMessage(
240                     messages,
241                     "jdo.versionconsistencycacheimpl.created",
242                     values)); // NOI18N
243
}
244
245         return rc;
246     }
247
248     /**
249      * Returns the value for the given property name. If not available,
250      * returns the default value. If the property's value cannot be parsed as
251      * an integer, logs a warning.
252      * @param propName Name of property for value
253      * @param defaultVal Default value used if property is not set.
254      * @return value for the property.
255      */

256     private static int getIntValue(String JavaDoc propName, int defaultVal) {
257         int rc = defaultVal;
258         String JavaDoc valString = System.getProperty(propName);
259         if (null != valString && valString.length() > 0) {
260             try {
261                 rc = Integer.parseInt(valString);
262             } catch (NumberFormatException JavaDoc ex) {
263                 logBadConfigValue(propName, valString);
264             }
265         }
266         return rc;
267     }
268
269     /**
270      * Returns the value for the given property name. If not available,
271      * returns the default value. If the property's value cannot be parsed as
272      * a float, logs a warning.
273      * @param propName Name of property for value
274      * @param defaultVal Default value used if property is not set.
275      * @return value for the property.
276      */

277     private static float getFloatValue(String JavaDoc propName, float defaultVal) {
278         float rc = defaultVal;
279         String JavaDoc valString = System.getProperty(propName);
280         if (null != valString && valString.length() > 0) {
281             try {
282                 rc = Float.parseFloat(valString);
283             } catch (NumberFormatException JavaDoc ex) {
284                 logBadConfigValue(propName, valString);
285             }
286         }
287         return rc;
288     }
289
290     /**
291      * Returns the value for the given property name. If not available,
292      * returns the default value. If the property's value cannot be parsed as
293      * a long, logs a warning.
294      * @param propName Name of property for value
295      * @param defaultVal Default value used if property is not set.
296      * @return value for the property.
297      */

298     private static long getLongValue(String JavaDoc propName, long defaultVal) {
299         long rc = defaultVal;
300         String JavaDoc valString = System.getProperty(propName);
301         if (null != valString && valString.length() > 0) {
302             try {
303                 rc = Long.parseLong(valString);
304             } catch (NumberFormatException JavaDoc ex) {
305                 logBadConfigValue(propName, valString);
306             }
307         }
308         return rc;
309     }
310
311     /**
312      * Logs a warning that the property's value is invalid.
313      * @param propName Name of property
314      * @param valString Value of property as a String.
315      */

316     private static void logBadConfigValue(String JavaDoc propName, String JavaDoc valString) {
317         logger.warning(
318             I18NHelper.getMessage(
319                 messages,
320                 "jdo.versionconsistencycacheimpl.badconfigvalue", // NOI18N
321
propName, valString));
322     }
323
324     /**
325      * @see VersionConsistencyCache#put
326      */

327     public StateManager put(Class JavaDoc pcType, Object JavaDoc oid, StateManager sm) {
328         boolean logAtFinest = logger.isLoggable(Logger.FINEST);
329
330         if (logAtFinest) {
331             logger.finest(
332                     I18NHelper.getMessage(
333                             messages,
334                             "jdo.versionconsistencycacheimpl.put.entering", // NOI18N
335
new Object JavaDoc[] {pcType, oid, sm}));
336         }
337
338         StateManager rc = null;
339         VCCache oid2sm = null;
340         synchronized (pcTypeMap) {
341             oid2sm = (VCCache) pcTypeMap.get(pcType);
342     
343             if (null == oid2sm) {
344                 oid2sm = cacheFactory.create();
345                 pcTypeMap.put(pcType, oid2sm);
346             }
347         }
348
349         rc = oid2sm.put(oid, sm);
350
351         if (logAtFinest) {
352             logger.finest(
353                     I18NHelper.getMessage(
354                             messages,
355                             "jdo.versionconsistencycacheimpl.put.returning", // NOI18N
356
rc));
357         }
358
359         return rc;
360     }
361
362     /**
363      * @see VersionConsistencyCache#get
364      */

365     public StateManager get(Class JavaDoc pcType, Object JavaDoc oid) {
366         boolean logAtFinest = logger.isLoggable(Logger.FINEST);
367
368         if (logAtFinest) {
369             logger.finest(
370                     I18NHelper.getMessage(
371                             messages,
372                             "jdo.versionconsistencycacheimpl.get.entering", // NOI18N
373
new Object JavaDoc[] {pcType, oid}));
374         }
375         StateManager rc = null;
376
377         VCCache oid2sm = null;
378         synchronized (pcTypeMap) {
379             oid2sm = (VCCache) pcTypeMap.get(pcType);
380         }
381     
382         if (null != oid2sm) {
383             rc = oid2sm.get(oid);
384         }
385
386         if (logAtFinest) {
387             logger.finest(
388                     I18NHelper.getMessage(
389                             messages,
390                             "jdo.versionconsistencycacheimpl.get.returning", // NOI18N
391
rc));
392         }
393         return rc;
394     }
395
396
397     /**
398      * @see VersionConsistencyCache#remove
399      */

400     public StateManager remove(Class JavaDoc pcType, Object JavaDoc oid) {
401         boolean logAtFinest = logger.isLoggable(Logger.FINEST);
402
403         if (logAtFinest) {
404             logger.finest(
405                     I18NHelper.getMessage(
406                             messages,
407                             "jdo.versionconsistencycacheimpl.remove.entering", // NOI18N
408
new Object JavaDoc[] {pcType, oid}));
409         }
410
411         StateManager rc = null;
412         synchronized (pcTypeMap) {
413             VCCache oid2sm = (VCCache) pcTypeMap.get(pcType);
414
415             if (null != oid2sm) {
416                 rc = (StateManager) oid2sm.remove(oid);
417                 if (oid2sm.isEmpty()) {
418                     pcTypeMap.remove(pcType);
419                 }
420             }
421         }
422
423         if (logAtFinest) {
424             logger.finest(
425                     I18NHelper.getMessage(
426                             messages,
427                             "jdo.versionconsistencycacheimpl.remove.returning", // NOI18N
428
rc));
429         }
430         return rc;
431     }
432
433     /**
434      * This implementation does nothing. Instead, we create buckets for each
435      * pcType as-needed; see {@link #put}
436      */

437     public void addPCType(Class JavaDoc pcType) {
438         if (logger.isLoggable(Logger.FINEST)) {
439             logger.finest(
440                     I18NHelper.getMessage(
441                             messages,
442                             "jdo.versionconsistencycacheimpl.addpctype", // NOI18N
443
pcType));
444         }
445         // Intentionally empty
446
}
447
448     /**
449      * @see VersionConsistencyCache#removePCType
450      */

451     public void removePCType(Class JavaDoc pcType) {
452         if (logger.isLoggable(Logger.FINEST)) {
453             logger.finest(
454                     I18NHelper.getMessage(
455                             messages,
456                             "jdo.versionconsistencycacheimpl.removepctype", // NOI18N
457
pcType));
458         }
459
460         synchronized (pcTypeMap) {
461             VCCache oid2sm = (VCCache) pcTypeMap.get(pcType);
462
463             if (null != oid2sm) {
464                 oid2sm.clear();
465             }
466             pcTypeMap.remove(pcType);
467         }
468     }
469
470     /**
471      * @return the number of elements in the cache.
472      */

473     public int size() {
474         int rc = 0;
475         synchronized (pcTypeMap) {
476             for (Iterator JavaDoc i = pcTypeMap.keySet().iterator(); i.hasNext();) {
477                 VCCache oid2sm = (VCCache) pcTypeMap.get(i.next());
478                 rc += oid2sm.size();
479             }
480         }
481         return rc;
482     }
483
484     /**
485      * @return true if this cache is based on LRU cache; false otherwise.
486      */

487     public boolean isHighPerf() {
488         return LruCacheFactory.class.equals(cacheFactory.getClass());
489     }
490     
491     //
492
// Support for the inner map. It is either HashMap- or Cache- based.
493
//
494

495     /** Provides cache operations of put, get, and remove. */
496     interface VCCache {
497         /** @see Map#put */
498         public StateManager put(Object JavaDoc key, StateManager value);
499
500         /** @see Map#get */
501         public StateManager get(Object JavaDoc key);
502
503         /** @see Map#remove */
504         public StateManager remove(Object JavaDoc key);
505
506         /** @see Map#clear */
507         public void clear();
508
509         /** @see Map#isEmpty */
510         public boolean isEmpty();
511
512         /** @see Map#size */
513         public int size();
514     }
515
516     /**
517      * VCCache that is HashMap-based. The methods are not synchronized but
518      * the underlying implemention <em>is</em>synchronized.
519      */

520     static class BasicVCCache implements VCCache {
521         private final Map JavaDoc cache;
522         
523         BasicVCCache() {
524             if (logger.isLoggable(Logger.FINER)) {
525                 logger.finer(
526                         I18NHelper.getMessage(
527                                 messages,
528                                 "jdo.versionconsistencycacheimpl.usinghashmap", // NOI18N
529
new Object JavaDoc[] {
530                                     new Integer JavaDoc(bucketSize),
531                                     new Long JavaDoc(initialCapacity),
532                                     new Float JavaDoc(loadFactor)}));
533             }
534
535             cache = Collections.synchronizedMap(
536                     new BucketizedHashtable(
537                             bucketSize, initialCapacity, loadFactor));
538         }
539         
540         /** @see Map#put */
541         public StateManager put(Object JavaDoc key, StateManager value) {
542             return (StateManager) cache.put(key, value);
543         }
544
545         /** @see Map#get */
546         public StateManager get(Object JavaDoc key) {
547             return (StateManager) cache.get(key);
548         }
549
550         /** @see Map#remove */
551         public StateManager remove(Object JavaDoc key) {
552             return (StateManager) cache.remove(key);
553         }
554         
555         /** @see Map#clear */
556         public void clear() {
557             cache.clear();
558         }
559
560         /** @see Map#isEmpty */
561         public boolean isEmpty() {
562             return cache.isEmpty();
563         }
564
565         /** @see Map#size */
566         public int size() {
567             return cache.size();
568         }
569     }
570         
571     /**
572      * VCCache that uses LRU cachd. Methods are not synchronized, but
573      * underlying cache implementation <em>is</em>.
574      */

575     static class LruVCCache implements VCCache {
576         /**
577          * We can't use the interface type Cache because we need to be able to
578          * clear out the cache, which is only supported by the implementation.
579          */

580         private final Cache cache;
581
582         /**
583          * @param maxEntries maximum number of entries expected in the cache
584          * @param loadFactor the load factor
585          * @param timeout to be used to trim the expired entries
586          */

587         LruVCCache(int maxEntries, long timeout, float loadFactor) {
588             if (logger.isLoggable(Logger.FINER)) {
589                 logger.finer(
590                         I18NHelper.getMessage(
591                                 messages,
592                                 "jdo.versionconsistencycacheimpl.usinglrucache", // NOI18N
593
new Object JavaDoc[] {
594                                     new Integer JavaDoc(maxEntries),
595                                     new Long JavaDoc(timeout),
596                                     new Float JavaDoc(loadFactor)}));
597             }
598
599             LruCache c = new LruCache();
600             c.init(maxEntries, timeout, loadFactor, (Properties JavaDoc) null);
601             c.addCacheListener(
602                     new CacheListener() {
603                         public void trimEvent(Object JavaDoc key, Object JavaDoc value) {
604                             cache.remove(key);
605                             if (logger.isLoggable(Logger.FINER)) {
606                                 logger.finer(
607                                         I18NHelper.getMessage(
608                                                 messages,
609                                                 "jdo.versionconsistencycacheimpl.trimevent")); // NOI18N
610
}
611                         }
612                     });
613             cache = c;
614         }
615         
616         /** @see Map#put */
617         public StateManager put(Object JavaDoc key, StateManager value) {
618             return (StateManager) cache.put(key, value);
619         }
620
621         /** @see Map#get */
622         public StateManager get(Object JavaDoc key) {
623             return (StateManager) cache.get(key);
624         }
625
626         /** @see Map#remove */
627         public StateManager remove(Object JavaDoc key) {
628             return (StateManager) cache.remove(key);
629         }
630
631         /** @see Map#clear */
632         public void clear() {
633             cache.clear();
634         }
635
636         /** @see Map#isEmpty */
637         public boolean isEmpty() {
638             return cache.isEmpty();
639         }
640
641         /** @see Map#size */
642         public int size() {
643             return cache.getEntryCount();
644         }
645     }
646
647     //
648
// Factory for creating VCCache instances.
649
//
650

651     /** Provides for creating an instance of a VCCache. */
652     interface CacheFactory {
653         /** @return an instance of a VCCache. */
654         public VCCache create();
655     }
656
657     /** Provides for creating an instance of a BasicVCCache. */
658     static class BasicCacheFactory implements CacheFactory {
659
660         /** @return an instance of a BasicVCCache. */
661         public VCCache create() {
662             return new BasicVCCache();
663         }
664     }
665
666     /** Provides for creating an instance of a LruVCCache. */
667     static class LruCacheFactory implements CacheFactory {
668
669         /** @return an instance of a LruVCCache. */
670         public VCCache create() {
671             return new LruVCCache(maxEntries, timeout, loadFactor);
672         }
673     }
674 }
675
Popular Tags