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.TCObject;
009: import com.tc.object.bytecode.Clearable;
010: import com.tc.object.bytecode.Manageable;
011: import com.tc.object.bytecode.ManagerUtil;
012: import com.tc.object.bytecode.TCMap;
013: import com.tc.object.bytecode.hook.impl.Util;
014:
015: import java.util.Collections.SynchronizedCollection;
016: import java.util.Collections.SynchronizedSet;
017: import java.util.Map.Entry;
018:
019: /*
020: * This class will be merged with java.lang.Hashtable in the bootjar. This hashtable can store ObjectIDs instead of
021: * Objects to save memory and transparently fault Objects as needed. It can also clear references. For General rules
022: *
023: * @see HashMapTC class
024: */
025: public class HashtableTC extends Hashtable implements TCMap,
026: Manageable, Clearable {
027:
028: private volatile transient TCObject $__tc_MANAGED;
029: private boolean evictionEnabled = true;
030:
031: public synchronized void clear() {
032: if (__tc_isManaged()) {
033: ManagerUtil.checkWriteAccess(this );
034: ManagerUtil.logicalInvoke(this , "clear()V", new Object[0]);
035: }
036: super .clear();
037: }
038:
039: public synchronized Object clone() {
040: if (__tc_isManaged()) {
041: Hashtable clone = new Hashtable(this );
042:
043: // This call to fixTCObjectReference isn't strictly required, but if someone every changes
044: // this method to actually use any built-in clone mechanism, it will be needed -- better safe than sorry here
045: return Util.fixTCObjectReferenceOfClonedObject(this , clone);
046: }
047:
048: return super .clone();
049: }
050:
051: // Values that contains ObjectIDs are already wrapped, so this should be fine
052: public synchronized boolean contains(Object value) {
053: return super .contains(value);
054: }
055:
056: // XXX:: Keys can't be ObjectIDs as of Now.
057: public synchronized boolean containsKey(Object key) {
058: return super .containsKey(key);
059: }
060:
061: public boolean containsValue(Object value) {
062: return super .containsValue(value);
063: }
064:
065: public synchronized boolean equals(Object o) {
066: return super .equals(o);
067: }
068:
069: /*
070: * This method uses __tc_getEntry() instead of a get() and put() to avoid changing the modCount in shared mode
071: */
072: public synchronized Object get(Object key) {
073: if (__tc_isManaged()) {
074: Map.Entry e = __tc_getEntry(key);
075: if (e == null)
076: return null;
077: Object value = e.getValue();
078: Object actualValue = unwrapValueIfNecessary(value);
079: if (actualValue != value) {
080: e.setValue(actualValue);
081: }
082: return actualValue;
083: } else {
084: return super .get(key);
085: }
086: }
087:
088: public synchronized int hashCode() {
089: return super .hashCode();
090: }
091:
092: public synchronized boolean isEmpty() {
093: return super .isEmpty();
094: }
095:
096: /*
097: * This method needs to call logicalInvoke before modifying the local state to avoid inconsistency when throwing
098: * NonPortableExceptions TODO:: provide special method for the applicator
099: */
100: public synchronized Object put(Object key, Object value) {
101: if (__tc_isManaged()) {
102: if (key == null || value == null) {
103: throw new NullPointerException();
104: }
105: ManagerUtil.checkWriteAccess(this );
106: Entry e = __tc_getEntry(key);
107: if (e == null) {
108: // New mapping
109: ManagerUtil
110: .logicalInvoke(
111: this ,
112: "put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
113: new Object[] { key, value });
114: // Sucks to do a second lookup !!
115: return unwrapValueIfNecessary(super .put(key,
116: wrapValueIfNecessary(value)));
117: } else {
118: Object old = unwrapValueIfNecessary(e.getValue());
119: if (old != value) {
120: ManagerUtil
121: .logicalInvoke(
122: this ,
123: "put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
124: new Object[] { e.getKey(), value });
125: e.setValue(wrapValueIfNecessary(value));
126: }
127: return old;
128: }
129: } else {
130: return super .put(key, value);
131: }
132: }
133:
134: /**
135: * This method is only to be invoked from the applicator thread. This method does not need to check if the map is
136: * managed as it will always be managed when called by the applicator thread. In addition, this method does not need
137: * to be synchronized under getResolveLock() as the applicator thread is already under the scope of such
138: * synchronization.
139: */
140: public synchronized void __tc_applicator_put(Object key,
141: Object value) {
142: if (key == null || value == null) {
143: throw new NullPointerException();
144: }
145: super .put(key, wrapValueIfNecessary(value));
146: }
147:
148: private static Object unwrapValueIfNecessary(Object value) {
149: if (value instanceof ValuesWrapper) {
150: return ((ValuesWrapper) value).getValue();
151: } else {
152: return value;
153: }
154: }
155:
156: private static Object unwrapValueIfNecessaryFaultBreadth(
157: Object value, ObjectID parentContext) {
158: if (value instanceof ValuesWrapper) {
159: return ((ValuesWrapper) value)
160: .getValueFaultBreadth(parentContext);
161: } else {
162: return value;
163: }
164: }
165:
166: private static Object wrapValueIfNecessary(Object value) {
167: if (value instanceof ObjectID) {
168: // value cant be NULL_ID as Hashtable doesnt handle null !
169: return new ValuesWrapper(value);
170: } else {
171: return value;
172: }
173: }
174:
175: public synchronized void putAll(Map arg0) {
176: super .putAll(arg0);
177: }
178:
179: public synchronized Object remove(Object key) {
180: if (__tc_isManaged()) {
181: ManagerUtil.checkWriteAccess(this );
182:
183: Entry entry = __tc_removeEntryForKey(key);
184: if (entry == null) {
185: return null;
186: }
187:
188: Object rv = unwrapValueIfNecessary(entry.getValue());
189:
190: ManagerUtil.logicalInvoke(this ,
191: "remove(Ljava/lang/Object;)Ljava/lang/Object;",
192: new Object[] { entry.getKey() });
193:
194: return rv;
195: } else {
196: return super .remove(key);
197: }
198: }
199:
200: /**
201: * This method is only to be invoked from the applicator thread. This method does not need to check if the map is
202: * managed as it will always be managed when called by the applicator thread. In addition, this method does not need
203: * to be synchronized under getResolveLock() as the applicator thread is already under the scope of such
204: * synchronization.
205: */
206: public synchronized void __tc_applicator_remove(Object key) {
207: super .remove(key);
208: }
209:
210: public synchronized void __tc_remove_logical(Object key) {
211: if (__tc_isManaged()) {
212: ManagerUtil.checkWriteAccess(this );
213:
214: Entry entry = __tc_removeEntryForKey(key);
215: if (entry == null) {
216: return;
217: }
218:
219: ManagerUtil.logicalInvoke(this ,
220: "remove(Ljava/lang/Object;)Ljava/lang/Object;",
221: new Object[] { entry.getKey() });
222:
223: } else {
224: super .remove(key);
225: }
226: }
227:
228: public synchronized Collection __tc_getAllEntriesSnapshot() {
229: Set entrySet = super .entrySet();
230: return new ArrayList(entrySet);
231: }
232:
233: public synchronized Collection __tc_getAllLocalEntriesSnapshot() {
234: Set entrySet = super .entrySet();
235: int entrySetSize = entrySet.size();
236: if (entrySetSize == 0) {
237: return Collections.EMPTY_LIST;
238: }
239:
240: Object[] tmp = new Object[entrySetSize];
241: int index = -1;
242: for (Iterator i = entrySet.iterator(); i.hasNext();) {
243: Map.Entry e = (Map.Entry) i.next();
244: if (!(e.getValue() instanceof ValuesWrapper)) {
245: index++;
246: tmp[index] = e;
247: }
248: }
249:
250: if (index < 0) {
251: return Collections.EMPTY_LIST;
252: }
253: Object[] rv = new Object[index + 1];
254: System.arraycopy(tmp, 0, rv, 0, index + 1);
255: return Arrays.asList(rv);
256: }
257:
258: public synchronized int size() {
259: return super .size();
260: }
261:
262: public synchronized String toString() {
263: return super .toString();
264: }
265:
266: public synchronized Enumeration keys() {
267: return new EnumerationWrapper(super .keys());
268: }
269:
270: public Set keySet() {
271: Collections.SynchronizedSet ss = (SynchronizedSet) super
272: .keySet();
273: return Collections.synchronizedSet(
274: new KeySetWrapper((Set) ss.c), ss.mutex);
275: }
276:
277: public synchronized Enumeration elements() {
278: return new EnumerationWrapper(super .elements());
279: }
280:
281: public Set entrySet() {
282: return nonOverridableEntrySet();
283: }
284:
285: private Set nonOverridableEntrySet() {
286: Collections.SynchronizedSet ss = (SynchronizedSet) super
287: .entrySet();
288: return Collections.synchronizedSet(new EntrySetWrapper(
289: (Set) ss.c), ss.mutex);
290: }
291:
292: public Collection values() {
293: Collections.SynchronizedCollection sc = (SynchronizedCollection) super
294: .values();
295: return Collections.synchronizedCollection(
296: new ValuesCollectionWrapper(sc.c), sc.mutex);
297: }
298:
299: /**
300: * Clearable interface - called by CacheManager thru TCObjectLogical
301: */
302: public synchronized int __tc_clearReferences(int toClear) {
303: if (!__tc_isManaged()) {
304: throw new AssertionError(
305: "clearReferences() called on Unmanaged Map");
306: }
307: int cleared = 0;
308: for (Iterator i = super .entrySet().iterator(); i.hasNext()
309: && toClear > cleared;) {
310: Map.Entry e = (Map.Entry) i.next();
311: if (e.getValue() instanceof Manageable) {
312: Manageable m = (Manageable) e.getValue();
313: TCObject tcObject = m.__tc_managed();
314: if (tcObject != null && !tcObject.recentlyAccessed()) {
315: e.setValue(wrapValueIfNecessary(tcObject
316: .getObjectID()));
317: cleared++;
318: }
319: }
320: }
321: return cleared;
322: }
323:
324: public boolean isEvictionEnabled() {
325: return evictionEnabled;
326: }
327:
328: public void setEvictionEnabled(boolean enabled) {
329: evictionEnabled = enabled;
330: }
331:
332: public void __tc_managed(TCObject tcObject) {
333: $__tc_MANAGED = tcObject;
334: }
335:
336: public TCObject __tc_managed() {
337: return $__tc_MANAGED;
338: }
339:
340: public boolean __tc_isManaged() {
341: // TCObject tcManaged = $__tc_MANAGED;
342: // return (tcManaged != null && (tcManaged instanceof TCObjectPhysical || tcManaged instanceof TCObjectLogical));
343: return $__tc_MANAGED != null;
344: }
345:
346: protected Map.Entry __tc_getEntry(Object key) {
347: // This method is instrumented during bootjar creation into the vanilla (which gets tainted) java.util.Hashtable.
348: // This is needed so that we can easily get access to the Original Key on put without a traversal or proxy Keys.
349: throw new RuntimeException(
350: "This should never execute! Check BootJarTool");
351: }
352:
353: protected Map.Entry __tc_removeEntryForKey(Object key) {
354: // This method is instrumented during bootjar creation into the vanilla (which gets tainted) java.util.Hashtable.
355: throw new RuntimeException(
356: "This should never execute! Check BootJarTool");
357: }
358:
359: private static class ValuesWrapper {
360:
361: private Object value;
362:
363: public ValuesWrapper(Object value) {
364: this .value = value;
365: }
366:
367: public boolean equals(Object obj) {
368: return getValue().equals(obj);
369: }
370:
371: Object getValue() {
372: if (value instanceof ObjectID) {
373: value = ManagerUtil.lookupObject((ObjectID) value);
374: }
375: return value;
376: }
377:
378: public Object getValueFaultBreadth(ObjectID parentContext) {
379: if (value instanceof ObjectID) {
380: value = ManagerUtil.lookupObjectWithParentContext(
381: (ObjectID) value, parentContext);
382: }
383: return value;
384: }
385:
386: public int hashCode() {
387: return getValue().hashCode();
388: }
389:
390: public String toString() {
391: return getValue().toString();
392: }
393: }
394:
395: private class EntryWrapper implements Map.Entry {
396:
397: private final Entry entry;
398:
399: public EntryWrapper(Entry entry) {
400: this .entry = entry;
401: }
402:
403: public boolean equals(Object o) {
404: if (__tc_isManaged()) {
405: synchronized (HashtableTC.this ) {
406: return entry.equals(o);
407: }
408: } else {
409: return entry.equals(o);
410: }
411: }
412:
413: public Object getKey() {
414: if (__tc_isManaged()) {
415: synchronized (HashtableTC.this ) {
416: return entry.getKey();
417: }
418: } else {
419: return entry.getKey();
420: }
421: }
422:
423: public Object getValue() {
424: if (__tc_isManaged()) {
425: synchronized (HashtableTC.this ) {
426: return unwrapValueIfNecessary(entry.getValue());
427: }
428: } else {
429: return entry.getValue();
430: }
431: }
432:
433: public Object getValueFaultBreadth() {
434: if (__tc_isManaged()) {
435: synchronized (HashtableTC.this ) {
436: return unwrapValueIfNecessaryFaultBreadth(entry
437: .getValue(), __tc_managed().getObjectID());
438: }
439: } else {
440: return entry.getValue();
441: }
442: }
443:
444: public int hashCode() {
445: if (__tc_isManaged()) {
446: synchronized (HashtableTC.this ) {
447: return entry.hashCode();
448: }
449: } else {
450: return entry.hashCode();
451: }
452: }
453:
454: public Object setValue(Object value) {
455: if (__tc_isManaged()) {
456: synchronized (HashtableTC.this ) {
457: // This check is done to solve the chicken and egg problem. Should I modify the local copy or the remote copy
458: // ? (both has error checks that we want to take place before any modification is propagated
459: if (value == null)
460: throw new NullPointerException();
461: ManagerUtil.checkWriteAccess(HashtableTC.this );
462: ManagerUtil
463: .logicalInvoke(
464: HashtableTC.this ,
465: "put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
466: new Object[] { getKey(), value });
467: return unwrapValueIfNecessary(entry.setValue(value));
468: }
469: } else {
470: return entry.setValue(value);
471: }
472: }
473: }
474:
475: private class EntrySetWrapper extends AbstractSet {
476:
477: private final Set entrySet;
478:
479: public EntrySetWrapper(Set entrySet) {
480: this .entrySet = entrySet;
481: }
482:
483: public boolean add(Object arg0) {
484: return entrySet.add(arg0);
485: }
486:
487: public void clear() {
488: // XXX:: Calls Hashtable.clear()
489: entrySet.clear();
490: }
491:
492: public boolean contains(Object o) {
493: return entrySet.contains(o);
494: }
495:
496: public Iterator iterator() {
497: return new EntriesIterator(entrySet.iterator());
498: }
499:
500: public boolean remove(Object o) {
501:
502: if (__tc_isManaged()) {
503: synchronized (HashtableTC.this ) {
504: ManagerUtil.checkWriteAccess(HashtableTC.this );
505:
506: if (!(o instanceof Map.Entry)) {
507: return false;
508: }
509:
510: Entry entryToRemove = (Entry) o;
511:
512: Entry entry = __tc_removeEntryForKey(entryToRemove
513: .getKey());
514: if (entry == null) {
515: return false;
516: }
517:
518: ManagerUtil
519: .logicalInvoke(
520: HashtableTC.this ,
521: "remove(Ljava/lang/Object;)Ljava/lang/Object;",
522: new Object[] { entry.getKey() });
523: return true;
524: }
525: } else {
526: return entrySet.remove(o);
527: }
528: }
529:
530: public int size() {
531: return entrySet.size();
532: }
533: }
534:
535: private class KeySetWrapper extends AbstractSet {
536:
537: private final Set keys;
538:
539: public KeySetWrapper(Set keys) {
540: this .keys = keys;
541: }
542:
543: public void clear() {
544: keys.clear();
545: }
546:
547: public boolean contains(Object o) {
548: return keys.contains(o);
549: }
550:
551: public Iterator iterator() {
552: return new KeysIterator(nonOverridableEntrySet().iterator());
553: }
554:
555: // XXX:: Calls Hashtable.remove();
556: public boolean remove(Object o) {
557: return keys.remove(o);
558: }
559:
560: public int size() {
561: return keys.size();
562: }
563:
564: }
565:
566: private class ValuesCollectionWrapper extends AbstractCollection {
567:
568: private final Collection values;
569:
570: public ValuesCollectionWrapper(Collection values) {
571: this .values = values;
572: }
573:
574: // XXX:: Calls Hashtable.clear();
575: public void clear() {
576: values.clear();
577: }
578:
579: // XXX:: Calls Hashtable.containsValue();
580: public boolean contains(Object o) {
581: return values.contains(o);
582: }
583:
584: public Iterator iterator() {
585: return new ValuesIterator(nonOverridableEntrySet()
586: .iterator());
587: }
588:
589: public int size() {
590: return values.size();
591: }
592:
593: }
594:
595: // Hashtable Iterator doesnt synchronize access to the table !!
596: private class EntriesIterator implements Iterator {
597:
598: private final Iterator entries;
599: private Map.Entry currentEntry;
600:
601: public EntriesIterator(Iterator entries) {
602: this .entries = entries;
603: }
604:
605: public boolean hasNext() {
606: if (__tc_isManaged()) {
607: synchronized (HashtableTC.this ) {
608: return entries.hasNext();
609: }
610: } else {
611: return entries.hasNext();
612: }
613: }
614:
615: public Object next() {
616: currentEntry = nextEntry();
617: if (currentEntry instanceof EntryWrapper) {
618: // This check is here since this class is extended by ValuesIterator too.
619: return currentEntry;
620: } else {
621: return new EntryWrapper(currentEntry);
622: }
623: }
624:
625: protected Map.Entry nextEntry() {
626: if (__tc_isManaged()) {
627: synchronized (HashtableTC.this ) {
628: return (Map.Entry) entries.next();
629: }
630: } else {
631: return (Map.Entry) entries.next();
632: }
633: }
634:
635: public void remove() {
636: if (__tc_isManaged()) {
637: synchronized (HashtableTC.this ) {
638: ManagerUtil.checkWriteAccess(HashtableTC.this );
639: entries.remove();
640: ManagerUtil
641: .logicalInvoke(
642: HashtableTC.this ,
643: "remove(Ljava/lang/Object;)Ljava/lang/Object;",
644: new Object[] { currentEntry
645: .getKey() });
646: }
647: } else {
648: entries.remove();
649: }
650: }
651: }
652:
653: private class KeysIterator extends EntriesIterator {
654:
655: public KeysIterator(Iterator entries) {
656: super (entries);
657: }
658:
659: public Object next() {
660: Map.Entry e = (Map.Entry) super .next();
661: return e.getKey();
662: }
663: }
664:
665: private class ValuesIterator extends EntriesIterator {
666:
667: public ValuesIterator(Iterator entries) {
668: super (entries);
669: }
670:
671: public Object next() {
672: Map.Entry e = (Map.Entry) super .next();
673: if (e instanceof EntryWrapper) {
674: EntryWrapper ew = (EntryWrapper) e;
675: return ew.getValueFaultBreadth();
676: }
677: return e.getValue();
678: }
679: }
680:
681: private class EnumerationWrapper implements Enumeration {
682:
683: private final Enumeration enumeration;
684:
685: public EnumerationWrapper(Enumeration enumeration) {
686: this .enumeration = enumeration;
687: }
688:
689: public boolean hasMoreElements() {
690: if (__tc_isManaged()) {
691: synchronized (HashtableTC.this ) {
692: return enumeration.hasMoreElements();
693: }
694: } else {
695: return enumeration.hasMoreElements();
696: }
697: }
698:
699: public Object nextElement() {
700: if (__tc_isManaged()) {
701: synchronized (HashtableTC.this ) {
702: // XXX:: This is done for both keys and values, for keys it has no effect
703: return unwrapValueIfNecessary(enumeration
704: .nextElement());
705: }
706: } else {
707: return enumeration.nextElement();
708: }
709: }
710: }
711: }
|