Play Framework 2.5.x: Inject Environment in a Module - scala

Play app contains a custom module that initializes DB/Tables during the app start.
Starting with v2.5.x, the application mode(prod, test, dev) needs to be injected rather than using Current. I tried to apply the following inject parameters in the module code but saw compile errors.
trait MyInit[Environment] {
val env: Environment
}
class MyInitClass[Environment] #Inject() (
val env: Environment
) extends MyInit[Environment] {
initializeDB()
def initializeDB() = {
Logger.info("MyINIT: Creating database...")
MyDBService.createDatabase()
Logger.info("MyINIT: Creating tables...")
MyDBService.createTables()
}
}
class MyModule(environment: Environment,
configuration: Configuration) extends AbstractModule with ScalaModule {
def configure() = {
bind[MyInit[Environment]].to[MyInitClass[Environment]].asEagerSingleton()
}
}
Is there a prescribed way of looking up Environment/Mode during module initialization in Play 2.5.x (Scala)?

I will rewrite your code to remove a small possible confusion here. This is exactly your code with a small rename of the type parameters:
import javax.inject.Inject
import play.api.Logger
import play.api.Environment
import play.api.Configuration
import com.google.inject.AbstractModule
trait MyInit[T] {
val env: T
}
class MyInitClass[T] #Inject() (val env: T) extends MyInit[T] {
initializeDB()
def initializeDB() = {
Logger.info("MyINIT: Creating database...")
MyDBService.createDatabase()
Logger.info("MyINIT: Creating tables...")
MyDBService.createTables()
}
}
I renamed the type parameters to avoid confusion with play.api.Environment. I have the felling that this is not what you want, since MyInitClass does not actively receive an play.api.Environment anywhere.
If you want to do some initialization, why not go with something more simple and direct like this:
import com.google.inject.AbstractModule
import play.api.{Configuration, Environment, Mode}
class DatabaseInitializerModule(
environment: Environment,
configuration: Configuration
) extends AbstractModule {
def configure() = {
environment.mode match {
case Mode.Dev =>
// Start dev database
case Mode.Test =>
// Start test database
case Mode.Prod =>
// Start prod database
}
}
}
Edit:
A more idiomatic and recommended way to replace GlobalSettings.onStart is described at the docs:
GlobalSettings.beforeStart and GlobalSettings.onStart: Anything that needs to happen on start up should now be happening in the constructor of a dependency injected class. A class will perform its initialisation when the dependency injection framework loads it. If you need eager initialisation (because you need to execute some code before the application is actually started), define an eager binding.
So, we can rewrite your code to be something like this:
import javax.inject.Inject
import play.api.{Environment, Mode}
import com.google.inject.AbstractModule
class DatabaseInitializer #Inject()(environment: Environment) {
environment.mode match {
case Mode.Dev =>
// Start dev database
case Mode.Test =>
// Start test database
case Mode.Prod =>
// Start prod database
}
}
class DatabaseInitializerModule extends AbstractModule {
override def configure() = {
bind(classOf[DatabaseInitializer]).asEagerSingleton()
}
}
Notice how I've removed the trait since it is not adding any value here.

Related

Scala Play run time injection based on request parameter

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"

Play 2.5 priming the web service

I have a restful web service developed using play 2.5 framework.
I want to prime my web service on start by calling itself. This is to make sure that my service is perfectly up and running.
The approach i am taking is using eagerBinding. But the code inside the class injected using eager binding gets executed just before the app starts
Here is what my eagerbinding code looks like
#Singleton
class PrimingMe #Inject()(ws: WSClient) {
isServicePrimed
def isServicePrimed: Boolean = {
println("PRIME ME!!!")
val response = ws.url("http://localhost:9000/index").get
.map {
response =>
response.status match {
case 200 => true
case _ => false
}
}
try {
Await.result(response, 5.second)
} catch {
case _ => false
}
}
}
class ServiceInjectionModule extends AbstractModule {
def configure(): Unit = {
bind(classOf[PrimingMe]).asEagerSingleton
}
}
Inside application.conf
play.modules.enabled += "util.ServiceInjectionModule"
I want to prime my application with a dummy service call so that when the real traffic starts coming all db connections are made. Currently my first api call to the service takes much longer than the usual. What other options do i have to achieve this.
No Purpose is not defeated. Eager loading still works as expected.
Ensure that the util.ServiceInjectionModule is the first module to load by declaring it on the top in the application.conf file.
I have done a small experiment after seeing your question to prove this
This is how my module looks like. It is declared in the root directory and as Module is in the root director i.e app, You do not have to explicitly add to the application.conf
class Module extends AbstractModule {
override def configure() = {
//It is very important for this call to be on the top.
bind(classOf[Initialize]).asEagerSingleton()
}
}
Eager singleton
import com.google.inject.Inject
import com.google.inject.Singleton
import play.api.Logger
import play.api.libs.ws.WSClient
import scala.concurrent.Await
import scala.concurrent.duration.Duration
#Singleton
class Initialize #Inject() (wsClient: WSClient) {
hit()
def hit(): Unit = {
val f = wsClient.url("http://www.google.com").get()
val result = Await.result(f, Duration.Inf)
Logger.info(s"status: ${result.status}")
}
}
output:
[info] application - status: 200
[info] play.api.Play - Application started (Dev)
From the above output you can see that Module has loaded and hit() is called.

PlayFramework Scala dependency Injection Javax

I am new to Scala and the PlayFramework and am trying to figure out how I can do a a dependency Injection. I basically want a file that will be a trait and inject that into a controller. My problem is that my Controller class is not seeing my Trait this is my code
ProfileTrait
package traitss
import play.api.mvc._
trait ProfileTrait extends Controller {
def Addone()
}
Then I try to inject that into my controller
import java.nio.file.{Files, Paths}
import traitss.ProfileTrait_
import play.api.mvc.{Action, Controller}
import javax.inject._
class Profiles #Inject() (profileTrait: ProfileTrait) extends Controller
{
}
However my controller is not seeing it, I am trying to follow the example here https://www.playframework.com/documentation/2.5.x/ScalaDependencyInjection .
I am using the play framework version 2.50
You cannot inject a trait directly. You need to specify the implementation of the trait that needs to be injected.
There are two ways to specify the implementation of a trait that is to be injected:
Using #ImplementedBy annotation. Here's a simple example:
package traitss
import play.api.mvc._
import com.google.inject.ImplementedBy
#ImplementedBy(classOf[ProfileTraitImpl])
trait ProfileTrait extends Controller {
def Addone()
}
class ProfileTraitImpl extends ProfileTrait {
// your implementation goes here
}
Using Programmatic Bindings
package traitss
import play.api.mvc._
import com.google.inject.ImplementedBy
#ImplementedBy(classOf[ProfileTraitImpl])
trait ProfileTrait extends Controller {
def Addone()
}
ProfileTraitImpl:
package traitss.impl
class ProfileTraitImpl extends ProfileTrait {
// your implementation goes here
}
Create a module where you can bind the implementation with trait
import com.google.inject.AbstractModule
class Module extends AbstractModule {
def configure() = {
bind(classOf[ProfileTrait])
.to(classOf[ProfileTraitImpl])
}
}
With using the modules approach you get an additional benefit of enabling or disabling the binding. For example, in your application.conf file you can enable/disable a module using play.modules.enabled += module OR play.modules.disabled += module
You can't inject a trait, you have to inject an object that implements that trait.
For dependency injection to work, you have to tell the framework (play uses Guice under the hood) how to resolve the dependency to be injected.
There are many ways to do it and it depends on your case, for more detail you can look at Guice's documentation, the simplest way in play is to create Module.scala into your app directory , if it's not there already, and put something like this:
import com.google.inject.AbstractModule
class Module extends AbstractModule {
override def configure() = {
bind(classOf[ProfileTrait]).toInstance( ... )
}
}
where in ... you put the logic to create the object you want to inject.

How to run code on startup in Play! framework 2.4

I im trying to print "Hello" to console on application start. Can You explain how to do it?
What i tried myself:
app/modules/HelloModule.scala:
package modules
import com.google.inject.AbstractModule
trait Hello {}
class MyHelloClass extends Hello {
initialize() // running initialization in constructor
def initialize() = {
println("Hello")
}
}
class HelloModule extends AbstractModule {
def configure() = {
bind(classOf[Hello])
.to(classOf[MyHelloClass]).asEagerSingleton
}
}
in conf/application.conf i added:
play.modules.enabled += "modules.HelloModule"
and "Hello" is not printed when i run activator run
You need to use Global object, and override "onStart" method:
Defining a Global object in your project allows you to handle global
settings for your application. This object must be defined in the
default (empty) package and must extend GlobalSettings.
import play.api._
object Global extends GlobalSettings {
override def onStart(app: Application) {
Logger.info("Application has started")
}
override def onStop(app: Application) {
Logger.info("Application shutdown...")
}
}
You can also specify a custom GlobalSettings implementation class name
using the application.global configuration key.
Update:
The correct way would be to use Dependency Injection, exactly like it described in the question. GlobalSettings could be removed later
There is no problem with the code in the question. I verified it on my local setup. The code write "Hello" after first request in the development mode "activator run" and after application start in the production mode "activator start".
Btw, try to use some more easy to find string in the log, like
"--------APP DZIABLO HAS BEEN STARTED--------"
It could be so you just missed "Hello" in the log (I did not recognise it from the start)

How to execute on start code in scala Play! framework application?

I need to execute a code allowing the launch of scheduled jobs on start of the application, how can I do this? Thanks.
Use the Global object which - if used - must be defined in the default package:
object Global extends play.api.GlobalSettings {
override def onStart(app: play.api.Application) {
...
}
}
Remember that in development mode, the app only loads on the first request, so you must trigger a request to start the process.
Since Play Framework 2.6x
The correct way to do this is to use a custom module with eager binding:
import scala.concurrent.Future
import javax.inject._
import play.api.inject.ApplicationLifecycle
// This creates an `ApplicationStart` object once at start-up and registers hook for shut-down.
#Singleton
class ApplicationStart #Inject() (lifecycle: ApplicationLifecycle) {
// Start up code here
// Shut-down hook
lifecycle.addStopHook { () =>
Future.successful(())
}
//...
}
import com.google.inject.AbstractModule
class StartModule extends AbstractModule {
override def configure() = {
bind(classOf[ApplicationStart]).asEagerSingleton()
}
}
See https://www.playframework.com/documentation/2.6.x/ScalaDependencyInjection#Eager-bindings
I was getting a similar error.
Like #Leo said, create Global object in app/ directory.
Only thing I had to make sure was to change "app: Application" to "app: play.api.Application".
app: Application referred to class Application in controllers package.