Refine Coroutines annotated controller support
This commit refines Coroutines annotated controller support by considering Kotlin Unit as Java void and using the right ReactiveAdapter to support all use cases, including suspending functions that return Flow (usual when using APIs like WebClient). It also fixes RSocket fire and forget handling and adds related tests for that use case. Closes gh-24057 Closes gh-23866
This commit is contained in:
@@ -26,6 +26,7 @@ import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import kotlinx.coroutines.reactor.asFlux
|
||||
|
||||
import kotlinx.coroutines.reactor.mono
|
||||
import org.reactivestreams.Publisher
|
||||
import reactor.core.publisher.Mono
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Method
|
||||
@@ -51,28 +52,29 @@ internal fun <T: Any> monoToDeferred(source: Mono<T>) =
|
||||
GlobalScope.async(Dispatchers.Unconfined) { source.awaitFirstOrNull() }
|
||||
|
||||
/**
|
||||
* Invoke a suspending function converting it to [Mono] or [reactor.core.publisher.Flux]
|
||||
* if necessary.
|
||||
* Return {@code true} if the method is a suspending function.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.2.2
|
||||
*/
|
||||
internal fun isSuspendingFunction(method: Method) = method.kotlinFunction!!.isSuspend
|
||||
|
||||
/**
|
||||
* Invoke a suspending function and converts it to [Mono] or [reactor.core.publisher.Flux].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.2
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun invokeSuspendingFunction(method: Method, bean: Any, vararg args: Any?): Any? {
|
||||
internal fun invokeSuspendingFunction(method: Method, bean: Any, vararg args: Any?): Publisher<*> {
|
||||
val function = method.kotlinFunction!!
|
||||
return if (function.isSuspend) {
|
||||
val mono = mono(Dispatchers.Unconfined) {
|
||||
function.callSuspend(bean, *args.sliceArray(0..(args.size-2)))
|
||||
.let { if (it == Unit) null else it }
|
||||
}.onErrorMap(InvocationTargetException::class.java) { it.targetException }
|
||||
if (function.returnType.classifier == Flow::class) {
|
||||
mono.flatMapMany { (it as Flow<Any>).asFlux() }
|
||||
}
|
||||
else {
|
||||
mono
|
||||
}
|
||||
val mono = mono(Dispatchers.Unconfined) {
|
||||
function.callSuspend(bean, *args.sliceArray(0..(args.size-2))).let { if (it == Unit) null else it }
|
||||
}.onErrorMap(InvocationTargetException::class.java) { it.targetException }
|
||||
return if (function.returnType.classifier == Flow::class) {
|
||||
mono.flatMapMany { (it as Flow<Any>).asFlux() }
|
||||
}
|
||||
else {
|
||||
function.call(bean, *args)
|
||||
mono
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.reflect.KFunction;
|
||||
import kotlin.reflect.KParameter;
|
||||
import kotlin.reflect.jvm.ReflectJvmMapping;
|
||||
@@ -929,6 +930,9 @@ public class MethodParameter {
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||
if (function != null && function.isSuspend()) {
|
||||
Type paramType = ReflectJvmMapping.getJavaType(function.getReturnType());
|
||||
if (paramType == Unit.class) {
|
||||
paramType = void.class;
|
||||
}
|
||||
return ResolvableType.forType(paramType).resolve(method.getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user