joesan
joesan

Reputation: 15425

Play 2.4 Adding Module instead of Plugin

I have a Play 2.3 app that I'm migrating to Play 2.4 and I'm working on one of the warnings which is about migrating the plugin's to modules. I'm following this documentation here:

https://www.playframework.com/documentation/2.4.x/PluginsToModules

I have now a couple of questions in my application that has some Akka actors. So far here is what I did with the plugins:

  class MyPlugin extends Plugin {

    // I get the current Application
    val app = play.api.Play.current

    // On start of the plugin, I call my initializers
    def onStart: Unit = {
      super.onStart()
      doInitialize(app)
    }
  }

Now in my doInitialize routine, I do the initialization of the different services that I need. I later expose these services as a trait and I mix this trait on the places where I need the service references. So for example., my controller looks like:

object Application extends Controller with MyServices {

  ....
}

where MyServices is

trait MyServices {

  def myActorRef: ActorRef
  ...
  ...
}

How should this setup now be migrated to using Modules with little impact to the overall application? It is also not clear as to where I should put my onStart method contents when I migrate from Plugin to Module!

Edit: I tried the following:

sealed trait MyComp
class MyCompImpl @Inject() (lifecycle: ApplicationLifecycle) extends MyComp {
  val application = MyConfig(play.api.Play.current)

  // initialize upon start
  Initializer.doInitialize(application)

  // destroy upon stop
  lifecycle.addStopHook(() => {
    Future.successful { Initializer.destroyConfig(application) }
  })
}

I have the module defined as below:

class MyModule extends Module {

  override def bindings(environment: play.api.Environment, configuration: Configuration): Seq[Binding[_]] = {
    logger.info(s"registering bindings")
    Seq(
      bind[MyComp].to[MyCompImpl].eagerly()
    )
  }
}

This now fails with the reason:

CreationException: Unable to create injector, see the following errors:

1) Error injecting constructor, java.lang.RuntimeException: There is no started application
  at com.config.MyCompImpl.<init>(MyModule.scala:25)
  while locating com.config.MyCompImpl

It is becoming a pain in the *** with each upgrade to a Play application's to newer versions!

Upvotes: 1

Views: 245

Answers (2)

joesan
joesan

Reputation: 15425

I managed to solve this as below:

sealed trait MyComp
class MyCompImpl @Inject() (app: Application, lifecycle: ApplicationLifecycle) extends MyComp {
  val application = Myconfig(app)

  // initialize upon start
  Initializer.doInitialize(application)

  // destroy upon stop
  lifecycle.addStopHook(() => {
    Future.successful { Initializer.destroyConfig(application) }
  })
}

Upvotes: 0

Andriy Kuba
Andriy Kuba

Reputation: 8263

You need to take in to account one more change in 2.4: "Removing GlobalSettings":

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.

on this page - https://www.playframework.com/documentation/2.4.x/PluginsToModules - you have:

bind[MyComponent].to[MyComponentImpl]

in your example you need to use

bind[MyComponent].to[MyComponentImpl].asEagerSingleton

and the code doInitialize(app) must be run in the constructor of MyComponentImpl like

class MyComponentImpl extends MyComponent {
  initialize() 
  def initialize() = {
     //initialize your app
  }
}

Upvotes: 1

Related Questions