[2.5.x] scala - How to supply router in application loader when using compile time injection

575 views
Skip to first unread message

Marc van Kempen

unread,
Jun 1, 2016, 12:45:38 PM6/1/16
to play-framework
Hi,

I'm trying to deploy a 2.5.x application. I just switched from 2.3.9 to 2.5.3 and I'm having some problems getting the production version to start.

We are using a separate configuration file which we are passing on the command line as follows:

-Dconfig.file=/full/path/to/conf/project.conf

In the conf directory there is also a logback.conf file which uses the notation ${application.home:.}, however it appears that application.home is not set. After some Googling I found that I must supply my own application loader to configure logging. Which I've done as follows:

class MyComponents(context: Context) extends BuiltInComponentsFromContext(context) {
  val sync = injector.instanceOf(classOf[SyncLK])
  lazy val assets = new Assets(httpErrorHandler)
  lazy val router = new Routes(httpErrorHandler, new HomeController(sync), assets)
}

class MyApplicationLoaderWithInitialization extends ApplicationLoader {
  def load(context: Context) = {
    LoggerConfigurator(context.environment.classLoader).foreach {
      _.configure(context.environment)
    }
    new MyComponents(context).application
  }
}


The example in the Play documentation uses a null router (Router.empty), which obviously is not going to work. However the subsequent example uses instantiation of the Controllers directly as in the snippet above.

However, the HomeController constructor above needs a component that is configured by Guice (compile time injection). I guessed that I can use the injector in BuiltInComponentsFromContext to get an instance of SyncLK, however that throws the following exception:

play.api.UnexpectedException: Unexpected exception[InstantiationException: esb.lk.services.SyncLK]

How am I supposed to construct the router in this case?

Thanks,
Marc.


Greg Methvin

unread,
Jun 1, 2016, 1:00:38 PM6/1/16
to play-framework
Hi Marc,

When using BuiltInComponentsFromContext, Play uses a simple injector that only binds specific components that Play wants to make available via global state (not using Guice). If you wanted to use Guice you could override the injector to use the GuiceInjectorBuilder and add your custom modules.

But if you're going to depend on Guice for one thing why not use it for your whole app? If this component actually needs Guice and can't just be instantiated normally, then it seems better to fix that.

Greg

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/f89f49e7-1450-4f25-9dce-4f2838940e70%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Senior Software Engineer

Marc van Kempen

unread,
Jun 1, 2016, 1:25:08 PM6/1/16
to play-framework
Hi Greg,

I'm not sure I understand how I should fix the application.

Can you give me an example?

Regards
Marc.

Greg Methvin

unread,
Jun 1, 2016, 1:32:21 PM6/1/16
to play-framework
Can you answer why you need Guice for just this one component?

What I was suggesting is to create:

override lazy val injector = new GuiceApplicationBuilder()
  .load(new MyModule) // add your custom Guice modules
  .injector

But it seems strange to do that. Why not use either Guice or compile-time DI for everything?

Marc.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Marc van Kempen

unread,
Jun 1, 2016, 3:10:18 PM6/1/16
to play-framework
Hi Greg,

I believe I understand why you don't understand my question. It appears I did not understand the application loader process enough to guess the right procedure. Upon rereading the docs, and helped by your question, I came up with this:

class MyApplicationLoader extends GuiceApplicationLoader {
  override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
    val extra = Configuration("a" -> 1)
    // Logger configuration goes here
    LoggerConfigurator(context.environment.classLoader).foreach {
      _.configure(context.environment)
    }
    initialBuilder
      .in(context.environment)
      .loadConfig(extra ++ context.initialConfiguration)
      .overrides(overrides(context): _*)
  }
}

Is this what you meant? It seems to work at least!

Kind regards,
Marc.
Message has been deleted

Will Sargent

unread,
Jun 3, 2016, 12:01:12 AM6/3/16
to play-fr...@googlegroups.com
Hi Marc,

You don't need to set a custom application loader or application builder here.  You can change your logback.xml file (not logback.conf) so that you define application.home yourself, or even not set application.home at all:


This doesn't explain why you'd not have application.home set, but that's another question.

Will.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

Marc van Kempen

unread,
Jun 3, 2016, 2:43:01 AM6/3/16
to play-framework
Hi Will,

Thanks for the feedback, but I've got it working now. Apparently in the new Play setup, you have to configure the logging system as shown below in order to have it get the application.home property set. While your solution would work, it would not be as flexible as having the property injected in the logging system.

For prosperty's sake, this is what I ended up with:

/**
 * Custom application loader that configures the logging system and makes
 * application.home (amongst other things) available.
 */

class MyApplicationLoader extends GuiceApplicationLoader {
  override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
    LoggerConfigurator(context.environment.classLoader).foreach {
      _.configure(context.environment)
    }
    initialBuilder
      .in(context.environment)
      .loadConfig(context.initialConfiguration)
      .overrides(overrides(context): _*)
Reply all
Reply to author
Forward
0 new messages