content: package com.alimama.mdrill.adhoc;
  
  import java.io.Serializable;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.LinkedList;
  import java.util.Map;
  import java.util.Map.Entry;
  
  
  
  
  public class TimeCacheMapLru<K, V> implements Serializable{
  	private static final long serialVersionUID = 1L;
      public static interface ExpiredCallback<K, V> {
          public void expire(K key, V val);
          public void commit();
      }
  
      private LinkedList<Map<K, V>> _buckets;
  
      private final Object _lock = new Object();
      private Thread _cleaner;
      private ExpiredCallback<K,V> _callback;
      
      private Map<K, V> CreateMap()
      {
  		return new LinkedHashMap<K, V>(
  				(int) Math.ceil(lursize / 0.75f) + 1,
  				0.75f, true) {
  			private static final long serialVersionUID = 1L;
  
  			@Override
  			protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
  					boolean rtn= size() > lursize;
  					if(rtn)
  					{
  						 if(_callback!=null) {
  	                         _callback.expire(eldest.getKey(), eldest.getValue());
  	                         _callback.commit();
  	                     }
  					}
  					return rtn;
  			}
  		};
      }
      
      private int numBuckets;
      private final int lursize;
      public TimeCacheMapLru(final int lrusize,int expirationSecs, int numBuckets, ExpiredCallback<K, V> callback) {
      	this.lursize=lrusize;
      	this.numBuckets=numBuckets;
          if(numBuckets<2) {
              throw new IllegalArgumentException("numBuckets must be >= 2");
          }
          _buckets = new LinkedList<Map<K, V>>();
          for(int i=0; i<numBuckets; i++) {
              _buckets.add(this.CreateMap());
          }
  
  
          _callback = callback;
          final long expirationMillis = expirationSecs * 1000L;
          final long sleepTime = expirationMillis / (numBuckets-1);
          _cleaner = new Thread(new Runnable() {
              public void run() {
                  try {
                      while(true) {
                          Map<K, V> dead = null;
                          Thread.currentThread().sleep(sleepTime);
                          synchronized(_lock) {
                              dead = _buckets.removeLast();
                              _buckets.addFirst(TimeCacheMapLru.this.CreateMap());
                          }
                          if(_callback!=null) {
                              for(Entry<K, V> entry: dead.entrySet()) {
                                  _callback.expire(entry.getKey(), entry.getValue());
                              }
                              _callback.commit();
                          }
                      }
                  } catch (InterruptedException ex) {
  
                  }
              }
          });
          _cleaner.setDaemon(true);
          _cleaner.start();
      }
      
      public void fourceTimeout(Timeout<K, V> fetch,Update<K, V> d)
      {
      	LinkedList<Map<K, V>> lastdata=null;
      	synchronized(_lock) {
      		lastdata=_buckets;
      		_buckets = new LinkedList<Map<K, V>>();
      		 for(int i=0; i<numBuckets; i++) {
      	            _buckets.add(this.CreateMap());
      	     }
      	}
  
           if(_callback!=null&&lastdata!=null) {
          	 HashMap<K, V> needupdate=new HashMap<K, V>();
          	 for(Map<K, V> dead:lastdata)
          	 {
  	             for(Entry<K, V> entry: dead.entrySet()) {
  	            	 K key=entry.getKey();
  	            	 V val=entry.getValue();
  	            	 if(fetch.timeout(key,val))
  	            	 {
  	            		 _callback.expire(key, val);
  	            	 }else{
  	            		 needupdate.put(key, val);
  	            	 }
  	             }
          	 }
          	 
          	 _callback.commit();
  
          	 if(needupdate.size()>0)
          	 {
          		 this.updateAll(needupdate, d);
          	 }
          	 
           }
      }
      
      public void fourceTimeout()
      {
      	LinkedList<Map<K, V>> lastdata=null;
      	synchronized(_lock) {
      		lastdata=_buckets;
      		_buckets = new LinkedList<Map<K, V>>();
      		 for(int i=0; i<numBuckets; i++) {
      	            _buckets.add(this.CreateMap());
      	        }
      	}
  
           if(_callback!=null&&lastdata!=null) {
          	 for(Map<K, V> dead:lastdata)
          	 {
  	             for(Entry<K, V> entry: dead.entrySet()) {
  	                 _callback.expire(entry.getKey(), entry.getValue());
  	             }
          	 }
          	 
          	 _callback.commit();
           }
      }
  
  
  
      public boolean containsKey(K key) {
          synchronized(_lock) {
              for(Map<K, V> bucket: _buckets) {
                  if(bucket.containsKey(key)) {
                      return true;
                  }
              }
              return false;
          }
      }
  
      public V get(K key) {
          synchronized(_lock) {
              return this.getNolock(key);
          }
      }
      
      
      private V getNolock(K key)
      {
      	 for(Map<K, V> bucket: _buckets) {
               if(bucket.containsKey(key)) {
                   return bucket.get(key);
               }
           }
           return null;
      }
      
      private void putNolock(K key, V value) {
              Iterator<Map<K, V>> it = _buckets.iterator();
              Map<K, V> bucket = it.next();
              bucket.put(key, value);
              while(it.hasNext()) {
                  bucket = it.next();
                  bucket.remove(key);
              }
      }
  
      public void put(K key, V value) {
          synchronized(_lock) {
              this.putNolock(key, value);
          }
      }
      
      public static interface Update<K, V>{
      	public V update(K key,V old,V newval);
      }
      
      public static interface Timeout<K, V>{
      	public boolean timeout(K key,V val);
      }
      
      public void updateAll(Map<K, V> bucket,Update<K, V> d)
      {
      	synchronized(_lock) {
      		for(Entry<K, V> e:bucket.entrySet())
      		{
      			K key=e.getKey();
      			V old=this.getNolock(key);
      			V newval=e.getValue();
      			V finalVal=d.update(key, old, newval);
      			this.putNolock(key, finalVal);
      		}
      	}
      }
      
      public void update(K key, V newval,Update<K, V> d)
      {
      	synchronized(_lock) {
      		V old=this.getNolock(key);
  			V finalVal=d.update(key, old, newval);
  			this.putNolock(key, finalVal);
      	}
      }
      
      
      public V remove(K key) {
          synchronized(_lock) {
              for(Map<K, V> bucket: _buckets) {
                  if(bucket.containsKey(key)) {
                      return bucket.remove(key);
                  }
              }
              return null;
          }
      }
  
      public int size() {
          synchronized(_lock) {
              int size = 0;
              for(Map<K, V> bucket: _buckets) {
                  size+=bucket.size();
              }
              return size;
          }
      }
  
      @Override
      protected void finalize() throws Throwable {
          try {
              _cleaner.interrupt();
          } finally {
              super.finalize();
          }
      }
  
      
  }
  
  