I recently started working with Spring boot + Kotlin. For one of my REST client calls, I decided to use WebClient alongwith Kotlin Coroutines to leverage the best of both worlds. Everything worked fine in my IDE (IntelliJ using 2020.2.1 version). When I tried to deploy my code to cloud (Pivotal Cloud Foundry), I started running into issues with error NoSuchMethodError. From the error, it is clear that the method instrumentResponse of class MetricsWebClientFilterFunction is unable to find the method deferWithContext in class Mono from the reactor-core dependency.

I am using the following versions:

  • Spring boot: 2.3.2.RELEASE
  • Kotlin: 1.3.72
  • Kotlin Coroutines version: 1.3.9
  • Spring Cloud Version: Hoxton.SR6
  • Reactor Core: 3.3.8.RELEASE

Eventually, I figured that both Spring boot (spring-boot-starter-webflux) and Kotlin (kotlinx-coroutines-reactor) rely on the reactor-core dependency. While Spring boot used the version 3.3.8.RELEASE, Kotlin was referencing 3.2.5.RELEASE of reactor-core dependency. Both use reactor-core as a compile dependency. I checked the version 3.2.5.RELEASE of reactor-code and it was missing the method deferWithContext in the Mono class. I updated my pom to force the dependency to 3.3.8.RELEASE but still whenever I hit the client, it keeps failing with the same error.

I checked my deployed instance and the lib contained only the 3.3.8.RELEASE of reactor-core, so there should be no reason why it should fail anymore. Here is a snippet of my logs from server: reactor.core.publisher.Mono.deferWithContext(Ljava/util/function/Function;)Lreactor/core/publisher/Mono;java.lang.NoSuchMethodError: reactor.core.publisher.Mono.deferWithContext(Ljava/util/function/Function;)Lreactor/core/publisher/Mono;\nat org.springframework.boot.actuate.metrics.web.reactive.client.MetricsWebClientFilterFunction.instrumentResponse(MetricsWebClientFilterFunction.java:82)\nat org.springframework.boot.actuate.metrics.web.reactive.client.MetricsWebClientFilterFunction.lambda$filter$0(MetricsWebClientFilterFunction.java:76)\nat reactor.core.publisher.Mono.as(Mono.java:1455)\nat org.springframework.boot.actuate.metrics.web.reactive.client.MetricsWebClientFilterFunction.filter(MetricsWebClientFilterFunction.java:76)\nat org.springframework.web.reactive.function.client.ExchangeFilterFunction.lambda$andThen$1(ExchangeFilterFunction.java:57)\nat org.springframework.web.reactive.function.client.ExchangeFilterFunction.lambda$andThen$1(ExchangeFilterFunction.java:57)\nat org.springframework.web.reactive.function.client.ExchangeFilterFunction.lambda$apply$2(ExchangeFilterFunction.java:68)\nat org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$0(DefaultWebClient.java:338)\nat reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)\nat reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)\nat reactor.core.publisher.Mono.subscribe(Mono.java:3695)\nat kotlinx.coroutines.reactive.AwaitKt.awaitOne(Await.kt:97)\nat kotlinx.coroutines.reactive.AwaitKt.awaitOne$default(Await.kt:95)\nat kotlinx.coroutines.reactive.AwaitKt.awaitSingle(Await.kt:81)\nat com.tmobile.dnd.deep.service.impl.DeepProducerServiceImpl$receiveMessageToPublish$1$2$result$1.invokeSuspend(DeepProducerServiceImpl.kt:89)\nat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)\nat kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)\nat kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)\nat kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)\nat kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)\nat kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

Since the actuator is by default starting the metrics collection and I would like that to be in place, I would like to understand why the cloud profile will cause issues on this code.

Comment From: wilkinsona

Thanks for the report. Unfortunately, the contents of your application's classpath is out of our control so I don't think there's anything that we'll be able to do in Spring Boot to fix this. As such, Stack Overflow or Gitter are better places to get the help that you're looking for as we prefer to use GitHub issues for only bugs and enhancements.

You may want to try starting your app with --verbose:class to see where reactor.core.publisher.Mono is being loaded from. This may help to identify why the method is missing.

I updated my pom to force the dependency to 3.3.8.RELEASE but still whenever I hit the client, it keeps failing with the same error.

You may also want to check that you are making use of Spring Boot's dependency management, either by using spring-boot-starter-parent or by importing spring-boot-dependencies. With the dependency management in place, it should not be necessary to force the version of any Reactor's modules.

Comment From: raj-work

@wilkinsona Thanks for your response. I found that the problem was some internal dependency had bundled reactor-core 3.2.5 in their jar. Since the issue was only happening in deployed cloud instance, I had to do remote debugging to find the actual loading location of the class. I used the Mono::class.java.protectionDomain.codeSource.location to watch for the location for anyone facing a similar issue.

Comment From: wilkinsona

@raj-work Thanks for letting us know.