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?
Related
I have a simple companion Object that is creating a connection pool that should be globally static.
object CoreDataSource {
val pool : DataSource = getDataSource
def getDataSource: DataSource = {
...
}
}
I then use that in a variety of classes like...
class Query {
var source: DataSource = CoreDataSource.pool
def getSalutations: String = {
val conn : Connection = source.getConnection
...
}
}
Now I would like to test the getSalutations without the getDataSource actually getting called. Is there a way to mock out this call so that when the initial getDataSource call happens it hits a mock instead of the actual function?
Here is the skeleton of my test suite...
#RunWith(classOf[JUnitRunner])
class MySuite extends AnyFunSuite with MockitoSugar {
test("Test mocking the Datasource") {
// Mock object here
}
}
I Also tried...
var pool : Option[DataSource] = None
def getConnection: Connection = {
if(pool.isEmpty)
pool = getDataSource
pool.get.getConnection
}
def getDataSource: Option[DataSource] = {
...
}
}
#RunWith(classOf[JUnitRunner])
class MySuite extends AnyFunSuite with MockitoSugar with Matchers {
test("Test mocking the Datasource") {
withObjectMocked[CoreDataSource.type]{
val source = mock[DataSource]
when(CoreDataSource.getDataSource).thenReturn(Some(source))
...
assert(result == "")
}
}
}
But this throws
Mockito cannot mock/spy because :
- final class
I am not going to accept my workaround because it is ugly but this ended up working...
class CoreDataSource {
def getConnection = {
CoreDataSource.getConnection
}
}
object CoreDataSource {
var pool : Option[DataSource] = None
def getConnection: Connection = {
if(pool.isEmpty)
pool = getDataSource
pool.get.getConnection
}
def getDataSource: Option[DataSource] = {
...
}
}
class Query(ds: CoreDataSource = new CoreDataSource) {
def getSalutations: String = {
...
}
}
Then the following will work...
test("Single record returned") {
val cds = mock[CoreDataSource]
val source = mock[Connection]
doNothing.when(source).close()
when(cds.getConnection).thenReturn(source)
val q = new Query(cds)
val result = q.getSalutations
assert(result == "This is the test value")
}
I have something like this:
abstract class HaveData{
val data:String
}
class HD1 extends HaveData{
val data = "HD1 data"
}
class HD2 extends HaveData{
val data = "HD2 data"
object InnerHD extends HD1{
def prt = println(data)
}
}
I want to print "HD2 data" not "HD1 data", how to do that?
I may turn InnerHD to a class and provide data as a param but is there a better way?
For this kind of situations you can use a self-type, for this specific problem, you do not need to add a different type.
abstract class HaveData {
def data: String
}
class HD1 extends HaveData {
override val data = "HD1 data"
}
class HD2 extends HaveData { self => // Alias to this in the HD2 level.
override val data = "HD2 data"
object InnerHD extends HD1 {
def prt(): Unit = {
println(self.data)
}
}
}
(new HD2).InnerHD.prt()
// HD2 data
You can also use
def prt = println(HD2.this.data)
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")
}
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
}
}
}
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?