KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > mdr > persistence > btreeimpl > btreestorage > MDRCache


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.mdr.persistence.btreeimpl.btreestorage;
20
21 import java.lang.ref.*;
22 import java.io.*;
23 import java.text.*;
24 import java.util.*;
25
26 import org.netbeans.mdr.persistence.*;
27
28 /**
29 * An in-memory cache of MDR objects. At any given moment, there are four
30 * kinds of objects in the cache:
31 * <ol>
32 * <li>
33 * Objects which are referenced by client code, and so cannot be collected.
34 * <li>
35 * Objects which have been created but not yes written to disk, and so
36 * should not be collected until they are written out.
37 * <li>
38 * Objects which have been marked dirty, and so should not be collected until
39 * they are written out.
40 * <li>
41 * Objects which are nother referenced nor dirty, and so are eligible for
42 * collection.
43 * </ol>
44 * The cache also keep s track of the keys of objects which have been deleted.
45 * <p>
46 * The creator of the cache specifies how many objects
47 * to cache in memory. Hard references are kept to the last N objects
48 * used, to enforce this. The cache keeps soft references to the other
49 * objects of the third type, so
50 * how often they are collected depends upon memory usage and the
51 * JVM's GC implementation.
52 */

53
54 public class MDRCache {
55     private static final ArrayList instances = new ArrayList();
56
57     /* hash MOF ID's to references */
58     private final FacilityCache hashOnId;
59
60     /* Hard references */
61     private final Map hardRef;
62
63     /* List of new objects */
64     private HashMap newOnes;
65
66     /* List of dirty objects */
67     private HashMap dirty;
68
69     /* List of deleted IDs */
70     private HashMap deleted;
71
72     /* our overflow handler */
73     OverflowHandler handler;
74
75     /* our threshhold for changed objects */
76     private static final int threshhold = Integer.getInteger(
77         "org.netbeans.mdr.persistence.btreeimpl.btreestorage.MDRCache.threshhold",
78         BtreeDatabase.MDR_CACHE_THRESHHOLD).intValue() * 2;
79     
80     private static final boolean CACHE_DEBUG = Boolean.getBoolean("perf.mdr.MDRCache");
81     
82     private static int size = 0;
83     
84     private static StringBuffer JavaDoc DEBUG_INFO = Boolean.getBoolean("debug.mdr.MDRCache") ? new StringBuffer JavaDoc(20000) : null;
85     
86     private static boolean alreadyChecking = false;
87     private static final Object JavaDoc LOCK = new Object JavaDoc();
88     
89     private int localThreshhold;
90     private int lastLocalSize = 0;
91     
92     public static final int M_DELETED = 1;
93     public static final int M_DIRTY = 2;
94     public static final int M_NEW = 4;
95
96     /* caching statistics */
97     private static int hits;
98     private static int misses;
99     //int maxSize;
100

101     /** Create the cache
102     * @param size how many objects to cache in memory
103     * @param hndlr handler to call when the cache has too many changed objects
104     * @param limit number of changed objects to allow
105     */

106     public MDRCache(int size, OverflowHandler hndlr, int limit, Map hardRef) {
107         this(size, hardRef);
108     handler = hndlr;
109         localThreshhold = limit;
110     }
111     
112     private static class CacheClass implements Map {
113         private final Object JavaDoc inner[];
114         private int size, cursor;
115         
116         public CacheClass(int size) {
117             inner = new Object JavaDoc[size];
118         }
119         
120         public Set keySet() {
121             throw new UnsupportedOperationException JavaDoc();
122         }
123
124         public Set entrySet() {
125             throw new UnsupportedOperationException JavaDoc();
126         }
127
128         public void putAll(Map t) {
129             throw new UnsupportedOperationException JavaDoc();
130         }
131
132         public boolean isEmpty() {
133             return size == 0;
134         }
135
136         public boolean containsKey(Object JavaDoc key) {
137             throw new UnsupportedOperationException JavaDoc();
138         }
139
140         public boolean containsValue(Object JavaDoc value) {
141             throw new UnsupportedOperationException JavaDoc();
142         }
143
144         public Collection values() {
145             throw new UnsupportedOperationException JavaDoc();
146         }
147
148         public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
149             if (value != inner[cursor]) {
150                 cursor++;
151                 if (size < inner.length) {
152                     size++;
153                 }
154                 if (cursor >= inner.length) {
155                     cursor = 0;
156                 }
157                 inner[cursor] = value;
158             }
159             return null;
160         }
161
162         public void clear() {
163             Arrays.fill(inner, null);
164             size = cursor = 0;
165         }
166
167         public int size() {
168             return size;
169         }
170
171         public Object JavaDoc get(Object JavaDoc key) {
172             throw new UnsupportedOperationException JavaDoc();
173         }
174
175         public Object JavaDoc remove(Object JavaDoc key) {
176             throw new UnsupportedOperationException JavaDoc();
177         }
178     }
179
180     /** Create the cache
181     * @param size how many objects to cache in memory */

182     public MDRCache(final int size, Map hardRef) {
183         hashOnId = new FacilityCache();
184         if (hardRef == null) {
185             hardRef = new CacheClass(size)/*LinkedHashMap(2 * size, 0.5f, true) {
186                 public boolean removeEldestEntry(Map.Entry entry) {
187                     return size() < size;
188                 }
189             }*/
;
190         }
191         this.hardRef = hardRef;
192         deleted = new HashMap();
193         dirty = new HashMap();
194         newOnes = new HashMap();
195         synchronized (MDRCache.class) {
196             instances.add(this);
197         }
198     }
199
200     public void shutDown() {
201         synchronized (MDRCache.class) {
202             instances.remove(this);
203         }
204     }
205
206     /** returns true if the cache contains any changed objects
207     * @return true if any objects have been modified or deleted
208     */

209     public synchronized int getModStatus() {
210         return (deleted.isEmpty() ? 0 : M_DELETED) + (dirty.isEmpty() ? 0 : M_DIRTY) + (newOnes.isEmpty() ? 0 : M_NEW);
211     }
212
213
214     /** add a new object to the cache
215     * @param m the object's MOF ID
216     * @param o the object to add
217     */

218     public synchronized void put(Object JavaDoc m, Object JavaDoc o) throws StorageException {
219         if (hashOnId.get(m) == null) {
220             hashOnId.put(m, o);
221         }
222         makeHardRef(m, o);
223 // int curSize = hashOnId.size();
224
// if (curSize > maxSize) {
225
// maxSize = curSize;
226
// }
227
}
228
229     /** get an object from the cache
230     * @param m the object's MOF ID
231     */

232     public synchronized Object JavaDoc get(Object JavaDoc m) {
233         Object JavaDoc o = hashOnId.get(m);
234         if (o != null) {
235             makeHardRef(m, o);
236         }
237         
238         if (CACHE_DEBUG) {
239             synchronized (MDRCache.class) {
240                 if (o != null) {
241                     hits++;
242                 } else {
243                     misses++;
244                 }
245                 if ((hits + misses) % 20000 == 0) {
246                     showStats(System.err);
247                 }
248             }
249         }
250         return o;
251     }
252
253     /** replace an object in the cache
254     * @param m the object's key
255     * @param o the object
256     */

257     public synchronized void replace(Object JavaDoc m, Object JavaDoc o) throws StorageException {
258         removeFromCache(m);
259         put(m, o);
260     }
261
262     /** remove an object from the cache
263     * @param m the object's MOF ID
264     * @return true if the object was found in the cache
265     */

266     public synchronized void remove(Object JavaDoc m) {
267         if (!removeFromCache(m)) {
268             deleted.put(m, m);
269         }
270     }
271
272     /** remove all traces from the cache.
273     * @return true if the object was new
274     */

275     private boolean removeFromCache(Object JavaDoc m) {
276         hashOnId.remove(m);
277         boolean wasNew = (newOnes.remove(m) != null);
278         dirty.remove(m);
279
280         return wasNew;
281     }
282
283
284     /** clear all unecessary objects from the cache
285     */

286     public synchronized void clear() {
287         hardRef.clear();
288         System.gc();
289     }
290
291     /* create a hard reference to the supplied object */
292     private void makeHardRef(Object JavaDoc m, Object JavaDoc o) {
293         hardRef.put(m, o);
294     }
295     
296     public void updateSize() {
297     int allChanged = newOnes.size() + dirty.size();
298         int sizeDelta = allChanged - lastLocalSize;
299         lastLocalSize = allChanged;
300         synchronized (MDRCache.class) {
301             size += sizeDelta;
302         }
303     }
304
305     /* Check to see if we have exceeded our threshhold for dirty objects
306     */

307     private void checkThreshhold() throws StorageException {
308         try {
309             synchronized (LOCK) {
310                 if (alreadyChecking) return;
311                 alreadyChecking = true;
312             }
313             int allChanged = newOnes.size() + dirty.size();
314             int sizeDelta = allChanged - lastLocalSize;
315             lastLocalSize = allChanged;
316             synchronized (MDRCache.class) {
317                 size += sizeDelta;
318                 if (size >= threshhold) {
319                     if (DEBUG_INFO != null) DEBUG_INFO.append("Global threshhold reached at: " + size);
320                     int newSize = 0;
321                     for (Iterator it = instances.iterator(); it.hasNext();) {
322                         MDRCache cache = (MDRCache) it.next();
323                         if (DEBUG_INFO != null) DEBUG_INFO.append("\nInstance: " + cache.handler.toString());
324                         allChanged = cache.newOnes.size() + cache.dirty.size();
325                         if (allChanged > 10) {
326                             if (DEBUG_INFO != null) {
327                                 DEBUG_INFO.append("\nnewOnes: " + cache.newOnes.keySet().toString());
328                                 DEBUG_INFO.append("\ndirty: " + cache.dirty.keySet().toString());
329                                 DEBUG_INFO.append("\ndeleted: " + cache.deleted.keySet().toString());
330                             }
331                             cache.handler.cacheThreshholdReached(cache, allChanged);
332                             allChanged = cache.newOnes.size() + cache.dirty.size();
333                         }
334                         cache.lastLocalSize = allChanged;
335                         newSize += allChanged;
336                     }
337                     size = newSize;
338                     return;
339                 }
340             }
341             if (allChanged >= localThreshhold) {
342                 if (DEBUG_INFO != null) DEBUG_INFO.append("\nLocal threshhold reached for: " + handler.toString());
343                 handler.cacheThreshholdReached(this, allChanged);
344             }
345         } catch (StorageException e) {
346             if (DEBUG_INFO != null) {
347                 System.err.println("Debug info:");
348                 System.err.println(DEBUG_INFO.toString());
349             }
350             throw e;
351         } finally {
352             synchronized (LOCK) {
353                 alreadyChecking = false;
354             }
355             if (DEBUG_INFO != null) DEBUG_INFO.delete(0, DEBUG_INFO.length());
356         }
357     }
358
359     /* throw an exception when a bad key is processed */
360     private void badKey(Object JavaDoc key) throws StorageException {
361     throw new StorageBadRequestException(
362         MessageFormat.format(
363         "No object with ID {0}", new Object JavaDoc[] {key}));
364     }
365
366     /**
367     * mark that the object with the given MOF ID is new
368     * @param key MOF ID
369     */

370     public synchronized void setNew(Object JavaDoc key) throws StorageException{
371         Object JavaDoc o = get(key);
372         if (o == null) {
373         badKey(key);
374         }
375         newOnes.put(key, o);
376     if (handler != null)
377         checkThreshhold();
378     }
379     
380     /** check if the object with the given key is new
381     * @return true if it is new
382     */

383     public synchronized boolean isNew(Object JavaDoc key) {
384         return newOnes.get(key) != null;
385     }
386
387     /** Get an iterator over the keys of all active objects in the cache
388     * @return the iterator
389     */

390     public synchronized Iterator iterateActive() {
391         return hashOnId.keySet().iterator();
392     }
393
394     /** Get an iterator over the keys of deleted objects
395     * @return the iterator
396     */

397     public synchronized Iterator iterateDeleted() {
398         return deleted.keySet().iterator();
399     }
400
401     /** Get an iterator over the keys of new objects
402     * @return the iterator
403     */

404     public synchronized Iterator iterateNew() {
405         return newOnes.keySet().iterator();
406     }
407
408     /** return the number of new objects
409     * @return number of new objects
410     */

411     public int numberNew() {
412         return newOnes.size();
413     }
414
415     /** return the number of deleted objects
416     * @return number of deleted objects
417     */

418     public int numberDeleted() {
419         return deleted.size();
420     }
421
422     /** check if the object with the given key is deleted
423     * @return true if it is deleted
424     */

425     public synchronized boolean isDeleted(Object JavaDoc key) {
426         return deleted.get(key) != null;
427     }
428
429     /**
430     * mark that the object with the given MOF ID is dirty
431     * @param key MOF ID
432     */

433     public synchronized void setDirty(Object JavaDoc key) throws StorageException{
434         Object JavaDoc o = get(key);
435         if (o == null) {
436         badKey(key);
437         }
438         if (newOnes.get(key) == null) {
439             dirty.put(key, o);
440         if (handler != null)
441         checkThreshhold();
442         }
443     }
444
445     private static final Comparator mofidComparator = new Comparator() {
446         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
447             MOFID id1=(MOFID) o1;
448             MOFID id2=(MOFID) o2;
449             
450             return (int)(id1.getSerialNumber() - id2.getSerialNumber());
451         }
452     };
453     
454     /** Get all of the new objects
455     * @return the keys of the new objects
456     */

457     public synchronized Collection getNew() {
458         Map result = new TreeMap(mofidComparator);
459         result.putAll(newOnes);
460         newOnes.clear();
461         return result.entrySet();
462     }
463
464     /** Get all of the dirty objects
465     * @return the keys of the dirty objects
466     */

467     public synchronized Collection getDirty() {
468         HashMap result = new HashMap(dirty);
469         dirty.clear();
470         return result.entrySet();
471     }
472
473     /** Get all of the deleted IDs
474     * @return the deleted IDs
475     */

476     public synchronized Collection getDeleted() {
477         ArrayList result = new ArrayList(deleted.values());
478         deleted.clear();
479         return result;
480     }
481
482     /** Clear the set of new, dirty, and deleted objects,
483     * presumably after having written them all out.
484     */

485     public synchronized void clearLists() {
486         dirty.clear();
487         newOnes.clear();
488         deleted.clear();
489     }
490
491     /**
492     * Show caching statistics
493     */

494     public void showStats(PrintStream strm) {
495         showStats(new PrintWriter(strm));
496     }
497
498     /**
499     * Show caching statistics
500     */

501     public void showStats(PrintWriter strm) {
502         strm.println(
503             "MDRCache hits: " + hits + " misses: " + misses +
504             " hit rate: " + 100. * (float)hits / (float)(hits + misses));
505         //strm.println("Maximum size: " + maxSize);
506
strm.flush();
507     }
508
509     /**
510     * The cache handler is called when the number of changes in the cache
511     * reaches its threshhold. The handler is expected to do something
512     * to reduce the number of changed objects in the cache, for instance,
513     * write them to disk
514     */

515     public interface OverflowHandler {
516     
517     /** Notify handler that the cache has reached its threshhold
518         * @param cache cache which reached threshhold
519         * @param size number of changed objects currently in cache
520     */

521     void cacheThreshholdReached(MDRCache cache, int size)
522                         throws StorageException;
523     }
524     
525     private static class FacilityCache extends HashMap {
526         private final ReferenceQueue queue = new ReferenceQueue();
527         private boolean cleaningUp = false;
528
529         private static class CacheReference extends WeakReference {
530             private Object JavaDoc key;
531
532             public CacheReference(Object JavaDoc key, Object JavaDoc object, ReferenceQueue q) {
533                 super(object, q);
534                 this.key = key;
535             }
536
537             public Object JavaDoc getKey() {
538                 return key;
539             }
540         }
541
542         private void cleanUp() {
543             assert !cleaningUp;
544             CacheReference reference;
545             cleaningUp = true;
546             try {
547                 while ((reference = (CacheReference) queue.poll()) != null) {
548                     Object JavaDoc key = reference.getKey();
549                     java.lang.ref.Reference JavaDoc currentRef = (java.lang.ref.Reference JavaDoc) super.remove(key);
550                     if (currentRef != null && currentRef != reference && currentRef.get() != null) {
551                         super.put(key, currentRef);
552                     }
553                 }
554             } finally {
555                 cleaningUp = false;
556             }
557         }
558
559         public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
560             cleanUp();
561             Object JavaDoc result = super.put(key, new CacheReference(key, value, queue));
562             assert result == null || ((CacheReference) result).get() == null : "replacing non-null reference";
563             return null;
564         }
565         
566         public Object JavaDoc remove(Object JavaDoc key) {
567             cleanUp();
568             Object JavaDoc result = super.remove(key);
569             return result == null ? null : ((CacheReference) result).get();
570         }
571
572         public Object JavaDoc get(Object JavaDoc key) {
573             cleanUp();
574             Object JavaDoc result = super.get(key);
575             return result == null ? null : ((CacheReference) result).get();
576         }
577     }
578 }
579
Popular Tags