Accessing Play configuration from Akka Actor - scala

I have an Akka Actor in my Play app that accesses Play's configuration using a now deprecated method.
class MyActor (supervisor: ActorRef) extends Actor {
val loc = Play.current.configuration.getString("my.location").get
def receive = { case _ => }
}
if I do this:
import javax.inject._
class MyActor #Inject(configuration: play.api.Configuration) (supervisor: ActorRef) extends Actor {
My class won't compile and the compler returns: "classfile annotation arguments have to be supplied as named arguments"
I assume you can only DI the configuration within a controller class. So, is it possible to access the configuration from within an Akka Actore within a Play app? I could pass the configuration to the actor during construction or just have a separate config file for the actors, but both seem pretty hacky. Is there a preferred method using the Play api?
Thanks!

The answer by mana above points out the most elegant way to use DI in combination with actors in Play, but within any Actor you can find the configuration like:
context.system.settings.config

This is working in my project:
Module.scala:
class ExampleModule extends AbstractModule with AkkaGuiceSupport {
override def configure(): Unit = {
bindActor[ExampleActor]("example-actor-name")
}
}
Actor.scala:
object ExampleActor {
def props = Props[ExampleActor]
}
#Singleton
class ExampleActor #Inject()(/*some DI*/) extends Actor {
...
}
And you can then even inject that very actor into other Classes (the #Named() is optional if you have only one Actor configured) via DI:
SomeOtherClass.scala
#Singleton
class SomeOtherClass #Inject()(#Named("example-actor-name") exampleActor: ActorRef) {
...
}

Related

How do I access the actor system used in Play 2.5 from within a Module

I need access to the default actor system that Play Framework 2.5 uses from within my Module class.
I see that there is a method on ActorSystemProvider to get this:
#Singleton
class ActorSystemProvider #Inject()(environment: Environment, configuration: Configuration, applicationLifecycle: ApplicationLifecycle) extends Provider[ActorSystem] {
private val logger = Logger(classOf[ActorSystemProvider])
lazy val get: ActorSystem = {
val (system, stopHook) = ActorSystemProvider.start(environment.classLoader, configuration)
applicationLifecycle.addStopHook(stopHook)
system
}
}
But how do I get access to this class in my Module class?
For example:
class Module extends AbstractModule {
val playSystem: ActorSytem = ???
...
}
You can access actorSystem by simply injecting it into any of the component constructor. You will get access to the actorSystem created by play and you need not do any of the provider gymnastics.
For example, I need actor system to be accessible in my HomeController. So, I just inject into my HomeController constructor.
class HomeController #Inject() (actorSystem: ActorSystem) extends Controller {
def index = Ok("bye!")
}

Scala + Play Framework + Slick + Akka - DB Access from Akka Actor

At the receival of a TCP specific message, i need to query my DB. For that, i created an Actor that is called DBActor and it's loaded in Application.scala file
class Application #Inject() (system: ActorSystem) extends Controller {
val tcpServer = system.actorOf(Props[TCPServer], "tcpserver")
val dbActor = system.actorOf(Props[DBActor], "dbActor")
}
Tcp server is the actor that received the message and need to push it on DB Actor with that
val handler = context.actorSelection("/dbActor")
DB actor is so initialized in this way, according to Play Framework specifications
object DBActor {
def props() =
Props(classOf[DBActor])
}
class DBActor #Inject() (protected val dbConfigProvider:
DatabaseConfigProvider) extends Actor
with HasDatabaseConfigProvider[JdbcProfile]
with ActorLogging
with TableComponent {
import akka.io.Tcp._
import driver.api._
val table = TableQuery[Table]
def receive: Receive = {
case GetConfig(id) => {
sender ! Await.result(db.run(table.filter(_.id=== id).result.headOption),
Duration.Inf)
.map { x => x.config }
}
}
}
At the moment, actor is not constructed due to the error
Error injecting constructor, java.lang.IllegalArgumentException:
no matching constructor found on class tcp.DBActor for arguments []
at controllers.Application.<init>(Application.scala:17)
at controllers.Application.class(Application.scala:17)
So i need a way to inject the db configuration in the DBactor for querying the database or an alternative. I evaluated before that to inject a DAO or transforming the DAO I needed into an actor, both failed.
Question at this point is, does it make any sense giving an actor the access to the db or, at least, a controller? If can't be done, what are the alternatives?
What you need is an injected actor. The full description can be found here in the play documentation (https://www.playframework.com/documentation/2.5.x/ScalaAkka#Dependency-injecting-actors), but here's the gist of it:
You define the actor binding like so:
bindActor[DBActor]("db-actor")
And inject the actor in the controller like so:
class Application #Inject() (#Named("db-actor") dbActor: ActorRef) extends Controller {
On a different note, you should avoid Await.result whenever possible. In your scenario, this could probably easily be replaced with:
val senderRef = sender()
db.run(table.filter(_.id=== id).result.headOption)
.map(res => senderRef ! res.config)
Note that the sender ref is stored before, because it is no longer valid inside the map (see scaladoc of the sender() method).

How can I inject arguments into a routee of akka RoundRobinPool with guice

I'm using Play Framework, Akka and Guice. The need is to simply inject some service (or another actor) into an actor which is created by a router which is injected into some controller.
I have a router config:
akka.actor.deployment {
/job-router {
router = "round-robin-pool"
nr-of-instances = 10
}
}
And a module to bind this router
class ComponentModule extends AbstractModule with AkkaGuiceSupport {
def configure() ={
bindActor[JobActor]("job-router",(p: Props) => FromConfig.props(JobActor.props))
}
}
And the actor, with another router injected
class JobActor #Inject() (#Named("b-router")bRouter: ActorRef) extends Actor {
def this() = {
this(null)
}
override def receive = {
case SayHello(name: String) => {
println(bRouter)
sender() ! "hello,"+name
}
}
}
object JobActor {
def props = Props[JobActor].withDispatcher("akka.actor.default-dispatcher")
}
But instead of injecting the b-router , the constructor with no argument is invoked, so I can't simply get a b-router into the routee of this RoundRobinPool.
I do understand that the child actor (say Actor[akka://application/user/job-router/$a#-22311049]) is not directly injected by Guice, just wonder if there is a simplified manner to do this.
Since the actual routee job-router/$a#-22311049 is a child of RoundRobinPool, is it the best practice that I implement the RoundRobinPool to inject and pass it down to the routee?
Update:
This would do the job for injecting an actor:
context.actorSelection("akka://application/user/b-router")
Hou about other service injected by Guice?
Update 2:
Temporarily solved, by injecting into another "top level" actor (dao-provider-actor):
lazy val dao=Await.result((context.actorSelection("/user/dao-provider-actor") ? GetXDao).mapTo[XDAO],10.milliseconds)

How to mock using external call in Akka Actor using ScalaTest

I am new to entire ecosystem including Scala, Akka and ScalaTest
I am working on a problem where my Actor gives call to external system.
case object LogProcessRequest
class LProcessor extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
LogReaderDisruptor main(Array())
}
}
The LogReaderDisruptor main(Array()) is a Java class that does many other things.
The test I have currently looks like
class LProcessorSpec extends UnitTestSpec("testSystem") {
"A mocked log processor" should {
"be called" in {
val logProcessorActor = system.actorOf(Props[LProcessor])
logProcessorActor ! LogProcessRequest
}
}
}
where UnitTestSpec looks like (and inspired from here)
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.matchers.MustMatchers
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
abstract class UnitTestSpec(name: String)
extends TestKit(ActorSystem(name))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll() {
system.shutdown()
}
}
Question
How can I mock the call to LogReaderDisruptor main(Array()) and verify that it was called?
I am coming from Java, JUnit, Mockito land and something that I would have done here would be
doNothing().when(logReaderDisruptor).main(Matchers.<String>anyVararg())
verify(logReaderDisruptor, times(1)).main(Matchers.<String>anyVararg())
I am not sure how to translate that with ScalaTest here.
Also, This code may not be idiomatic, since I am very new and learning
There are a few ways to do this. The kind of OO way is to wrap logDisrupter as an object and pass it in. I would set up a companion object to instantiate the actor. Something like below. Then you can pass alternate implementation. You can also achieve a similar approach by using traits and mixing in an alternative logDisrupter only as needed.
object LProcessor {
def props(logDisrupter : LogDisrupter) = Props(new LProcessor(logDisrupter))
}
class LProcessor(logDisrupter : LogDisrupter) extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
logDisrupter.doSomething();
}
}
Then instatiate as
val logProcessorActor = system.actorOf(LProcessor.props(logDisrupter))

Eagerly initialize singleton actor in scalaguice

I would like to eagerly initialize a singleton actor. I currently do the below and then later in my app startup get the instance of the actor.
`bind[Actor].annotatedWith(Names.named(LockCoordinator.name)).to[LockCoordinator].in[Singleton]`
I have tried
bind[Actor].annotatedWith(Names.named(LockCoordinator.name)).to[LockCoordinator].asEagerSingleton()
but fails at runtime with
1) Error injecting constructor, akka.actor.ActorInitializationException: You cannot create an instance of [LockCoordinator] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
I do create an eagerSingleton already for the system, but couldn't figure out how to apply it for an Actor (not ActorRef)
class ActorSystemProvider #Inject() (val config: Config, val injector: Injector) extends Provider[ActorSystem] {
override def get() = {
val system = ActorSystem(config.getString("mysystem"), config)
GuiceAkkaExtension(system).initialize(injector)
system
}
}
Is there a boilerplate free way of achieving this? As I want to apply this to 3-4 other actors
Does this help?
https://gist.github.com/fancellu/e4e8acdc3d7fd3b9d749352f9d6c68e3
import actors.ActorBrowserActor
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class Module extends AbstractModule with AkkaGuiceSupport{
def configure(): Unit = {
bindActor[ActorBrowserActor](ActorBrowserActor.NAME, _=>ActorBrowserActor.props)
}
}
here we inject the actor by name
class SampleController #Inject()(implicit system: ActorSystem, val messagesApi: MessagesApi, #Named("actor-browser-actor") actorBrowserActor: ActorRef)
extends Controller with I18nSupport{
}