1 20 package org.apache.mina.util; 21 22 import java.util.Collection ; 23 import java.util.Map ; 24 import java.util.Set ; 25 import java.util.concurrent.ConcurrentHashMap ; 26 import java.util.concurrent.CopyOnWriteArrayList ; 27 import java.util.concurrent.locks.ReadWriteLock ; 28 import java.util.concurrent.locks.ReentrantReadWriteLock ; 29 30 35 public class ExpiringMap<K, V> implements Map <K, V> { 36 public static final int DEFAULT_TIME_TO_LIVE = 60; 37 38 public static final int DEFAULT_EXPIRATION_INTERVAL = 1; 39 40 private static volatile int expirerCount = 1; 41 42 private final ConcurrentHashMap <K, ExpiringObject> delegate; 43 44 private final CopyOnWriteArrayList <ExpirationListener> expirationListeners; 45 46 private final Expirer expirer; 47 48 public ExpiringMap() { 49 this(DEFAULT_TIME_TO_LIVE, DEFAULT_EXPIRATION_INTERVAL); 50 } 51 52 public ExpiringMap(int timeToLive) { 53 this(timeToLive, DEFAULT_EXPIRATION_INTERVAL); 54 } 55 56 public ExpiringMap(int timeToLive, int expirationInterval) { 57 this(new ConcurrentHashMap <K, ExpiringObject>(), 58 new CopyOnWriteArrayList <ExpirationListener>(), timeToLive, 59 expirationInterval); 60 } 61 62 private ExpiringMap(ConcurrentHashMap <K, ExpiringObject> delegate, 63 CopyOnWriteArrayList <ExpirationListener> expirationListeners, 64 int timeToLive, int expirationInterval) { 65 this.delegate = delegate; 66 this.expirationListeners = expirationListeners; 67 68 this.expirer = new Expirer(); 69 expirer.setTimeToLive(timeToLive); 70 expirer.setExpirationInterval(expirationInterval); 71 } 72 73 public V put(K key, V value) { 74 ExpiringObject answer = delegate.put(key, new ExpiringObject(key, 75 value, System.currentTimeMillis())); 76 if (answer == null) { 77 return null; 78 } 79 80 return answer.getValue(); 81 } 82 83 public V get(Object key) { 84 ExpiringObject object = delegate.get(key); 85 86 if (object != null) { 87 object.setLastAccessTime(System.currentTimeMillis()); 88 89 return object.getValue(); 90 } 91 92 return null; 93 } 94 95 public V remove(Object key) { 96 ExpiringObject answer = delegate.remove(key); 97 if (answer == null) { 98 return null; 99 } 100 101 return answer.getValue(); 102 } 103 104 public boolean containsKey(Object key) { 105 return delegate.containsKey(key); 106 } 107 108 public boolean containsValue(Object value) { 109 return delegate.containsValue(value); 110 } 111 112 public int size() { 113 return delegate.size(); 114 } 115 116 public boolean isEmpty() { 117 return delegate.isEmpty(); 118 } 119 120 public void clear() { 121 delegate.clear(); 122 } 123 124 @Override 125 public int hashCode() { 126 return delegate.hashCode(); 127 } 128 129 public Set <K> keySet() { 130 return delegate.keySet(); 131 } 132 133 @Override 134 public boolean equals(Object obj) { 135 return delegate.equals(obj); 136 } 137 138 public void putAll(Map <? extends K, ? extends V> inMap) { 139 for (Entry<? extends K, ? extends V> e : inMap.entrySet()) { 140 this.put(e.getKey(), e.getValue()); 141 } 142 } 143 144 public Collection <V> values() { 145 throw new UnsupportedOperationException (); 146 } 147 148 public Set <Map.Entry <K, V>> entrySet() { 149 throw new UnsupportedOperationException (); 150 } 151 152 public void addExpirationListener(ExpirationListener<? extends V> listener) { 153 expirationListeners.add(listener); 154 } 155 156 public void removeExpirationListener( 157 ExpirationListener<? extends V> listener) { 158 expirationListeners.remove(listener); 159 } 160 161 public Expirer getExpirer() { 162 return expirer; 163 } 164 165 public int getExpirationInterval() { 166 return expirer.getExpirationInterval(); 167 } 168 169 public int getTimeToLive() { 170 return expirer.getTimeToLive(); 171 } 172 173 public void setExpirationInterval(int expirationInterval) { 174 expirer.setExpirationInterval(expirationInterval); 175 } 176 177 public void setTimeToLive(int timeToLive) { 178 expirer.setTimeToLive(timeToLive); 179 } 180 181 private class ExpiringObject { 182 private K key; 183 184 private V value; 185 186 private long lastAccessTime; 187 188 private ReadWriteLock lastAccessTimeLock = new ReentrantReadWriteLock (); 189 190 ExpiringObject(K key, V value, long lastAccessTime) { 191 if (value == null) { 192 throw new IllegalArgumentException ( 193 "An expiring object cannot be null."); 194 } 195 196 this.key = key; 197 this.value = value; 198 this.lastAccessTime = lastAccessTime; 199 } 200 201 public long getLastAccessTime() { 202 lastAccessTimeLock.readLock().lock(); 203 204 try { 205 return lastAccessTime; 206 } finally { 207 lastAccessTimeLock.readLock().unlock(); 208 } 209 } 210 211 public void setLastAccessTime(long lastAccessTime) { 212 lastAccessTimeLock.writeLock().lock(); 213 214 try { 215 this.lastAccessTime = lastAccessTime; 216 } finally { 217 lastAccessTimeLock.writeLock().unlock(); 218 } 219 } 220 221 public K getKey() { 222 return key; 223 } 224 225 public V getValue() { 226 return value; 227 } 228 229 @Override 230 public boolean equals(Object obj) { 231 return value.equals(obj); 232 } 233 234 @Override 235 public int hashCode() { 236 return value.hashCode(); 237 } 238 } 239 240 public class Expirer implements Runnable { 241 private ReadWriteLock stateLock = new ReentrantReadWriteLock (); 242 243 private long timeToLiveMillis; 244 245 private long expirationIntervalMillis; 246 247 private boolean running = false; 248 249 private final Thread expirerThread; 250 251 public Expirer() { 252 expirerThread = new Thread (this, "ExpiringMapExpirer-" 253 + (expirerCount++)); 254 expirerThread.setDaemon(true); 255 } 256 257 public void run() { 258 while (running) { 259 processExpires(); 260 261 try { 262 Thread.sleep(expirationIntervalMillis); 263 } catch (InterruptedException e) { 264 } 265 } 266 } 267 268 private void processExpires() { 269 long timeNow = System.currentTimeMillis(); 270 271 for (ExpiringObject o : delegate.values()) { 272 273 if (timeToLiveMillis <= 0) 274 continue; 275 276 long timeIdle = timeNow - o.getLastAccessTime(); 277 278 if (timeIdle >= timeToLiveMillis) { 279 delegate.remove(o.getKey()); 280 281 for (ExpirationListener<V> listener : expirationListeners) { 282 listener.expired(o.getValue()); 283 } 284 } 285 } 286 } 287 288 public void startExpiring() { 289 stateLock.writeLock().lock(); 290 291 try { 292 if (!running) { 293 running = true; 294 expirerThread.start(); 295 } 296 } finally { 297 stateLock.writeLock().unlock(); 298 } 299 } 300 301 public void startExpiringIfNotStarted() { 302 stateLock.readLock().lock(); 303 try { 304 if (running) { 305 return; 306 } 307 } finally { 308 stateLock.readLock().unlock(); 309 } 310 311 stateLock.writeLock().lock(); 312 try { 313 if (!running) { 314 running = true; 315 expirerThread.start(); 316 } 317 } finally { 318 stateLock.writeLock().unlock(); 319 } 320 } 321 322 public void stopExpiring() { 323 stateLock.writeLock().lock(); 324 325 try { 326 if (running) { 327 running = false; 328 expirerThread.interrupt(); 329 } 330 } finally { 331 stateLock.writeLock().unlock(); 332 } 333 } 334 335 public boolean isRunning() { 336 stateLock.readLock().lock(); 337 338 try { 339 return running; 340 } finally { 341 stateLock.readLock().unlock(); 342 } 343 } 344 345 public int getTimeToLive() { 346 stateLock.readLock().lock(); 347 348 try { 349 return (int) timeToLiveMillis / 1000; 350 } finally { 351 stateLock.readLock().unlock(); 352 } 353 } 354 355 public void setTimeToLive(long timeToLive) { 356 stateLock.writeLock().lock(); 357 358 try { 359 this.timeToLiveMillis = timeToLive * 1000; 360 } finally { 361 stateLock.writeLock().unlock(); 362 } 363 } 364 365 public int getExpirationInterval() { 366 stateLock.readLock().lock(); 367 368 try { 369 return (int) expirationIntervalMillis / 1000; 370 } finally { 371 stateLock.readLock().unlock(); 372 } 373 } 374 375 public void setExpirationInterval(long expirationInterval) { 376 stateLock.writeLock().lock(); 377 378 try { 379 this.expirationIntervalMillis = expirationInterval * 1000; 380 } finally { 381 stateLock.writeLock().unlock(); 382 } 383 } 384 } 385 } 386 | Popular Tags |