├── README.md ├── SampleListenerImplementation └── RESTMeteringFilter.java ├── WeakConcurrentHashMap.java └── WeakConcurrentHashMapListener.java /README.md: -------------------------------------------------------------------------------- 1 | # WeakConcurrentHashMap 2 | 3 | A Weak Concurrent Hash Map Solution which stores the keys and values only for a specific amount of time, and then expires after that 4 | time. 5 | 6 |
 7 |   
 8 |   // Create a Map Object
 9 |   long expiryInMillis = 1 * 60 * 1000;	// 1 minute
10 |   WeakConcurrentHashMap<String, Long> map = new WeakConcurrentHashMap<String, Long>(expiryInMillis);
11 |   
12 |   // Use it
13 |   map.put("key", valueObject);
14 |   Long valueObject = map.get("key");
15 |   
16 |   // quit using it
17 |   map.quitMap();
18 |   
19 | 20 | And to check if the map is alive 21 | 22 |
23 |   if (map.isAlive()) {
24 |   	// Your operations on map
25 |   }
26 |   
27 | 28 | 29 | Listener Implementation 30 | 31 | 32 |
33 | class MyMapListener implements WeakConcurrentHashMapListener {
34 | 
35 | 	@Override
36 | 	public void notifyOnAdd(String key, Long value) {
37 | 		System.out.println("New key added to map. Key : " + key + ", Value : " + value);
38 | 	}
39 | 
40 | 	@Override
41 | 	public void notifyOnRemoval(String key, Long value) {
42 | 		RestLogger.info("Key Removed from Map Key: " + key + ", Value : " + value);
43 | 	}
44 | }
45 | 
46 | 47 | @author Vivekananthan M (vivekjustthink@gmail.com) 48 | -------------------------------------------------------------------------------- /SampleListenerImplementation/RESTMeteringFilter.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | 3 | import javax.ws.rs.container.ContainerRequestContext; 4 | import javax.ws.rs.container.ContainerRequestFilter; 5 | import javax.ws.rs.ext.Provider; 6 | 7 | import WeakConcurrentHashMap; 8 | import WeakConcurrentHashMapListener; 9 | 10 | /** 11 | * This servlet filter is used to track the number of api hits module wise on the FE Side. For now it just prints the api hit count once in 5 mintues in the FE logs. 12 | * 13 | * @author Vivekananthan M 14 | * 15 | */ 16 | @Provider 17 | public class RESTMeteringFilter implements ContainerRequestFilter { 18 | 19 | private WeakConcurrentHashMap apiCounter = new WeakConcurrentHashMap<>(5 * 60 * 1000); // Keys in the map will expire in 5 minutes 20 | 21 | public RESTMeteringFilter() { 22 | apiCounter.registerRemovalListener(new PrintAPIHits()); 23 | } 24 | 25 | @Override 26 | public void filter(ContainerRequestContext paramContainerRequestContext) throws IOException 27 | { 28 | RestLogger.debug("Processing Request for Metering. URI " + paramContainerRequestContext.getUriInfo().getPath()); //No I18N 29 | String uriHit = paramContainerRequestContext.getUriInfo().getPath(); 30 | 31 | if(uriHit != null) { 32 | String[] pathList = uriHit.split("/"); 33 | String key = null; 34 | if(pathList.length >= 4) { 35 | key = pathList[2] + "/" + pathList[3]; 36 | } else { 37 | key = pathList[pathList.length-1]; 38 | } 39 | 40 | if(key != null && !key.isEmpty()) { 41 | if(apiCounter.containsKey(key)) { 42 | apiCounter.put(key, apiCounter.get(key) + 1); 43 | } else { 44 | apiCounter.put(key, 1l); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | class PrintAPIHits implements WeakConcurrentHashMapListener { 52 | 53 | @Override 54 | public void notifyOnAdd(String key, Long value) {} 55 | 56 | @Override 57 | public void notifyOnRemoval(String key, Long value) { 58 | System.out.println("Refreshing API Hits " + key + " : " + value); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /WeakConcurrentHashMap.java: -------------------------------------------------------------------------------- 1 | import java.util.Date; 2 | import java.util.Map; 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | /** 6 | * A Weak Concurrent Hash Map Solution which stores the keys and values only for a specific amount of time, and then expires after that 7 | * time. 8 | * 9 | *
 10 |  * 
 11 |  * // Create a Map Object
 12 |  * long expiryInMillis = 1 * 60 * 1000;	// 1 minute
 13 |  * WeakConcurrentHashMap<String, Object> map = new WeakConcurrentHashMap<String, Object>(expiryInMillis);
 14 |  * 
 15 |  * // Use it
 16 |  * map.put("key", valueObject);
 17 |  * Object valueObject = map.get("key");
 18 |  * 
 19 |  * // quit using it
 20 |  * map.quitMap();
 21 |  * 
22 | * 23 | * And to check if the map is alive 24 | * 25 | *
 26 |  * if (map.isAlive()) {
 27 |  * 	// Your operations on map
 28 |  * }
 29 |  * 
30 | * 31 | * @author Vivekananthan M 32 | * 33 | * @param 34 | * @param 35 | */ 36 | public class WeakConcurrentHashMap extends ConcurrentHashMap { 37 | 38 | private static final long serialVersionUID = 1L; 39 | 40 | private Map timeMap = new ConcurrentHashMap(); 41 | private WeakConcurrentHashMapListener listener; 42 | private long expiryInMillis; 43 | private boolean mapAlive = true; 44 | 45 | public WeakConcurrentHashMap() { 46 | this.expiryInMillis = 10000; 47 | initialize(); 48 | } 49 | 50 | public WeakConcurrentHashMap(WeakConcurrentHashMapListener listener) { 51 | this.listener = listener; 52 | this.expiryInMillis = 10000; 53 | initialize(); 54 | } 55 | 56 | public WeakConcurrentHashMap(long expiryInMillis) { 57 | this.expiryInMillis = expiryInMillis; 58 | initialize(); 59 | } 60 | 61 | public WeakConcurrentHashMap(long expiryInMillis, WeakConcurrentHashMapListener listener) { 62 | this.expiryInMillis = expiryInMillis; 63 | this.listener = listener; 64 | initialize(); 65 | } 66 | 67 | void initialize() { 68 | new CleanerThread().start(); 69 | } 70 | 71 | public void registerRemovalListener(WeakConcurrentHashMapListener listener) { 72 | this.listener = listener; 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | * 78 | * @throws IllegalStateException if trying to insert values into map after quiting 79 | */ 80 | @Override 81 | public V put(K key, V value) { 82 | if (!mapAlive) { 83 | throw new IllegalStateException("WeakConcurrent Hashmap is no more alive.. Try creating a new one."); // No I18N 84 | } 85 | Date date = new Date(); 86 | timeMap.put(key, date.getTime()); 87 | V returnVal = super.put(key, value); 88 | if (listener != null) { 89 | listener.notifyOnAdd(key, value); 90 | } 91 | return returnVal; 92 | } 93 | 94 | /** 95 | * {@inheritDoc} 96 | * 97 | * @throws IllegalStateException if trying to insert values into map after quiting 98 | */ 99 | @Override 100 | public void putAll(Map m) { 101 | if (!mapAlive) { 102 | throw new IllegalStateException("WeakConcurrent Hashmap is no more alive.. Try creating a new one."); // No I18N 103 | } 104 | for (K key : m.keySet()) { 105 | put(key, m.get(key)); 106 | } 107 | } 108 | 109 | /** 110 | * {@inheritDoc} 111 | * 112 | * @throws IllegalStateException if trying to insert values into map after quiting 113 | */ 114 | @Override 115 | public V putIfAbsent(K key, V value) { 116 | if (!mapAlive) { 117 | throw new IllegalStateException("WeakConcurrent Hashmap is no more alive.. Try creating a new one."); // No I18N 118 | } 119 | if (!containsKey(key)) { 120 | return put(key, value); 121 | } else { 122 | return get(key); 123 | } 124 | } 125 | 126 | /** 127 | * Should call this method when it's no longer required 128 | */ 129 | public void quitMap() { 130 | mapAlive = false; 131 | } 132 | 133 | public boolean isAlive() { 134 | return mapAlive; 135 | } 136 | 137 | /** 138 | * 139 | * This thread performs the cleaning operation on the concurrent hashmap once in a specified interval. This wait interval is half of the 140 | * time from the expiry time. 141 | * 142 | * 143 | */ 144 | class CleanerThread extends Thread { 145 | 146 | @Override 147 | public void run() { 148 | while (mapAlive) { 149 | cleanMap(); 150 | try { 151 | Thread.sleep(expiryInMillis / 2); 152 | } catch (InterruptedException e) { 153 | e.printStackTrace(); 154 | } 155 | } 156 | } 157 | 158 | private void cleanMap() { 159 | long currentTime = new Date().getTime(); 160 | for (K key : timeMap.keySet()) { 161 | if (currentTime > (timeMap.get(key) + expiryInMillis)) { 162 | V value = remove(key); 163 | timeMap.remove(key); 164 | if (listener != null) { 165 | listener.notifyOnRemoval(key, value); 166 | } 167 | } 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /WeakConcurrentHashMapListener.java: -------------------------------------------------------------------------------- 1 | public interface WeakConcurrentHashMapListener { 2 | public void notifyOnAdd(K key, V value); 3 | public void notifyOnRemoval(K key, V value); 4 | } 5 | --------------------------------------------------------------------------------