1 package org.sourceforge.jemm.client.shared;
2
3 import java.lang.ref.Reference;
4 import java.lang.ref.ReferenceQueue;
5 import java.lang.ref.WeakReference;
6 import java.util.Map;
7 import java.util.concurrent.ConcurrentHashMap;
8
9 import org.sourceforge.jemm.util.managed.ECMAction;
10 import org.sourceforge.jemm.util.managed.ECMEntry;
11 import org.sourceforge.jemm.util.managed.EnhancedConcurrentHashMap;
12
13 public abstract class WeakSingletonFactory<K,V> {
14
15 protected final EnhancedConcurrentHashMap<K, WeakReference<V>> keysToWeakRefs;
16 protected final Map<WeakReference<V>,K> refsToKeys;
17 protected final ReferenceQueue<V> queue;
18 protected final Thread queueListener;
19
20 public WeakSingletonFactory() {
21 keysToWeakRefs = new EnhancedConcurrentHashMap<K, WeakReference<V>>();
22 refsToKeys = new ConcurrentHashMap<WeakReference<V>,K>();
23
24 queue = new ReferenceQueue<V>();
25 queueListener = new Thread(new QueueListener(),"WeakSingletonQueueListener");
26 queueListener.setDaemon(true);
27 queueListener.start();
28 }
29
30 class RemoveExpiredAction implements ECMAction<K, WeakReference<V>> {
31
32 boolean cleared;
33 @Override public void performAction(ECMEntry<K, WeakReference<V>> entry) {
34 WeakReference<V> ref = entry.getValue();
35 if(ref == null) {
36 cleared = false;
37 } else if(ref.get() == null) {
38 entry.setValue(null);
39 entry.markAsDead();
40 cleared = true;
41 } else
42 cleared = false;
43 }
44
45 }
46
47 public void put(K key,V value) {
48 WeakReference<V> reference = new WeakReference<V>(value,queue);
49 refsToKeys.put(reference, key);
50 keysToWeakRefs.put(key, reference);
51 }
52
53 void removeExpired(Reference<V> ref) {
54
55 K unreferencedValue = refsToKeys.remove(ref);
56 RemoveExpiredAction action = new RemoveExpiredAction();
57 keysToWeakRefs.perform(unreferencedValue, action);
58
59 if(action.cleared)
60 notifyExpired(unreferencedValue);
61 }
62
63 class ContainsAction implements ECMAction<K, WeakReference<V>> {
64 boolean result;
65
66 @Override public void performAction(ECMEntry<K, WeakReference<V>> entry) {
67 WeakReference<V> ref = entry.getValue();
68 result = ref != null && ref.get() != null;
69 }
70
71 }
72
73 public boolean contains(final K key) {
74
75 ContainsAction c = new ContainsAction();
76 keysToWeakRefs.perform(key, c);
77 return c.result;
78 }
79
80 public int size() {
81 return keysToWeakRefs.size();
82 }
83
84 protected abstract void notifyExpired(K k);
85
86 public V get(K k) {
87 WeakReference<V> trackedReference = keysToWeakRefs.get(k);
88 if(trackedReference == null)
89 return null;
90 else
91 return trackedReference.get();
92 }
93
94 public synchronized void remove(K key) {
95 WeakReference<V> ref = keysToWeakRefs.remove(key);
96 if(ref != null)
97 refsToKeys.remove(ref);
98 }
99
100
101
102
103 public void shutdown() {
104 queueListener.interrupt();
105 }
106
107 class GetOrCreateAction implements ECMAction<K, WeakReference<V>> {
108 V resultValue;
109
110 @Override public void performAction(ECMEntry<K, WeakReference<V>> entry) {
111 WeakReference<V> ref = entry.getValue();
112 V value = null;
113 if(ref != null)
114 value = ref.get();
115
116 if(value == null) {
117 value = createValue(entry.getKey());
118 WeakReference<V> reference = new WeakReference<V>(value,queue);
119 refsToKeys.put(reference, entry.getKey());
120 entry.setValue(reference);
121 }
122
123 resultValue = value;
124 }
125 }
126
127 public V create(K k) {
128 GetOrCreateAction action = new GetOrCreateAction();
129 keysToWeakRefs.perform(k, action);
130 return action.resultValue;
131 }
132
133 protected abstract V createValue(K k);
134
135 class QueueListener implements Runnable {
136
137 @SuppressWarnings("unchecked")
138 @Override
139 public void run() {
140 try {
141 while(true) {
142 removeExpired((Reference<V>) queue.remove());
143 }
144 } catch (InterruptedException e) {
145 }
146 }
147 }
148
149 }