Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 309
Setu Kumar Basak Avatar asked Jun 01 '16 03:06

Setu Kumar Basak


1 Answers

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

like image 140
rethab Avatar answered Nov 16 '22 14:11

rethab