Scala Play Framework - controller as class or singleton - scala

I'm trying Play 2.4.2 for Scala and I'm not clear whether controllers should be defined as classes or singletons. The docs state:
A Controller is nothing more than a singleton object that generates
Action values.
However the code sample shows:
class Application extends Controller { ... }
To further complicate things, intellij gives me a warning if I define a class:
However I get a compilation error (but no warning) if I use a singleton:
package controllers
import play.api._
import play.api.mvc._
object Application extends Controller { ... }
Error:(6, -1) Play 2 Compiler:
/Users/Toby/IdeaProjects/play-scala/conf/routes:6: type Application is
not a member of package controllers
Which approach is correct?

Your controllers should be objects if you use the static router. The static is the default router in Play 2.4 and has the same behavior from Play 2.3 and before.
You can convert your controllers to classes if you use the injected router which is new in Play 2.4. You need to enable the injected router in your build.sbt:
routesGenerator := InjectedRoutesGenerator
Update: the injected router is now the default in Play 2.5

Related

How do make a `CustomExecutionContext` available for dependency injection in a Play 2.6 controller?

I'm following along with Play 2.6's Scala documentation and sample code for creating non-blocking actions, and am running into some runtime issues. I have created a new Play application using the Scala template (sbt new playframework/play-scala-seed.g8).
The code that the Play documentation suggests should work in a new controller is (this code is taken verbatim from the Play documentation page, with some extra imports from me):
// some imports added by me to get the code to compile
import javax.inject.Inject
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import akka.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext
import play.api.mvc._
import play.api.mvc.ControllerComponents
// end imports added by me
import play.api.libs.concurrent.CustomExecutionContext
trait MyExecutionContext extends ExecutionContext
class MyExecutionContextImpl #Inject()(system: ActorSystem)
extends CustomExecutionContext(system, "my.executor") with MyExecutionContext
class HomeController #Inject()(myExecutionContext: MyExecutionContext, val controllerComponents: ControllerComponents) extends BaseController {
def index = Action.async {
Future {
// Call some blocking API
Ok("result of blocking call")
}(myExecutionContext)
}
}
Then, according to the documentation for using other thread pools, I've defined the my.executor thread pool in the application.conf file of my application:
my.executor {
fork-join-executor {
parallelism-factor = 20.0
parallelism-max = 200
}
}
I should note that I do not want to use the default execution context as I want to prepare for running futures in a separate context that may be used for a limited resource like a database connection pool.
All of this compiles just fine with sbt compile. However, when I run this with sbt run and access my app in a web browser, I get this error:
CreationException: Unable to create injector, see the following errors:
1) No implementation for controllers.MyExecutionContext was bound.
while locating controllers.MyExecutionContext
for the 1st parameter of controllers.NewController.(NewController.scala:17)
while locating controllers.NewController
for the 2nd parameter of router.Routes.(Routes.scala:29)
at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:121):
Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
I've used Play 2.3 in the past, and know that dependency injection works when you define an instance of an object (via #Singleton or in a module); however, Play 2.6's documentation on DI indicates that "Guice is able to automatically instantiate any class with an #Inject on its constructor without having to explicitly bind it. This feature is called just in time bindings is described in more detail in the Guice documentation."
My question is: what specific lines of code or configuration do I need to add to Play's own sample to make this work, and why?
I found one possible solution when reading further in the Binding Annotations section of the Scala Dependency Injection documentation page. In particular, it states:
The simplest way to bind an implementation to an interface is to use the Guice #ImplementedBy annotation.
So, by adding that to the my MyExecutionContext trait, like so:
import com.google.inject.ImplementedBy
#ImplementedBy(classOf[MyExecutionContextImpl])
trait MyExecutionContext extends ExecutionContext
an instance of the MyExecutionContextImpl is instantiated and properly injected into the controller.
Too bad that this #ImplementedBy annotation isn't listed in the sample code for the non-blocking action documentation!

How to use guice dependency injection in Play tests (Play 2.6.3)

I have a test specification called MyTestSpec. The aim it is to test a controller that uses dependency injection in its constructor.
class StatusController #Inject()(cc: ControllerComponents, counter: Counter) extends AbstractController(cc) { ...
The Counter class is another controller that provides a status over to the other. When I now try to test this controller, I have the option to either mock the required Counter class, construct the whole chain of controllers / dependencies or inject it. Let's ignore mocking for now, although it probably would be the more correct thing to do.
The issue is that always when I try to inject the Counter controller, I get a NullPointerException.
What I tried so far:
Inject the Counter into the constructor of MyTestSpec, but then MyTestSpec won't even be constructed and tested.
Inject into a local var with the #Inject annotation.
Inject into a val via app.injector.instanceOf[Counter].
Inject via the shortform inject[Counter].
All don't actually give me this other Controller.
Below a rough outline of the test, the new application to get started with Play can be easily adjusted to demonstrate the issue:
class MyTestSpec extends PlaySpec with GuiceOneAppPerTest with Injecting {
"StatusController GET" should {
"render the status page from a new instance of controller" in {
val controller = new StatusController(stubControllerComponents(), counter)
...
}
}
The question: is there a way to simply inject this Counter controller? If there is no simple way, the follow on question would be what components I can then inject anyway if I am already being restrained... ?
Note: this is different to the question asked here, although I tried the solution provided in it.
Note 2: thank you to the Play and ScalaTest developers as well as all these contributors behind the scenes - a wonderful framework.

Play 2.5.X dependency injection

I am upgrading play framework app from 2.4.6 to 2.5.x.
There are several occurrences where I call helper methods which belongs to some object. These helper methods use play's built-in classes(for example play.api.Play.current.configuration.underlying.getString) to get the job done.
I get following warning: "method current in object Play is deprecated: This is a static reference to application, use DI instead"
If I face this problem in class method then I can use dependency injection. How to deal with such a situation where method belongs to object and I am warned to use DI?
Play Framework usually provides a class you can inject instead of using the old static references.
For example, the below would mean you can stop using Play.current.configuration and DB:
import javax.inject.Inject
import play.api.db.Database
import play.api.Configuration
class MyClass #Inject() (configuration: Configuration, db: Database) {
...
}

Use WSClient in scala app (play framework)

I'm not sure if there's something really basic that I'm missing, but I can't figure out how to use WSClient. I've seen all of the examples saying you need to pass the WSClient to a class as a dependency, which I've done, but when I run the program what do I actually pass to my class?
For example, my class signature is:
class myClassName(ws: WSClient)
But when I instantiate the class what do I actually pass to it? I'm also happy to ignore the Play! framework stuff if that makes it easier and just use SBT to run it (which I'm more familiar with).
It's unclear where you might be using a WSClient, but it is recommended that you let the Play framework 'manage' the instance of the client. When you instantiate your application, it gets injected:
class Application #Inject() (ws: WSClient) extends Controller {
...
}
What that means is that inside the ... you have access to ws as a value. You can instantiate myClassName using it:
class Application #Inject() (ws: WSClient) extends Controller {
val myclass = myClassName(ws) // passes the injected WSClient to myClassName
}
Or you can write a function that returns the WSClient, so some other area of your code can call into your Application object to get a object handler for it.
But the key is that the Application object gets that handle because of injection, which is the #Inject annotation.
If you need to generate a WSClient and manage it manually, there are good instructions here. The recommended implementation is reliant on Play! framework libraries, but doesn't depend on the Application.

Controller not seen

I have created a Scala project and I added the following controller in /app/controllers:
package controllers
import play.api._
import play.api.mvc._
class Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
}
I have added its index method to the routes file:
GET / controllers.Application.index
However, when I access localhost:9000 in the browser, I get the error:
type Application is not a member of package controllers
Why is the controller not visible?
This is likely because your route refers to a static controller, i.e. an object rather than a class. There are a few different ways you could fix this:
Declare your controller as an object:
object Application extends Controller {
...
}
Alternately, you can prefix the routes action reference with a #, indicating that it's a class that should be instantiated when the route is invoked:
GET / #controllers.Application.index
You can also use the injected, rather than the (default) static router, which involves adding the following line to your build.sbt:
routesGenerator := InjectedRoutesGenerator
In the early versions of Play 2.0 controllers were always static objects, but in recent versions it is typical to use a class, into which you can inject components. Have a look at the routes documentation for details.