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

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.

Related

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.

Play Framework 2.5.x: Inject Environment in a Module

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.

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)

Calling Controller DBAction with scheduled Akka Runnable (in Scala / Play 2.2)

I'm attempting to schedule an Akka job when my Play 2.2 application starts.
In it's simplest form, this is what my code looks like:
import play.api.Application
import play.api.Play.current
import play.api.GlobalSettings
import play.api.Logger
import play.api.db.DB
import scala.concurrent.duration._
import play.api.libs.concurrent.Akka
import play.api.libs.concurrent.Execution.Implicits._
import scala.slick.driver.MySQLDriver.simple._
object Global extends GlobalSettings {
override def onStart(app: Application) {
lazy val database = Database.forDataSource(DB.getDataSource())
scheduleTheThing(app)
}
private def scheduleTheThing(app: Application) {
Akka.system.scheduler.scheduleOnce(1.minute, new Runnable {
override def run() {
Logger.info("running the thing!")
controllers.Application.runTheThing
}
})
}
}
As you can see, I'm creating a new Runnable that will be executed 1 minute after the application starts. Logger.info("Running the thing!") gets executed just fine... I'm able to see "Running the thing!" in the application log 1 minute after starting the server. However, controllers.Application.runTheThing doesn't seem to get called:
object Application extends Controller {
def runTheThing = DBAction {
implicit session => {
Logger.info("It's happening!")
DBThing.doSomeDBStuff()
}
}
// ...
}
My "It's happening!" log statement never shows up, and the stuff that DBThing.doSomeDBStuff() is supposed to do never happens. The curious thing is that there are no errors in the console or log files.
How is one supposed to call a controller DBAction from a scheduled runnable? Or How should I rework my scheduler so that this works? Any help would be greatly appreciated, thanks.
Calling the action method will do nothing but return an instance of Action, to actually execute it you would have to call it like play normally does with a request. I would recommend to extract this to non-controller code and call that from your scheduled task instead.