KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdo > LocalPMCache


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdo;
13
14 import com.versant.core.common.*;
15
16 import java.lang.ref.ReferenceQueue JavaDoc;
17 import java.util.Set JavaDoc;
18 import java.util.Iterator JavaDoc;
19
20 /**
21  * This is an implementation PM managed cache that uses a linked list to
22  * reference the colisions in the collection.
23  */

24 public final class LocalPMCache {
25     /**
26      * The default initial capacity - MUST be a power of two.
27      */

28     static final int DEFAULT_INITIAL_CAPACITY = 16;
29     /**
30      * The load factor used when none specified in constructor.
31      **/

32     static final float DEFAULT_LOAD_FACTOR = 0.75f;
33     /**
34      * The load factor for the hash table.
35      */

36     final float loadFactor;
37     /**
38      * The next size value at which to resize (capacity * load factor).
39      */

40     int threshold;
41     /**
42      * The maximum capacity, used if a higher value is implicitly specified
43      * by either of the constructors with arguments.
44      * MUST be a power of two <= 1<<30.
45      */

46     static final int MAXIMUM_CAPACITY = 1 << 30;
47
48     /**
49      * Reference queue for cleared WeakKeys
50      */

51     public ReferenceQueue JavaDoc queue = new ReferenceQueue JavaDoc();
52     private VersantPersistenceManagerImp pm;
53     private final TransactionalList processList = new TransactionalList();
54     private boolean overWriteMode;
55
56     private int currentRefType = VersantPersistenceManager.PM_CACHE_REF_TYPE_SOFT;
57
58     /**
59      * Array of value table slots.
60      */

61     private PMCacheEntry[] m_keyTable;
62
63     /**
64      * The number of key-value mappings contained in this identity hash map.
65      */

66     transient int size;
67
68     private final int createdSize;
69
70     public LocalPMCache() {
71         this.loadFactor = DEFAULT_LOAD_FACTOR;
72         threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
73         m_keyTable = new PMCacheEntry[DEFAULT_INITIAL_CAPACITY];
74         createdSize = DEFAULT_INITIAL_CAPACITY;
75     }
76
77     public LocalPMCache(int initialCapacity) {
78         this(initialCapacity, DEFAULT_LOAD_FACTOR);
79     }
80
81     public LocalPMCache(int initialCapacity, float loadFactor) {
82         if (initialCapacity < 0)
83             throw new IllegalArgumentException JavaDoc("Illegal initial capacity: " +
84                                                initialCapacity);
85         if (initialCapacity > MAXIMUM_CAPACITY)
86             initialCapacity = MAXIMUM_CAPACITY;
87         if (loadFactor <= 0 || Float.isNaN(loadFactor))
88             throw new IllegalArgumentException JavaDoc("Illegal load factor: " +
89                                                loadFactor);
90
91         // Find a power of 2 >= initialCapacity
92
int capacity = 1;
93         while (capacity < initialCapacity)
94             capacity <<= 1;
95
96         this.loadFactor = loadFactor;
97         threshold = (int)(capacity * loadFactor);
98         m_keyTable = new PMCacheEntry[capacity];
99         createdSize = initialCapacity;
100     }
101
102     public int getCurrentRefType() {
103         return currentRefType;
104     }
105
106     public void setCurrentRefType(int currentRefType) {
107         checkRefType(currentRefType);
108         this.currentRefType = currentRefType;
109     }
110
111     public static void checkRefType(int currentRefType) {
112         if (currentRefType != VersantPersistenceManager.PM_CACHE_REF_TYPE_WEAK
113                 && currentRefType != VersantPersistenceManager.PM_CACHE_REF_TYPE_SOFT
114                 && currentRefType != VersantPersistenceManager.PM_CACHE_REF_TYPE_STRONG) {
115             throw BindingSupportImpl.getInstance().invalidOperation("The option '"
116                     + currentRefType + "' is not a valid choice for a PMCacheRefType.");
117         }
118     }
119
120     public boolean isOverWriteMode() {
121         return overWriteMode;
122     }
123
124     public void setOverWriteMode(boolean overWriteMode) {
125         this.overWriteMode = overWriteMode;
126     }
127
128     public void setPm(VersantPersistenceManagerImp pm) {
129         this.pm = pm;
130     }
131
132     /**
133      * Add a newly created sm to the managed cache.
134      */

135     public PCStateMan add(PCStateMan sm) {
136         //this will create a realOID if needed
137
sm.getRealOIDIfAppId();
138         addSm(sm.oid.getAvailableOID(), sm);
139         addForProcessing(sm);
140         if (sm.isTx()) pm.addTxStateObject(sm);
141         return sm;
142     }
143
144     public PCStateMan add(OID oid, State state, PCStateMan[] sms) {
145         return addState(oid, state, true, sms);
146     }
147
148     /**
149      * Provide the oid-state pair to local cache. This will not result in a PCStateman
150      * being created.
151      */

152     public void addStateOnly(OID oid, State state) {
153         addState(oid, state, false, null);
154     }
155
156     /**
157      * This must create a CacheEntryBase for the sm.
158      */

159     public PMCacheEntry createCacheKey(PCStateMan sm) {
160         if (sm.cacheEntry != null) {
161             throw BindingSupportImpl.getInstance().internal("StateManager already has a PMCacheEntry");
162         }
163         return sm.cacheEntry = new PMCacheEntry(currentRefType, sm, queue);
164     }
165
166     /**
167      * This must create a CacheEntryBase for the sm.
168      */

169     public PMCacheEntry createCacheKey(OID oid, State state) {
170         return new PMCacheEntry(currentRefType, oid, state, queue);
171     }
172
173     /**
174      * Add to the head of the processList.
175      */

176     public PCStateMan addForProcessing(PCStateMan sm) {
177         if (!pm.isActive()) return sm;
178         processList.add(sm);
179         return sm;
180     }
181
182     PCStateMan updateSm(State value, PCStateMan sm, OID key) {
183         if (value == NULLState.NULL_STATE) {
184             /**
185              * Must throw exception as the instance was deleted from under us
186              */

187             throw BindingSupportImpl.getInstance().objectNotFound("No row for " +
188                     sm.getClassMetaData().storeClass + " " + key.toSString());
189         }
190
191         if (sm != null) {
192             sm.updateWith(value, pm, overWriteMode);
193             addForProcessing(sm);
194         }
195         return sm;
196     }
197
198     /**
199      * Process the ReferenceQueue holding keys for GCed values.
200      */

201     public void processReferenceQueue() {
202         processReferenceQueueImp();
203     }
204
205     /**
206      * Remove all invalidated entries from the map, that is, remove all entries
207      * whose keys have been discarded. This method should be invoked once by
208      * each public mutator in this class. We don't invoke this method in
209      * public accessors because that can lead to surprising
210      * ConcurrentModificationExceptions.
211      */

212     private void processReferenceQueueImp() {
213         PMCacheEntryOwnerRef ref;
214         while ((ref = (PMCacheEntryOwnerRef)queue.poll()) != null) {
215             PMCacheEntry ce = ref.getOwner();
216             if (!ce.hasReference(ref))
217                 // the ref of the PMCacheEntry might have changed,
218
// due to PMCacheEntry.upgradeToSm(). In this case,
219
// we must not remove the entry.
220

221                 continue;
222             removeImp(ce, m_keyTable, indexFor(ce.mappedOID.hashCode(), m_keyTable.length));
223         }
224     }
225
226     public void doCommit(boolean retainValues) {
227         processReferenceQueueImp();
228         //only do processList
229
Iterator JavaDoc iter = processList.iterator();
230         while (iter.hasNext()) {
231             PMCacheEntry ce = (PMCacheEntry) iter.next();
232             PCStateMan sm = (PCStateMan) ce.get();
233             if (sm != null) {
234                 sm.commit(pm);
235             }
236         }
237         processList.clear();
238     }
239
240     public void doRollback(boolean retainValues) {
241         processReferenceQueueImp();
242         Iterator JavaDoc iter = processList.iterator();
243         while (iter.hasNext()) {
244             PMCacheEntry ce = (PMCacheEntry) iter.next();
245             PCStateMan sm = (PCStateMan) ce.get();
246             if (sm != null) {
247                 sm.rollback();
248             }
249         }
250         processList.clear();
251     }
252
253     public void doRefresh(boolean strict) {
254         Iterator JavaDoc iter = processList.iterator();
255         while (iter.hasNext()) {
256             PMCacheEntry ce = (PMCacheEntry) iter.next();
257             PCStateMan sm = (PCStateMan) ce.get();
258             if (sm != null) {
259                 sm.refresh();
260             }
261         }
262     }
263
264     /**
265      * This add the real oid of the NewOID to the mapping.
266      */

267     public void addRealOID(PCStateMan sm) {
268         reMapWithRealOID(sm);
269     }
270
271     private void reMapWithRealOID(PCStateMan sm) {
272         if (Debug.DEBUG) {
273             validate();
274             if (!sm.oid.isNew()) {
275                 throw BindingSupportImpl.getInstance().internal("The instance is not new");
276             }
277             if (sm.cacheEntry.mappedOID != sm.oid) {
278                 throw BindingSupportImpl.getInstance().internal("The instance is not mapped with its newOID");
279             }
280             if (sm.oid.getRealOID() == null) {
281                 throw BindingSupportImpl.getInstance().internal("The realOID may not be null: " + sm.oid);
282             }
283         }
284
285         if (sm.cacheEntry.mappedOID == sm.oid.getRealOID()) return;
286         final int currentIndex = indexFor(sm.cacheEntry.hash, m_keyTable.length);
287         final int newIndex = indexFor(sm.oid.getRealOID().hashCode(), m_keyTable.length);
288
289         if (currentIndex == newIndex) {
290             OID realOID = sm.oid.getRealOID();
291             sm.cacheEntry.reHash(realOID);
292             realOID.resolve(sm.state);
293             return;
294         }
295         //remove from current pos
296
if (sm.cacheEntry.prev == null) {
297             m_keyTable[currentIndex] = sm.cacheEntry.next;
298         }
299         sm.cacheEntry.unlinkNextList();
300
301         OID realOID = sm.oid.getRealOID();
302         sm.cacheEntry.reHash(realOID);
303         realOID.resolve(sm.state);
304
305         sm.cacheEntry.setNext(m_keyTable[newIndex]);
306         m_keyTable[newIndex] = sm.cacheEntry;
307     }
308
309     public void checkModelConsistency() {
310         processReferenceQueueImp();
311         PMCacheEntry[] src = m_keyTable;
312         for (int j = 0; j < src.length; j++) {
313             PMCacheEntry e = src[j];
314             if (e != null) {
315                 Object JavaDoc val = e.get();
316                 if (val != null && (val instanceof PCStateMan)) {
317                     ((PCStateMan)val).checkModelConsistency();
318                 }
319                 e = e.next;
320             }
321         }
322     }
323
324     /**
325      * If the instance for this oid is already managed then it will be updated
326      * with the state information.
327      * Else the instance will be managed with this state.
328      */

329     protected PCStateMan addState(OID key, State value, boolean manage, PCStateMan[] addSm) {
330         if (Debug.DEBUG) validate();
331         final PMCacheEntry[] m_keyTable = this.m_keyTable;
332         final int hash = key.hashCode();
333         final int i = indexFor(hash, m_keyTable.length);
334
335         for (PMCacheEntry e = m_keyTable[i]; e != null; e = e.next) {
336             if (e.hashCode() == hash && eq(key, e.mappedOID)) {
337                 Object JavaDoc o = e.get();
338                 if (o == null) {
339                     removeImp(e, m_keyTable, i);
340                     break;
341                 }
342                 if (o instanceof PCStateMan) {
343                     return updateSm(value, (PCStateMan) o, key);
344                 } else {
345                     State currentState = (State)o;
346                     if (value == NULLState.NULL_STATE) {
347                         removeImp(e, m_keyTable, i);
348                         return null;
349                     } else {
350                         if (overWriteMode) {
351                             currentState.clear();
352                         }
353                         if (currentState != null) {
354                             value.updateNonFilled(currentState);
355                             if (manage) {
356                                 return e.upgradeToSm(addSm[0] = pm.reManage(key, value), queue);
357                             } else {
358                                 currentState.updateNonFilled(value);
359                                 return null;
360                             }
361                         }
362                     }
363                 }
364             }
365         }
366         if (Debug.DEBUG) validate();
367         if (value == NULLState.NULL_STATE) {
368             //ignore
369
return null;
370         }
371         //add new entry
372
PMCacheEntry ce;
373         PCStateMan sm = null;
374         if (manage) {
375             //create sm and add
376
ce = createCacheKey(sm = addSm[0] = pm.reManage(key, value));
377         } else {
378             //just add the oid-state pair
379
ce = createCacheKey(key, value);
380         }
381         ce.setNext(m_keyTable[i]);
382         m_keyTable[i] = ce;
383         if (size++ >= threshold) resize(2 * m_keyTable.length);
384         if (Debug.DEBUG) validate();
385         return sm;
386     }
387
388     /**
389      * Remove the entry. Note that this is a NOP if it is not in the cache.
390      */

391     private void removeImp(PMCacheEntry e, final PMCacheEntry[] m_keyTable, final int i) {
392         if (Debug.DEBUG) validate();
393         if (m_keyTable[i] == null) return;
394         if (m_keyTable[i] == e) {
395             m_keyTable[i] = e.next;
396             clearCE(e);
397         } else {
398             for (PMCacheEntry ce = m_keyTable[i].next; ce != null; ce = ce.next) {
399                 if (ce == e) {
400                     clearCE(e);
401                     break;
402                 }
403             }
404
405         }
406         if (Debug.DEBUG) validate();
407     }
408
409     private void clearCE(PMCacheEntry e) {
410         e.unlinkNextList();
411         e.clear();
412         size--;
413     }
414
415     /**
416      * This method does not replace the {@link PCStateMan} if the {@link OID} is present.
417      * If the mapping exist it will be verified.
418      * If the mapping does not exist it will be created.
419      */

420     public PCStateMan addSm(OID key, PCStateMan value) {
421         key = key.getAvailableOID();
422         final PMCacheEntry[] m_keyTable = this.m_keyTable;
423         final int hash = key.hashCode();
424         final int i = indexFor(hash, m_keyTable.length);
425
426         for (PMCacheEntry e = m_keyTable[i]; e != null; e = e.next) {
427             if (e.hash == hash && eq(key, e.mappedOID)) {
428                 Object JavaDoc o = e.get();
429                 if (o != null) {
430                     if (o != null && o != value) {
431                         throw BindingSupportImpl.getInstance().internal("Inconsistent mapping for id '" + key.toPkString() + "'");
432                     }
433                     return value;
434                 }
435                 remove(e);
436                 break;
437             }
438         }
439         //add new entry
440
PMCacheEntry ce = value.cacheEntry;
441         if (ce == null) {
442             ce = createCacheKey(value);
443         }
444
445         ce.setNext(m_keyTable[i]);
446         m_keyTable[i] = ce;
447         if (size++ >= threshold) resize(2 * m_keyTable.length);
448
449         if (Debug.DEBUG) validate();
450         return value;
451     }
452
453     public void setInterceptDfgFieldAccess(boolean on) {
454         processReferenceQueueImp();
455
456         PMCacheEntry[] src = m_keyTable;
457         for (int j = 0; j < src.length; j++) {
458             PMCacheEntry e = src[j];
459             while (e != null) {
460                 Object JavaDoc o = e.get();
461                 if (o != null && (o instanceof PCStateMan)) {
462                     ((PCStateMan)o).setInterceptDfgFieldAccess(on);
463                 }
464                 e = e.next;
465             }
466         }
467     }
468
469     public void evict() {
470         PMCacheEntry[] src = m_keyTable;
471         for (int j = 0; j < src.length; j++) {
472             PMCacheEntry e = src[j];
473             while (e != null) {
474                 Object JavaDoc o = e.get();
475                 if (o != null && (o instanceof PCStateMan)) {
476                     ((PCStateMan)o).evict();
477                 }
478                 e = e.next;
479             }
480         }
481         processReferenceQueueImp();
482     }
483
484     /**
485      * Return the state if present. This will not result in the PCStateMan being
486      * created if not currently managed.
487      */

488     public State getStateByOID(OID oid) {
489         Object JavaDoc value = getValueByOid(oid);
490         if (value == null) return null;
491         if (value instanceof PCStateMan) {
492             return ((PCStateMan) value).state;
493         } else {
494             return (State) value;
495         }
496     }
497
498     /**
499      * Do we have a State or PCStateMan for the oid? Note the data may be
500      * evicted at any time depending on the reference type.
501      */

502     public boolean contains(OID oid) {
503         return (getValueByOid(oid) != null);
504     }
505
506     /**
507      * If the sm is already managed then return it. If the oid and state is present
508      * then manage it and return it. Else return null.
509      */

510     public PCStateMan getByOID(OID oid, boolean manage) {
511         PMCacheEntry ce = getByOID(oid.getAvailableOID());
512         if (ce == null) return null;
513         Object JavaDoc value = ce.get();
514         if (value == null) {
515             remove(ce);
516             return null;
517         }
518         if (value instanceof PCStateMan) {
519             return (PCStateMan) value;
520         }
521         if (!manage) return null;
522         return ce.upgradeToSm(pm.reManage(oid, (State)value), queue);
523     }
524
525     /**
526      * If the sm is already managed then return it. If the oid and state is present
527      * then manage it and return it. Else return null. This looks up using the
528      * oid as is and does not assume that realOID has been set.
529      */

530     public PCStateMan getByNewObjectOID(NewObjectOID oid) {
531         PMCacheEntry ce = getByOID(oid);
532         if (ce == null) return null;
533         Object JavaDoc value = ce.get();
534         if (value == null) {
535             remove(ce);
536             return null;
537         }
538         if (value instanceof PCStateMan) {
539             return (PCStateMan) value;
540         }
541         return ce.upgradeToSm(pm.reManage(oid, (State)value), queue);
542     }
543
544     private Object JavaDoc getValueByOid(OID oid) {
545         PMCacheEntry ce = getByOID(oid.getAvailableOID());
546         if (ce == null) return null;
547         Object JavaDoc value = ce.get();
548         if (value == null) {
549             remove(ce);
550             return null;
551         }
552         return value;
553     }
554
555     private PMCacheEntry getByOID(OID oid) {
556         final int hash = oid.hashCode();
557         for (PMCacheEntry e = m_keyTable[indexFor(hash, m_keyTable.length)]; e != null; e = e.next) {
558             if (e.hash == hash && eq(oid, e.mappedOID)) {
559                 return e;
560             }
561         }
562         return null;
563     }
564
565     public void clear() {
566         PMCacheEntry[] src = m_keyTable;
567         for (int j = 0; j < src.length; j++) {
568             PMCacheEntry e = src[j];
569             PMCacheEntry next;
570             while (e != null) {
571                 next = e.next;
572                 e.reset();
573                 e.next = null;
574                 e.prev = null;
575                 e = next;
576             }
577         }
578         processList.clear();
579         size = 0;
580         m_keyTable = new PMCacheEntry[createdSize];
581     }
582
583     public boolean inProcessList(PCStateMan sm) {
584         return processList.contains(sm.cacheEntry);
585     }
586
587     public void remove(PCStateMan sm) {
588         remove(sm.cacheEntry);
589     }
590
591     /**
592      * Remove entry from collection. This must also ensure that the entry
593      * is removed from the processList.
594      */

595     private void remove(PMCacheEntry ce) {
596         if (Debug.DEBUG) validate();
597         int hash = ce.mappedOID.hashCode();
598         int i = indexFor(hash, m_keyTable.length);
599         PMCacheEntry e = m_keyTable[i];
600         for (;e != null; e = e.next) {
601             if (e.hash == hash && eq(ce.mappedOID, e.mappedOID)) {
602                 removeImp(e, m_keyTable, i);
603             }
604         }
605         if (Debug.DEBUG) validate();
606     }
607
608     /**
609      * This will validate the consistency of the cache. Only for debugging
610      */

611     private Set JavaDoc[] validate() {
612         int count = 0;
613 // ObjectHashSet[] sets = {new ObjectHashSet(size, ObjectHashSet.IDENTITY_COMP), new ObjectHashSet(size, ObjectHashSet.IDENTITY_COMP)};
614
PMCacheEntry[] src = m_keyTable;
615         for (int j = 0; j < src.length; j++) {
616             PMCacheEntry e = src[j];
617             while (e != null) {
618                 int index = indexFor(e.mappedOID.hashCode(), m_keyTable.length);
619                 if (index != j) {
620                     throw BindingSupportImpl.getInstance().internal("The entry is not at the correct pos");
621                 }
622 // Object val = e.get();
623
// if (!sets[0].add(e)) {
624
// throw BindingSupportImpl.getInstance().internal("The entry "
625
// + e + " is more than once in the cache");
626
// }
627
// if (val != null) {
628
// if (!sets[1].add(val)) {
629
// throw BindingSupportImpl.getInstance().internal("The value " + val + " is in the cache more than once");
630
// }
631
// }
632
count++;
633                 e = e.next;
634             }
635         }
636
637         if (count != size) {
638             throw BindingSupportImpl.getInstance().internal("The counted size == " + count + " but size is " + size);
639         }
640 // return sets;
641
return null;
642     }
643
644     public void dump() {
645         System.out.println("\n\n\nLocalPMCache.dump: START");
646         PMCacheEntry[] src = m_keyTable;
647         for (int j = 0; j < src.length; j++) {
648             PMCacheEntry e = src[j];
649             boolean first = true;
650             while (e != null) {
651                 if (first) {
652                     System.out.println("j = " + j);
653                     first = false;
654                 }
655                 System.out.println("e = " + e);
656                 e = e.next;
657             }
658         }
659         System.out.println("LocalPMCache.dump: END \n\n\n");
660     }
661
662     private void resize(int newCapacity) {
663         if (Debug.DEBUG) validate();
664         PMCacheEntry[] oldTable = m_keyTable;
665         int oldCapacity = oldTable.length;
666         if (oldCapacity == MAXIMUM_CAPACITY) {
667             threshold = Integer.MAX_VALUE;
668             return;
669         }
670
671         PMCacheEntry[] newTable = new PMCacheEntry[newCapacity];
672         transfer(newTable);
673         m_keyTable = newTable;
674         threshold = (int)(newCapacity * loadFactor);
675         if (Debug.DEBUG) validate();
676     }
677
678     /**
679      * Transfer all entries from current table to newTable.
680      */

681     private void transfer(PMCacheEntry[] newTable) {
682         PMCacheEntry[] src = m_keyTable;
683         int newCapacity = newTable.length;
684         size = 0;
685         for (int j = 0; j < src.length; j++) {
686             PMCacheEntry e = src[j];
687             if (e != null) {
688                 //skip entries with gc'd refs
689
for (;;) {
690                     if (e == null) break;
691                     if (e.get() == null) {
692                         e.clear();
693                         e = e.next;
694                     } else {
695                         break;
696                     }
697                 }
698                 if (e == null) continue;
699                 src[j] = null;
700
701                 do {
702                     size++;
703                     PMCacheEntry next = e.next;
704                     int i = indexFor(e.hash, newCapacity);
705                     e.unlinkNextList();
706                     e.setNext(newTable[i]);
707                     newTable[i] = e;
708                     e = next;
709                 } while (e != null);
710             }
711         }
712     }
713
714     /**
715      * Check for equality of non-null reference x and possibly-null y.
716      */

717     static boolean eq(OID x, OID y) {
718         return x == y || x.equals(y);
719     }
720
721     /**
722      * Returns index for hash code h.
723      */

724     static int indexFor(int h, int length) {
725         return h & (length-1);
726     }
727
728     /**
729      * How many keys are in the cache?
730      */

731     public int size() {
732         return size;
733     }
734
735 }
736
Popular Tags