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

no.nextgentel.oss.akkatools.utils.ActorCache.scala Maven / Gradle / Ivy

There is a newer version: 2.5.0.2
Show newest version
package no.nextgentel.oss.akkatools.utils

import java.util.concurrent.TimeUnit

import akka.actor._
import com.google.common.cache._

import scala.concurrent.duration.{FiniteDuration, Duration}
import scala.reflect.ClassTag

case class CheckCache()
case class ForwardToCachedActor[K](key:K, msg:AnyRef)

object ActorCache {
  def props[K:ClassTag](cacheLoader:(K)=>Props, expireAfter: Duration = Duration(2, TimeUnit.MINUTES)) = Props(new ActorCache[K](cacheLoader, expireAfter))
}

class ActorCache[K:ClassTag](cacheLoader:(K)=>Props, expireAfter: Duration) extends Actor with ActorLogging {

  implicit val ec = context.dispatcher

  val removalListener = new RemovalListener[AnyRef, ActorRef] {
    override def onRemoval(notification: RemovalNotification[AnyRef, ActorRef]): Unit = {
      val key = notification.getKey.asInstanceOf[K]
      log.debug("Stopping actor for " + key)
      val actor = notification.getValue
      actor ! PoisonPill
    }
  }

  val realCachLoader = new CacheLoader[AnyRef,ActorRef] {
    override def load(key: AnyRef): ActorRef = {
      log.debug("Creating actor for " + key)
      val props:Props = cacheLoader(key.asInstanceOf[K])
      context.actorOf( props )
    }
  }

  val cache:LoadingCache[AnyRef, ActorRef] = CacheBuilder.newBuilder
    .expireAfterAccess(expireAfter.toMillis, TimeUnit.MILLISECONDS)
    .removalListener(removalListener)
    .build(realCachLoader)

  val waitPeriode = FiniteDuration.apply(expireAfter.toMillis / 2, TimeUnit.MILLISECONDS)

  scheduleNextCacheCheck()

  def scheduleNextCacheCheck(): Unit = {
    context.system.scheduler.scheduleOnce(waitPeriode, self, CheckCache())
  }

  def receive = {
    case CheckCache() => {
      cache.cleanUp()
      scheduleNextCacheCheck()
    }
    case ForwardToCachedActor(key:K, msg) =>
      try {
        val actor = cache.get(key.asInstanceOf[AnyRef])
        log.debug("Forwarding message for " + key + " to " + actor)
        actor forward msg
      } catch {
        case e:Exception =>
          log.error(e, "Error forwarding message (with key "+key+") " + msg)
      }
    case x:AnyRef =>
      log.warning("Droping unknown msg: " + x)
  }

  @throws(classOf[Exception])
  override def postStop {
    super.postStop
    cache.invalidateAll
    cache.cleanUp
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy