Invoking an injected Scala class from an Akka actor - scala

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
}
}
}

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")
}

How to use a #Singleton from an Akka actor?

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 {

Play framework, configuration parameter injection in my custom class.

Is there another way than using a subclass of the Controller to benefit from the injected Play Framework pooled database parameter db ?
This is what the Documentation recommends :
class ScalaControllerInject #Inject()(db: Database) extends Controller {
def index = Action {
var outString = "Number is "
val conn = db.getConnection()
...
Ok(outString)
}
}
However I would like to have another class querying the database. This is because I am not using a subclass of the Controller class : I am using a websocket Actor.
This something I would like to aim for:
object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}
class MyWebSocketActor(out: ActorRef) extends Actor {
val gson = ToolSingleton.getInstance().getGsonTool()
def receive = {
case msg: String => {
val dbm=(new DbManager(... )). query(msg)
}
}
}
class DbManager #Inject()(db: Database) extends /* ? */ {
def query(s : String) {
var outString = "Number is "
val conn = db.getConnection()
...
}
}
How should I extend DbManager in order to use the function query from my actor MyWebSocketActor?

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)
}
}

Specs2 with Scaldi - wrong implicit injector being invoked

I'm trying to run a test with scaldi and specs2. In the test I need to override a StringManipulator function that uses an injected ProxyManipulator. The ProxyManipulator takes a string and returns its upper case in a Future. The replacement manipulator in the test returns a Future("Test Message").
Here is the StringManipulator class where the injection occurs:
class StringManipulator {
def manip (str : String) (implicit inj: Injector) : String = {
val prox = inject[ProxyManipulator]
Await.result(prox.manipulate(str), 1 second)
}
}
I'm using a package.object that contains the implicit injector:
import modules.MyModule
package object controllers {
implicit val appModule = new MyModule
}
And here is the specs2 test with the new binding:
#RunWith(classOf[JUnitRunner])
class StringManipScaldiSpec extends Specification {
class TestModule extends Module {
bind [ProxyManipulator] to new ProxyManipulator {
override def manipulate(name: String) = Future("Test Message")
}
}
"Application" should {
"do something" in {
val myTestModule = new TestModule
val str = "my string"
val stringMan = new StringManipulator() //(myTestModule)
stringMan.manip(str)(myTestModule) === "Test Message"
}
}
}
The problem is that when the test runs the class StringManipulator is still using the original Proxy Manipulator instead of the one passed in the TestModule. Any ideas?