View Javadoc

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 	 * Stops the listener thread and releases native resources.
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 }