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

com.jejking.rprng.rng.actors.RngActor.scala Maven / Gradle / Ivy

The newest version!
package com.jejking.rprng.rng.actors

import akka.actor._
import akka.util.ByteString
import com.jejking.rprng.rng._

import scala.concurrent.{Future, blocking}
import scala.language.postfixOps

/**
 * Actor wrapping a [[Rng]] to allow thread-safe access to it and to manage its lifecycle,
 * especially with regard to re-seeding.
 *
 * @param rng PRNG wrapped by actor
 * @param secureSeeder source for higher-quality seed for the PRNG
 */
class RngActor(private val rng: Rng, private val secureSeeder: SecureSeeder,
               private val scheduleHelperCreator: (ActorSystem) => ScheduleHelper = a => new AkkaScheduleHelper(a.scheduler),
               private val timeRangeToReseed: TimeRangeToReseed = TimeRangeToReseed()) extends Actor with ActorLogging {

  import Protocol._

  import scala.concurrent.ExecutionContext.Implicits.global

  private val scheduleHelper  =  scheduleHelperCreator(context.system)
  private val actorPath = context.self.path


  override def preStart(): Unit = {
    log.info(s"reseed interval is: $timeRangeToReseed")
    val seed = secureSeeder.generateSeed()
    this.rng.reseed(seed)
    scheduleReseed()
    log.info("completed pre-start of " + actorPath)

  }

  override def receive: Receive = {

    // main use case, fetch random bytes on demand
    case r: RandomByteRequest => {
      sender() ! ByteString(rng.randomBytes(r))
      if (log.isDebugEnabled) {
        log.debug("processed request for " + r.count + "bytes")
      }
    }

    // trigger reseed
    case Reseed => fetchSeedAndNotify()

    // apply fresh seed, non blocking action
    case newSeed: Seed => applyNewSeedAndScheduleReseed(newSeed)

    // ooops
    case _ => {
      log.warning("received unknown message type")
      sender() ! UnknownInputType
    }
  }

  def applyNewSeedAndScheduleReseed(newSeed: Seed): Unit = {
    this.rng.reseed(newSeed)
    log.info("applied new seed in actor " + actorPath)

    // and off we go for the next round at some point in the future
    scheduleReseed()
  }

  def fetchSeedAndNotify(): Unit = {
    log.info("about to trigger future to collect fresh seed for actor " + actorPath)
    // generate seed is likely to block as the seeder gathers entropy
    // therefore we do this in a future and send a message onto the actor's mailbox
    // for async application once we have a result
    Future {
      blocking {
        val seed = secureSeeder.generateSeed()
        log.info("obtained fresh seed in actor " + actorPath)
        self ! seed
      }

    }
  }

  def scheduleReseed(): Unit = {
    // non-blocking computation
    val timeToReseed = TimeRangeToReseed.durationToReseed(timeRangeToReseed, rng)
    scheduleHelper.scheduleOnce(timeToReseed) {
      self ! Reseed
      log.info("sent reseed message to actor " + actorPath)
    }
  }
}

/**
 * Defines constants and helper function.
 */
object RngActor {

  /**
   * Assembles Akka Props for the actor to avoid closing over actor state.
   * @param randomByteSource a random byte source that will be wrapped by the actor.
   * @param secureSeeder a secure seeder to use to fetch initial seeding of the random byte source and for subsequent reseeding
   * @return akka props
   */
  def props(randomByteSource: Rng, secureSeeder: SecureSeeder): Props = Props(new RngActor(randomByteSource, secureSeeder))


  /**
   * Assembles Akka Props for the actor.
   * @param randomByteSource a random byte source that will be wrapped by the actor.
   * @param secureSeeder a secure seeder to use to fetch initial seeding of the random byte source and for subsequent reseeding
   * @param timeRangeToReseed specified time range to reseed
   * @return akka props
   */
  def props(randomByteSource: Rng, secureSeeder: SecureSeeder, timeRangeToReseed: TimeRangeToReseed): Props =
    Props(new RngActor(rng = randomByteSource, secureSeeder = secureSeeder, timeRangeToReseed = timeRangeToReseed))


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy