Scala Play run time injection based on request parameter - scala

I am using Scala Play 2.6 and trying to use dependency injection to instantiate a service class based on request parameter. As below example code, the controller class get payment method from query string
package controllers
import com.google.inject.Inject
import play.api.mvc._
import scala.concurrent.ExecutionContext
class PaymentController #Inject()()
(implicit ec: ExecutionContext)
extends InjectedController {
def doPayment() = Action.async { implicit request =>
request.getQueryString("payment-method").getOrElse("") match {
case "paypal" => // Inject a PaypalPaymentService
val paymentService = Play.current.injector.instanceOf[PaypalPaymentService]
paymentService.processPayment()
case "creditcard" => // Inject a CreditCardPaymentService
val paymentService = Play.current.injector.instanceOf[CreditCardPaymentService]
paymentService.processPayment()
case _ => // Return error
}
}
}
And services class to process Paypal or CreditCard payment
package services
import scala.concurrent.Future
trait PaymentService {
def processPayment(): Future[Boolean]
}
package services
import com.google.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.ws.WSClient
class PaypalPaymentService #Inject()(ws: WSClient)
(implicit ec: ExecutionContext)
extends PaymentService {
def processPayment(): Future[Boolean] = {
//Process paypal payment
}
}
class CreditCardPaymentService #Inject()(ws: WSClient)
(implicit ec: ExecutionContext)
extends PaymentService {
def processPayment(): Future[Boolean] = {
//Process credit card payment
}
}
For Play 2.5 onwards, Play.current and Play.application have been deprecated.
I have two questions:
Is the above example code a correct way to inject a class based on
request parameter? or is there some other better way to do so?
For Play 2.5/2.6, what is the way to get the application injector?

You have stated correctly that Play.current and Play.application have been deprecated and from 2.5 onwards the way to use them is indeed by injecting them.
I would change your controller definition so that you make use of DI to include the needed components. Something like:
class PaymentController #Inject()(configuration: Configuration)
(implicit ec: ExecutionContext) extends Controller {
// your code goes here
}
Now comes the tricky part. You might thing that it is possible just to inject application: play.Application but this is not entirely true because you are going to run into circular dependencies. This is normal because you want to inject the whole application while actually being in it. There is one hack for this and it is by injecting Provider[Application]. I call this a hack because normally you don't need/want to inject the whole application. In 99% of the cases you are interested only in specific parts - e.g. the Configuration, Environment, etc.
And here comes the solution. You can just inject your Injector
class PaymentController #Inject()(injector: Injector)
(implicit ec: ExecutionContext) extends Controller {
// your code goes here
}
From here the game is an easy one. Just use the Injector to get the needed services. Like this:
case "paypal" => // Inject a PaypalPaymentService
val paymentService = injector.instanceOf(classOf[PaypalPaymentService])
paymentService.processPayment()
Last words regarding the "correct way" of using this. I actually find your approach OK and would not necessarily change it. Just one idea in this direction is that you create a Module like this:
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class PaymentModule extends AbstractModule {
def configure() = {
bind(classOf[PaymentService])
.annotatedWith(Names.named("paypal"))
.to(classOf[PaypalPaymentService])
bind(classOf[PaymentService])
.annotatedWith(Names.named("creditcard"))
.to(classOf[CreditCardPaymentService])
}
}
Having a common trait (as you do it) helps in this case and you can have multiple implementations, even mocked ones for your tests. The module will be registered automatically if it lies in the root package. Otherwise you should tell Play the location of it:
play.modules.enabled += "modules.PaymentModule"

Related

Unable to provision because of "No implementation for controllers.MyExecutionContext was bound"

I'm trying to follow the instructions to create a Play Framework async controller. So far my code is little more than a cut & paste from the Play documentation:
package controllers
import akka.actor.ActorSystem
import com.google.inject.Inject
import play.api.libs.concurrent.CustomExecutionContext
import play.api.mvc.{AbstractController, ControllerComponents}
import scala.concurrent.{ExecutionContext, Future}
trait MyExecutionContext extends ExecutionContext
class MyExecutionContextImpl #Inject()(system: ActorSystem)
extends CustomExecutionContext(system, "my.executor") with MyExecutionContext
class FooController #Inject() (myExecutionContext: MyExecutionContext, cc:ControllerComponents) extends AbstractController(cc) {
def foo = Action.async(
Future {
// Call some blocking API
Ok("result of blocking call")
}(myExecutionContext)
)
}
When I try to run this new controller, I get the following error:
ProvisionException: Unable to provision, see the following errors:
1) No implementation for controllers.MyExecutionContext was bound.
while locating controllers.MyExecutionContext
for the 1st parameter of controllers.FooController.<init>(FooController.scala:14)
while locating controllers.FooController
for the 4th parameter of router.Routes.<init>(Routes.scala:33)
while locating router.Routes
while locating play.api.inject.RoutesProvider
Can anybody explain what might be going wrong here?
The exception indicates that you have not bound the implementation (MyExecutionContext) to the trait (MyExecutionContextImpl) in your Module.
Try this:
class Module extends AbstractModule {
override def configure(): Unit = {
bind(classOf[MyExecutionContext])
.to(classOf[MyExecutionContextImpl])
}
}
However I never used your approach. I use only the default Execution Context like:
class FooController #Inject()()(implicit val ec: ExecutionContext)
The previous answer does not work. Try instead using #ImplementedBy
(source: https://stackoverflow.com/a/53162884/4965515)
In addition you will need to configure your executor in conf/application.conf. For instance try adding:
my.executor {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
core-pool-size-factor = 10.0
core-pool-size-max = 10
}
}
(source https://stackoverflow.com/a/46013937/4965515)

having problems calling a method from controller in scala

i'm using playframework with scala and slick.
in my dto (dao) I do this:
class processDTO #Inject() (protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile]
with ProcessTemplatesComponent {
import driver.api._
private val processTemplates = TableQuery[ProcessTemplates]
def getAll(): Future[Seq[ProcessTemplatesModel]] = db.run { processTemplates.to[Seq].result }
}
and in controller I do this:
#Singleton
class ProcessesController #Inject() (processDTO: processDTO, actionBuilder: ActionBuilders) extends Controller{
def getProcesses() = actionBuilder.DynamicAction(name = "pureLuck").defaultHandler() {
request =>
processDTO.getAll().map(_.map(result => {
}))
.map(result => Ok(Json.toJson(result)))
}
}
and now I got this error
First of all you need to import execution context. PlayFramework has his own context. Add this import play.api.libs.concurrent.Execution.Implicits._ Play documentation
Also you need to return something from this code of block:
_.map(result => {})
Currently you return Unit which can't be transformed to json.
The error tells you what you need to know. Adding import scala.concurrent.ExecutionContext.Implicits.global at the top of your file should fix it.
One issue is the lack of execution context as other had mentioned.
As of your json error, you need to implement a Write converter to transform your ProcessTemplatesModel objects into json.

Why are implicit variables not initialized in Scala when called from unit test?

Given the the following singleton Object in Scala:
package demo
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.io.StdIn
object WebServer extends App {
implicit val system = ActorSystem("myActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
val route = {
path("api" / "done-as-promised") {
get {
complete {
Future.successful("done")
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
And the following unit test
package demo
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Inspectors, Matchers, WordSpec}
class WebServerSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with Inspectors with ScalatestRouteTest {
"The WebServer /done-as-promised" should {
"return done" in {
// tests:
Get("/api/done-as-promised") ~> WebServer.route ~> check {
status.intValue() shouldEqual 200
responseAs[String] shouldEqual "done"
}
}
}
}
I get the following error:
[ERROR] [04/19/2016 07:12:18.995]
[ScalaTest-run-running-WebServerSpec]
[akka.actor.ActorSystemImpl(demo-WebServerSpec)] Error during
processing of request
HttpRequest(HttpMethod(GET),http://example.com/api/done-as-promised,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))
java.lang.NullPointerException at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:33)
at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:29)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:162)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:150)
It took me a while to figure out. The thing is: removing the extends app will make the test succeed.
The reason for the problem is that when WebServer is declared as extends App, it uses the DelayedInit functionality of the App trait. Because of this, the initialization code in the contructor is not added to the constructor of the WebServer object. Instead is called when the main method is called on the WebServer. So when he references the "route" inside the tests, those are all coming up null.
Mixing in the DelayedInit trait (App extends from DelayedInit) will rewrite your class or object template. Instead of adding your val's and var's to the constructor, it will be added to the def delayedInit(body: => Unit) hook (inaccessible to user code). Apparently this one is called whenever the main method is called.
You can verify this by simply calling "main" on the WebServer inside the test. If you do this, then the test will pass. This is because calling main triggers the initialization resulting in those objects being created.
Generally speaking though the right solution is probably to move the routing to somewhere else, rather than having it inside of the base App.

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