Combining Dependency Injections and inheritance with Play 2.5 - scala

In my process of migrating an App from 2.4 to 2.5 (and getting rid of all the static references), I have done the following:
class Generic #Inject()(implicit val mat: Materializer, cache: CacheApi, wsClient: WSClient, configuration: play.api.Configuration)
{ ... }
#Singleton
class Concrete1 #Inject() (gw:Generic) { ... }
#Singleton
class Concrete2 #Inject() (gw:Generic) { ... }
To use it, I do inject Concrete1/2 with an instance of Generic.
It works, but after having seen several other examples about that on the web it doesn't seem quite correct.
I am thinking about modifying it like this :
abstract class Generic(cache: CacheApi, wsClient: WSClient, configuration: play.api.Configuration)
{ ... }
#Singleton
class Concrete1(cache: CacheApi, wsClient: WSClient, configuration: play.api.Configuration)
extends Generic(cache, wsClient, configuration) { ... }
#Singleton
class Concrete2(cache: CacheApi, wsClient: WSClient, configuration: play.api.Configuration)
extends Generic(cache, wsClient, configuration) { ... }
Then in order to be able to do : #Inject() (c1:Concrete1, c2:Concrete2)
I guess I need them to be modules as defined by : https://www.playframework.com/documentation/2.5.x/ScalaDependencyInjection#Programmatic-bindings ?
What makes more sense to do here ?

I'd actually disagree with your "doesn't seem quite correct" statement.
I'd argue that your first example more closely reflects the composition over inheritance philosophy that has become accepted as a more maintainable way to build software.
Without knowing anything about your Concrete or Generic classes, it's hard to say more, but I would be very surprised if the latter, inheritance-based structure, was easier to properly unit test, whereas it would be trivial to mock a Generic and inject it to test Concrete classes.
Other benefits:
less noisy/repetitive declaration
less tied to Play framework (no need for a module definition)
future flexibility (for when your concrete classes share a second "common" class)

Related

Use tapir router with play-framework without jsr330

I use play framework with tapir. My usage similar to sample https://github.com/gaeljw/tapir-play-sample
And we define router like this:
#Singleton
class ApiRouter #Inject() (apiController: BookController, apiDocumentation: ApiDocumentation, bookEndpoints: BookEndpoints)(implicit
val materializer: Materializer,
ec: ExecutionContext
) extends SimpleRouter {
...
override def routes: Routes = {
openApiRoute
.orElse(booksListingRoute)
.orElse(booksStreamingRoute)
.orElse(oneOfStreamingRoute)
.orElse(addBookRoute)
.orElse(getBookRoute)
}
routes
-> / routers.ApiRouter
Promblem, that our implementation of #Singleton is scaldi jsr330, that has some problems
How can I remove dependency on jsr330 (#Singleton) and use router as singleton without it? Note that just not using singleton leaves to performance problem.
If you don't need the routes file at all as all your routes are defined as code, you can get rid of the routes file and declare the ApiRouter class directly in application.conf in the play.http.router key.
That being said, there's no reason to necessary use DI. You could also define everything in an object.

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

Dependency Injection to Play Framework 2.5 modules

I have a module class with the following signature:
class SilhouetteModule extends AbstractModule with ScalaModule {
I would like to inject configuration:
class SilhouetteModule #Inject() (configuration: Configuration) extends AbstractModule with ScalaModule {
But it fails with the following error.
No valid constructors
Module [modules.SilhouetteModule] cannot be instantiated.
The Play documentation mentions that
In most cases, if you need to access Configuration when you create a component, you should inject the Configuration object into the component itself or...
, but I can't figure out how to do it successfully. So the question is, how do I inject a dependency into a module class in Play 2.5?
There are two solutions to solve your problem.
First one (and the more straight forward one):
Do not extend the com.google.inject.AbstractModule. Instead use the play.api.inject.Module. Extending that forces you to override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]]. Within that method you could do all your bindings and you get the configuration inserted as a method-parameter.
Second one (and the more flexible one):
Depending on your needs of the components you want to inject, you could define a provider for the component you want to bind. In that provider you could inject whatever you want. E.g.
import com.google.inject.Provider
class MyComponentProvider #Inject()(configuration:Configuration) extends Provider[MyComponent] {
override def get(): MyComponent = {
//do what ever you like to do with the configuration
// return an instance of MyComponent
}
}
Then you could bind your component within your module:
class SilhouetteModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[MyComponent]).toProvider(classOf[MyComponentProvider])
}
}
The advantage of the second version, is that you are able to inject what ever you like. In the first version you get "just" the configuration.
Change your constructor signature from:
class SilhouetteModule #Inject() (configuration: Configuration) extends AbstractModule with ScalaModule
to:
class SilhouetteModule(env: Environment, configuration: Configuration) extends AbstractModule with ScalaModule
see here for more info:
https://github.com/playframework/playframework/issues/8474

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

(Play 2.4) Dependency injection in a trait?

In play 2.4, is it possible to use dependency injection in a trait ?
Is there any example ?
Thanks.
I talk about runtime DI with Guice here because it's the default method used by Play. Other DI methods or frameworks may differ here.
It isn't possible to inject a dependency into a trait because a trait isn't instantiable. A trait doesn't have a constructor to define the dependencies.
In Play you could use the injector directly as long as the Application trait is in scope. But this isn't considered good practice in production code. In test code this would be an option.
class MySpec extends PlaySpecification {
"My test" should {
"Use the injector" in new WithApplication extends Context {
val messages = Messages(Lang("en-US"), messagesApi)
}
}
trait Context extends Scope {
self: WithApplication =>
val messagesApi = app.injector.instanceOf[MessagesApi]
}
}