How to use a #Singleton from an Akka actor? - scala

I am new to Dependency Injection and now when I migrated my application to Play 2.5.x I need to learn.
I have a singleton service looking something like this:
import javax.inject._
#Singleton
class WorkerService {
def doWork(work:String) {
...
}
}
I have an actor looking something like this:
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
import akka.actor._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scalaj.http._
import WorkerService
object PollActor {
def props(host: String) = Props(new PollActionActor(host))
}
// Actor that polls a host for information
class PollActor(host: String) extends Actor with ActorLogging {
// timer for poll
var timer: Cancellable = context.system.scheduler.schedule(0 second, 10 second, self, TimeStep())
// handle messages
override def receive = {
case TimeStep() =>
getWork match {
case Some(work:String) =>
// How to do this?: ChecklistService.doWork(work)
case None =>
}
}
def getWork = {
try {
Some(Http(host)
.option(HttpOptions.readTimeout(10000))
.option(HttpOptions.connTimeout(10000))
.asString.body)
} catch {
case _:Throwable =>
None
}
}
case class TimeStep()
}
And a controller something like this:
#Singleton
class Application #Inject() (implicit system: ActorSystem) extends Controller {
val pollActor = system.actorOf(PollActor.props("127.0.0.1"))
def index = Action {
pollActor ! TimeStep
}
}
How can I call WorkerService.doWork in the time step of the actor?

You probably should not create the actor in the Application class.
Try using a module to create your actor like this
class ApplicationConfigModule extends AbstractModule with AkkaGuiceSupport {
override def configure(): Unit = {
bindActor[PollActor]("poll-actor")
}
}
in your resources/application.conf put this
play.modules.enabled += "com.nowtv.platform.search.module.ApplicationConfigModule"
Then inject the service into your actor
class PollActor #Inject()(WorkerService: workerService) extends Actor
And inject the Actor into your Controller so you can use it there
#Singleton
class Application #Inject() (#Named("poll-actor") pollActor: ActorRef) extends Controller {

Related

Constructor and preStart/postStop hooks NOT invoked for injected Akka RouterActor

I am trying some example in Play Framework (Scala). I'm injecting an actor into controller.
Configuration
Java (1.8.0_144)
Scala (2.12.3)
Play (2.6.5)
I have following code:
ExampleController
class ExampleController #Inject() (
controllerComponents: ControllerComponents,
#Named("injected-router") injectedRouterActor: ActorRef,
#Named("injected") injectedActor: ActorRef)
extends AbstractController(controllerComponents) {
def alive = Action {
injectedActor ! "Hi from Example"
injectedRouterActor ! "Hi From Example to Router"
Ok("Alive")
}
}
InjectedActor
class InjectedActor extends Actor {
val name = s"IA-${self.path}-${System.nanoTime}"
println(s"constructor: $name")
def receive = {
case m => println(s"$name received: $m")
}
override def preStart() = {
println(s"preStart: $name")
super.preStart()
}
}
InjectedRouterActor
class InjectedRouterActor extends Actor {
val name = s"IRA-${self.path}-${System.nanoTime}"
println(s"constructor: $name")
def receive = {
case m => println(s"$name received: $m")
}
override def preStart() = {
println(s"preStart: $name")
super.preStart()
}
}
ChildActor
class ChildActor extends Actor {
val name = s"CH-${self.path}-${System.nanoTime}"
println(s"constructor: $name")
def receive = {
case m => println(s"$name received: $m")
}
}
Module
class BindingModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[InjectedActor]("injected")
bindActor[InjectedRouterActor]("injected-router", _ => RoundRobinPool(5).props(Props[ChildActor]))
}
}
When I run this, and hit route for alive, I see printlns from all actors visible on console, except for InjectedRouterActor.
Unable to understand why? Any help is appreciated.
Thanks
In Akka routers are special implementation actors that you don't explicitly implement.
In your example RoundRobinPool(5).props(Props[ChildActor]) creates Props that would create round robin pool router with routees being actors of type ChildActor. You don't really need InjectedRouterActor. You might still want to have it if you want to create router based on configuration (see examples in Akka docs).
If you look at the source of ActorRefProvider that creates actors when you use Guice helper
class ActorRefProvider[T <: Actor: ClassTag](name: String, props: Props => Props) extends Provider[ActorRef] {
#Inject private var actorSystem: ActorSystem = _
#Inject private var injector: Injector = _
lazy val get = {
val creation = Props(injector.instanceOf[T])
actorSystem.actorOf(props(creation), name)
}
}
you can see that it creates default Props that gets an instance of InjectedRouterActor (the type informations is passed as T type parameter) from the injector but, since you provide props as a function where the function argument is ignored (_ => RoundRobinPool(5).props(Props[ChildActor])), it will just ignore creation variable. Also injector.instanceOf[T] is passed to Props#apply as by-name parameter so it is not called right away, so your InjectedRouterActor is not created.
Instead of creating binding for the router actor in configure you can create it manually:
#Provides
#Named("injected-router")
#Singleton
def routerProvider(system: ActorSystem): ActorRef = {
system.actorOf(RoundRobinPool(5).props(Props[ChildActor]), name = "injected-router")
}

Invoking an injected Scala class from an Akka actor

I have the following class in Play for Scala that injects another class:
class MainEtl2 #Inject() (ic: injectedClass) {
def run (option: String) = {
ic.method1()
// ... some code
}
}
I need to call the method run in an Akka Actor. This is my attempt, assuming that Guice will inject injectedClass when MainEtl2 is invoked:
class MainEtl extends Actor {
#Inject val me2 : MainEtl2
def receive = {
case option: String => {
val x = me2.run(option)
// ... more code
}
}
}
MainEtl class does not compile with the followint error:
class MainEtl needs to be abstract, since value me2 is not defined
How to make this work?
I would propose such solution based on Play documentation. Define your actor in such way:
class MainEtl #Inject() (me2: MainEtl2) extends Actor {
def receive = {
case option: String => {
val x = me2.run(option)
}
}
}
Define play module with Akka support and bind named actor:
class AkkaBindings extends AbstractModule with AkkaGuiceSupport {
bindActor[MainEtl]("mainEtl")
}
Register this module in application.conf by adding
play.modules.enabled += "some.package.AkkaBindings"
Now you can inject your actor by name reference:
class Scheduler #Inject()(#Named("mainEtl") mainEtl: ActorRef) {
//some code
val scheduler = QuartzSchedulerExtension(system)
scheduler.schedule("dailyproc", mainEtl, "abc", None)
}
I would try inject MainEtl2 similar to how CountingService is injected in this example:
class MainEtl #Inject() (me2: MainEtl2) extends Actor {
def receive = {
case option: String => {
val x = me2.run(option)
// ... more code
}
}
}
Though you are specifying #Inject annotation, you still need an initial value which guice will override while injecting dependencies, so try this,
class MainEtl extends Actor {
#Inject val me2 : MainEtl2 = null //initial value.
def receive = {
case option: String => {
val x = me2.run(option)
// ... more code
}
}
}

How to test composition of futures inside a receive method of actor?

I have the following actor code:
class MyActor #Inject()(dao1: MyDao, dao2: OtherDao, eventBus: EventBus) extends Actor with ActorLogging {
import context.dispatcher
eventBus.subscribe(context.self, MyTopics.FooTopic)
override def receive: Receive = {
case Foo(name: String) => {
dao.get(name)
.flatMap(result => dao2.getSomeMoreStuff(result)
.flatMap( data => //do more stuff)
)
}
}
}
When the actor finish to process Foo, it will invoke this future composition, and will move to process another Foo message, before this composition is finished, which is ok (I think).
My problem is testing this actor:
class MyActorTest(_system: ActorSystem) extends TestKit(_system)
with WordSpecLike with Matchers with BeforeAndAfterAll
with MockitoSugar with DefaultTimeout with ImplicitSender{
def this() = this(ActorSystem("myActorSpec"))
override def afterAll {
TestKit.shutdownActorSystem(system)
}
trait MyActorScope extends Scope {
val myDao = mock[MyDao]
val otherDao = mock[OtherDao]
val eventBus = new MyEventBus()
val myActor = TestActorRef(new MyActor(myDao, otherDao, eventBus)
}
"Test" must {
"verify dao" in new MyActorScope {
when(myDao.get(any)).thenReturn(Future.successful(("myDaoResult")))
when(otherDao.getSomeMoreStuff(any)).thenReturn(Future.successful(("otherDaoResult")))
eventBus.publish(new FooMessage(FooPayload("foo")))
verify(myDao).get(any)
verify(otherDao).getSomeMoreStuff(any)
}
}
}
So what happens here, is that myDao is verified successfully but the other Dao isn't.
I think it's because the composition of the futures did not happened before the end of this message processing.
Any way to handle this? Does the actor code make sense?
Thanks!
Fixed it with using Timeout VerificationMode
"Test" must {
"verify dao" in new MyActorScope {
when(myDao.get(any)).thenReturn(Future.successful(("myDaoResult")))
when(otherDao.getSomeMoreStuff(any)).thenReturn(Future.successful(("otherDaoResult")))
eventBus.publish(new FooMessage(FooPayload("foo")))
verify(myDao, new Timeout(500, times(1))).get(any)
verify(otherDao, new Timeout(500, times(1))).getSomeMoreStuff(any)
}
}

Testing Spray API with Akka Router

I'm working on a Spray API, with an Akka router to send the incoming messages on to a pool of actors for handling the logic. Now I want to write some tests for the API, but I'm struggling to find the right structure for the code. The API looks as follows at the moment:
import akka.actor.{ActorRef, ActorSystem, Props, Actor}
import akka.io.IO
import akka.routing.SmallestMailboxPool
import akka.util.Timeout
import akka.pattern.ask
import com.typesafe.config.ConfigFactory
import spray.json._
import spray.can.Http
import scala.concurrent.duration._
import spray.routing._
import spray.http._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure
object implicits{
implicit val system = ActorSystem("ApiSystem")
implicit val timeout = Timeout(5.seconds)
implicit val conf = ConfigFactory.load()
// Custom case class for parsing JSON parameter.
case class Msg(key1:String, key2:String, key3:Int)
object JsonProtocol extends DefaultJsonProtocol {
implicit val msg = jsonFormat3(Msg)
}
case class PostMsg(msg:String)
case object PostSuccess
case class PostFailure(msg:String)
}
import implicits._
object MyApi extends App {
override def main(Args: Array[String]):Unit = {
// create and start our service actor
val service = system.actorOf(Props(new MyApiActor(system)), "MyApi-service")
IO(Http) ? Http.Bind(service, interface = conf.getString("http.host"), port = conf.getInt("http.port"))
}
}
class MyApiActor(system: ActorSystem) extends Actor with MyApiService {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)
}
// this trait defines our service behavior independently from the service actor
trait MyApiService extends HttpService {
import implicits.JsonProtocol._
var actorPool = system.actorOf(SmallestMailboxPool(conf.getInt("actor-number")).props(Props(new HandlingActor(conf))), "msgRouter")
val myRoute =
path("msg") {
post {
entity(as[String]) { obj =>
try{
// if this parsing succeeds, the posted msg satisfies the preconditions set.
obj.parseJson.convertTo[Msg]
} catch {
case e: DeserializationException => {
complete(HttpResponse(status=StatusCodes.BadRequest, entity="Invalid json provided."))
}
case e: Exception => {
complete(HttpResponse(status=StatusCodes.InternalServerError, entity="Unknown internal server error."))
}
}
onComplete(actorPool ? PostMsg(obj)) {
case Success(value) => complete(HttpResponse(status = StatusCodes.OK, entity = "Pushed Msg"))
case Failure(value) => complete(HttpResponse(status = StatusCodes.InternalServerError, entity = "Handling failed."))
}
}
}
}
}
What I would like to test is the response of the API to various HTTP messages (i.e. correct calls, incorrect calls etc.). The logic in the handling actor is simply to push the message to a Kafka bus, so I would like to "mock" this behaviour (i.e. be able to test the API response if this push succeeds and also what happens when this push fails).
The thing I'm struggling with most at the moment is how to setup the test. For now, I am setting up the API using the same commands as in the main method shown, but I need to specify a different actorPool, as I don't want any messages to actually be pushed. How should I best go about achieving such tests?
I am using Scalatest, with the Akka and Spray testkit. (plus possibly mockito for mocking if necessary)
I have few suggestions to make your testing easier:
Do not create the actor pool in your trait. Instead inject the ActorRef from the ActorPool using a def instead of a val in the route. Then it will be easier to inject your actorPool TestProbe() to test. For example (I have not tried/compiled this code):
class MyApiActor extends Actor with MyApiService {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
val actorPool = context.actorOf(SmallestMailboxPool(conf.getInt("actor-number")).props(Props(new HandlingActor(conf))), "msgRouter")
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute(actorPool))
}
// this trait defines our service behavior independently from the service actor
trait MyApiService extends HttpService {
import implicits.JsonProtocol._
def myRoute(actorPool: ActorRef) =
path("msg") {
post {
entity(as[String]) { obj =>
try{
// if this parsing succeeds, the posted msg satisfies the preconditions set.
obj.parseJson.convertTo[Msg]
} catch {
case e: DeserializationException => {
complete(StatusCodes.BadRequest, "Invalid json provided.")
}
case e: Exception => {
complete(StatusCodes.InternalServerError, "Unknown internal server error.")
}
}
onComplete(actorPool ? PostMsg(obj)) {
case Success(value) => complete(StatusCodes.OK, "Pushed Msg")
case Failure(value) => complete(StatusCodes.InternalServerError, "Handling failed.")
}
}
}
}
}
Then the test can look like this:
class HttpListenerSpec extends WordSpecLike with Matchers with ScalatestRouteTest with MyApiService {
"An HttpListener" should {
"accept GET at /msg" in {
val actorPool = TestProbe()
(stuff for responding with TestProbe()...)
Get("/msg") ~> myRoute(actorPool.ref) ~> check {
status shouldBe OK
val response = responseAs[String]
assert(...)
}
}
}
}
Also, as a final suggestion. There are implicit conversions that integrate spray json and spray so you can do entity(as[Msg]). Look for the following:
import spray.httpx.marshalling._
import spray.httpx.unmarshalling._
import spray.httpx.SprayJsonSupport._
import MsgJsonProtocol._

How to implement Secure.Authenticator in scala Controller?

I am following this guide -
https://www.playframework.com/documentation/2.1.1/JavaGuide4#Implementing-authenticators
I have implemented the authenticator as below in Scala:
package controllers
import play.mvc.Security
import play.mvc.Http.Context
import play.mvc.Result
import play.mvc.Results
class Secured extends Security.Authenticator {
override def getUsername(ctx: Context) : String = {
return ctx.session().get("username");
}
override def onUnauthorized(ctx: Context) : Result = {
Results.redirect(routes.Application.login())
}
}
I applied it as an annotation to the hello action in the below controller.
package controllers
import services.UserServiceImpl
import models.User._
import play.api.mvc._
import play.mvc.Security.Authenticated
object Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
#Authenticated(classOf[Secured])
def hello = Action {
Ok("Hello")
}
def login = Action {
Ok("Login")
}
}
I am using Play 2.4 and play.api.mvc.Controller instead of play.mvc.Controller as in the guide. Is this the reason this is not working? How do I get it to work with play.api.mvc.Controller?
I figured an example would probably help explain things. If you want similar functionality in Play Scala you would do something like the following:
object AuthenticatedAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
request.session.get("username") match {
case Some(user) => block(request)
case None => Redirect(routes.Application.login())
}
}
You would then use this in your controller:
def hello = AuthenticatedAction { implicit request =>
Ok("Hello")
}