On creating RequestScoped like objects?

221 views
Skip to first unread message

Alexis Hernandez

unread,
Sep 3, 2017, 1:29:46 AM9/3/17
to Play Framework

Hi,

I'm trying to get dependency injection working on request based scopes, I've read some previous threads about this, these were the more useful:
- https://groups.google.com/forum/#!topic/google-guice/qZqkaWPkhoo
- https://stackoverflow.com/a/34981902/3211175

Sadly, I haven't been able to get the working on Scala, I couldn't even find 'Http.Context.current' on Scala API to give a try to the code given in StackOverflow..

Have any of you been able to get this working? Any guidance would be appreciated.

Thanks.

Greg Methvin

unread,
Sep 3, 2017, 7:32:37 AM9/3/17
to play-framework
Hi Alexis,

There are basically two strategies to have "request-scoped" objects.

The first way is to store the request state in a ThreadLocal. Normally in a servlet-based framework this is really easy to do, because we know each request has its own thread. In Play that's not the case, since Play is non-blocking and may reuse the same thread for multiple simultaneous requests. That means if we use a ThreadLocal we need to make sure it's set properly whenever we execute something on that thread. Since in the Java API the Play already sets the Http.Context thread local that seems like the natural place to start. This is what the custom scope in the linked StackOverflow answer was using. You also need to make sure that thread local state is propagated to any threads where you want to use it, e.g. using the HttpExecutionContext helper.

Another approach is to create a child injector for each request and make the controllers themselves request scoped. That means we create a new injector that inherits the bindings from the parent injector, also adding bindings for the request and other request-scoped components. Now the request-scoped dependencies can be injected into your controllers like any other dependency.

I was trying to find a generic solution to do this in Play (as you can see from that thread), but didn't have time to come up with a complete solution. You can see my work-in-progress pull request here: https://github.com/playframework/playframework/pull/6798. There are not really any changes to Play itself there so that code should already be usable as a starting point in your own application.

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-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/2d126d4b-1211-47f8-9885-429e07c75ca6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Tech Lead - Play Framework

Alexis Hernandez

unread,
Sep 3, 2017, 3:58:40 PM9/3/17
to Play Framework
Hi Greg,

Your answer is very useful, I'm particularly interested in the child injector approach, I haven't been able to get useful information for integrating them, it would be useful if you can provide any source.

My understanding is that I could follow a process like this:
1. the first filter in the chain creates the child injector, the filter should be able to get the injector (very likely with @Inject annotation).
2. tell Play! to use the new child injector.
3. destroy the child injector after the filter chain is completed? (maybe not necessary).

I have not worked enough with Play to be sure that I can do the 1st step but I'm confident it should not be that hard, for the 2nd step, I have no idea about what to look for.

Thanks.


El domingo, 3 de septiembre de 2017, 6:32:37 (UTC-5), Greg Methvin escribió:
Hi Alexis,

There are basically two strategies to have "request-scoped" objects.

The first way is to store the request state in a ThreadLocal. Normally in a servlet-based framework this is really easy to do, because we know each request has its own thread. In Play that's not the case, since Play is non-blocking and may reuse the same thread for multiple simultaneous requests. That means if we use a ThreadLocal we need to make sure it's set properly whenever we execute something on that thread. Since in the Java API the Play already sets the Http.Context thread local that seems like the natural place to start. This is what the custom scope in the linked StackOverflow answer was using. You also need to make sure that thread local state is propagated to any threads where you want to use it, e.g. using the HttpExecutionContext helper.

Another approach is to create a child injector for each request and make the controllers themselves request scoped. That means we create a new injector that inherits the bindings from the parent injector, also adding bindings for the request and other request-scoped components. Now the request-scoped dependencies can be injected into your controllers like any other dependency.

I was trying to find a generic solution to do this in Play (as you can see from that thread), but didn't have time to come up with a complete solution. You can see my work-in-progress pull request here: https://github.com/playframework/playframework/pull/6798. There are not really any changes to Play itself there so that code should already be usable as a starting point in your own application.

Greg
On Sat, Sep 2, 2017 at 10:26 PM, Alexis Hernandez <alexi...@gmail.com> wrote:

Hi,

I'm trying to get dependency injection working on request based scopes, I've read some previous threads about this, these were the more useful:
- https://groups.google.com/forum/#!topic/google-guice/qZqkaWPkhoo
- https://stackoverflow.com/a/34981902/3211175

Sadly, I haven't been able to get the working on Scala, I couldn't even find 'Http.Context.current' on Scala API to give a try to the code given in StackOverflow..

Have any of you been able to get this working? Any guidance would be appreciated.

Thanks.

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

Alexis Hernandez

unread,
Sep 3, 2017, 9:25:05 PM9/3/17
to Play Framework
Hi Greg,

I've looked to your pull request, its been quite useful! turns out that I was looking to the wrong part for creating the child injector.

I was able to get request based objects created, these objects should not depend in anything that is coming from the request, do you have any idea for injecting the RequestHeader into MyRequestModule? My current idea would be to modify RequestScopedRouter to instantiate the guice modules passing the request instead of receiving the guice modules instances.

Last, your implementation seems good enough for starting an external component, I'm sure I'm not the only one requiring this functionality.

Thanks!


El domingo, 3 de septiembre de 2017, 6:32:37 (UTC-5), Greg Methvin escribió:
Hi Alexis,

There are basically two strategies to have "request-scoped" objects.

The first way is to store the request state in a ThreadLocal. Normally in a servlet-based framework this is really easy to do, because we know each request has its own thread. In Play that's not the case, since Play is non-blocking and may reuse the same thread for multiple simultaneous requests. That means if we use a ThreadLocal we need to make sure it's set properly whenever we execute something on that thread. Since in the Java API the Play already sets the Http.Context thread local that seems like the natural place to start. This is what the custom scope in the linked StackOverflow answer was using. You also need to make sure that thread local state is propagated to any threads where you want to use it, e.g. using the HttpExecutionContext helper.

Another approach is to create a child injector for each request and make the controllers themselves request scoped. That means we create a new injector that inherits the bindings from the parent injector, also adding bindings for the request and other request-scoped components. Now the request-scoped dependencies can be injected into your controllers like any other dependency.

I was trying to find a generic solution to do this in Play (as you can see from that thread), but didn't have time to come up with a complete solution. You can see my work-in-progress pull request here: https://github.com/playframework/playframework/pull/6798. There are not really any changes to Play itself there so that code should already be usable as a starting point in your own application.

Greg
On Sat, Sep 2, 2017 at 10:26 PM, Alexis Hernandez <alexi...@gmail.com> wrote:

Hi,

I'm trying to get dependency injection working on request based scopes, I've read some previous threads about this, these were the more useful:
- https://groups.google.com/forum/#!topic/google-guice/qZqkaWPkhoo
- https://stackoverflow.com/a/34981902/3211175

Sadly, I haven't been able to get the working on Scala, I couldn't even find 'Http.Context.current' on Scala API to give a try to the code given in StackOverflow..

Have any of you been able to get this working? Any guidance would be appreciated.

Thanks.

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

Greg Methvin

unread,
Sep 3, 2017, 10:32:38 PM9/3/17
to play-framework
It won't work to create the child injector in a filter, at least not if you want your controllers to be able to inject the request scoped things. You need to replace some component like the Router or HttpRequestHandler that would normally have things injected into it. This should give you a pretty clear idea: https://github.com/playframework/playframework/pull/6798/files#diff-ff06157a897cb0a8a02b115a55d367dbR23. The HttpRequestHandler is probably a better place to do it, since that allows you to also request-scope filters and the error handler, but the concept is exactly the same: replace Play's default implementation with one that creates the child injector and asks for an instance of the HttpRequestHandler for that request.

Also you will probably have to go through Play's BuiltInModule and decide if you might need request scoped instances for any of those. You can't override bindings in the child injector so you'll want to move to your RequestScopedModule anything that needs to depend on the request or a request-scoped dependency.

Cheers,
Greg

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/66117ee3-ef9d-4ddd-84a6-3bf03559cf27%40googlegroups.com.

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

Greg Methvin

unread,
Sep 3, 2017, 10:55:16 PM9/3/17
to play-framework
On Sun, Sep 3, 2017 at 6:25 PM, Alexis Hernandez <alexi...@gmail.com> wrote:
Hi Greg,

I've looked to your pull request, its been quite useful! turns out that I was looking to the wrong part for creating the child injector.

I was able to get request based objects created, these objects should not depend in anything that is coming from the request, do you have any idea for injecting the RequestHeader into MyRequestModule? My current idea would be to modify RequestScopedRouter to instantiate the guice modules passing the request instead of receiving the guice modules instances.

You really only need one module that binds the request. Then you should be able to use the request as a dependency in all the other modules used by the child injector. But yes, you could also dynamically instantiate modules by looking to see if it has a constructor that takes a request, then call the constructor via reflection.
 
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/efdf492d-b816-4186-ba27-e9dc3cbac7e9%40googlegroups.com.

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

Alexis Hernandez

unread,
Sep 4, 2017, 10:35:32 AM9/4/17
to Play Framework
At this time, I feel that your code is good enough, at least it is working in my use case, would you mind if I publish a project using this code (I just modified it to instantiate guice modules for allowing them to depend on the request)?

Thanks!

Greg Methvin

unread,
Sep 5, 2017, 12:20:06 AM9/5/17
to play-framework
No I don't mind. It would be good to have an example project showing how to do it.

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/761c3b5f-469e-4f5e-bbe1-5fca19804a47%40googlegroups.com.

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

Alexis Hernandez

unread,
Sep 5, 2017, 12:27:49 AM9/5/17
to Play Framework
I'll keep this post updated if I publish anything.

Hopefully we will be able to extract a pluggable module for Play! and move the logic to HttpRequestHandler.

Thanks a lot Greg!

Alexis Hernandez

unread,
Sep 7, 2017, 12:01:01 AM9/7/17
to Play Framework
Hi Greg,

I've tried to use child injectors with HttpRequestHandler with no luck, as I understand, we would need to create the return value using the child injector for the following method:
def handlerForRequest(request: RequestHeader): (RequestHeader, Handler)

In this case, the result is a tuple, what I understand is that we need to create the result with the child injector, or maybe I'm understanding something wrong.

Thanks.



El lunes, 4 de septiembre de 2017, 23:20:06 (UTC-5), Greg Methvin escribió:

Alexis Hernandez

unread,
Sep 8, 2017, 1:24:36 AM9/8/17
to Play Framework
Hi Greg,

Could you explain how this method works (from RequestScopedRouter)? We are creating the Router with the child injector but how does Play knows about the child injector in order to create new instances from it?

  /**
   * Provide a router that is scoped to this request.
   */

 
def routerForRequest(request: RequestHeader): Router = {
    val childInjector
= injectorWithRequest
       
.createChildInjector(requestScopedModules: _*)

    childInjector
       
.getInstance(routerClassTag.runtimeClass)
       
.asInstanceOf[T]
       
.withPrefix(prefix)
 
}

Also, as I understand, this approach doesn't work for injecting request-scoped objects in filters because the filters seems to be singletons, in this case, is there a way to set filters to be created every time they are used?


Thanks.


El lunes, 4 de septiembre de 2017, 23:20:06 (UTC-5), Greg Methvin escribió:

Greg Methvin

unread,
Sep 8, 2017, 4:44:00 AM9/8/17
to play-framework
On Thu, Sep 7, 2017 at 10:24 PM, Alexis Hernandez <alexi...@gmail.com> wrote:
Hi Greg,

Could you explain how this method works (from RequestScopedRouter)? We are creating the Router with the child injector but how does Play knows about the child injector in order to create new instances from it?

The injector is only needed by Play (i.e. the Play server running your app) when it wants to create a new instance of your Application, and its only job is to automate the process of creating objects. Everything else is created automatically by Guice as a result of satisfying the dependencies for Application.

Diving a bit deeper, we see the default Application implementation bound is DefaultApplication. Among other components, DefaultApplication needs an HttpRequestHandler, which is typically bound to an instance of DefaultHttpRequestHandler (often its subclass JavaCompatibleHttpRequestHandler). Then that depends on the router, which depends on your controllers, and so on.

The Router is just an interface. The basic idea is to override the binding for this interface with a proxy. That proxy's job is to create a router specifically for that request, and proxy the calls. It is actually only needed once since Play only needs to call the router once per request. That injector is able to inject everything the parent injector knows about plus whatever is in the additional modules. The injector only needs to be called once to wire together the dependencies for the router class you've provided it.


  /**
   * Provide a router that is scoped to this request.
   */

 
def routerForRequest(request: RequestHeader): Router = {
    val childInjector
= injectorWithRequest
       
.createChildInjector(requestScopedModules: _*)

    childInjector
       
.getInstance(routerClassTag.runtimeClass)
       
.asInstanceOf[T]
       
.withPrefix(prefix)
 
}

Also, as I understand, this approach doesn't work for injecting request-scoped objects in filters because the filters seems to be singletons, in this case, is there a way to set filters to be created every time they are used?

If you decide to proxy the HttpRequestHandler instead of the Router, you will be able to do that. It would look something like:

class RequestScopedHttpRequestHandler @Inject() (injector: Injector) extends HttpRequestHandler {
  def handlerForRequest(request: RequestHeader): (RequestHeader, Handler) = {
    val childInjector = injector.createChildInjector(requestScopeModules: _*)
    val handler = childInjector.getInstance(classOf[DefaultHttpRequestHandler])
    handler.handlerForRequest(request)
  }
}
 
This works because DefaultHttpRequestHandler injects HttpFilters, and is created by the child injector. So it is now possible to bind HttpFilters in the child injector.

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/040b7846-3d35-4530-8a07-f3fa6f050d10%40googlegroups.com.

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

Alexis Hernandez

unread,
Sep 8, 2017, 6:06:35 PM9/8/17
to Play Framework
Hi Greg,

I apologize because I might not explained my issue properly.

I have already tried to do something like that [1] and I'm getting this stacktrace [2].

My understanding is that while guice is trying to create the class 'DefaultHttpRequestHandler' (using the child injector), it detects that it could be created using the parent injector (root injector), then, while trying to provide the 'Router' class it failes because a controller depends in a request-scoped object (PlayRequestId), this is maybe a bug in Guice or I might be understanding something wrong.

Thanks.

[1] - code

// MyRequestScopedRequestHandler.scala

import javax.inject.Inject

import com.google.inject.{AbstractModule, Injector}
import play.api.http.{DefaultHttpRequestHandler, HttpRequestHandler}
import play.api.mvc.{Handler, RequestHeader}

import scala.reflect.ClassTag

class MyRequestScopedRequestHandler @Inject()(injector: Injector)
   
extends RequestScopedHttpRequestHandler[DefaultHttpRequestHandler](
      injector
,
      modules
= Seq(new MyRequestModule)
   
)

class RequestScopedHttpRequestHandler[T <: HttpRequestHandler] @Inject() (
    injector
: Injector,
    modules
: Seq[AbstractModule] = Seq.empty)(
   
implicit requestHandlerClassTag: ClassTag[T])
   
extends HttpRequestHandler {

 
override def handlerForRequest(request: RequestHeader): (RequestHeader, Handler) = {
    val requestScopedModules
= new RequestScopedModule(request) +: modules

    val childInjector
= injector
       
.createChildInjector(requestScopedModules: _*)

    val requestHandler
= childInjector
       
.getInstance(requestHandlerClassTag.runtimeClass)
       
.asInstanceOf[T]

    requestHandler
.handlerForRequest(request)
 
}
}

// RequestScopedModule.scala
import com.google.inject.AbstractModule
import play.api.mvc.RequestHeader

class RequestScopedModule(request: RequestHeader) extends AbstractModule {

 
override def configure(): Unit = {
    bind
(classOf[RequestHeader])
       
.toInstance(request)

    bind
(classOf[play.mvc.Http.RequestHeader])
       
.toInstance(new play.core.j.RequestHeaderImpl(request))
 
}
}

[2] - Stacktrace:
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[ProvisionException: Unable to provision, see the following errors:

1) Unable to create binding for com.alexitc.PlayRequestId. It was already configured on one or more child injectors or private modules
    bound at com
.alexitc.MyRequestModule.configure(MyModules.scala:28)
 
If it was in a PrivateModule, did you forget to expose the binding?
 
while locating com.alexitc.PlayRequestId
   
for the 1st parameter of controllers.MyService.<init>(HomeController.scala:41)
 
while locating controllers.MyService
   
for the 2nd parameter of controllers.HomeController.<init>(HomeController.scala:18)
 
while locating controllers.HomeController
   
for the 2nd parameter of router.Routes.<init>(Routes.scala:30)
 
while locating router.Routes
 
while locating play.api.inject.RoutesProvider
 
while locating play.api.routing.Router
   
for the 1st parameter of play.api.http.DefaultHttpRequestHandler.<init>(HttpRequestHandler.scala:91)
 
while locating play.api.http.DefaultHttpRequestHandler

1 error]]
    at play
.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:255)
    at play
.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:182)
    at play
.api.http.DefaultHttpErrorHandler$.onServerError(HttpErrorHandler.scala:286)
    at play
.core.server.Server.logExceptionAndGetResult$1(Server.scala:52)
    at play
.core.server.Server.getHandlerFor(Server.scala:82)
    at play
.core.server.Server.getHandlerFor$(Server.scala:48)
    at play
.core.server.AkkaHttpServer.getHandlerFor(AkkaHttpServer.scala:41)
    at play
.core.server.AkkaHttpServer.getHandler(AkkaHttpServer.scala:214)
    at play
.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:194)
    at play
.core.server.AkkaHttpServer.$anonfun$createServerBinding$3(AkkaHttpServer.scala:106)
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:

1) Unable to create binding for com.alexitc.PlayRequestId. It was already configured on one or more child injectors or private modules
    bound at com
.alexitc.MyRequestModule.configure(MyModules.scala:28)
 
If it was in a PrivateModule, did you forget to expose the binding?
 
while locating com.alexitc.PlayRequestId
   
for the 1st parameter of controllers.MyService.<init>(HomeController.scala:41)
 
while locating controllers.MyService
   
for the 2nd parameter of controllers.HomeController.<init>(HomeController.scala:18)
 
while locating controllers.HomeController
   
for the 2nd parameter of router.Routes.<init>(Routes.scala:30)
 
while locating router.Routes
 
while locating play.api.inject.RoutesProvider
 
while locating play.api.routing.Router
   
for the 1st parameter of play.api.http.DefaultHttpRequestHandler.<init>(HttpRequestHandler.scala:91)
 
while locating play.api.http.DefaultHttpRequestHandler

1 error
    at com
.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1028)
    at com
.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1054)
    at com
.alexitc.RequestScopedHttpRequestHandler.handlerForRequest(MyRequestHandler.scala:30)
    at play
.core.server.Server.getHandlerFor(Server.scala:69)
    at play
.core.server.Server.getHandlerFor$(Server.scala:48)
    at play
.core.server.AkkaHttpServer.getHandlerFor(AkkaHttpServer.scala:41)
    at play
.core.server.AkkaHttpServer.getHandler(AkkaHttpServer.scala:214)
    at play
.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:194)
    at play
.core.server.AkkaHttpServer.$anonfun$createServerBinding$3(AkkaHttpServer.scala:106)
    at akka
.stream.impl.fusing.MapAsync$$anon$23.onPush(Ops.scala:1172)

Greg Methvin

unread,
Sep 8, 2017, 6:21:20 PM9/8/17
to play-framework
If DefaultHttpRequestHandler's binding is in the parent injector, then you need to move it to a module used by the child injector. You can inject parent injector components into child injector components but not the other way around. I also believe implicit bindings in Guice are created as far up as possible, so if you want to make sure something is created in the child injector, you should create an explicit binding for that in the child injector's modules.

In other words, you need to separate your components into ones that need to be created per-request and ones that can be created globally, and put them in separate modules. You'll probably want to disable the default modules and write your own. You can copy most of the bindings from here: https://github.com/playframework/playframework/blob/2.6.3/framework/src/play/src/main/scala/play/api/inject/BuiltinModule.scala

I didn't get that far in my attempt so maybe there are some other issues you run into later, but conceptually it should work. It's mostly a question of moving around bindings.


To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/b5758469-c7a0-45a1-8727-7cb8b2c35a91%40googlegroups.com.

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

Alexis Hernandez

unread,
Sep 8, 2017, 7:05:04 PM9/8/17
to Play Framework
By DefaultHttpRequestHandler is not in the parent injector, it would be a Just In Time binding (jit), guice seems to try to create them as up as possible, surprisingly, I expected that binding DefaultHttpRequestHandler in my request scoped module would lead it to work but it didn't, I'll keep playing this weekend.

Thanks.

Greg Methvin

unread,
Sep 8, 2017, 8:00:04 PM9/8/17
to play-framework
With JIT bindings Guice tries to create it in the parentmost injector. You'll have to explicitly bind in the child injector.

On Fri, Sep 8, 2017 at 4:05 PM, Alexis Hernandez <alexi...@gmail.com> wrote:
By DefaultHttpRequestHandler is not in the parent injector, it would be a Just In Time binding (jit), guice seems to try to create them as up as possible, surprisingly, I expected that binding DefaultHttpRequestHandler in my request scoped module would lead it to work but it didn't, I'll keep playing this weekend.

Thanks.

--
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-framework+unsubscribe@googlegroups.com.

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

Alexis Hernandez

unread,
Sep 8, 2017, 8:14:41 PM9/8/17
to Play Framework
In fact, I think the issue is that Play needs a binding to the Router to startup properly. This means that we cannot override this Router in child injectors.

I think that the ideal scenario would be to mark 'play.http.router' as 'provided' in 'application.conf' file and bind it in a request scoped module, the issue with this is that Play will look for the binding at startup time instead of when the first request is received.

At this time I can not find a way to get this working without modifying Play source code.

Thanks for all your help Greg.


El viernes, 8 de septiembre de 2017, 19:00:04 (UTC-5), Greg Methvin escribió:
With JIT bindings Guice tries to create it in the parentmost injector. You'll have to explicitly bind in the child injector.

On Fri, Sep 8, 2017 at 4:05 PM, Alexis Hernandez <alexi...@gmail.com> wrote:
By DefaultHttpRequestHandler is not in the parent injector, it would be a Just In Time binding (jit), guice seems to try to create them as up as possible, surprisingly, I expected that binding DefaultHttpRequestHandler in my request scoped module would lead it to work but it didn't, I'll keep playing this weekend.

Thanks.

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

Greg Methvin

unread,
Sep 8, 2017, 10:52:11 PM9/8/17
to play-framework
On Fri, Sep 8, 2017 at 5:14 PM, Alexis Hernandez <alexi...@gmail.com> wrote:
In fact, I think the issue is that Play needs a binding to the Router to startup properly. This means that we cannot override this Router in child injectors. 

I don't think that's the case. The only things I see that depend on Router are DefaultHttpErrorHandler and DefaultHttpRequestHandler/JavaCompatibleHttpRequestHandler. As long as those are both bound in the child injector, Router can be bound in the child injector.
 

I think that the ideal scenario would be to mark 'play.http.router' as 'provided' in 'application.conf' file and bind it in a request scoped module, the issue with this is that Play will look for the binding at startup time instead of when the first request is received.

Router is bound in the parent module to RoutesProvider, which reads that configuration. So it should be possible to change just by changing the bindings. You might have to dig into the Play code a bit to understand what it's doing though.
 

At this time I can not find a way to get this working without modifying Play source code.  
 

Thanks for all your help Greg.

El viernes, 8 de septiembre de 2017, 19:00:04 (UTC-5), Greg Methvin escribió:
With JIT bindings Guice tries to create it in the parentmost injector. You'll have to explicitly bind in the child injector.

On Fri, Sep 8, 2017 at 4:05 PM, Alexis Hernandez <alexi...@gmail.com> wrote:
By DefaultHttpRequestHandler is not in the parent injector, it would be a Just In Time binding (jit), guice seems to try to create them as up as possible, surprisingly, I expected that binding DefaultHttpRequestHandler in my request scoped module would lead it to work but it didn't, I'll keep playing this weekend.

Thanks.

--
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/2375aa73-3ceb-4d8e-8133-b3987082d14d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Tech Lead - Play Framework


--
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-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/830d6020-d185-44ba-9951-ca341dcf16e8%40googlegroups.com.

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

Greg Methvin

unread,
Sep 10, 2017, 6:11:06 PM9/10/17
to play-framework
I've been experimenting with creating a request scope in compile-time DI and the concept is a bit more straightforward there. I've created an example project demonstrating it: https://github.com/gmethvin/play-scala-compile-di-example/blob/requestscope/app/MyApplicationLoader.scala.

Basically you use an inner class for the RequestScope, and override the HttpRequestHandler to create an instance of the RequestScope for each request.

I was able to do this without modifying BuiltInComponents, but I had to leave a couple methods unimplemented when I moved those components to the request scope. So if you wanted to have full control you might want to create your own trait to replace BuiltInComponents. Also, if you want to use traits like HttpFiltersComponents in the request scope, you need to proxy the existing components to satisfy their dependencies.

Alexis Hernandez

unread,
Sep 11, 2017, 12:05:52 AM9/11/17
to Play Framework
Hey Greg,

I will dig more into the source code to see how to adapt my current code.

About the compile time DI, it sounds really good, it looks very simple.

I've created a project using the current request-scoped pieces, I've used action composition in order to overcome to the limitation in filters: https://github.com/AlexITC/play-request-tracer

I'll keep working until get the scoped object injected into filters.

I want to thank you for all your help, it's been quite useful.



--
Greg Methvin
Tech Lead - Play Framework

Reply all
Reply to author
Forward
0 new messages