Skip to content

Commit

Permalink
Add support for @RequestMapping on Kotlin property accessors
Browse files Browse the repository at this point in the history
This commit refines InvocableHandlerMethod (both Servlet and
Reactive variants) in order to support annotated property
accessors as they translate into regular Java methods, instead
of throwing a NullPointerException.

Closes spring-projectsgh-31856
  • Loading branch information
sdeleuze committed Dec 19, 2023
1 parent 917978c commit 1523c0e
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

import kotlin.Unit;
import kotlin.jvm.JvmClassMappingKt;
Expand Down Expand Up @@ -306,8 +305,12 @@ private static class KotlinDelegate {

@Nullable
@SuppressWarnings("deprecation")
public static Object invokeFunction(Method method, Object target, Object[] args) {
KFunction<?> function = Objects.requireNonNull(ReflectJvmMapping.getKotlinFunction(method));
public static Object invokeFunction(Method method, Object target, Object[] args) throws Exception {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
// For property accessors
if (function == null) {
return method.invoke(target, args);
}
if (method.isAccessible() && !KCallablesJvm.isAccessible(function)) {
KCallablesJvm.setAccessible(function, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ class InvocableHandlerMethodKotlinTests {
Assertions.assertThat(value).isEqualTo(3.1)
}

@Test
fun propertyAccessor() {
val value = getInvocable(PropertyAccessorHandler::class.java).invokeForRequest(request, null)
Assertions.assertThat(value).isEqualTo("foo")
}

private fun getInvocable(clazz: Class<*>, vararg argTypes: Class<*>): InvocableHandlerMethod {
val method = ResolvableMethod.on(clazz).argTypes(*argTypes).resolveMethod()
val handlerMethod = InvocableHandlerMethod(clazz.constructors.first().newInstance(), method)
Expand Down Expand Up @@ -138,6 +144,12 @@ class InvocableHandlerMethodKotlinTests {
limit.value
}

private class PropertyAccessorHandler {

val prop: String
get() = "foo"
}

@JvmInline
value class LongValueClass(val value: Long)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

import kotlin.Unit;
Expand Down Expand Up @@ -306,7 +305,7 @@ private static class KotlinDelegate {
@Nullable
@SuppressWarnings("deprecation")
public static Object invokeFunction(Method method, Object target, Object[] args, boolean isSuspendingFunction,
ServerWebExchange exchange) {
ServerWebExchange exchange) throws Exception {

if (isSuspendingFunction) {
Object coroutineContext = exchange.getAttribute(COROUTINE_CONTEXT_ATTRIBUTE);
Expand All @@ -318,7 +317,11 @@ public static Object invokeFunction(Method method, Object target, Object[] args,
}
}
else {
KFunction<?> function = Objects.requireNonNull(ReflectJvmMapping.getKotlinFunction(method));
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
// For property accessors
if (function == null) {
return method.invoke(target, args);
}
if (method.isAccessible() && !KCallablesJvm.isAccessible(function)) {
KCallablesJvm.setAccessible(function, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test
import org.springframework.core.ReactiveAdapterRegistry
import org.springframework.http.HttpStatus
import org.springframework.http.server.reactive.ServerHttpResponse
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.reactive.BindingContext
Expand Down Expand Up @@ -194,6 +195,14 @@ class InvocableHandlerMethodKotlinTests {
assertHandlerResultValue(result, "3.1")
}

@Test
fun propertyAccessor() {
this.resolvers.add(stubResolver(Mono.empty()))
val method = PropertyAccessorController::prop.getter.javaMethod!!
val result = invoke(PropertyAccessorController(), method)
assertHandlerResultValue(result, "foo")
}


private fun invokeForResult(handler: Any, method: Method, vararg providedArgs: Any): HandlerResult? {
return invoke(handler, method, *providedArgs).block(Duration.ofSeconds(5))
Expand Down Expand Up @@ -293,6 +302,13 @@ class InvocableHandlerMethodKotlinTests {

}

class PropertyAccessorController {

val prop: String
@GetMapping("/")
get() = "foo"
}

@JvmInline
value class LongValueClass(val value: Long)

Expand Down

0 comments on commit 1523c0e

Please sign in to comment.