How to implement the lifecycle callbacks of play framework(2.5.x)
Asked Answered
C

1

6

I am trying to learn play framework. I want to implement the lifecycle callbacks of play framework in my application. Now i saw that it can be easily done using GlobalSettings below:

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    Logger.info("Application has started")
  }  

  override def onStop(app: Application) {
    Logger.info("Application shutdown...")
  }  

}

But it's been deprecated in the play framework(2.5.x). And they are providing eager binding for onStart callbacks and for onStop and onError there are other mechanisms. I looked into the documentation of release 2.5.x and saw a code there like below:

import com.google.inject.AbstractModule
import com.google.inject.name.Names

class Module extends AbstractModule {
  def configure() = {

    bind(classOf[Hello])
      .annotatedWith(Names.named("en"))
      .to(classOf[EnglishHello]).asEagerSingleton

    bind(classOf[Hello])
      .annotatedWith(Names.named("de"))
      .to(classOf[GermanHello]).asEagerSingleton
  }
}

But unfortunately i couldn't understand it. As using GlobalSettings, it was easy enough to implement the lifecycle callbacks. Suppose that i will just implement a Logger info in the lifecycle callbacks. No complex codes.
How can i implement this for start, stop and error callbacks in 2.5.x ??

Credential answered 1/6, 2016 at 3:11 Comment(0)
B
8

In general having moved these mechanisms away from the GlobalSettings thing also means that you no longer register such 'callbacks' globally, but you tie them to a component/class. This gives the benefit that initialization and shutdown of a certain component may happen directly inside the respective class. However if you have code that you want to run at startup (or shutdown) that is not tied to a specific component (eg. logging, startup checks, etc) you will have to create new classes for them and bind them in you module.

Note that in the latter case you typically bind the respective classes as eager singletons (to make sure they are instantiated) whereas in the former case, the classes are instantiated as part of the dependency tree.

Startup: Run code in constructor of any class that is managed by the dependency injection container.

  1. Bind class in module

    bind(classOf[Hello]).to(classOf[EnglishHello]).asEagerSingleton
    
  2. Put code in constructor

    class EnglishHello extends Hello {
      println("hello")
    }
    

Note that the asEagerSingleton is not required per se. As I'm assuming you're using Guice as DI-provider, you may read more about that here: https://github.com/google/guice/wiki/Scopes

Shutdown: In any class that needs to run some shutdown code, register a lifecycle callback.

  1. Bind class in module

    bind(classOf[Hello]).to(classOf[EnglishHello]).asEagerSingleton
    
  2. Register lifecycle callback (and inject ApplicationLifecycle)

    class EnglishHello @Inject() (lifecycle: ApplicationLifecycle) extends Hello {
      lifecycle.addStopHook { () =>
        Future.successful(connection.stop())
      }
    }
    

Note that you may want to scope these classes as singletons, as otherwise you end up registering stop hooks for each instance - depending on what your stop hook does, this may be what you want. Read more about this here: https://www.playframework.com/documentation/2.5.x/ScalaDependencyInjection#Stopping/cleaning-up

Errors: Implement your own HttpErrorHandler. The basic idea is that you implement a class with a number of methods that will be called by Play! on the respective errors. This is documented here: https://www.playframework.com/documentation/2.5.x/ScalaErrorHandling

Brutalize answered 1/6, 2016 at 5:55 Comment(3)
I am not understanding this line bind(classOf[Hello]).to(classOf[EnglishHello]).asEagerSingleton . can u explain it in details like what the Hello class and why using to(classOf[EnglishHello]) and what is is being done here by writing this line?? I am new to this framework. Can u explain it in more easy way plz ??Credential
It means whenever you request an instance of Hello (eg. class Bye @Inject() (h: Hello) { .. }) an instance of EnglishHello will be injected (passed in). Learn more about how dependency injection can be beneficial in this talk from the creator of guice: youtube.com/watch?v=0iSB0L9avmgBrutalize
how can you unit test classes that take a ApplicationLifecycle param?Protohuman

© 2022 - 2024 — McMap. All rights reserved.