Skip to content

Commit 88a2729

Browse files
committed
Introduce spring-core-coroutines module
This commit introduces the spring-core-coroutines module in order to avoid referencing Kotlin code from Java one, which is currently not supported by Eclipse. During the build, spring-core-coroutines is merged into spring-core, so this change is expected to have no impact for end users. This module contains functions accessible from Java via the CoroutinesUtils class to adapt Coroutines and Deferred instances to and from Mono. See gh-19975
1 parent 837be3e commit 88a2729

File tree

9 files changed

+64
-46
lines changed

9 files changed

+64
-46
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ext {
2525
linkScmDevConnection = "scm:git:ssh://[email protected]:spring-projects/spring-framework.git"
2626

2727
moduleProjects = subprojects.findAll {
28-
!it.name.equals("spring-build-src") && !it.name.equals("spring-framework-bom")
28+
!it.name.equals("spring-build-src") && !it.name.equals("spring-framework-bom") && !it.name.equals("spring-core-coroutines")
2929
}
3030

3131
aspectjVersion = "1.9.2"

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ include "spring-context"
55
include "spring-context-support"
66
include "spring-context-indexer"
77
include "spring-core"
8+
include "spring-core-coroutines"
89
include "spring-expression"
910
include "spring-instrument"
1011
include "spring-jcl"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
description = "Spring Core Coroutines support"
2+
3+
dependencies {
4+
compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
5+
compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
6+
compile("io.projectreactor:reactor-core")
7+
compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutinesVersion}")
8+
compile("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:${coroutinesVersion}")
9+
}
Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,40 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.web.reactive.result.method
17+
@file:JvmName("CoroutinesUtils")
18+
package org.springframework.core
1819

20+
import kotlinx.coroutines.Deferred
1921
import kotlinx.coroutines.GlobalScope
22+
import kotlinx.coroutines.async
23+
import kotlinx.coroutines.reactive.awaitFirstOrNull
24+
2025
import kotlinx.coroutines.reactor.mono
26+
import reactor.core.publisher.Mono
2127
import reactor.core.publisher.onErrorMap
2228
import java.lang.reflect.InvocationTargetException
2329
import java.lang.reflect.Method
2430
import kotlin.reflect.full.callSuspend
2531
import kotlin.reflect.jvm.kotlinFunction
2632

2733
/**
28-
* Invoke an handler method converting suspending method to {@link Mono} if necessary.
34+
* Convert a [Deferred] instance to a [Mono] one.
35+
*
36+
* @author Sebastien Deleuze
37+
* @since 5.2
38+
*/
39+
internal fun <T: Any> deferredToMono(source: Deferred<T>) = GlobalScope.mono { source.await() }
40+
41+
/**
42+
* Convert a [Mono] instance to a [Deferred] one.
43+
*
44+
* @author Sebastien Deleuze
45+
* @since 5.2
46+
*/
47+
internal fun <T: Any> monoToDeferred(source: Mono<T>) = GlobalScope.async { source.awaitFirstOrNull() }
48+
49+
/**
50+
* Invoke an handler method converting suspending method to [Mono] if necessary.
2951
*
3052
* @author Sebastien Deleuze
3153
* @since 5.2
@@ -40,4 +62,4 @@ internal fun invokeHandlerMethod(method: Method, bean: Any, vararg args: Any?):
4062
else {
4163
function.call(bean, *args)
4264
}
43-
}
65+
}

spring-core/spring-core.gradle

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ configurations {
1717
jarjar
1818
cglib
1919
objenesis
20+
coroutines {
21+
transitive = false
22+
}
2023
}
2124

2225
task cglibRepackJar(type: Jar) { repackJar ->
@@ -65,10 +68,12 @@ dependencies {
6568
cglib("cglib:cglib:${cglibVersion}@jar")
6669
objenesis("org.objenesis:objenesis:${objenesisVersion}@jar")
6770
jarjar("org.pantsbuild:jarjar:1.7.2")
71+
coroutines(project(":spring-core-coroutines"))
6872

6973
compile(files(cglibRepackJar))
7074
compile(files(objenesisRepackJar))
7175
compile(project(":spring-jcl"))
76+
compileOnly(project(":spring-core-coroutines"))
7277
optional("net.sf.jopt-simple:jopt-simple:5.0.4")
7378
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
7479
optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
@@ -78,15 +83,14 @@ dependencies {
7883
optional("io.reactivex:rxjava-reactive-streams:${rxjavaAdapterVersion}")
7984
optional("io.reactivex.rxjava2:rxjava:${rxjava2Version}")
8085
optional("io.netty:netty-buffer")
81-
optional("org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutinesVersion}")
82-
optional("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:${coroutinesVersion}")
8386
testCompile("io.projectreactor:reactor-test")
8487
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
8588
testCompile("org.xmlunit:xmlunit-matchers:2.6.2")
8689
testCompile("javax.xml.bind:jaxb-api:2.3.1")
8790
testCompile("com.fasterxml.woodstox:woodstox-core:5.2.0") {
8891
exclude group: "stax", module: "stax-api"
8992
}
93+
testCompile(project(":spring-core-coroutines"))
9094
}
9195

9296
jar {
@@ -107,4 +111,6 @@ jar {
107111
from(zipTree(objenesisRepackJar.archivePath)) {
108112
include "org/springframework/objenesis/**"
109113
}
114+
115+
from { configurations.coroutines.collect { it.isDirectory() ? it : zipTree(it) } }
110116
}

spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
import io.reactivex.BackpressureStrategy;
2727
import io.reactivex.Flowable;
28+
import kotlinx.coroutines.CompletableDeferredKt;
29+
import kotlinx.coroutines.Deferred;
2830
import org.reactivestreams.Publisher;
2931
import reactor.core.publisher.Flux;
3032
import reactor.core.publisher.Mono;
@@ -93,7 +95,7 @@ public ReactiveAdapterRegistry() {
9395

9496
// Coroutines
9597
if (ClassUtils.isPresent("kotlinx.coroutines.Deferred", classLoader)) {
96-
CoroutinesRegistrarKt.registerAdapter(this);
98+
new CoroutinesRegistrar().registerAdapters(this);
9799
}
98100
}
99101

@@ -324,4 +326,16 @@ public <T> Publisher<T> toPublisher(@Nullable Object source) {
324326
}
325327
}
326328

329+
private static class CoroutinesRegistrar {
330+
331+
@SuppressWarnings("KotlinInternalInJava")
332+
void registerAdapters(ReactiveAdapterRegistry registry) {
333+
registry.registerReactiveType(
334+
ReactiveTypeDescriptor.singleOptionalValue(Deferred.class, () -> CompletableDeferredKt.CompletableDeferred(null)),
335+
source -> CoroutinesUtils.deferredToMono((Deferred<?>) source),
336+
source -> CoroutinesUtils.monoToDeferred(Mono.from(source)));
337+
}
338+
339+
}
340+
327341
}

spring-core/src/main/kotlin/org/springframework/core/CoroutinesRegistrar.kt

Lines changed: 0 additions & 38 deletions
This file was deleted.

spring-webflux/spring-webflux.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies {
1313
compile(project(":spring-core"))
1414
compile(project(":spring-web"))
1515
compile("io.projectreactor:reactor-core")
16+
compileOnly(project(":spring-core-coroutines"))
1617
optional(project(":spring-context"))
1718
optional(project(":spring-context-support")) // for FreeMarker support
1819
optional("javax.servlet:javax.servlet-api:4.0.1")
@@ -55,6 +56,7 @@ dependencies {
5556
testCompile("org.eclipse.jetty:jetty-reactive-httpclient:1.0.2")
5657
testCompile("com.squareup.okhttp3:mockwebserver:3.14.0")
5758
testCompile("org.jetbrains.kotlin:kotlin-script-runtime:${kotlinVersion}")
59+
testCompile(project(":spring-core-coroutines"))
5860
testRuntime("org.jetbrains.kotlin:kotlin-script-util:${kotlinVersion}")
5961
testRuntime("org.jetbrains.kotlin:kotlin-compiler:${kotlinVersion}")
6062
testRuntime("org.jruby:jruby:9.2.6.0")

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import reactor.core.publisher.Mono;
2828

29+
import org.springframework.core.CoroutinesUtils;
2930
import org.springframework.core.DefaultParameterNameDiscoverer;
3031
import org.springframework.core.KotlinDetector;
3132
import org.springframework.core.MethodParameter;
@@ -131,6 +132,7 @@ public void setReactiveAdapterRegistry(ReactiveAdapterRegistry registry) {
131132
* @param providedArgs optional list of argument values to match by type
132133
* @return a Mono with a {@link HandlerResult}.
133134
*/
135+
@SuppressWarnings("KotlinInternalInJava")
134136
public Mono<HandlerResult> invoke(
135137
ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) {
136138

@@ -140,7 +142,7 @@ public Mono<HandlerResult> invoke(
140142
ReflectionUtils.makeAccessible(getBridgedMethod());
141143
Method method = getBridgedMethod();
142144
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(method.getDeclaringClass())) {
143-
value = InvocableHandlerMethodKt.invokeHandlerMethod(method, getBean(), args);
145+
value = CoroutinesUtils.invokeHandlerMethod(method, getBean(), args);
144146
}
145147
else {
146148
value = method.invoke(getBean(), args);

0 commit comments

Comments
 (0)