From 9aded3fcad92e25ba2de5500d21512296f9af559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Mon, 11 Dec 2023 21:21:34 +0100 Subject: [PATCH] Fix a NPE in proxied suspending functions Closes gh-31809 --- .../aop/framework/CoroutinesUtils.java | 4 +- .../aop/framework/CoroutinesUtilsTests.kt | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 spring-aop/src/test/kotlin/org/springframework/aop/framework/CoroutinesUtilsTests.kt diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java b/spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java index 092099d2e63b..ce1601222daa 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java @@ -38,8 +38,8 @@ static Object asFlow(Object publisher) { @Nullable @SuppressWarnings({"unchecked", "rawtypes"}) - static Object awaitSingleOrNull(Object value, Object continuation) { - return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.just(value), + static Object awaitSingleOrNull(@Nullable Object value, Object continuation) { + return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.justOrEmpty(value), (Continuation) continuation); } diff --git a/spring-aop/src/test/kotlin/org/springframework/aop/framework/CoroutinesUtilsTests.kt b/spring-aop/src/test/kotlin/org/springframework/aop/framework/CoroutinesUtilsTests.kt new file mode 100644 index 000000000000..6e079a70276e --- /dev/null +++ b/spring-aop/src/test/kotlin/org/springframework/aop/framework/CoroutinesUtilsTests.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.aop.framework + +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono +import kotlin.coroutines.Continuation + +/** + * Tests for [CoroutinesUtils]. + * + * @author Sebastien Deleuze + */ +class CoroutinesUtilsTests { + + @Test + fun awaitSingleNonNullValue() { + val value = "foo" + val continuation = Continuation(CoroutineName("test")) { } + runBlocking { + assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isEqualTo(value) + } + } + + @Test + fun awaitSingleNullValue() { + val value = null + val continuation = Continuation(CoroutineName("test")) { } + runBlocking { + assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isNull() + } + } + + @Test + fun awaitSingleMonoValue() { + val value = "foo" + val continuation = Continuation(CoroutineName("test")) { } + runBlocking { + assertThat(CoroutinesUtils.awaitSingleOrNull(Mono.just(value), continuation)).isEqualTo(value) + } + } + + @Test + @Suppress("UNCHECKED_CAST") + fun flow() { + val value1 = "foo" + val value2 = "bar" + val values = Flux.just(value1, value2) + val flow = CoroutinesUtils.asFlow(values) as Flow + runBlocking { + assertThat(flow.toList()).containsExactly(value1, value2) + } + } + +}