001: /**
002: * Copyright (C) 2006 NetMind Consulting Bt.
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 3 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package hu.netmind.persistence;
018:
019: import java.util.HashMap;
020: import java.util.Map;
021: import java.util.Iterator;
022: import java.lang.ref.ReferenceQueue;
023: import java.lang.ref.Reference;
024: import java.lang.ref.WeakReference;
025: import java.util.List;
026: import java.util.Vector;
027: import org.apache.log4j.Logger;
028:
029: /**
030: * This is exactly like weak hashmap, but this implementation disregards
031: * the object's <code>equals()</code> and <code>hashCode()</code> methods,
032: * and uses object equality for testing. This means it will only return
033: * an entry, if the object given as key exatcly matches the key in the map.
034: * This is not so trivial, since the <code>hashCode()</code> need not to be
035: * unique between objects, and we can't refer to the object directly
036: * either, because then this wouldn't be a weak map.<br>
037: * <i>Note</i>: Ok, this is not an implementation of Map, if you wish
038: * you can write the necessary methods.
039: * @author Brautigam Robert
040: * @version Revision: $Revision$
041: */
042: public class WeakMap {
043: private static Logger logger = Logger.getLogger(WeakMap.class);
044: private static ProfileLogger profLogger = ProfileLogger.getLogger();
045:
046: private Map objectMap;
047: private Map referenceMap;
048: private ReferenceQueue queue;
049: private WeakMapListener listener = null;
050:
051: public WeakMap() {
052: objectMap = new HashMap();
053: referenceMap = new HashMap();
054: queue = new ReferenceQueue();
055: }
056:
057: public void setListener(WeakMapListener listener) {
058: this .listener = listener;
059: }
060:
061: public WeakMapListener getListener() {
062: return listener;
063: }
064:
065: /**
066: * Put an object with a key to the map.
067: * @param key The object which will be referred weakly, may not be null.
068: * @param value The value to the key.
069: * @param id Some identifier which does not refer to the key.
070: */
071: public void put(Object key, Object value, Object id) {
072: Integer hashCode = new Integer(System.identityHashCode(key));
073: // Clear some entries
074: clear();
075: // Make entries
076: WeakReference ref = new WeakReference(key, queue);
077: List entries = (List) objectMap.get(hashCode);
078: if (entries == null) {
079: entries = new Vector();
080: objectMap.put(hashCode, entries);
081: }
082: entries.add(new Entry(ref, value, id));
083: referenceMap.put(ref, hashCode);
084: }
085:
086: /**
087: * Clear obsolete entries.
088: */
089: private void clear() {
090: // Clear obsolate entries
091: Reference ref = null;
092: while ((ref = queue.poll()) != null) {
093: // Clear that ref's entries
094: Integer hashCode = (Integer) referenceMap.get(ref);
095: referenceMap.remove(ref);
096: List entries = (List) objectMap.get(hashCode);
097: if (entries != null) {
098: // We need to find the entry and clear it
099: Iterator iterator = entries.iterator();
100: boolean deleted = false;
101: while ((iterator.hasNext()) && (!deleted)) {
102: Entry entry = (Entry) iterator.next();
103: if (entry.getReference() == ref) {
104: iterator.remove();
105: deleted = true;
106: // Notify
107: if (getListener() != null)
108: getListener().notifyValueLeave(
109: entry.getId());
110: }
111: }
112: }
113: if (entries.size() == 0)
114: objectMap.remove(hashCode);
115: }
116: // Profile
117: profLogger.profile("weakmap", "Current map sizes: "
118: + objectMap.size() + "," + referenceMap.size());
119: }
120:
121: /**
122: * Get a value for the given key.
123: * @param key The key object.
124: * @return The value for exatcly the given object instance.
125: */
126: public Object get(Object key) {
127: if (key == null)
128: return null;
129: // Clear some entries
130: clear();
131: // After all entries are cleared it is still possible that now the gc
132: // run and objects possibly became gc'd. This is not a problem,
133: // since we're going to use '==' operator. == never lies.
134: Integer hashCode = new Integer(System.identityHashCode(key));
135: List entries = (List) objectMap.get(hashCode);
136: if (entries == null) {
137: logger.debug("weak map did not find object of hashcode: "
138: + hashCode + ". Number of codes in map: "
139: + objectMap.size() + ", key was: " + key);
140: return null;
141: }
142: for (int i = 0; i < entries.size(); i++) {
143: Entry entry = (Entry) entries.get(i);
144: // This is the thing that's making sure the key fits.
145: // We get the referenced object and see if it is ==. Note,
146: // that the referenced object may be gc'd by this time, it does not
147: // matter, it will be null, our key is not.
148: if (entry.getReference().get() == key)
149: return entry.getValue();
150: }
151: logger
152: .debug("weak map did not find object with equality, altough there were "
153: + entries.size()
154: + " objects of same identity hash code: "
155: + hashCode + ", key was: " + key);
156: return null;
157: }
158:
159: public class Entry {
160: private Reference ref;
161: private Object value;
162: private Object id;
163:
164: public Entry(Reference ref, Object value, Object id) {
165: this .ref = ref;
166: this .value = value;
167: this .id = id;
168: }
169:
170: public Reference getReference() {
171: return ref;
172: }
173:
174: public Object getValue() {
175: return value;
176: }
177:
178: public Object getId() {
179: return id;
180: }
181: }
182: }
|