zalando-incubator / markscheider

Metrics module for Play2
Apache License 2.0
13 stars 6 forks source link

Usage in 2.6.5 app: java.lang.NoSuchMethodError: play.api.ApplicationLoader$Context.lifecycle() #19

Closed matterche closed 7 years ago

matterche commented 7 years ago

Steps to reproduce:

Actual Behaviour

During startup an exception is thrown:

Error injecting method, java.lang.NoSuchMethodError: play.api.ApplicationLoader$Context.lifecycle()Lplay/api/inject/DefaultApplicationLifecycle;
  at com.google.inject.util.Providers$GuicifiedProviderWithDependencies.initialize(Providers.java:149)
  at play.api.db.slick.SlickModule.$anonfun$namedDatabaseConfigBindings$1(SlickModule.scala:43):
Binding(interface play.api.db.slick.DatabaseConfigProvider qualified with QualifierInstance(@play.db.NamedDatabase(value=default)) to ProviderTarget(play.api.db.slick.NamedDatabaseConfigProvider@3c20ec67)) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
Caused by: java.lang.NoSuchMethodError: play.api.ApplicationLoader$Context.lifecycle()Lplay/api/inject/DefaultApplicationLifecycle;
    at play.api.inject.guice.GuiceApplicationLoader$.$anonfun$defaultOverrides$3(GuiceApplicationLoader.scala:54)
    at play.api.inject.BindingKey$$anon$1.get(Binding.scala:194)
    at com.google.inject.util.Providers$GuicifiedProvider.get(Providers.java:119)
    at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:81)
    at com.google.inject.internal.InternalFactoryToInitializableAdapter.provision(InternalFactoryToInitializableAdapter.java:53)
    at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:61)
    at com.google.inject.internal.InternalFactoryToInitializableAdapter.get(InternalFactoryToInitializableAdapter.java:45)
    at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:56)
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
    at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:110)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:90)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:268)
    at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:56)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1092)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:194)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:41)
    at com.google.inject.internal.SingleFieldInjector.inject(SingleFieldInjector.java:54)
    at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:132)
    at com.google.inject.internal.MembersInjectorImpl$1.call(MembersInjectorImpl.java:93)
    at com.google.inject.internal.MembersInjectorImpl$1.call(MembersInjectorImpl.java:80)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1092)
    at com.google.inject.internal.MembersInjectorImpl.injectAndNotify(MembersInjectorImpl.java:80)
    at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:62)
    at com.google.inject.internal.InjectorImpl.injectMembers(InjectorImpl.java:987)
    at com.google.inject.util.Providers$GuicifiedProviderWithDependencies.initialize(Providers.java:149)
    at com.google.inject.util.Providers$GuicifiedProviderWithDependencies$$FastClassByGuice$$2a7177aa.invoke(<generated>)
    at com.google.inject.internal.SingleMethodInjector$1.invoke(SingleMethodInjector.java:54)
    at com.google.inject.internal.SingleMethodInjector.inject(SingleMethodInjector.java:89)
    at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:132)
    at com.google.inject.internal.MembersInjectorImpl$1.call(MembersInjectorImpl.java:93)
    at com.google.inject.internal.MembersInjectorImpl$1.call(MembersInjectorImpl.java:80)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1085)
    at com.google.inject.internal.MembersInjectorImpl.injectAndNotify(MembersInjectorImpl.java:80)
    at com.google.inject.internal.Initializer$InjectableReference.get(Initializer.java:223)
    at com.google.inject.internal.Initializer.injectAll(Initializer.java:132)
    at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:174)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:110)
    at com.google.inject.Guice.createInjector(Guice.java:99)
    at com.google.inject.Guice.createInjector(Guice.java:84)
    at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:181)
    at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:137)
    at play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
    at play.core.server.DevServerStart$$anon$1.$anonfun$reload$3(DevServerStart.scala:174)
    at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
    at play.core.server.DevServerStart$$anon$1.reload(DevServerStart.scala:171)
    at play.core.server.DevServerStart$$anon$1.get(DevServerStart.scala:124)
    at play.core.server.AkkaHttpServer.modelConversion(AkkaHttpServer.scala:183)
    at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:189)
    at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$3(AkkaHttpServer.scala:106)
    at akka.stream.impl.fusing.MapAsync$$anon$23.onPush(Ops.scala:1172)
    at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:499)
    at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:462)
    at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:368)
    at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:571)
    at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:457)
    at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:546)
    at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:725)
    at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:740)
    at akka.actor.Actor.aroundReceive(Actor.scala:514)
    at akka.actor.Actor.aroundReceive$(Actor.scala:512)
    at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:650)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:527)
    at akka.actor.ActorCell.invoke(ActorCell.scala:496)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
    at akka.dispatch.Mailbox.run(Mailbox.scala:224)
    at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
    at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

Findings

markscheider currently depends on Play 2.6.0. It looks like some incompatible change has been introduced in com.typesafe.play.play-guice between 2.6.0 and 2.6.5.

Workaround

Pin com.typesafe.play.play-guice version to 2.6.5 in build.sbt:

dependencyOverrides += "com.typesafe.play" %% "play-guice" % "2.6.5"
alexkops commented 7 years ago

Hmm, I can't reproduce this. I made a new Play 2.6.5 project and used markscheider 2.6.0 as dependency. Then I added some module with named binding like this:

import play.api.inject._
import play.api.Configuration
import play.api.Environment
class TestModule extends Module {

  def bindings(env: Environment, conf: Configuration) = Seq(
    bind[Counter].qualifiedWith("count").to[AtomicCounter]
  )

}

Also the default syntax works:

import com.google.inject.AbstractModule
import com.google.inject.name.Names
class TestModule extends AbstractModule {
  def configure() = {
    bind(classOf[Counter]).annotatedWith(Names.named("count")).to(classOf[AtomicCounter])
  }
}

I used the module like so:

@Singleton
class CountController @Inject() (cc: ControllerComponents,
                                  @Named("count") counter: Counter) extends AbstractController(cc) {...}

And everything worked fine. You seemed to have used some other bind syntax though? And from the error it looks more related to Slick...

matterche commented 7 years ago

Yep, you're right. I stopped investigating the issue while also trying to reproduce it with a simple project without success. My goal was to find out whether it makes sense to report a bug to the Play team. To be honest my conclusion was an educated guess ;)

I can reproduce the issue in a project where we use play.api.libs.concurrent.AkkaGuiceSupport and bind actors this way:

class EventProcessingJobsModule extends AbstractModule with AkkaGuiceSupport {
  def configure() = {
    bindActor[SendOrdersActor]("send-orders-actor")
    bindActor[SendProducedEventsActor]("send-produced-events-actor")
    bindActor[EventStorageActor]("event-storage-actor")
  }
}

In addition we're using SlickModule provided by play-slick.

Both cause the exception.

alexkops commented 7 years ago

Ok, tried this in my simple project:

class TestModule extends AbstractModule with AkkaGuiceSupport  {
  def configure() = {
    bind(classOf[Counter]).annotatedWith(Names.named("count")).to(classOf[AtomicCounter])
    bindActor[CounterActor]("count-actor")
  }
}

and

@Singleton
class CountController @Inject() (cc: ControllerComponents,
                                  @Named("count") counter: Counter,
                                 @Named("count-actor") myActor: ActorRef ) extends AbstractController(cc) {...}

And it still works. Let's see what happens when I drop Slick in the mix :)

alexkops commented 7 years ago

Ok, added play-slick to the project

libraryDependencies ++= Seq(
  "com.typesafe.play" %% "play-slick" % "3.0.1",
  "com.typesafe.play" %% "play-slick-evolutions" % "3.0.1",
  "com.h2database" % "h2" % "1.4.196"
)

and then used the db in the project

@Singleton
class AtomicCounter @Inject()(dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext)  extends Counter {

  private val dbConfig = dbConfigProvider.get[JdbcProfile]
  dbConfig.db.run(sql"SELECT 1".as[Int])
 ...}

(together with the Actor DI previously mentioned). And still everything works. Needs to have something to do with your special setup...

alexkops commented 7 years ago

Last test: Made it a named database, because that's mentioned in your stacktrace

@Singleton
class AtomicCounter @Inject()(@NamedDatabase("test") dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext)  extends Counter {...}

Still works fine. Hard to reproduce :)

matterche commented 7 years ago

Thanks a lot for your support!

matterche commented 7 years ago

Looks like I found my "special configuration". I overlooked that Guice DI support moved to a separate module. @alexkops If you remove guice from libraryDependencies of the example project you should be able to reproduce the issue. I added it to my project and it just works.