Auto-configure RSocketRequester.Builder
This commit auto-configures a prototype `RSocketRequester.Builder` bean for building requester instances. This builder is pre-configured with auto-detected `RSocketStrategies` (same as the server side). Closes gh-16280
This commit is contained in:
parent
6544d19fbf
commit
20dfeddbb3
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.autoconfigure.rsocket;
|
||||
|
||||
import io.rsocket.RSocketFactory;
|
||||
import io.rsocket.transport.netty.server.TcpServerTransport;
|
||||
import reactor.netty.http.server.HttpServer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for
|
||||
* {@link org.springframework.messaging.rsocket.RSocketRequester}. This auto-configuration
|
||||
* creates {@link org.springframework.messaging.rsocket.RSocketRequester.Builder}
|
||||
* prototype beans, as the builders are stateful and should not be reused to build
|
||||
* requester instances with different configurations.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ RSocketRequester.class, RSocketFactory.class, HttpServer.class,
|
||||
TcpServerTransport.class })
|
||||
@AutoConfigureAfter(RSocketStrategiesAutoConfiguration.class)
|
||||
public class RSocketRequesterAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
public RSocketRequester.Builder rsocketRequesterBuilder(
|
||||
RSocketStrategies strategies) {
|
||||
return RSocketRequester.builder().rsocketStrategies(strategies);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -100,6 +100,7 @@ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
|
|||
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.autoconfigure.rsocket;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link RSocketRequesterAutoConfiguration}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class RSocketRequesterAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(RSocketStrategiesAutoConfiguration.class,
|
||||
RSocketRequesterAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void shouldCreateBuilder() {
|
||||
this.contextRunner.run((context) -> assertThat(context)
|
||||
.hasSingleBean(RSocketRequester.Builder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetPrototypeScopedBean() {
|
||||
this.contextRunner.run((context) -> {
|
||||
RSocketRequester.Builder first = context
|
||||
.getBean(RSocketRequester.Builder.class);
|
||||
RSocketRequester.Builder second = context
|
||||
.getBean(RSocketRequester.Builder.class);
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateBuilderIfAlreadyPresent() {
|
||||
this.contextRunner.withUserConfiguration(CustomRSocketRequesterBuilder.class)
|
||||
.run((context) -> {
|
||||
RSocketRequester.Builder builder = context
|
||||
.getBean(RSocketRequester.Builder.class);
|
||||
assertThat(builder).isInstanceOf(MyRSocketRequesterBuilder.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomRSocketRequesterBuilder {
|
||||
|
||||
@Bean
|
||||
public MyRSocketRequesterBuilder myRSocketRequesterBuilder() {
|
||||
return mock(MyRSocketRequesterBuilder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface MyRSocketRequesterBuilder extends RSocketRequester.Builder {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3528,6 +3528,42 @@ about customization possibilities.
|
|||
Developers can create `RSocketStrategiesCustomizer` beans to add other strategies,
|
||||
assuming there are `Encoder` and `Decoder` implementations available.
|
||||
|
||||
[[boot-features-rsocket-requester]]
|
||||
=== Calling RSocket Services with `RSocketRequester`
|
||||
|
||||
Once the `RSocket` channel is established between server and client, any party can send or
|
||||
receive requests to the other.
|
||||
|
||||
As a server, you can get injected an `RSocketRequester` instance on any handler method of
|
||||
an RSocket `@Controller`. As a client, you need to configure and establish an RSocket
|
||||
connection first. Spring Boot auto-configures an `RSocketRequester.Builder` for such cases
|
||||
with the expected codecs.
|
||||
|
||||
The `RSocketRequester.Builder` instance is a prototype bean, meaning each injection point
|
||||
will provide you with a new instance - this is done on purpose since this builder is stateful
|
||||
and you shouldn't create requesters with different setups using the same instance.
|
||||
|
||||
The following code shows a typical example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Service
|
||||
public class MyService {
|
||||
|
||||
private final RSocketRequester rsocketRequester;
|
||||
|
||||
public MyService(RSocketRequester.Builder rsocketRequesterBuilder) {
|
||||
this.rsocketRequester = rsocketRequesterBuilder
|
||||
.connectTcp("example.org", 9090).block();
|
||||
}
|
||||
|
||||
public Mono<User> someRSocketCall(String name) {
|
||||
return this.requester.route("user").data(payload)
|
||||
.retrieveMono(User.class);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[[boot-features-security]]
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import io.rsocket.Payload;
|
|||
import io.rsocket.RSocket;
|
||||
import io.rsocket.RSocketFactory;
|
||||
import io.rsocket.SocketAcceptor;
|
||||
import io.rsocket.transport.netty.client.TcpClientTransport;
|
||||
import io.rsocket.transport.netty.client.WebsocketClientTransport;
|
||||
import io.rsocket.util.DefaultPayload;
|
||||
import org.assertj.core.api.Assertions;
|
||||
|
|
@ -45,7 +44,6 @@ import org.springframework.core.codec.StringDecoder;
|
|||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -96,7 +94,7 @@ public class NettyRSocketServerFactoryTests {
|
|||
factory.setPort(specificPort);
|
||||
this.rSocketServer = factory.create(new EchoRequestResponseAcceptor());
|
||||
this.rSocketServer.start();
|
||||
this.requester = getRSocketRequester(createRSocketTcpClient());
|
||||
this.requester = createRSocketTcpClient();
|
||||
String payload = "test payload";
|
||||
String response = this.requester.route("test").data(payload)
|
||||
.retrieveMono(String.class).block(TIMEOUT);
|
||||
|
|
@ -111,7 +109,7 @@ public class NettyRSocketServerFactoryTests {
|
|||
factory.setTransport(RSocketServer.TRANSPORT.WEBSOCKET);
|
||||
this.rSocketServer = factory.create(new EchoRequestResponseAcceptor());
|
||||
this.rSocketServer.start();
|
||||
this.requester = getRSocketRequester(createRSocketWebSocketClient());
|
||||
this.requester = createRSocketWebSocketClient();
|
||||
String payload = "test payload";
|
||||
String response = this.requester.route("test").data(payload)
|
||||
.retrieveMono(String.class).block(TIMEOUT);
|
||||
|
|
@ -136,29 +134,28 @@ public class NettyRSocketServerFactoryTests {
|
|||
}
|
||||
}
|
||||
|
||||
private RSocket createRSocketTcpClient() {
|
||||
private RSocketRequester createRSocketTcpClient() {
|
||||
Assertions.assertThat(this.rSocketServer).isNotNull();
|
||||
InetSocketAddress address = this.rSocketServer.address();
|
||||
return RSocketFactory.connect().dataMimeType(MimeTypeUtils.TEXT_PLAIN_VALUE)
|
||||
.transport(TcpClientTransport.create(address)).start().block();
|
||||
return createRSocketRequesterBuilder()
|
||||
.connectTcp(address.getHostString(), address.getPort()).block();
|
||||
}
|
||||
|
||||
private RSocket createRSocketWebSocketClient() {
|
||||
private RSocketRequester createRSocketWebSocketClient() {
|
||||
Assertions.assertThat(this.rSocketServer).isNotNull();
|
||||
InetSocketAddress address = this.rSocketServer.address();
|
||||
return RSocketFactory.connect().dataMimeType(MimeTypeUtils.TEXT_PLAIN_VALUE)
|
||||
.transport(WebsocketClientTransport.create(address)).start().block();
|
||||
return createRSocketRequesterBuilder()
|
||||
.connect(WebsocketClientTransport.create(address)).block();
|
||||
}
|
||||
|
||||
private RSocketRequester getRSocketRequester(RSocket rSocketClient) {
|
||||
private RSocketRequester.Builder createRSocketRequesterBuilder() {
|
||||
RSocketStrategies strategies = RSocketStrategies.builder()
|
||||
.decoder(StringDecoder.allMimeTypes())
|
||||
.encoder(CharSequenceEncoder.allMimeTypes())
|
||||
.dataBufferFactory(
|
||||
new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT))
|
||||
.build();
|
||||
return RSocketRequester.create(rSocketClient, MimeTypeUtils.TEXT_PLAIN,
|
||||
strategies);
|
||||
return RSocketRequester.builder().rsocketStrategies(strategies);
|
||||
}
|
||||
|
||||
static class EchoRequestResponseAcceptor implements SocketAcceptor {
|
||||
|
|
|
|||
Loading…
Reference in New Issue