Cherry-pick type of merge the work from Artem in #580

Resolves #580
This commit is contained in:
Oleg Zhurakousky
2020-08-27 14:34:24 +02:00
parent 2607cfc34b
commit b681fb96d6
9 changed files with 610 additions and 527 deletions

View File

@@ -18,23 +18,25 @@ package org.springframework.cloud.function.rsocket;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import io.rsocket.Payload;
import io.rsocket.util.DefaultPayload;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.test.StepVerifier;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.boot.rsocket.context.RSocketServerBootstrap;
import org.springframework.boot.rsocket.server.RSocketServer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.util.Assert;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.SocketUtils;
/**
@@ -44,235 +46,342 @@ import org.springframework.util.SocketUtils;
*/
public class RSocketAutoConfigurationTests {
@Test
public void testImperativeFunctionAsRequestReply() throws Exception {
public void testImperativeFunctionAsRequestReply() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Mono<String> result = requester.rsocket().requestResponse(DefaultPayload.create("\"hello\"")).map(Payload::getDataUtf8);
StepVerifier
.create(result)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("uppercase")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
}
}
@Test
public void testImperativeFunctionAsRequestReplyWithMetadata() throws Exception {
public void testSupplierAsRequestReply() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=source",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Mono<String> result = requester.rsocket().requestResponse(DefaultPayload.create("\"hello\"", "{\"name\":\"bob\", \"age\":23}"))
.map(payload -> {
Assert.hasText(payload.getMetadataUtf8(), "Metadata must not be null");
return payload.getDataUtf8();
});
StepVerifier
.create(result)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("source")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"test data\"")
.expectComplete()
.verify();
}
}
@Test
public void testImperativeFunctionAsRequestStream() throws Exception {
public void testImperativeFunctionAsRequestStream() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Flux<String> result = requester.rsocket().requestStream(DefaultPayload.create("\"hello\"")).map(Payload::getDataUtf8);
StepVerifier
.create(result)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("uppercase")
.data("\"hello\"")
.retrieveFlux(String.class)
.as(StepVerifier::create)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
}
}
@Test
public void testImperativeFunctionAsRequestChannel() throws Exception {
public void testImperativeFunctionAsRequestChannel() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Flux<String> result = requester.rsocket().requestChannel(Flux.just(
DefaultPayload.create("\"Ricky\""),
DefaultPayload.create("\"Julien\""),
DefaultPayload.create("\"Bubbles\""))
)
.map(Payload::getDataUtf8);
StepVerifier.create(result)
.expectNext("\"RICKY\"")
.expectNext("\"JULIEN\"")
.expectNext("\"BUBBLES\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("uppercase")
.data(Flux.just("\"Ricky\"", "\"Julien\"", "\"Bubbles\""))
.retrieveFlux(String.class)
.as(StepVerifier::create)
.expectNext("\"RICKY\"", "\"JULIEN\"", "\"BUBBLES\"")
.expectComplete()
.verify();
}
}
@Test
public void testReactiveFunctionAsRequestReply() throws Exception {
public void testReactiveFunctionAsRequestReply() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Mono<String> result = requester.rsocket().requestResponse(DefaultPayload.create("\"hello\"")).map(Payload::getDataUtf8);
StepVerifier
.create(result)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("uppercaseReactive")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
}
}
@Test
public void testReactiveFunctionAsRequestStream() throws Exception {
public void testReactiveFunctionAsRequestStream() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Flux<String> result = requester.rsocket().requestStream(DefaultPayload.create("\"hello\"")).map(Payload::getDataUtf8);
StepVerifier
.create(result)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("uppercaseReactive")
.data("\"hello\"")
.retrieveFlux(String.class)
.as(StepVerifier::create)
.expectNext("\"HELLO\"")
.expectComplete()
.verify();
}
}
@Test
public void testReactiveFunctionAsRequestChannel() throws Exception {
public void testReactiveFunctionAsRequestChannel() {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketRequester requester = context.getBean(RSocketRequester.class);
Flux<String> result = requester.rsocket().requestChannel(Flux.just(
DefaultPayload.create("\"Ricky\""),
DefaultPayload.create("\"Julien\""),
DefaultPayload.create("\"Bubbles\""))
)
.map(Payload::getDataUtf8);
StepVerifier
.create(result)
.expectNext("\"RICKY\"")
.expectNext("\"JULIEN\"")
.expectNext("\"BUBBLES\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", port)
.route("uppercaseReactive")
.data(Flux.just("\"Ricky\"", "\"Julien\"", "\"Bubbles\""))
.retrieveFlux(String.class)
.as(StepVerifier::create)
.expectNext("\"RICKY\"", "\"JULIEN\"", "\"BUBBLES\"")
.expectComplete()
.verify();
}
}
@Disabled
@Test
public void testRequestReplyFunctionWithComposition() throws Exception {
public void testRequestReplyFunctionWithComposition() {
int portA = SocketUtils.findAvailableTcpPort();
int portB = SocketUtils.findAvailableTcpPort();
new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase|concat",
"--spring.rsocket.server.port=" + portA);
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase|concat",
"--spring.rsocket.server.port=" + portA);
) {
ApplicationContext bContext = new SpringApplicationBuilder(AdditionalFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=reverse>localhost:" + portA + "|wrap",
"--spring.rsocket.server.port=" + portB);
try (
ConfigurableApplicationContext applicationContext2 =
new SpringApplicationBuilder(AdditionalFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=reverse>localhost:" + portA + "|wrap",
"--spring.rsocket.server.port=" + portB);
) {
RSocketRequester requester = bContext.getBean(RSocketRequester.class);
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext2.getBean(RSocketRequester.Builder.class);
Mono<String> result = requester.rsocket().requestResponse(DefaultPayload.create("\"hello\"")).map(Payload::getDataUtf8);
StepVerifier
.create(result)
.expectNext("\"(OLLEHOLLEH)\"")
.expectComplete()
.verify();
rsocketRequesterBuilder.tcp("localhost", portB)
.route("reverse")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"(OLLEHOLLEH)\"")
.expectComplete()
.verify();
}
}
}
@Disabled("TODO")
@Test
public void testCompositionOverWebSocket() {
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.REACTIVE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercase|concat",
"--spring.rsocket.server.transport=websocket",
"--spring.rsocket.server.mapping-path=rsockets",
"--server.port=0");
) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String httpServerPort = environment.getProperty("local.server.port");
try (
ConfigurableApplicationContext applicationContext2 =
new SpringApplicationBuilder(AdditionalFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=reverse>http://localhost:" + httpServerPort + "/rsockets/uppercase|wrap",
"--spring.rsocket.server.port=0");
) {
RSocketServerBootstrap serverBootstrap = applicationContext2.getBean(RSocketServerBootstrap.class);
RSocketServer server = (RSocketServer) ReflectionTestUtils.getField(serverBootstrap, "server");
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext2.getBean(RSocketRequester.Builder.class);
rsocketRequesterBuilder.tcp("localhost", server.address().getPort())
.route("reverse")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"(OLLEHOLLEH)\"")
.expectComplete()
.verify();
}
}
}
@Test
public void testRequestChannelFunction() throws Exception {
int port = SocketUtils.findAvailableTcpPort();
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=uppercaseReactive",
"--spring.rsocket.server.port=" + port);
public void testFireAndForgetConsumer() {
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.cloud.function.definition=log",
"--spring.rsocket.server.port=0");
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketServerBootstrap serverBootstrap = applicationContext.getBean(RSocketServerBootstrap.class);
RSocketServer server = (RSocketServer) ReflectionTestUtils.getField(serverBootstrap, "server");
RSocketRequester requester = context.getBean(RSocketRequester.class);
Flux<String> result = requester.rsocket().requestChannel(Flux.just(
DefaultPayload.create("\"Ricky\""),
DefaultPayload.create("\"Julien\""),
DefaultPayload.create("\"Bubbles\""))
)
.map(Payload::getDataUtf8);
rsocketRequesterBuilder.tcp("localhost", server.address().getPort())
.route("log")
.data("\"hello\"")
.send()
.as(StepVerifier::create)
.expectComplete()
.verify();
StepVerifier
.create(result)
.expectNext("\"RICKY\"")
.expectNext("\"JULIEN\"")
.expectNext("\"BUBBLES\"")
.expectComplete()
.verify();
applicationContext.getBean(SampleFunctionConfiguration.class).consumerData
.asMono()
.map(String::new)
.as(StepVerifier::create)
.expectNext("\"hello\"")
.expectComplete()
.verify();
}
}
@Test
public void testRsocketRoutesForAllFunctions() {
try (
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(AdditionalFunctionConfiguration.class)
.web(WebApplicationType.NONE)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.rsocket.server.port=0");
) {
RSocketRequester.Builder rsocketRequesterBuilder =
applicationContext.getBean(RSocketRequester.Builder.class);
RSocketServerBootstrap serverBootstrap = applicationContext.getBean(RSocketServerBootstrap.class);
RSocketServer server = (RSocketServer) ReflectionTestUtils.getField(serverBootstrap, "server");
RSocketRequester requester = rsocketRequesterBuilder.tcp("localhost", server.address().getPort());
requester.route("reverse")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"olleh\"")
.expectComplete()
.verify();
requester.route("wrap")
.data("\"hello\"")
.retrieveMono(String.class)
.as(StepVerifier::create)
.expectNext("\"(hello)\"")
.expectComplete()
.verify();
}
}
// @Test
// public void testFireAndForgetConsumer() throws Exception {
// new SpringApplicationBuilder(SampleFunctionConfiguration.class)
// .run("--logging.level.org.springframework.cloud.function=DEBUG",
// "--spring.cloud.function.definition=log");
//
// RSocket socket = RSocketConnector.connectWith(TcpClientTransport.create("localhost", 7000))
// .log()
// .retryWhen(Retry.backoff(5, Duration.ofSeconds(1)))
// .block();
// socket.fireAndForget(DefaultPayload.create("Hello"))
// .log()
// .onErrorContinue((e, x) -> {
// System.out.println(e);
// })
// .block();
// Thread.sleep(2000);
// System.out.println();
// }
@EnableAutoConfiguration
@Configuration
@Import(RSocketTestConfiguration.class)
public static class SampleFunctionConfiguration {
final Sinks.One<byte[]> consumerData = Sinks.one();
@Bean
public Function<String, String> uppercase() {
return v -> {
return v.toUpperCase();
};
return String::toUpperCase;
}
@Bean
public Function<String, String> concat() {
return v -> {
return v + v;
};
return v -> v + v;
}
@Bean
@@ -290,28 +399,30 @@ public class RSocketAutoConfigurationTests {
@Bean
public Consumer<byte[]> log() {
return v -> {
System.out.println("==> In Consumer: " + new String(v));
};
return this.consumerData::emitValue;
}
@Bean
public Supplier<String> source() {
return () -> "test data";
}
}
@EnableAutoConfiguration
@Configuration
@Import(RSocketTestConfiguration.class)
public static class AdditionalFunctionConfiguration {
@Bean
public Function<String, String> reverse() {
return v -> {
return new StringBuilder(v).reverse().toString();
};
return v -> new StringBuilder(v).reverse().toString();
}
@Bean
public Function<String, String> wrap() {
return v -> {
return "(" + v + ")";
};
return v -> "(" + v + ")";
}
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2020-2020 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.cloud.function.rsocket;
import java.net.InetSocketAddress;
import java.time.Duration;
import io.rsocket.RSocket;
import io.rsocket.core.RSocketConnector;
import io.rsocket.transport.netty.client.TcpClientTransport;
import reactor.util.retry.Retry;
import reactor.util.retry.RetrySpec;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.RSocketStrategies;
import org.springframework.util.Assert;
import org.springframework.util.MimeTypeUtils;
/**
*
* @author Oleg Zhurakousky
*
*/
@Configuration
public class RSocketTestConfiguration {
@Bean
@Scope("prototype")
RSocketRequester rSocketRequester(RSocketStrategies rSocketStrategies, Environment environment,
@Nullable RetrySpec retrySpec) {
String port = environment.getProperty("spring.rsocket.server.port");
Assert.hasText(port, "'spring.rsocket.server.port' must be specified");
String host = environment.getProperty("spring.rsocket.server.address", "localhost");
RSocket socket = RSocketConnector
.connectWith(
TcpClientTransport.create(InetSocketAddress.createUnresolved(host, Integer.parseInt(port))))
.log()
.retryWhen(retrySpec == null ? Retry.backoff(5, Duration.ofSeconds(1)) : retrySpec).block();
return RSocketRequester.wrap(socket, MimeTypeUtils.APPLICATION_JSON, MimeTypeUtils.APPLICATION_JSON,
rSocketStrategies);
}
}

View File

@@ -62,7 +62,7 @@ public class RoutingBrokerTests {
public void testRoutingWithProperty() throws Exception {
this.setup(true);
RSocketRequester requester = clientContext.getBean(RSocketRequester.class);
Mono<String> result = requester.route("toupper") // used to find a messagemapping, so unused here
Mono<String> result = requester.route("uppercase") // used to find a messagemapping, so unused here
// auto creates metadata
.data("\"hello\"")
.retrieveMono(String.class);
@@ -79,7 +79,7 @@ public class RoutingBrokerTests {
this.setup(false);
RSocketRequester requester = clientContext.getBean(RSocketRequester.class);
RoutingMetadata metadata = clientContext.getBean(RoutingMetadata.class);
Mono<String> result = requester.route("toupper") // used to find a messagemapping, so unused here
Mono<String> result = requester.route("uppercase") // used to find a messagemapping, so unused here
.metadata(metadata.address("samplefn"))
.data("\"hello\"")
.retrieveMono(String.class);