001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package java.util;
006:
007: import com.tc.object.ObjectID;
008: import com.tc.object.SerializationUtil;
009: import com.tc.object.TCObject;
010: import com.tc.object.bytecode.Clearable;
011: import com.tc.object.bytecode.Manageable;
012: import com.tc.object.bytecode.ManagerUtil;
013: import com.tc.object.bytecode.TCMap;
014: import com.tc.object.bytecode.hook.impl.Util;
015: import com.tc.util.Assert;
016:
017: /*
018: * This class will be merged with java.lang.HashMap in the bootjar. This HashMap can store ObjectIDs instead of Objects
019: * to save memory and transparently fault Objects as needed. It can also clear references.
020: */
021: public class HashMapTC extends HashMap implements TCMap, Manageable,
022: Clearable {
023:
024: // General Rules to follow in this class
025: // 1) Values could be ObjectIDs. In shared mode, always do a lookup before returning to outside world.
026: // 2) If you do a lookup, try to store back the actual object into the Map if the key is available.
027: // 3) Be careful about existing iterators. It shouldn't throw exception because of (2)
028: // 4) When you do so, call markAccessed() to make the cache eviction correct
029: // 5) Check write access before any shared changed
030: // 6) Call logical Invoke before changing internal state so that NonPortableExceptions don't modify state.
031: //
032:
033: // TODO:: markAccessed()
034: private volatile transient TCObject $__tc_MANAGED;
035: private boolean evictionEnabled = true;
036:
037: public void clear() {
038: if (__tc_isManaged()) {
039: synchronized (__tc_managed().getResolveLock()) {
040: ManagerUtil.checkWriteAccess(this );
041: ManagerUtil.logicalInvoke(this ,
042: SerializationUtil.CLEAR_SIGNATURE,
043: new Object[0]);
044: super .clear();
045: }
046: } else {
047: super .clear();
048: }
049: }
050:
051: public boolean containsKey(Object key) {
052: // XXX:: Keys can't be ObjectIDs as of Now.
053: if (__tc_isManaged()) {
054: synchronized (__tc_managed().getResolveLock()) {
055: // Just to have proper memory boundary
056: return super .containsKey(key);
057: }
058: } else {
059: return super .containsKey(key);
060: }
061: }
062:
063: /*
064: * This method is overridden in LinkedHashMap. so any change here needs to be propagated to LinkedHashMap too.
065: */
066: public boolean containsValue(Object value) {
067: if (__tc_isManaged()) {
068: synchronized (__tc_managed().getResolveLock()) {
069: if (value != null) {
070: // XXX:: This is tied closely to HashMap implementation which calls equals on the passed value rather than the
071: // other way around
072: return super .containsValue(new ValueWrapper(value));
073: } else {
074: // It is a little weird to do this like this, o well...
075: return super .containsValue(value)
076: || super .containsValue(ObjectID.NULL_ID);
077: }
078: }
079: } else {
080: return super .containsValue(value);
081: }
082: }
083:
084: // XXX: This uses entrySet iterator and since we fix that, this should work.
085: public boolean equals(Object o) {
086: return super .equals(o);
087: }
088:
089: /*
090: * This method is overridden in LinkedHashMapTC. so any change here needs to be propagated to LinkedHashMapTC too.
091: * XXX:: This method uses getEntry instead of a get and put to avoid changing the modCount in shared mode
092: */
093: public Object get(Object key) {
094: if (__tc_isManaged()) {
095: synchronized (__tc_managed().getResolveLock()) {
096: Map.Entry e = getEntry(key);
097: return lookUpAndStoreIfNecessary(e);
098: }
099: } else {
100: return super .get(key);
101: }
102: }
103:
104: private Object lookUpAndStoreIfNecessary(Map.Entry e) {
105: if (e == null)
106: return null;
107: Object value = e.getValue();
108: if (value instanceof ObjectID) {
109: Object newVal = ManagerUtil.lookupObject((ObjectID) value);
110: e.setValue(newVal);
111: return newVal;
112: }
113: return value;
114: }
115:
116: private static Object lookUpIfNecessary(Object o) {
117: if (o instanceof ObjectID) {
118: return ManagerUtil.lookupObject((ObjectID) o);
119: }
120: return o;
121: }
122:
123: private Object lookUpFaultBreadthIfNecessary(Object o) {
124: if (o instanceof ObjectID) {
125: return ManagerUtil.lookupObjectWithParentContext(
126: (ObjectID) o, __tc_managed().getObjectID());
127: }
128: return o;
129: }
130:
131: // XXX: This uses entrySet iterator and since we fix that, this should work.
132: public int hashCode() {
133: return super .hashCode();
134: }
135:
136: public boolean isEmpty() {
137: if (__tc_isManaged()) {
138: synchronized (__tc_managed().getResolveLock()) {
139: // Just to have proper memory boundary
140: return super .isEmpty();
141: }
142: } else {
143: return super .isEmpty();
144: }
145: }
146:
147: public void putAll(Map map) {
148: super .putAll(map);
149: }
150:
151: public Object remove(Object key) {
152: if (__tc_isManaged()) {
153: // Managed Version
154: synchronized (__tc_managed().getResolveLock()) {
155: ManagerUtil.checkWriteAccess(this );
156:
157: Entry entry = removeEntryForKey(key);
158: if (entry == null) {
159: return null;
160: }
161:
162: ManagerUtil
163: .logicalInvoke(
164: this ,
165: SerializationUtil.REMOVE_ENTRY_FOR_KEY_SIGNATURE,
166: new Object[] { entry.getKey() });
167:
168: return lookUpIfNecessary(entry.getValue());
169: }
170: } else {
171: return super .remove(key);
172: }
173: }
174:
175: /**
176: * This method is only to be invoked from the applicator thread. This method does not need to check if the map is
177: * managed as it will always be managed when called by the applicator thread. In addition, this method does not need
178: * to be synchronized under getResolveLock() as the applicator thread is already under the scope of such
179: * synchronization.
180: */
181: public void __tc_applicator_remove(Object key) {
182: super .remove(key);
183: }
184:
185: /**
186: * This method is to be invoked when one needs a remove to get broadcast, but do not want to fault in the value of a
187: * map entry.
188: */
189: public void __tc_remove_logical(Object key) {
190: if (__tc_isManaged()) {
191: // Managed Version
192: synchronized (__tc_managed().getResolveLock()) {
193: ManagerUtil.checkWriteAccess(this );
194:
195: ManagerUtil.checkWriteAccess(this );
196:
197: Entry entry = removeEntryForKey(key);
198: if (entry == null) {
199: return;
200: }
201:
202: ManagerUtil
203: .logicalInvoke(
204: this ,
205: SerializationUtil.REMOVE_ENTRY_FOR_KEY_SIGNATURE,
206: new Object[] { entry.getKey() });
207:
208: return;
209: }
210: } else {
211: super .remove(key);
212: }
213: }
214:
215: public Collection __tc_getAllEntriesSnapshot() {
216: if (__tc_isManaged()) {
217: synchronized (__tc_managed().getResolveLock()) {
218: return __tc_getAllEntriesSnapshotInternal();
219: }
220: } else {
221: return __tc_getAllEntriesSnapshotInternal();
222: }
223: }
224:
225: public synchronized Collection __tc_getAllEntriesSnapshotInternal() {
226: Set entrySet = super .entrySet();
227: return new ArrayList(entrySet);
228: }
229:
230: public Collection __tc_getAllLocalEntriesSnapshot() {
231: if (__tc_isManaged()) {
232: synchronized (__tc_managed().getResolveLock()) {
233: return __tc_getAllLocalEntriesSnapshotInternal();
234: }
235: } else {
236: return __tc_getAllLocalEntriesSnapshotInternal();
237: }
238: }
239:
240: private Collection __tc_getAllLocalEntriesSnapshotInternal() {
241: Set entrySet = super .entrySet();
242: int entrySetSize = entrySet.size();
243: if (entrySetSize == 0) {
244: return Collections.EMPTY_LIST;
245: }
246:
247: Object[] tmp = new Object[entrySetSize];
248: int index = -1;
249: for (Iterator i = entrySet.iterator(); i.hasNext();) {
250: Map.Entry e = (Map.Entry) i.next();
251: if (!(e.getValue() instanceof ObjectID)) {
252: index++;
253: tmp[index] = e;
254: }
255: }
256:
257: if (index < 0) {
258: return Collections.EMPTY_LIST;
259: }
260: Object[] rv = new Object[index + 1];
261: System.arraycopy(tmp, 0, rv, 0, index + 1);
262: return Arrays.asList(rv);
263: }
264:
265: public Object clone() {
266: Manageable clone = (Manageable) super .clone();
267: return Util.fixTCObjectReferenceOfClonedObject(this , clone);
268: }
269:
270: /*
271: * This method needs to call logicalInvoke before modifying the local state to avoid inconsistency when throwing
272: * NonPortableExceptions TODO:: provide special method for the applicator
273: */
274: public Object put(Object key, Object value) {
275: if (__tc_isManaged()) {
276: synchronized (__tc_managed().getResolveLock()) {
277: ManagerUtil.checkWriteAccess(this );
278: // It sucks todo two lookups
279: HashMap.Entry e = getEntry(key);
280: if (e == null) {
281: // New mapping
282: ManagerUtil.logicalInvoke(this ,
283: SerializationUtil.PUT_SIGNATURE,
284: new Object[] { key, value });
285: return lookUpIfNecessary(super .put(key, value));
286: } else {
287: // without this, LinkedHashMap will not function properly
288: e.recordAccess(this );
289:
290: // Replacing old mapping
291: Object old = lookUpIfNecessary(e.getValue());
292: if (value != old) {
293: ManagerUtil.logicalInvoke(this ,
294: SerializationUtil.PUT_SIGNATURE,
295: new Object[] { e.getKey(), value });
296: e.setValue(value);
297: }
298: return old;
299: }
300: }
301: } else {
302: return super .put(key, value);
303: }
304: }
305:
306: /**
307: * This method is only to be invoked from the applicator thread. This method does not need to check if the map is
308: * managed as it will always be managed when called by the applicator thread. In addition, this method does not need
309: * to be synchronized under getResolveLock() as the applicator thread is already under the scope of such
310: * synchronization.
311: */
312: public void __tc_applicator_put(Object key, Object value) {
313: super .put(key, value);
314: }
315:
316: public int size() {
317: if (__tc_isManaged()) {
318: synchronized (__tc_managed().getResolveLock()) {
319: // Just to have proper memory boundary
320: return super .size();
321: }
322: } else {
323: return super .size();
324: }
325: }
326:
327: public String toString() {
328: return super .toString();
329: }
330:
331: public Set keySet() {
332: return new KeySetWrapper(super .keySet());
333: }
334:
335: public Collection values() {
336: return new ValuesCollectionWrapper(super .values());
337: }
338:
339: public Set entrySet() {
340: return nonOverridableEntrySet();
341: }
342:
343: private final Set nonOverridableEntrySet() {
344: return new EntrySetWrapper(super .entrySet());
345: }
346:
347: /**
348: * Clearable interface - called by CacheManager thru TCObjectLogical
349: */
350: public int __tc_clearReferences(int toClear) {
351: if (!__tc_isManaged()) {
352: throw new AssertionError(
353: "clearReferences() called on Unmanaged Map");
354: }
355: synchronized (__tc_managed().getResolveLock()) {
356: int cleared = 0;
357: for (Iterator i = super .entrySet().iterator(); i.hasNext()
358: && toClear > cleared;) {
359: Map.Entry e = (Map.Entry) i.next();
360: if (e.getValue() instanceof Manageable) {
361: Manageable m = (Manageable) e.getValue();
362: TCObject tcObject = m.__tc_managed();
363: if (tcObject != null
364: && !tcObject.recentlyAccessed()) {
365: e.setValue(tcObject.getObjectID());
366: cleared++;
367: }
368: }
369: }
370: return cleared;
371: }
372: }
373:
374: public boolean isEvictionEnabled() {
375: return evictionEnabled;
376: }
377:
378: public void setEvictionEnabled(boolean enabled) {
379: evictionEnabled = enabled;
380: }
381:
382: public void __tc_managed(TCObject tcObject) {
383: $__tc_MANAGED = tcObject;
384: }
385:
386: public TCObject __tc_managed() {
387: return $__tc_MANAGED;
388: }
389:
390: public boolean __tc_isManaged() {
391: // TCObject tcManaged = $__tc_MANAGED;
392: // return (tcManaged != null && (tcManaged instanceof TCObjectPhysical || tcManaged instanceof TCObjectLogical));
393: return $__tc_MANAGED != null;
394: }
395:
396: /*
397: * This wrapper depends on the fact that key.equals() gets called on the wrapper instead of the other way around
398: */
399: static class ValueWrapper {
400:
401: private final Object value;
402:
403: public ValueWrapper(Object value) {
404: this .value = value;
405: }
406:
407: public int hashCode() {
408: return value.hashCode();
409: }
410:
411: public boolean equals(Object o) {
412: Object pojo = lookUpIfNecessary(o); // XXX:: This is not stored in the Map since we dont know the key
413: return pojo == value || value.equals(pojo);
414: }
415: }
416:
417: private class EntryWrapper implements Map.Entry {
418:
419: private final Map.Entry entry;
420:
421: public EntryWrapper(Map.Entry entry) {
422: this .entry = entry;
423: }
424:
425: public Object getKey() {
426: if (__tc_isManaged()) {
427: synchronized (__tc_managed().getResolveLock()) {
428: return entry.getKey();
429: }
430: } else {
431: return entry.getKey();
432: }
433: }
434:
435: // XXX:: This method has the side effect of looking up the object and setting the value in the Managed case.
436: public Object getValue() {
437: if (__tc_isManaged()) {
438: synchronized (__tc_managed().getResolveLock()) {
439: Object value = lookUpIfNecessary(entry.getValue());
440: if (entry.getValue() != value) {
441: entry.setValue(value);
442: }
443: return value;
444: }
445: } else {
446: return entry.getValue();
447: }
448: }
449:
450: // This method not only does a faulting on this value, but depending on the fault depth, it faults peer objects too.
451: public Object getValueFaultBreadth() {
452: Assert.assertFalse(entry instanceof EntryWrapper);
453: if (__tc_isManaged()) {
454: synchronized (__tc_managed().getResolveLock()) {
455: Object value = lookUpFaultBreadthIfNecessary(entry
456: .getValue());
457: if (entry.getValue() != value) {
458: entry.setValue(value);
459: }
460: return value;
461: }
462: } else {
463: return entry.getValue();
464: }
465: }
466:
467: /*
468: * Even though we do a lookup of oldVal after we change the value in the transaction, DGC will not be able to kick
469: * the oldVal out since the transaction is not committed.
470: */
471: public Object setValue(Object value) {
472: if (__tc_isManaged()) {
473: synchronized (__tc_managed().getResolveLock()) {
474: ManagerUtil.checkWriteAccess(HashMapTC.this );
475: ManagerUtil.logicalInvoke(HashMapTC.this ,
476: SerializationUtil.PUT_SIGNATURE,
477: new Object[] { entry.getKey(), value });
478: Object oldVal = entry.setValue(value);
479: return lookUpIfNecessary(oldVal);
480: }
481: } else {
482: return entry.setValue(value);
483: }
484: }
485:
486: public boolean equals(Object o) {
487: if (__tc_isManaged()) {
488: synchronized (__tc_managed().getResolveLock()) {
489: // XXX:: make sure value is lookedup
490: getValue();
491: return entry.equals(o);
492: }
493: } else {
494: return entry.equals(o);
495: }
496: }
497:
498: public int hashCode() {
499: if (__tc_isManaged()) {
500: synchronized (__tc_managed().getResolveLock()) {
501: // XXX:: make sure value is lookedup
502: getValue();
503: return entry.hashCode();
504: }
505: } else {
506: return entry.hashCode();
507: }
508: }
509:
510: }
511:
512: private class EntrySetWrapper extends AbstractSet {
513:
514: private final Set entries;
515:
516: public EntrySetWrapper(Set entries) {
517: this .entries = entries;
518: }
519:
520: public void clear() {
521: HashMapTC.this .clear();
522: }
523:
524: // Has to take care of ObjectIDs
525: public boolean contains(Object o) {
526: if (__tc_isManaged()) {
527: synchronized (__tc_managed().getResolveLock()) {
528: if (!(o instanceof Map.Entry))
529: return false;
530: Map.Entry e = (Map.Entry) o;
531: Object key = e.getKey();
532: if (!HashMapTC.this .containsKey(key)) {
533: return false;
534: }
535: Object value = HashMapTC.this .get(key);
536: return value == e.getValue()
537: || (value != null && value.equals(e
538: .getValue()));
539: }
540: } else {
541: return entries.contains(o);
542: }
543: }
544:
545: public Iterator iterator() {
546: return new EntriesIterator(entries.iterator());
547: }
548:
549: public boolean remove(Object o) {
550: if (__tc_isManaged()) {
551: synchronized (__tc_managed().getResolveLock()) {
552: if (!(o instanceof Map.Entry))
553: return false;
554: Map.Entry e = (Map.Entry) o;
555: Object key = e.getKey();
556: int sizeB4 = size();
557: HashMapTC.this .remove(key);
558: return (sizeB4 != size());
559: }
560: } else {
561: return entries.remove(o);
562: }
563: }
564:
565: public int size() {
566: return HashMapTC.this .size();
567: }
568:
569: }
570:
571: // These wrapper object are needed only for giving proper memory boundary to size() calls.
572: private class KeySetWrapper extends AbstractSet {
573:
574: private final Set _keySet;
575:
576: public KeySetWrapper(Set keySet) {
577: this ._keySet = keySet;
578: }
579:
580: public void clear() {
581: HashMapTC.this .clear();
582: }
583:
584: public boolean contains(Object o) {
585: if (__tc_isManaged()) {
586: synchronized (__tc_managed().getResolveLock()) {
587: return _keySet.contains(o);
588: }
589: } else {
590: return _keySet.contains(o);
591: }
592: }
593:
594: public Iterator iterator() {
595: return new KeysIterator(HashMapTC.this
596: .nonOverridableEntrySet().iterator());
597: }
598:
599: public boolean remove(Object o) {
600: if (__tc_isManaged()) {
601: synchronized (__tc_managed().getResolveLock()) {
602: // Managed version
603: int sizeB4 = size();
604: HashMapTC.this .remove(o);
605: return (size() != sizeB4);
606: }
607: } else {
608: return _keySet.remove(o);
609: }
610: }
611:
612: public int size() {
613: return HashMapTC.this .size();
614: }
615:
616: }
617:
618: private class ValuesCollectionWrapper extends AbstractCollection {
619:
620: private final Collection _values;
621:
622: public ValuesCollectionWrapper(Collection values) {
623: this ._values = values;
624: }
625:
626: public void clear() {
627: HashMapTC.this .clear();
628: }
629:
630: public boolean contains(Object o) {
631: if (__tc_isManaged()) {
632: synchronized (__tc_managed().getResolveLock()) {
633: // Managed version
634: if (o != null) {
635: return _values.contains(new ValueWrapper(o));
636: } else {
637: return _values.contains(o);
638: }
639: }
640: } else {
641: return _values.contains(o);
642: }
643: }
644:
645: public Iterator iterator() {
646: return new ValuesIterator(HashMapTC.this
647: .nonOverridableEntrySet().iterator());
648: }
649:
650: public int size() {
651: return HashMapTC.this .size();
652: }
653:
654: }
655:
656: private class EntriesIterator implements Iterator {
657:
658: private final Iterator iterator;
659: private Map.Entry currentEntry;
660:
661: public EntriesIterator(Iterator iterator) {
662: this .iterator = iterator;
663: }
664:
665: public boolean hasNext() {
666: if (__tc_isManaged()) {
667: synchronized (__tc_managed().getResolveLock()) {
668: return iterator.hasNext();
669: }
670: } else {
671: return iterator.hasNext();
672: }
673: }
674:
675: public Object next() {
676: currentEntry = nextEntry();
677: if (currentEntry instanceof EntryWrapper) {
678: // This check is here since this class is extended by ValuesIterator too.
679: return currentEntry;
680: } else {
681: return new EntryWrapper(currentEntry);
682: }
683: }
684:
685: protected Map.Entry nextEntry() {
686: if (__tc_isManaged()) {
687: synchronized (__tc_managed().getResolveLock()) {
688: return (Map.Entry) iterator.next();
689: }
690: } else {
691: return (Map.Entry) iterator.next();
692: }
693: }
694:
695: public void remove() {
696: if (__tc_isManaged()) {
697: synchronized (__tc_managed().getResolveLock()) {
698: ManagerUtil.checkWriteAccess(HashMapTC.this );
699: iterator.remove();
700: ManagerUtil
701: .logicalInvoke(
702: HashMapTC.this ,
703: SerializationUtil.REMOVE_ENTRY_FOR_KEY_SIGNATURE,
704: new Object[] { currentEntry
705: .getKey() });
706: }
707: } else {
708: iterator.remove();
709: }
710: }
711: }
712:
713: private class KeysIterator extends EntriesIterator {
714:
715: public KeysIterator(Iterator iterator) {
716: super (iterator);
717: }
718:
719: public Object next() {
720: Map.Entry e = (Map.Entry) super .next();
721: return e.getKey();
722: }
723: }
724:
725: private class ValuesIterator extends EntriesIterator {
726:
727: public ValuesIterator(Iterator iterator) {
728: super (iterator);
729: }
730:
731: public Object next() {
732: Map.Entry e = (Map.Entry) super .next();
733: if (e instanceof EntryWrapper) {
734: EntryWrapper ew = (EntryWrapper) e;
735: return ew.getValueFaultBreadth();
736: }
737: return e.getValue();
738: }
739:
740: }
741: }
|