Skip to content

Commit 9aded3f

Browse files
committed
Fix a NPE in proxied suspending functions
Closes gh-31809
1 parent aabe4d0 commit 9aded3f

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ static Object asFlow(Object publisher) {
3838

3939
@Nullable
4040
@SuppressWarnings({"unchecked", "rawtypes"})
41-
static Object awaitSingleOrNull(Object value, Object continuation) {
42-
return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.just(value),
41+
static Object awaitSingleOrNull(@Nullable Object value, Object continuation) {
42+
return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.justOrEmpty(value),
4343
(Continuation<Object>) continuation);
4444
}
4545

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.aop.framework
18+
19+
import kotlinx.coroutines.CoroutineName
20+
import kotlinx.coroutines.flow.Flow
21+
import kotlinx.coroutines.flow.toList
22+
import kotlinx.coroutines.runBlocking
23+
import org.assertj.core.api.Assertions.assertThat
24+
import org.junit.jupiter.api.Test
25+
import reactor.core.publisher.Flux
26+
import reactor.core.publisher.Mono
27+
import kotlin.coroutines.Continuation
28+
29+
/**
30+
* Tests for [CoroutinesUtils].
31+
*
32+
* @author Sebastien Deleuze
33+
*/
34+
class CoroutinesUtilsTests {
35+
36+
@Test
37+
fun awaitSingleNonNullValue() {
38+
val value = "foo"
39+
val continuation = Continuation<Any>(CoroutineName("test")) { }
40+
runBlocking {
41+
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isEqualTo(value)
42+
}
43+
}
44+
45+
@Test
46+
fun awaitSingleNullValue() {
47+
val value = null
48+
val continuation = Continuation<Any>(CoroutineName("test")) { }
49+
runBlocking {
50+
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isNull()
51+
}
52+
}
53+
54+
@Test
55+
fun awaitSingleMonoValue() {
56+
val value = "foo"
57+
val continuation = Continuation<Any>(CoroutineName("test")) { }
58+
runBlocking {
59+
assertThat(CoroutinesUtils.awaitSingleOrNull(Mono.just(value), continuation)).isEqualTo(value)
60+
}
61+
}
62+
63+
@Test
64+
@Suppress("UNCHECKED_CAST")
65+
fun flow() {
66+
val value1 = "foo"
67+
val value2 = "bar"
68+
val values = Flux.just(value1, value2)
69+
val flow = CoroutinesUtils.asFlow(values) as Flow<String>
70+
runBlocking {
71+
assertThat(flow.toList()).containsExactly(value1, value2)
72+
}
73+
}
74+
75+
}

0 commit comments

Comments
 (0)