[scaldi-play 0.5.8] Unable to add stop hook

222 views
Skip to first unread message

Ulrik Lejon

unread,
Jun 26, 2015, 7:12:42 AM6/26/15
to sca...@googlegroups.com
Hey

I'm currently migrating away from the Global object in my application.
Right now I'm removing the onStop(...) function.
I tried to to the following:


class ShutdownModule extends Module {

    inject [ApplicationLifecycle] addStopHook { () =>
        Future {
            // Do some clean up stuff
        }
    }
}

However, I'm unable to start my application. I get the following error:

play.api.PlayException: Cannot load module[Module [ShutdownModule] cannot be instantiated.]
at play.api.inject.Modules$$anonfun$locate$1.apply(Module.scala:131) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.inject.Modules$$anonfun$locate$1.apply(Module.scala:103) ~[play_2.11-2.4.0.jar:2.4.0]
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245) ~[scala-library-2.11.7.jar:na]
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245) ~[scala-library-2.11.7.jar:na]
at scala.collection.immutable.HashSet$HashSet1.foreach(HashSet.scala:322) ~[scala-library-2.11.7.jar:na]
Caused by: java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_45]
at java.lang.reflect.Constructor.newInstance(Constructor.java:422) ~[na:1.8.0_45]
at play.api.inject.Modules$$anonfun$locate$1$$anonfun$play$api$inject$Modules$$anonfun$$tryConstruct$1$1.apply(Module.scala:115) ~[play_2.11-2.4.0.jar:2.4.0]
Caused by: scaldi.InjectException: No binding found with following identifiers:
  * TypeTagIdentifier(play.api.inject.ApplicationLifecycle)
at scaldi.Injectable$class.noBindingFound(Injectable.scala:46) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at ShutdownModule.noBindingFound(ShutdownModule.scala:15) ~[na:na]
at scaldi.Injectable$$anonfun$inject$1$$anonfun$apply$1.apply(Injectable.scala:22) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at scaldi.Injectable$$anonfun$inject$1$$anonfun$apply$1.apply(Injectable.scala:22) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at scala.Option.getOrElse(Option.scala:121) ~[scala-library-2.11.7.jar:na]


Am I missing something?

Cheers,
Ulrik

Ulrik Lejon

unread,
Jun 26, 2015, 7:39:18 AM6/26/15
to sca...@googlegroups.com
Okay, I just realized that ApplicationLifecycle is a trait.
So here's how I do it now:

class ShutdownModule extends Module {

    bind [ShutdownHandler] toNonLazy injected [ShutdownHandler]
}

class ShutdownHandler(applicationLifecycle: ApplicationLifecycle) {
    applicationLifecycle.addStopHook { () =>
        Future.successful {
            // Do some clean up stuff     
        }
    }
}

Looks okay?

Oleg Ilyenko

unread,
Jun 26, 2015, 3:43:20 PM6/26/15
to sca...@googlegroups.com, ulrik...@gmail.com
Hi Ulrik,

yes, your solution looks good to me. Ideally I would suggest to co-locate the lifecycle methods together with the binding definition itself, if possible. For example:

bind [Server] to new HttpServer initWith (_.init()) destroyWith (_.shutdown())

But if you need to cleanup things that have no bindings, then your solution looks suitable to me. You can even avoid dependency on Play's `ApplicationLifecycle` by using `destroyWith`:

bind [ShutdownHandler] toNonLazy injected [ShutdownHandler] destroyWith (_.shutdown())

class ShutdownHandler {
  def shutdown(): Unit = {
    println("AAAAAA")
  }
}

Cheers, 
Oleg

Ulrik Lejon

unread,
Jun 27, 2015, 11:31:23 AM6/27/15
to sca...@googlegroups.com, ulrik...@gmail.com
Hi Oleg,

Thanx for the pointers!

Now I have an additional question regarding moving away from the Global object.
I want to use the second option, extending the default error handler. However, I'm having some problems. I tried to copy paste their example:

import javax.inject._

import play.api.http.DefaultHttpErrorHandler
import play.api._
import play.api.mvc._
import play.api.mvc.Results._
import play.api.routing.Router
import scala.concurrent._

class ServerErrorHandler(env: Environment, config: Configuration, sourceMapper: OptionalSourceMapper,
                         router: Provider[Router] ) extends DefaultHttpErrorHandler(env, config, sourceMapper, router) {

    override def onProdServerError(request: RequestHeader, exception: UsefulException) = {
        Future.successful(
            InternalServerError("A server error occurred: " + exception.getMessage)
        )
    }

    override def onForbidden(request: RequestHeader, message: String) = {
        Future.successful(
            Forbidden("You're not allowed to access this resource.")
        )
    }
}

My module looks like this:

class ServerModule extends Module {
    bind [HttpErrorHandler] to injected [ServerErrorHandler]
}

And my application.conf:

play.modules.enabled=enabled += "ServerModule"

I get the following compile error:


[error] application -

! @6migd284e - Internal server error, for (GET) [/companies/12/view] ->

play.api.UnexpectedException: Unexpected exception[InjectException: No binding found with following identifiers:
  * TypeTagIdentifier(javax.inject.Provider[play.api.routing.Router])]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:165) ~[play-server_2.11-2.4.0.jar:2.4.0]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:121) ~[play-server_2.11-2.4.0.jar:2.4.0]
at scala.Option.map(Option.scala:146) ~[scala-library-2.11.7.jar:na]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:121) ~[play-server_2.11-2.4.0.jar:2.4.0]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:119) ~[play-server_2.11-2.4.0.jar:2.4.0]
Caused by: scaldi.InjectException: No binding found with following identifiers:
  * TypeTagIdentifier(javax.inject.Provider[play.api.routing.Router])
at scaldi.Injectable$class.noBindingFound(Injectable.scala:46) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at ServerModule.noBindingFound(ServerModule.scala:8) ~[na:na]
at scaldi.Injectable$$anonfun$inject$1$$anonfun$apply$1.apply(Injectable.scala:22) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at scaldi.Injectable$$anonfun$inject$1$$anonfun$apply$1.apply(Injectable.scala:22) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at scala.Option.getOrElse(Option.scala:121) ~[scala-library-2.11.7.jar:na]

What am I missing?

Oleg Ilyenko

unread,
Jun 28, 2015, 1:24:13 PM6/28/15
to sca...@googlegroups.com, ulrik...@gmail.com
Hi Ulrik,

The issue is in the difference between JSR 330 and Scaldi. The equivalent of `Provider[Router]` in Scaldi is just a function `() => Router`, which serves as a provider of bindings of type `Router`. 

`injected` macro does not support providers by default, but you can customize `router` injection like this:

bind [HttpErrorHandler] to injected [ServerErrorHandler] ('router -> injectProvider[Router])

This also means, that you need to customize `ServerErrorHandler` like this:

class ServerErrorHandler(env: Environment, config: Configuration, sourceMapper: OptionalSourceMapper, router: () => Router)
    extends DefaultHttpErrorHandler(env, config, sourceMapper, new Provider[Router] {def get() = router()}) {
  // ...
}

`new Provider[Router] {def get() = router()}` looks pretty ugly, but you can create a separate class for it.

Cheers,
Oleg

Ulrik Lejon

unread,
Jun 28, 2015, 4:02:40 PM6/28/15
to Oleg Ilyenko, sca...@googlegroups.com
Hmm,

Now I'm getting this error instead:

play.api.UnexpectedException: Unexpected exception[InjectException: No binding found with following identifiers:
  * TypeTagIdentifier(ServerErrorHandler)]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:165) ~[play-server_2.11-2.4.0.jar:2.4.0]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:121) ~[play-server_2.11-2.4.0.jar:2.4.0]
at scala.Option.map(Option.scala:146) ~[scala-library-2.11.7.jar:na]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:121) ~[play-server_2.11-2.4.0.jar:2.4.0]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:119) ~[play-server_2.11-2.4.0.jar:2.4.0]
Caused by: scaldi.InjectException: No binding found with following identifiers:
  * TypeTagIdentifier(ServerErrorHandler)
at scaldi.Injectable$class.noBindingFound(Injectable.scala:46) ~[scaldi_2.11-0.5.6.jar:0.5.6]
at scaldi.play.ScaldiBuilder$.noBindingFound(ScaldiBuilder.scala:128) ~[scaldi-play_2.11-0.5.8.jar:0.5.8]
at scaldi.play.ScaldiBuilder$$anonfun$6$$anonfun$apply$1$$anonfun$apply$14$$anonfun$apply$15.apply(ScaldiBuilder.scala:223) ~[scaldi-play_2.11-0.5.8.jar:0.5.8]
at scaldi.play.ScaldiBuilder$$anonfun$6$$anonfun$apply$1$$anonfun$apply$14$$anonfun$apply$15.apply(ScaldiBuilder.scala:223) ~[scaldi-play_2.11-0.5.8.jar:0.5.8]
at scala.Option.getOrElse(Option.scala:121) ~[scala-library-2.11.7.jar:na]

I did exactly what you suggested. 

--
You received this message because you are subscribed to a topic in the Google Groups "scaldi" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scaldi/xksKB1K_bnE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scaldi+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Oleg Ilyenko

unread,
Jun 28, 2015, 4:13:50 PM6/28/15
to sca...@googlegroups.com, ulrik...@gmail.com, oleg.i...@gmail.com
I tested it with my app and it was working. Judging on the error, looks like you tried to inject `ServerErrorHandler` instead of `HttpErrorHandler` somewhere. Is it possible that you also defined something like this in the play config:

play.http.errorHandler = "ServerErrorHandler"

If yes, then you can safely remove it. It should work without extra config.

Ulrik Lejon

unread,
Jun 28, 2015, 4:15:39 PM6/28/15
to sca...@googlegroups.com, ulrik...@gmail.com
Yes, that was the error :D
Thanx!
Reply all
Reply to author
Forward
0 new messages