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

Cannot call Kotlin suspend function in @Around advice after Spring Framework 6.1 #31855

Closed
T45K opened this issue Dec 16, 2023 · 9 comments
Closed
Assignees
Labels
status: invalid An issue that we don't feel is valid theme: kotlin An issue related to Kotlin support

Comments

@T45K
Copy link
Contributor

T45K commented Dec 16, 2023

Affects: <Spring Framework version>

  • org.springframework.boot: 3.2.0
  • org.springframework: 6.1.1

We used this workaround to execute suspend function in advice.
But, after updating Spring Boot to 3.2.0, ClassCastException occurs.

stack trace

Details

java.lang.ClassCastException: class reactor.core.publisher.MonoOnErrorResume cannot be cast to class java.lang.CharSequence (reactor.core.publisher.MonoOnErrorResume is in unnamed module of loader 'app'; java.lang.CharSequence is in module java.base of loader 'bootstrap')
	at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106) ~[reactor-core-3.6.0.jar:3.6.0]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ Handler io.github.t45k.r2dbc_aop_trial.R2dbcAopTrialApplication#aop(Continuation) [DispatcherHandler]
	*__checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP GET "/blocking/aop" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:176) ~[reactor-core-3.6.0.jar:3.6.0]
		at kotlinx.coroutines.reactor.MonoCoroutine.onCompleted(Mono.kt:103) ~[kotlinx-coroutines-reactor-1.7.3.jar:na]
		at kotlinx.coroutines.AbstractCoroutine.onCompletionInternal(AbstractCoroutine.kt:93) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:237) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:910) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:867) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:832) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.debug.internal.DebugProbesImpl$CoroutineOwner.resumeWith(DebugProbesImpl.kt:549) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) ~[kotlin-stdlib-1.9.0.jar:1.9.0-release-358]
		at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:68) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:245) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:163) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.reactive.AwaitKt$awaitOne$2$1.onComplete(Await.kt:272) ~[kotlinx-coroutines-reactive-1.7.3.jar:na]
		at org.jooq.impl.R2DBC$AbstractSubscription.lambda$new$0(R2DBC.java:179) ~[jooq-3.18.7.jar:na]
		at org.jooq.impl.Internal$1.onComplete(Internal.java:497) ~[jooq-3.18.7.jar:na]
		at org.jooq.impl.R2DBC$AbstractResultSubscriber.lambda$onComplete$1(R2DBC.java:293) ~[jooq-3.18.7.jar:na]
		at org.jooq.impl.Internal$1.onComplete(Internal.java:497) ~[jooq-3.18.7.jar:na]
		at reactor.core.publisher.StrictSubscriber.onComplete(StrictSubscriber.java:123) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2231) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.pool.SimpleDequePool.maybeRecycleAndDrain(SimpleDequePool.java:537) ~[reactor-pool-1.0.3.jar:1.0.3]
		at reactor.pool.SimpleDequePool$QueuePoolRecyclerInner.onComplete(SimpleDequePool.java:767) ~[reactor-pool-1.0.3.jar:1.0.3]
		at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:46) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.pool.SimpleDequePool$QueuePoolRecyclerMono.subscribe(SimpleDequePool.java:879) ~[reactor-pool-1.0.3.jar:1.0.3]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:241) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:46) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:264) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:241) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onComplete(FluxHandleFuseable.java:239) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoSupplier$MonoSupplierSubscription.request(MonoSupplier.java:148) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.request(FluxHandleFuseable.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onSubscribe(MonoIgnoreElements.java:72) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onSubscribe(FluxHandleFuseable.java:164) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:48) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:264) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.0.jar:3.6.0]
		at org.jooq.impl.R2DBC$AbstractNonBlockingSubscription.lambda$cancel0$4(R2DBC.java:663) ~[jooq-3.18.7.jar:na]
		at java.base/java.util.concurrent.atomic.AtomicReference.updateAndGet(AtomicReference.java:210) ~[na:na]
		at org.jooq.impl.R2DBC$AbstractNonBlockingSubscription.cancel0(R2DBC.java:647) ~[jooq-3.18.7.jar:na]
		at org.jooq.impl.R2DBC$AbstractSubscription.complete(R2DBC.java:213) ~[jooq-3.18.7.jar:na]
		at org.jooq.impl.R2DBC$AbstractResultSubscriber.complete(R2DBC.java:303) ~[jooq-3.18.7.jar:na]
		at org.jooq.impl.R2DBC$AbstractResultSubscriber.onComplete(R2DBC.java:293) ~[jooq-3.18.7.jar:na]
		at reactor.core.publisher.StrictSubscriber.onComplete(StrictSubscriber.java:123) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.checkTerminated(FluxWindowPredicate.java:540) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.drainLoop(FluxWindowPredicate.java:488) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.drain(FluxWindowPredicate.java:432) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.onComplete(FluxWindowPredicate.java:312) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onComplete(MonoFlatMapMany.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onComplete(FluxContextWrite.java:126) ~[reactor-core-3.6.0.jar:3.6.0]
		at io.asyncer.r2dbc.mysql.internal.util.DiscardOnCancelSubscriber.onComplete(DiscardOnCancelSubscriber.java:84) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
		at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxHandle$HandleConditionalSubscriber.onNext(FluxHandle.java:359) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:854) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.SinkManyEmitterProcessor.drain(SinkManyEmitterProcessor.java:476) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.SinkManyEmitterProcessor.tryEmitNext(SinkManyEmitterProcessor.java:273) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.SinkManySerialized.tryEmitNext(SinkManySerialized.java:100) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27) ~[reactor-core-3.6.0.jar:3.6.0]
		at io.asyncer.r2dbc.mysql.client.ReactorNettyClient$ResponseSink.next(ReactorNettyClient.java:383) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
		at io.asyncer.r2dbc.mysql.client.ReactorNettyClient.lambda$new$0(ReactorNettyClient.java:121) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:185) ~[reactor-core-3.6.0.jar:3.6.0]
		at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:294) ~[reactor-netty-core-1.1.13.jar:1.1.13]
		at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:403) ~[reactor-netty-core-1.1.13.jar:1.1.13]
		at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:426) ~[reactor-netty-core-1.1.13.jar:1.1.13]
		at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:114) ~[reactor-netty-core-1.1.13.jar:1.1.13]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.asyncer.r2dbc.mysql.client.MessageDuplexCodec.handleDecoded(MessageDuplexCodec.java:162) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
		at io.asyncer.r2dbc.mysql.client.MessageDuplexCodec.channelRead(MessageDuplexCodec.java:73) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1475) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1338) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1387) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
		at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
		at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

I guess this is due to updating AopUtils, which calls CoroutinesUtils.

I know this is really workaround way to invocate suspend function in AOP advice, so I can't help but think there are problems when updating the libraries.

Do you know another way to invocate suspend function in AOP advice?

Thank you.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 16, 2023
@sdeleuze sdeleuze self-assigned this Dec 19, 2023
@sdeleuze sdeleuze added the theme: kotlin An issue related to Kotlin support label Dec 19, 2023
@sdeleuze
Copy link
Contributor

Can you please provide a reproducer as an attached archive or a link to a repsoitory?

@sdeleuze sdeleuze added the status: waiting-for-feedback We need additional information before we can continue label Dec 19, 2023
@T45K
Copy link
Contributor Author

T45K commented Dec 19, 2023

@sdeleuze
thank you for checking this issue!
i prepared reproducing repo

https://github.com/T45K/spring-aop-around-repro

please take a look 🙏

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 19, 2023
@T45K
Copy link
Contributor Author

T45K commented Dec 21, 2023

ah, i found a solution by myself

@Component
@Aspect
class Aspect {
    @Around("xxx")
    fun execute(joinPoint: ProceedingJoinPoint): Any? = mono {
        callSuspendFunction()
    }.then(joinPoint.proceed() as Mono<*>)
}

this looks working fine

@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 3, 2024

Yeah, your solution looks fine. Before Spring Framework 6.1, there was no support for Coroutines in Spring AOP, so you had to invoke the suspending function manually like you did in your repro. As of Spring Framework 6.1, suspending functions are translated to their Reactive equivalent, as shown by your comment above.

@sdeleuze sdeleuze closed this as not planned Won't fix, can't repro, duplicate, stale Jan 3, 2024
@sdeleuze sdeleuze added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Jan 3, 2024
@T45K
Copy link
Contributor Author

T45K commented Jan 3, 2024

thank you for answering.

i have another question.
do you (and spring team) have any plan to implement additional support for AOP Before and After, and Reactor (or coroutine) like the following style?

// return Mono directly as Before advice
@Before("...")
fun execute(joinPoint): Any? = mono {
  callSuspendFunction()
}

// or, use suspend function directly as Before advice
@Before("...")
suspend fun execute(joinPoint): Any {
  callSuspendFunction()
}

@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 3, 2024

I will discuss that with the team and let you know.

@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 9, 2024

We don't plan to add Coroutines dedicated support for the use cases mentioned in this comment since it would likely require, as far as I can tell, AspectJ support for Coroutines and/or Reactor which is unlikely to happen.

If you think that would be useful, we can review a related documentation PR for Kotlin documentation that would provide guidance for AspectJ + Coroutines for Spring use case.

@T45K
Copy link
Contributor Author

T45K commented Jan 10, 2024

i see. thank you

@ilya40umov
Copy link
Contributor

@sdeleuze I was looking for a way to manipulate coroutine context in an advice, as it would be quite handy in order to let @Observed annotation also support suspending methods: micrometer-metrics/micrometer#4827

I managed to hack something together without this support for now: ObservedSuspendingAspect, but I am wondering if this is the way to go for each and every aspect that ends up needing to manipulate coroutine context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid theme: kotlin An issue related to Kotlin support
Projects
None yet
Development

No branches or pull requests

4 participants