All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nirvana.support.services.TimeoutMap.scala Maven / Gradle / Ivy

// Copyright 2014 Jun Tsai. All rights reserved.
// site: http://www.ganshane.com
package nirvana.support.services

import java.util
import java.util.concurrent.{ConcurrentHashMap, LinkedBlockingDeque}
import java.util.concurrent.atomic.AtomicBoolean

/**
 * timeout map
 */
trait TimeoutMap[K,V] {
  def containsKey(key: K): Boolean

  def get(key: K): Option[V]

  def putHead(key: K, value: V)

  def put(key: K, value: V)

  def remove(key: K): Option[V]

  def size: Int
}

abstract trait ExpiredCallback[K,V] {
  def expire(key: K, value: V)
}
/**
 * RotatingMap must be used under thread-safe environment
 *
 * Expires keys that have not been updated in the configured number of seconds.
 * The algorithm used will take between expirationSecs and expirationSecs * (1 +
 * 1 / (numBuckets-1)) to actually expire the message.
 *
 * get, put, remove, containsKey, and size take O(numBuckets) time to run.
 *
 */
object RotatingMap{
  val DEFAULT_NUM_BUCKETS: Int = 3
}
class RotatingMap[K,V] extends TimeoutMap[K, V] {
  def this(numBuckets: Int, callback: ExpiredCallback[K, V], isSingleThread: Boolean) {
    this()
    if (numBuckets < 2) {
      throw new IllegalArgumentException("numBuckets must be >= 2")
    }
    if (isSingleThread == true) {
      _buckets = new util.LinkedList[java.util.Map[K, V]]
    }
    else {
      _buckets = new LinkedBlockingDeque[java.util.Map[K, V]]
    }

    0 until numBuckets foreach{case i=>
      _buckets.add(new ConcurrentHashMap[K, V])
    }
    _callback = callback
  }

  def this(callback: ExpiredCallback[K, V]) {
    this(RotatingMap.DEFAULT_NUM_BUCKETS, callback, false)
  }

  def this(numBuckets: Int) {
    this(numBuckets, null, false)
  }

  private val flag = new AtomicBoolean(false)
  def rotate: Option[java.util.Map[K, V]]= {
    if(flag.compareAndSet(false,true)){
      try{
      val dead: java.util.Map[K, V] = _buckets.removeLast()
      _buckets.addFirst(new ConcurrentHashMap[K, V])
      if (_callback != null) {
        val it = dead.entrySet().iterator();
        while(it.hasNext){
          val entry = it.next()
          _callback.expire(entry.getKey,entry.getValue)
        }
      }
      return Some(dead)
      }finally{
        flag.set(false)
      }
    }
    return None
  }

  def containsKey(key: K): Boolean = {
    val it = _buckets.iterator();
    while(it.hasNext){
      val bucket = it.next()
      if(bucket.containsKey(key))
        return true
    }
    return false
  }

  def get(key: K): Option[V] = {
    val it = _buckets.iterator();
    while(it.hasNext){
      val bucket = it.next()
      if(bucket.containsKey(key))
        return Some(bucket.get(key))
    }
    return None
  }

  def putHead(key: K, value: V) {
    _buckets.peekFirst.put(key, value)
  }

  def put(key: K, value: V) {
    val it = _buckets.iterator
    var bucket = it.next
    bucket.put(key, value)
    while (it.hasNext) {
      bucket = it.next
      bucket.remove(key)
    }
  }

  /**
   * Remove item from Rotate
   *
   * On the side of performance, scanning from header is faster On the side of
   * logic, it should scan from the end to first.
   *
   * @param key
   * @return
   */
  def remove(key: K): Option[V]= {
    val it = _buckets.iterator();
    while(it.hasNext){
      val bucket = it.next()
      val value = bucket.remove(key)
      if(value != null)
        return Some(value)
    }
    return None
  }

  def size: Int = {
    var size: Int = 0
    val it = _buckets.iterator();
    while(it.hasNext){
      size += it.next().size()
    }
    return size
  }

  private var _buckets: util.Deque[java.util.Map[K, V]] = null
  private var _callback: ExpiredCallback[K, V] = null
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy