Play Framework without dependency injection? - scala

Without going into why, say someone wanted an old-fashioned Play Framework web service and did not want to use dependency injection nor rely on Google's Guice. Is it still possible in Play 2.8.x?
The api documentation along with the current Play examples recommend this as a "typical" HomeController.scala:
package controllers
import javax.inject._
import play.api.mvc._
class HomeController #Inject() (val controllerComponents: ControllerComponents) extends BaseController {
def index = Action {
Ok("It works!")
}
}
My desired code is the same, but without #Inject() (similar to when I last used Play 2.4.0 in 2016)? Back in the day my code looked like this:
package controllers
import play.api.mvc.{Action, AnyContent, Controller}
object TestController {
def index:Action[AnyContent] = Action {
Ok("It used to work.")
}
}
Console:
[info] Compiling 1 Scala source to /Volumes/.../play-scala-seed/target/scala-2.13/classes ...
[error] p.a.h.DefaultHttpErrorHandler -
! #7ef69nl6l - Internal server error, for (GET) [/test/] ->
play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:
1) Could not find a suitable constructor in controllers.TestController. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at controllers.TestController.class(TestController.scala:3)
while locating controllers.TestController
for the 4th parameter of router.Routes.<init>(Routes.scala:33)
at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:137):
Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$4)
1 error]
at play.core.server.DevServerStart$$anon$1.reload(DevServerStart.scala:210)
at play.core.server.DevServerStart$$anon$1.get(DevServerStart.scala:141)
at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:296)
at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:186)
at akka.stream.impl.fusing.MapAsync$$anon$30.onPush(Ops.scala:1261)
at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:541)
at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:423)
at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:624)
at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:501)
at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:599)
Caused by: com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Could not find a suitable constructor in controllers.TestController. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at controllers.TestController.class(TestController.scala:3)
while locating controllers.TestController
for the 4th parameter of router.Routes.<init>(Routes.scala:33)
at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:137):
Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$4)
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:543)
at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:159)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:106)
at com.google.inject.Guice.createInjector(Guice.java:87)
at com.google.inject.Guice.createInjector(Guice.java:78)
at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:200)
at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:155)
at play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
at play.core.server.DevServerStart$$anon$1.$anonfun$reload$3(DevServerStart.scala:189)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
Is there a simple fix to stay old school--without going here?
I acknowledge but don't totally understand https://www.playframework.com/documentation/2.4.x/Migration24. I assume my problem has to do with static routing having been removed in 2.7.

My reputation doesn't allow me to comment to the answer by Mario Galic, but you can easily modify his example by using the "right" (non-test) controllerComponents that are provided by BuiltInComponentsFromContext.
The whole example would look like
class HomeController(override protected val controllerComponents: ControllerComponents)
extends BaseController {
def index = Action { Ok("It works!") }
}
class MyApplicationLoader extends ApplicationLoader {
def load(context: ApplicationLoader.Context): Application = {
new BuiltInComponentsFromContext(context) {
lazy val homeController = HomeController(controllerComponents)
override lazy val router: Router = Routes(httpErrorHandler, homeController)
}.application
}
}

To answer comment by #kujosHeist about an equivalent example in Java. This seems to work for me (Following the Play docs):
package controllers;
import play.mvc.Controller;
import play.mvc.Result;
public class HomeController extends Controller {
public Result index() {
return ok("It works!");
}
}
public class MyApplicationLoader implements ApplicationLoader {
#Override
public Application load(Context context) {
return new MyComponents(context).application();
}
}
class MyComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents, AssetsComponents {
public MyComponents(ApplicationLoader.Context context) {
super(context);
}
#Override
public Router router() {
HomeController homeController = new HomeController();
Assets assets = new Assets(scalaHttpErrorHandler(), assetsMetadata());
return new router.Routes(scalaHttpErrorHandler(), homeController, assets).asJava();
}
}
You might want to define things like e.g. error handlers differently, but this could be roughly the structure.

Indeed StaticRoutesGenerator has been removed which is need to have controllers as singleton objects. Perhaps using compile time dependency injection, with an example here, might bring you closer to what you were used to, however ControllerComponents will still need to be injected. Technically, it might be possible to do something ill-advised by putting play-test on the Compile classpath and make use stubControllerComponents like so
class HomeController extends BaseController {
def index = Action { Ok("It works!") }
override protected def controllerComponents: ControllerComponents =
play.api.test.Helpers.stubControllerComponents()
}
and corresponding minimal ApplicationLoader
class MyApplicationLoader extends ApplicationLoader {
def load(context: ApplicationLoader.Context): Application = {
new BuiltInComponentsFromContext(context) {
override def httpFilters: Seq[EssentialFilter] = Nil
lazy val homeController = new _root_.controllers.HomeController
lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController)
}.application
}
}
This way HomeController, although still a class, is now completely hardwired, and there is only a single instance of it created in ApplicationLoader.
Personally, I would advise against such shenanigans, and believe there are good arguments why Play moved away from singletons, for example, testability, thread-safety, etc.

Related

Is it possible to use scala self types with guice?

I have a controller:
class HomeController #Inject() (cc: ControllerComponents) extends AbstractController(cc)with Logging
{
this: SecuredActionByToken =>
def index = CheckedToken{ ...
Where SecuredActionByToken trait is using class names CheckTokenService to verify that user can run 'index' (we are trying to move from kind of cake pattern to Guice).
Is it possible to inject CheckTokenService to SecuredActionByToken? Of course, I could inject it to HomeController itself and work somehow with it, but I don't really want to push some services to controllers when they don't directly use them.
i ended up creating my own provider for this bean, this way I can inject service directly to provider and use it in overriden trait method:
class HomeControllerFactory #Inject()
(controllerComponents: ControllerComponents, cts: CheckTokenService, dbConfigProvider: DatabaseConfigProvider)
extends Provider[HomeController]{
override def get(): HomeController = {
new HomeController(controllerComponents) with SecuredActionByToken {
override def checkTokenService: CheckTokenService = cts
}
}
}
plus binding:
bind(classOf[HomeController]).toProvider(classOf[HomeControllerFactory])
I can also test is by creating homeController like this:
val testCheckTokenService =...
val homeController = new HomeController(stubControllerComponents()) with SecuredActionByToken {
override def checkTokenService: CheckTokenService = testCheckTokenService....
}

Scala Guice - inject with a mixin

Is it possible to instantiate the dependency first and then bind it in the module config method?
Currently I have the following config:
class PersonServiceImpl #Inject()(addressService: AddressService) {
...
}
class AppModule extends AbstractModule with ScalaModule {
def configure() {
bind[PersonService].to[PersonServiceImpl]
bind[AddressBook].to[AddressBookImpl]
}
#Provides #Singleton
def provideAddressService(addressBook: AddressBook): AddressService = {
new AddressServiceImpl(addressBook) with SecureAddressView
}
}
... which works fine. What I want to do now is to move the instantiation of the AddressServiceImpl into a separate module. So, the problem is that in order to create an instance of AddressServiceImpl I need Guice to inject the addressBook parameter for me, but I also want to create the instance myself so I can mix SecureAddressView in:
class AddressModule extends AbstractModule with ScalaModule {
def configure() {
bind[AddressService].to[AddressServiceImpl]
}
#Provides #Singleton
def provideAddressService(addressBook: AddressBook): AddressService = {
new AddressServiceImpl(addressBook) with SecureAddressView
}
}
This fails, though, as Guice comes back complaining about the provideAddressService method. It basically says that A binding to AddressService was already configured and points to the bind[AddressService].to[AddressServiceImpl] line in the configure method.
Any idea how to create an instance and mix in a trait while still delegating the resolution of downstream parameter dependencies to Guice?
OK, quite an obvious one but I was misled by the fact that I had to override the configure method. So, all I had to do is provide a dummy implementation for configure.
class AddressModule extends AbstractModule with ScalaModule {
override def configure(): Unit = ()
#Provides #Singleton
def provideAddressService(addressBook: AddressBook): AddressService = {
new AddressServiceImpl(addressBook) with SecureAddressView
}
}
Although this still looks quite dodgy as I have to provide explicitly all the parameters to the AddressService constructor. There must be a more elegant way of mixin traits. Or maybe not...

Manual Dependancy Injection App testing Play 2.5.x

I have project with manual dependency injection. Can I test my application with standard Play test suite?
play.application.loader="AppLoader"
class AppLoader extends ApplicationLoader {
override def load(context: Context): Application = {
LoggerConfigurator(context.environment.classLoader).foreach(_.configure(context.environment))
new AppComponents(context).application
}
}
}
class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) with EhCacheComponents with EvolutionsComponents with DBComponents with HikariCPComponents{
lazy val applicationController = new controllers.Application(defaultCacheApi, dbApi.database("default"))
lazy val usersController = new controllers.Users(defaultCacheApi)
lazy val assets = new controllers.Assets(httpErrorHandler)
//applicationEvolutions
// Routes is a generated class
override def router: Router = new Routes(httpErrorHandler, applicationController, usersController, assets)
For now test is very simple
class ApplicationTest extends PlaySpec with OneAppPerTest {
"Application" must {
"send 404 on a bad request" in {
route(FakeRequest(GET, "/boum")) mustBe None
}
}
}
Test ends up with error:
Could not find a suitable constructor in controllers.Application. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument
I presume I need somehow use my AppLoader instead of defualt Guice mechanisam inside ApplicationTest class, because Application controller has dependacy ( cacheApi, dbApi ...)
route method can take application as argument but how can I obtain context to manually instantiate AppLoader class ? Being newbie in Scala recommendations are most welcomed.
This example answered all of my questions:
https://github.com/playframework/play-scala-compile-di-with-tests
Using term compile time dependency injection yield much more results then manual dependency injection.

Guice in Scala: Module for a class that has a DI-constructor itself

I'm using codingwell/scala-guice and trying to inject DAO-classes into constructors of other components/classes.
In the first attempt, I only used one DAO-class to see if it works:
class DaoModule extends AbstractModule with ScalaModule {
override def configure() {
val dao1 = new FirstDaoImpl
bind(new TypeLiteral[FirstDaoTrait] {}).toInstance(dao1)
}
}
The binding works as expected, it can be used for constructor injection.
In the second step, I wanted to add another DAO class to the module. However, that DAO-class depends on the first DAO:
class SecondDaoImpl #Inject()(firstDao: FirstDaoTrait) extends SecondDaoTrait
I'm not sure how to add the necessary binding to the existing module. Repeating the first step would result in this:
val dao2 = new SecondDaoImpl(???)
bind(new TypeLiteral[SecondDaoTrait] {}).toInstance(dao2)
But of course this class can only be instantiated by providing the first DAO (therefore the "???"). How can I do this?
Use bind and let scala-guice resolve the dependencies for you:
class DaoModule extends AbstractModule with ScalaModule {
override def configure() {
bind[FirstDaoTrait].to[FirstDaoImpl]
bind[SecondDaoTrait].to[SecondDaoImpl]
}
}
And now using the injector:
val injector = Guice.createInjector(new DaoModule())
val secondDao = injector.instance[SecondDaoTrait]

Dependency injection with abstract class and object in Play Framework 2.5

I'm trying to migrate from Play 2.4 to 2.5 avoiding deprecated stuff.
I had an abstract class Microservice from which I created some objects. Some functions of the Microservice class used play.api.libs.ws.WS to make HTTP requests and also play.Play.application.configuration to read the configuration.
Previously, all I needed was some imports like:
import play.api.libs.ws._
import play.api.Play.current
import play.api.libs.concurrent.Execution.Implicits.defaultContext
But now you should use dependency injection to use WS and also to use access the current Play application.
I have something like this (shortened):
abstract class Microservice(serviceName: String) {
// ...
protected lazy val serviceURL: String = play.Play.application.configuration.getString(s"microservice.$serviceName.url")
// ...and functions using WS.url()...
}
An object looks something like this (shortened):
object HelloWorldService extends Microservice("helloWorld") {
// ...
}
Unfortunately I don't understand how I get all the stuff (WS, configuration, ExecutionContect) into the abstract class to make it work.
I tried to change it to:
abstract class Microservice #Inject() (serviceName: String, ws: WSClient, configuration: play.api.Configuration)(implicit context: scala.concurrent.ExecutionContext) {
// ...
}
But this doesn't solve the problem, because now I have to change the object too, and I can't figure out how.
I tried to turn the object into a #Singleton class, like:
#Singleton
class HelloWorldService #Inject() (implicit ec: scala.concurrent.ExecutionContext) extends Microservice ("helloWorld", ws: WSClient, configuration: play.api.Configuration) { /* ... */ }
I tried all sorts of combinations, but I'm not getting anywhere and I feel I'm not really on the right track here.
Any ideas how I can use things like WS the proper way (not using deprecated methods) without making things so complicated?
This is more related to how Guice handles inheritance and you have to do exactly what you would do if you were not using Guice, which is declaring the parameters to the superclass and calling the super constructor at your child classes. Guice even suggest it at its docs:
Wherever possible, use constructor injection to create immutable objects. Immutable objects are simple, shareable, and can be composed.
Constructor injection has some limitations:
Subclasses must call super() with all dependencies. This makes constructor injection cumbersome, especially as the injected base class changes.
In pure Java, it will means doing something like this:
public abstract class Base {
private final Dependency dep;
public Base(Dependency dep) {
this.dep = dep;
}
}
public class Child extends Base {
private final AnotherDependency anotherDep;
public Child(Dependency dep, AnotherDependency anotherDep) {
super(dep); // guaranteeing that fields at superclass will be properly configured
this.anotherDep = anotherDep;
}
}
Dependency injection won't change that and you will just have to add the annotations to indicate how to inject the dependencies. In this case, since Base class is abstract, and then no instances of Base can be created, we may skip it and just annotate Child class:
public abstract class Base {
private final Dependency dep;
public Base(Dependency dep) {
this.dep = dep;
}
}
public class Child extends Base {
private final AnotherDependency anotherDep;
#Inject
public Child(Dependency dep, AnotherDependency anotherDep) {
super(dep); // guaranteeing that fields at superclass will be properly configured
this.anotherDep = anotherDep;
}
}
Translating to Scala, we will have something like this:
abstract class Base(dep: Dependency) {
// something else
}
class Child #Inject() (anotherDep: AnotherDependency, dep: Dependency) extends Base(dep) {
// something else
}
Now, we can rewrite your code to use this knowledge and avoid deprecated APIs:
abstract class Microservice(serviceName: String, configuration: Configuration, ws: WSClient) {
protected lazy val serviceURL: String = configuration.getString(s"microservice.$serviceName.url")
// ...and functions using the injected WSClient...
}
// a class instead of an object
// annotated as a Singleton
#Singleton
class HelloWorldService(configuration: Configuration, ws: WSClient)
extends Microservice("helloWorld", configuration, ws) {
// ...
}
The last point is the implicit ExecutionContext and here we have two options:
Use the default execution context, which will be play.api.libs.concurrent.Execution.Implicits.defaultContext
Use other thread pools
This depends on you, but you can easily inject an ActorSystem to lookup the dispatcher. If you decide to go with a custom thread pool, you can do something like this:
abstract class Microservice(serviceName: String, configuration: Configuration, ws: WSClient, actorSystem: ActorSystem) {
// this will be available here and at the subclass too
implicit val executionContext = actorSystem.dispatchers.lookup("my-context")
protected lazy val serviceURL: String = configuration.getString(s"microservice.$serviceName.url")
// ...and functions using the injected WSClient...
}
// a class instead of an object
// annotated as a Singleton
#Singleton
class HelloWorldService(configuration: Configuration, ws: WSClient, actorSystem: ActorSystem)
extends Microservice("helloWorld", configuration, ws, actorSystem) {
// ...
}
How to use HelloWorldService?
Now, there are two things you need to understand in order to proper inject an instance of HelloWorldService where you need it.
From where HelloWorldService gets its dependencies?
Guice docs has a good explanation about it:
Dependency Injection
Like the factory, dependency injection is just a design pattern. The core principle is to separate behaviour from dependency resolution.
The dependency injection pattern leads to code that's modular and testable, and Guice makes it easy to write. To use Guice, we first need to tell it how to map our interfaces to their implementations. This configuration is done in a Guice module, which is any Java class that implements the Module interface.
And then, Playframework declare modules for WSClient and for Configuration. Both modules gives Guice enough information about how to build these dependencies, and there are modules to describe how to build the dependencies necessary for WSClient and Configuration. Again, Guice docs has a good explanation about it:
With dependency injection, objects accept dependencies in their constructors. To construct an object, you first build its dependencies. But to build each dependency, you need its dependencies, and so on. So when you build an object, you really need to build an object graph.
In our case, for HelloWorldService, we are using constructor injection to enable Guice to set/create our object graph.
How HelloWorldService is injected?
Just like WSClient has a module to describe how an implementation is binded to an interface/trait, we can do the same for HelloWorldService. Play docs has a clear explanation about how to create and configure modules, so I won't repeat it here.
But after creating an module, to inject a HelloWorldService to your controller, you just declare it as a dependency:
class MyController #Inject() (service: Microservice) extends Controller {
def index = Action {
// access "service" here and do whatever you want
}
}
In scala,
-> If you do not want to explicitly forward all the injected parameters to the base constructor, you can do it like that :
abstract class Base {
val depOne: DependencyOne
val depTwo: DependencyTwo
// ...
}
case class Child #Inject() (param1: Int,
depOne: DependencyOne,
depTwo: DependencyTwo) extends Base {
// ...
}