Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Classpath issues in dev mode #22

Open
jsw opened this issue Mar 22, 2018 · 4 comments
Open

Classpath issues in dev mode #22

jsw opened this issue Mar 22, 2018 · 4 comments

Comments

@jsw
Copy link

jsw commented Mar 22, 2018

I expect to be able to set the play config resource via command-line argument. I have tried several different ways.

sbt -Dconfig.resource=application.local.conf run

Relevant output

--- (Running the application, auto-reloading is enabled) ---

[error] com.typesafe.config.ConfigException$IO: application.local.conf: java.io.IOException: resource not found on classpath: application.local.conf
[error] 	at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:188)
[error] 	at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:174)
[error] 	at com.typesafe.config.impl.Parseable.parse(Parseable.java:299)
[error] 	at com.typesafe.config.ConfigFactory.parseResources(ConfigFactory.java:957)
[error] 	at com.typesafe.config.ConfigFactory.parseResources(ConfigFactory.java:885)
[error] 	at com.typesafe.config.DefaultConfigLoadingStrategy.parseApplicationConfig(DefaultConfigLoadingStrategy.java:49)
[error] 	at com.typesafe.config.ConfigFactory.defaultApplication(ConfigFactory.java:473)
[error] 	at com.typesafe.config.ConfigFactory$1.call(ConfigFactory.java:259)
[error] 	at com.typesafe.config.ConfigFactory$1.call(ConfigFactory.java:256)
[error] 	at com.typesafe.config.impl.ConfigImpl$LoaderCache.getOrElseUpdate(ConfigImpl.java:65)
[error] 	at com.typesafe.config.impl.ConfigImpl.computeCachedConfig(ConfigImpl.java:92)
[error] 	at com.typesafe.config.ConfigFactory.load(ConfigFactory.java:256)
[error] 	at com.typesafe.config.ConfigFactory.load(ConfigFactory.java:232)
[error] 	at kamon.Kamon$.<init>(Kamon.scala:35)
[error] 	at kamon.Kamon$.<clinit>(Kamon.scala)
[error] 	at kamon.context.HasContext$.fromCurrentContext(Mixin.scala:44)
[error] 	at kamon.scala.instrumentation.FutureInstrumentation.mixinTraceContextAwareToFutureRelatedRunnable(FutureInstrumentation.scala:29)
[error] 	at scala.concurrent.impl.CallbackRunnable.context(Promise.scala:1)
[error] 	at kamon.scala.instrumentation.FutureInstrumentation.afterCreation(FutureInstrumentation.scala:37)
[error] 	at scala.concurrent.impl.CallbackRunnable.<init>(Promise.scala:54)
[error] 	at scala.concurrent.impl.Promise$DefaultPromise.onComplete(Promise.scala:303)
[error] 	at akka.actor.ActorSystemImpl$TerminationCallbacks.<init>(ActorSystem.scala:1008)
[error] 	at akka.actor.ActorSystemImpl.<init>(ActorSystem.scala:800)
[error] 	at akka.actor.ActorSystem$.apply(ActorSystem.scala:246)
[error] 	at akka.actor.ActorSystem$.apply(ActorSystem.scala:289)
[error] 	at akka.actor.ActorSystem$.apply(ActorSystem.scala:264)
[error] 	at play.core.server.DevServerStart$.$anonfun$mainDev$1(DevServerStart.scala:224)
[error] 	at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
[error] 	at play.core.server.DevServerStart$.mainDev(DevServerStart.scala:59)
[error] 	at play.core.server.DevServerStart$.mainDevHttpMode(DevServerStart.scala:49)
[error] 	at play.core.server.DevServerStart.mainDevHttpMode(DevServerStart.scala)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] 	at java.lang.reflect.Method.invoke(Method.java:498)
[error] 	at kamon.aspectj.sbt.play.AspectJReloader$.startDevMode(AspectJReloader.scala:221)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.devModeServer$lzycompute$1(AspectJPlayRun.scala:60)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.devModeServer$1(AspectJPlayRun.scala:44)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.$anonfun$playRunTask$3(AspectJPlayRun.scala:67)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.$anonfun$playRunTask$3$adapted(AspectJPlayRun.scala:30)
[error] 	at scala.Function1.$anonfun$compose$1(Function1.scala:44)
[error] 	at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:39)
[error] 	at sbt.std.Transform$$anon$4.work(System.scala:66)
[error] 	at sbt.Execute.$anonfun$submit$2(Execute.scala:262)
[error] 	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error] 	at sbt.Execute.work(Execute.scala:271)
[error] 	at sbt.Execute.$anonfun$submit$1(Execute.scala:262)
[error] 	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:174)
[error] 	at sbt.CompletionService$$anon$2.call(CompletionService.scala:36)
[error] 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error] 	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[error] 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error] 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[error] 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[error] 	at java.lang.Thread.run(Thread.java:748)
[error] Caused by: java.io.IOException: resource not found on classpath: application.local.conf
[error] 	at com.typesafe.config.impl.Parseable$ParseableResources.rawParseValue(Parseable.java:735)
[error] 	at com.typesafe.config.impl.Parseable$ParseableResources.rawParseValue(Parseable.java:710)
[error] 	at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:180)
[error] 	at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:174)
[error] 	at com.typesafe.config.impl.Parseable.parse(Parseable.java:299)
[error] 	at com.typesafe.config.ConfigFactory.parseResources(ConfigFactory.java:957)
[error] 	at com.typesafe.config.ConfigFactory.parseResources(ConfigFactory.java:885)
[error] 	at com.typesafe.config.DefaultConfigLoadingStrategy.parseApplicationConfig(DefaultConfigLoadingStrategy.java:49)
[error] 	at com.typesafe.config.ConfigFactory.defaultApplication(ConfigFactory.java:473)
[error] 	at com.typesafe.config.ConfigFactory$1.call(ConfigFactory.java:259)
[error] 	at com.typesafe.config.ConfigFactory$1.call(ConfigFactory.java:256)
[error] 	at com.typesafe.config.impl.ConfigImpl$LoaderCache.getOrElseUpdate(ConfigImpl.java:65)
[error] 	at com.typesafe.config.impl.ConfigImpl.computeCachedConfig(ConfigImpl.java:92)
[error] 	at com.typesafe.config.ConfigFactory.load(ConfigFactory.java:256)
[error] 	at com.typesafe.config.ConfigFactory.load(ConfigFactory.java:232)
[error] 	at kamon.Kamon$.<init>(Kamon.scala:35)
[error] 	at kamon.Kamon$.<clinit>(Kamon.scala)
[error] 	at kamon.context.HasContext$.fromCurrentContext(Mixin.scala:44)
[error] 	at kamon.scala.instrumentation.FutureInstrumentation.mixinTraceContextAwareToFutureRelatedRunnable(FutureInstrumentation.scala:29)
[error] 	at scala.concurrent.impl.CallbackRunnable.context(Promise.scala:1)
[error] 	at kamon.scala.instrumentation.FutureInstrumentation.afterCreation(FutureInstrumentation.scala:37)
[error] 	at scala.concurrent.impl.CallbackRunnable.<init>(Promise.scala:54)
[error] 	at scala.concurrent.impl.Promise$DefaultPromise.onComplete(Promise.scala:303)
[error] 	at akka.actor.ActorSystemImpl$TerminationCallbacks.<init>(ActorSystem.scala:1008)
[error] 	at akka.actor.ActorSystemImpl.<init>(ActorSystem.scala:800)
[error] 	at akka.actor.ActorSystem$.apply(ActorSystem.scala:246)
[error] 	at akka.actor.ActorSystem$.apply(ActorSystem.scala:289)
[error] 	at akka.actor.ActorSystem$.apply(ActorSystem.scala:264)
[error] 	at play.core.server.DevServerStart$.$anonfun$mainDev$1(DevServerStart.scala:224)
[error] 	at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
[error] 	at play.core.server.DevServerStart$.mainDev(DevServerStart.scala:59)
[error] 	at play.core.server.DevServerStart$.mainDevHttpMode(DevServerStart.scala:49)
[error] 	at play.core.server.DevServerStart.mainDevHttpMode(DevServerStart.scala)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] 	at java.lang.reflect.Method.invoke(Method.java:498)
[error] 	at kamon.aspectj.sbt.play.AspectJReloader$.startDevMode(AspectJReloader.scala:221)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.devModeServer$lzycompute$1(AspectJPlayRun.scala:60)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.devModeServer$1(AspectJPlayRun.scala:44)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.$anonfun$playRunTask$3(AspectJPlayRun.scala:67)
[error] 	at kamon.aspectj.sbt.play.AspectJPlayRun$.$anonfun$playRunTask$3$adapted(AspectJPlayRun.scala:30)
[error] 	at scala.Function1.$anonfun$compose$1(Function1.scala:44)
[error] 	at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:39)
[error] 	at sbt.std.Transform$$anon$4.work(System.scala:66)
[error] 	at sbt.Execute.$anonfun$submit$2(Execute.scala:262)
[error] 	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error] 	at sbt.Execute.work(Execute.scala:271)
[error] 	at sbt.Execute.$anonfun$submit$1(Execute.scala:262)
[error] 	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:174)
[error] 	at sbt.CompletionService$$anon$2.call(CompletionService.scala:36)
[error] 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error] 	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[error] 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error] 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[error] 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[error] 	at java.lang.Thread.run(Thread.java:748)

Also tried sbt -Dconfig.file=conf/application.local.conf run

Relevant output

--- (Running the application, auto-reloading is enabled) ---

[error] k.c.Codecs - Failed to initialize codec for key [request-id]
java.lang.ClassNotFoundException: util.RequestIdCodec
	at org.aspectj.weaver.bcel.ExtensibleURLClassLoader.findClass(ExtensibleURLClassLoader.java:56)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at kamon.util.DynamicAccess.$anonfun$getClassFor$1(DynamicAccess.scala:35)
	at scala.util.Try$.apply(Try.scala:209)
	at kamon.util.DynamicAccess.getClassFor(DynamicAccess.scala:34)
	at kamon.util.DynamicAccess.createInstanceFor(DynamicAccess.scala:52)
	at kamon.context.Codecs.$anonfun$readEntryCodecs$1(Codecs.scala:65)
[error] k.c.Codecs - Failed to initialize codec for key [span]
java.lang.ClassNotFoundException: util.NoopCodec
	at org.aspectj.weaver.bcel.ExtensibleURLClassLoader.findClass(ExtensibleURLClassLoader.java:56)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at kamon.util.DynamicAccess.$anonfun$getClassFor$1(DynamicAccess.scala:35)
	at scala.util.Try$.apply(Try.scala:209)
	at kamon.util.DynamicAccess.getClassFor(DynamicAccess.scala:34)
	at kamon.util.DynamicAccess.createInstanceFor(DynamicAccess.scala:52)
	at kamon.context.Codecs.$anonfun$readEntryCodecs$1(Codecs.scala:65)
[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Enter to stop and go back to the console...)

prod mode via sbt -Dconfig.resource=application.local.conf runProd does not produce an error at startup, but the configuration setting is not respected, which can be observed by hitting localhost:9000 and seeing that b3 headers are propagated, whereas they should not be based on the config file.

Repro via https://github.com/jsw/play-2.6-with-kamon/tree/78c5c32e20807477df3d5d675a980d0eb0cd873a

@jsw
Copy link
Author

jsw commented Mar 24, 2018

Some additional debugging information, gathered via -Dconfig.trace=loads...

If the sbt-aspectj-runner-play-2.6 plugin is removed from the project, the app loads normally. Once the first request is made, the resource is loaded. Relevant output:

sbt -Dconfig.resource=application.local.conf -Dconfig.trace=loads run
...
Loading config from resource 'application.local.conf' URL file:/Users/jswhite/code/play-2.6-with-kamon/target/scala-2.12/classes/application.local.conf from class loader ReloadableClassLoader(v1){file:/Users/jswhite/code/play-2.6-with-kamon/target/scala-2.12/classes/}
Loading config from a URL: file:/Users/jswhite/code/play-2.6-with-kamon/target/scala-2.12/classes/application.local.conf

When the same command is run with the aspectj runner in place, there is an attempt to load the resource before the first request, and it doesn't look like the ReloadableClassLoader is in that set, so there's a fail-fast.

This should provide some additional clues. I'll keep digging.

@jsw
Copy link
Author

jsw commented Mar 24, 2018

Getting more information via IntelliJ debugger.

When the config is not specified as a JVM parameter, the ReloadableClassLoader is used (target/scala-2.12/classes) to find application.conf

at com.typesafe.config.impl.Parseable$ParseableResources.rawParseValue(Parseable.java:731)
	  at com.typesafe.config.impl.Parseable$ParseableResources.rawParseValue(Parseable.java:710)
	  at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:180)
	  at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:174)
	  at com.typesafe.config.impl.Parseable.parse(Parseable.java:152)
	  at com.typesafe.config.impl.SimpleIncluder.fromBasename(SimpleIncluder.java:185)
	  at com.typesafe.config.impl.ConfigImpl.parseResourcesAnySyntax(ConfigImpl.java:132)
	  at com.typesafe.config.ConfigFactory.parseResourcesAnySyntax(ConfigFactory.java:979)
	  at com.typesafe.config.DefaultConfigLoadingStrategy.parseApplicationConfig(DefaultConfigLoadingStrategy.java:36)
	  at com.typesafe.config.ConfigFactory.defaultApplication(ConfigFactory.java:473)
	  at play.api.Configuration$.$anonfun$load$5(Configuration.scala:74)
	  at play.api.Configuration$$$Lambda$3395.285389917.apply(Unknown Source:-1)
	  at scala.Option.getOrElse(Option.scala:121)
	  at play.api.Configuration$.load(Configuration.scala:70)
	  at play.api.Configuration$.load(Configuration.scala:110)
	  at play.api.ApplicationLoader$.createContext(ApplicationLoader.scala:113)
	  at play.core.server.DevServerStart$$anon$1.$anonfun$reload$3(DevServerStart.scala:172)
	  at play.core.server.DevServerStart$$anon$1$$Lambda$5823.844870249.apply(Unknown Source:-1)
	  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)
	  - locked <0x53ad> (a play.core.server.DevServerStart$$anon$1)
	  at play.core.server.AkkaHttpServer.handleRequest_aroundBody0(AkkaHttpServer.scala:189)
	  at play.core.server.AkkaHttpServer$AjcClosure1.run(AkkaHttpServer.scala:1)
	  at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
	  at kamon.play.instrumentation.RequestHandlerInstrumentation.$anonfun$routeRequestNumberTwo$1(RequestHandlerInstrumentation.scala:47)
	  at kamon.play.instrumentation.RequestHandlerInstrumentation$$Lambda$3962.819075163.apply(Unknown Source:-1)
	  at kamon.Kamon$.withContext(Kamon.scala:120)
	  at kamon.play.instrumentation.RequestHandlerInstrumentation.routeRequestNumberTwo(RequestHandlerInstrumentation.scala:47)
	  at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:186)
	  at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:106)
	  at play.core.server.AkkaHttpServer$$Lambda$3793.403343240.apply(Unknown Source:-1)
	  at akka.stream.impl.fusing.MapAsync$$anon$25.onPush(Ops.scala:1194)
	  at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:519)
	  at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:482)
	  at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:378)
	  at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:585)
	  at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:469)
	  at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:560)
	  at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:742)
	  at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:757)
	  at akka.actor.Actor.aroundReceive(Actor.scala:517)
	  at akka.actor.Actor.aroundReceive$(Actor.scala:515)
	  at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:667)
	  at akka.actor.ActorCell.receiveMessage(ActorCell.scala:590)
	  at akka.actor.ActorCell.invoke(ActorCell.scala:559)
	  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)

When the config is specified on the command-line the DependenciesClassLoader is used (all the jars) to attempt to find application.local.conf, but obviously that's the wrong one.

at com.typesafe.config.impl.Parseable$ParseableResources.rawParseValue(Parseable.java:730)
	  at com.typesafe.config.impl.Parseable$ParseableResources.rawParseValue(Parseable.java:710)
	  at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:180)
	  at com.typesafe.config.impl.Parseable.parseValue(Parseable.java:174)
	  at com.typesafe.config.impl.Parseable.parse(Parseable.java:152)
	  at com.typesafe.config.impl.SimpleIncluder.fromBasename(SimpleIncluder.java:185)
	  at com.typesafe.config.impl.ConfigImpl.parseResourcesAnySyntax(ConfigImpl.java:132)
	  at com.typesafe.config.ConfigFactory.parseResourcesAnySyntax(ConfigFactory.java:979)
	  at com.typesafe.config.DefaultConfigLoadingStrategy.parseApplicationConfig(DefaultConfigLoadingStrategy.java:36)
	  at com.typesafe.config.ConfigFactory.defaultApplication(ConfigFactory.java:473)
	  at play.api.Configuration$.$anonfun$load$5(Configuration.scala:74)
	  at play.api.Configuration$$$Lambda$3380.989164591.apply(Unknown Source:-1)
	  at scala.Option.getOrElse(Option.scala:121)
	  at play.api.Configuration$.load(Configuration.scala:70)
	  at play.core.server.DevServerStart$.$anonfun$mainDev$1(DevServerStart.scala:205)
	  at play.core.server.DevServerStart$$$Lambda$3355.2043158498.apply(Unknown Source:-1)
	  at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
	  at play.core.server.DevServerStart$.mainDev(DevServerStart.scala:59)
	  at play.core.server.DevServerStart$.mainDevHttpMode(DevServerStart.scala:49)
	  at play.core.server.DevServerStart.mainDevHttpMode(DevServerStart.scala:-1)
	  at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
	  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	  at java.lang.reflect.Method.invoke(Method.java:498)
	  at kamon.aspectj.sbt.play.AspectJReloader$.startDevMode(AspectJReloader.scala:221)
	  at kamon.aspectj.sbt.play.AspectJPlayRun$.devModeServer$lzycompute$1(AspectJPlayRun.scala:60)
	  - locked <0x2dc6> (a scala.runtime.LazyRef)
	  at kamon.aspectj.sbt.play.AspectJPlayRun$.devModeServer$1(AspectJPlayRun.scala:44)
	  at kamon.aspectj.sbt.play.AspectJPlayRun$.$anonfun$playRunTask$3(AspectJPlayRun.scala:67)
	  at kamon.aspectj.sbt.play.AspectJPlayRun$.$anonfun$playRunTask$3$adapted(AspectJPlayRun.scala:30)
	  at kamon.aspectj.sbt.play.AspectJPlayRun$$$Lambda$3302.895329692.apply(Unknown Source:-1)
	  at scala.Function1.$anonfun$compose$1(Function1.scala:44)
	  at scala.Function1$$Lambda$594.1374115041.apply(Unknown Source:-1)
	  at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:39)
	  at sbt.internal.util.$tilde$greater$$Lambda$1811.943051773.apply(Unknown Source:-1)
	  at sbt.std.Transform$$anon$4.work(System.scala:66)
	  at sbt.Execute.$anonfun$submit$2(Execute.scala:262)
	  at sbt.Execute$$Lambda$1828.1216623919.apply(Unknown Source:-1)
	  at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
	  at sbt.Execute.work(Execute.scala:271)
	  at sbt.Execute.$anonfun$submit$1(Execute.scala:262)
	  at sbt.Execute$$Lambda$1819.738867238.apply(Unknown Source:-1)
	  at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:174)
	  at sbt.ConcurrentRestrictions$$anon$4$$Lambda$1826.1180504901.apply(Unknown Source:-1)
	  at sbt.CompletionService$$anon$2.call(CompletionService.scala:36)
	  at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	  at java.util.concurrent.FutureTask.run(FutureTask.java:-1)
	  at java.util.concurrent.Executors$RunnableAdapter.call$$$capture(Executors.java:511)
	  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:-1)
	  at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	  at java.util.concurrent.FutureTask.run(FutureTask.java:-1)
	  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	  at java.lang.Thread.run(Thread.java:748)

Note that even if you specify the default config file. e.g. -Dconfig.resource=application.conf, the unexpected behavior is the same. It seems to come down to a timing issue of when the resource is loaded that determines which stack, and hence, ClassLoader, will ultimately load the config.

Also note that that in the working case, the thread is an Akka thread (e.g. play-dev-mode-akka.actor.default-dispatcher-4), and therefore probably owned by Play and has the appropriate ClassLoader set, whereas in the failure case, it's a regular pooled thread (e.g. pool-x-thread-y).

@jsw
Copy link
Author

jsw commented Mar 24, 2018

More digging.. The issue is more fundamental than providing config overrides. A config override signals to typesafe-config that the file must be found, or it will fail fast. With no override, it's actually a silent failure and Kamon is not picking up config from application.conf when run via sbt run. The issue seems to be related to #5

The workaround of starting via sbt -Dconfig.file=conf/application.conf works, though because my config references a codec, again, the wrong ClassLoader is used and it can't be loaded. So the workaround would also have to include putting the codec classes in a dependency jar.

@jsw
Copy link
Author

jsw commented Mar 24, 2018

I believe the root cause is the kamon future instrumentation is kicking in before the Play server has started, or more specifically the AspectJReloader.currentApplicationClassLoader has been initialized, which doesn't occur until the first request and reload is called.

I verified a few hacks which somewhat alleviate the issue, but none are likely ideal.

  1. Add the following after the reloader is defined in AspectJReloader.scala. This makes the user resources available, but not the user classes, since the DelegatingClassLoader only delegates to the applicationClassLoaderProvider for resources, and not classes.
reloader.reload
reloader.forceReloadNextTime = true
  1. Another approach in Kamon.scala is to initialize _context = ConfigFactory.defaultReference, and then the play application loader can call Kamon.reconfigure(ConfigFactory.load). I don't think this, at least in isolation, is a good solution for general consumption, but just presenting it since it worked for me, and may spark other ideas.

  2. Another approach is to move the kamon config to a separate file kamon.conf, and then in the application loader call Kamon.reconfigure(ConfigFactory.load("kamon.conf")). This works for finding the resource. Any configured codecs that are user classes will not load unless the following change is made to DynamicAccess.scala

//val c = Class.forName(fqcn, false, classLoader).asInstanceOf[Class[_ <: T]]
val c = Thread.currentThread().getContextClassLoader().loadClass(fqcn).asInstanceOf[Class[_ <: T]] 

Note the comments from jroper about not using Class.forName in playframework/playframework#2847

Although there's an unfortunate ordering of events. It looks like the new settings aren't in effect until the 2nd request. The incoming context is decoded before ApplicationLoader.load is called.

  1. I guess the cleanest workaround for now is to use a combination of -Dconfig.file instead of -Dconfig.resource and put all codecs in a jar.

So, lot's of things going on here. There may be a combination of small changes to iron out all the individual cases, or there may be a more fundamental approach such as preventing Kamon from initializing until an appropriate time in the Play dev mode flow.

@jsw jsw changed the title Classpath issues when overriding Play configuration Classpath issues in dev mode Mar 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant