@ -17,11 +17,16 @@
package org.springframework.messaging.rsocket ;
package org.springframework.messaging.rsocket ;
import java.lang.reflect.Field ;
import java.lang.reflect.Field ;
import java.util.Collections ;
import java.util.List ;
import java.util.Map ;
import java.util.function.Consumer ;
import java.util.function.Consumer ;
import io.netty.buffer.ByteBuf ;
import io.netty.buffer.ByteBuf ;
import io.netty.buffer.ByteBufAllocator ;
import io.rsocket.DuplexConnection ;
import io.rsocket.DuplexConnection ;
import io.rsocket.RSocketFactory ;
import io.rsocket.RSocketFactory ;
import io.rsocket.frame.decoder.PayloadDecoder ;
import io.rsocket.transport.ClientTransport ;
import io.rsocket.transport.ClientTransport ;
import org.junit.Before ;
import org.junit.Before ;
import org.junit.Test ;
import org.junit.Test ;
@ -29,13 +34,19 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux ;
import reactor.core.publisher.Flux ;
import reactor.core.publisher.Mono ;
import reactor.core.publisher.Mono ;
import org.springframework.core.codec.CharSequenceEncoder ;
import org.springframework.core.ResolvableType ;
import org.springframework.core.codec.StringDecoder ;
import org.springframework.core.codec.Decoder ;
import org.springframework.core.codec.DecodingException ;
import org.springframework.core.io.buffer.DataBuffer ;
import org.springframework.core.io.buffer.DataBufferFactory ;
import org.springframework.core.io.buffer.DefaultDataBufferFactory ;
import org.springframework.core.io.buffer.NettyDataBufferFactory ;
import org.springframework.util.MimeType ;
import org.springframework.util.MimeType ;
import org.springframework.util.MimeTypeUtils ;
import org.springframework.util.MimeTypeUtils ;
import org.springframework.util.ReflectionUtils ;
import org.springframework.util.ReflectionUtils ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThatThrownBy ;
import static org.mockito.ArgumentMatchers.any ;
import static org.mockito.ArgumentMatchers.any ;
import static org.mockito.ArgumentMatchers.anyInt ;
import static org.mockito.ArgumentMatchers.anyInt ;
import static org.mockito.BDDMockito.given ;
import static org.mockito.BDDMockito.given ;
@ -52,6 +63,8 @@ public class DefaultRSocketRequesterBuilderTests {
private ClientTransport transport ;
private ClientTransport transport ;
private final TestRSocketFactoryConfigurer rsocketFactoryConfigurer = new TestRSocketFactoryConfigurer ( ) ;
@Before
@Before
public void setup ( ) {
public void setup ( ) {
@ -63,53 +76,113 @@ public class DefaultRSocketRequesterBuilderTests {
@Test
@Test
@SuppressWarnings ( "unchecked" )
@SuppressWarnings ( "unchecked" )
public void shouldApplyCustomizationsAtSubscription ( ) {
public void shouldApplyCustomizationsAtSubscription ( ) {
ClientRSocketFactoryConfigurer factoryConfigurer = mock ( ClientRSocketFactoryConfigurer . class ) ;
Consumer < RSocketStrategies . Builder > strategiesConfigurer = mock ( Consumer . class ) ;
Consumer < RSocketStrategies . Builder > strategiesConfigurer = mock ( Consumer . class ) ;
RSocketRequester . builder ( )
RSocketRequester . builder ( )
. rsocketFactory ( f actoryConfigurer)
. rsocketFactory ( this . rsocketF actoryConfigurer)
. rsocketStrategies ( strategiesConfigurer )
. rsocketStrategies ( strategiesConfigurer )
. connect ( this . transport ) ;
. connect ( this . transport ) ;
verifyZeroInteractions ( this . transport , factoryConfigurer , strategiesConfigurer ) ;
verifyZeroInteractions ( this . transport ) ;
assertThat ( this . rsocketFactoryConfigurer . rsocketFactory ( ) ) . isNull ( ) ;
}
}
@Test
@Test
@SuppressWarnings ( "unchecked" )
@SuppressWarnings ( "unchecked" )
public void shouldApplyCustomizations ( ) {
public void shouldApplyCustomizations ( ) {
RSocketStrategies strategies = RSocketStrategies . builder ( )
Consumer < RSocketStrategies . Builder > rsocketStrategiesConfigurer = mock ( Consumer . class ) ;
. encoder ( CharSequenceEncoder . allMimeTypes ( ) )
. decoder ( StringDecoder . allMimeTypes ( ) )
. build ( ) ;
ClientRSocketFactoryConfigurer factoryConfigurer = mock ( ClientRSocketFactoryConfigurer . class ) ;
Consumer < RSocketStrategies . Builder > strategiesConfigurer = mock ( Consumer . class ) ;
RSocketRequester . builder ( )
RSocketRequester . builder ( )
. rsocketStrategies ( strategies )
. rsocketFactory ( this . rsocketFactoryConfigurer )
. rsocketFactory ( factoryConfigurer )
. rsocketStrategies ( rsocketStrategiesConfigurer )
. rsocketStrategies ( strategiesConfigurer )
. connect ( this . transport )
. connect ( this . transport )
. block ( ) ;
. block ( ) ;
// RSocketStrategies and RSocketFactory configurers should have been called
verify ( this . transport ) . connect ( anyInt ( ) ) ;
verify ( this . transport ) . connect ( anyInt ( ) ) ;
verify ( factoryConfigurer ) . configureWithStrategies ( any ( RSocketStrategies . class ) ) ;
verify ( rsocketStrategiesConfigurer ) . accept ( any ( RSocketStrategies . Builder . class ) ) ;
verify ( factoryConfigurer ) . configure ( any ( RSocketFactory . ClientRSocketFactory . class ) ) ;
assertThat ( this . rsocketFactoryConfigurer . rsocketStrategies ( ) ) . isNotNull ( ) ;
verify ( strategiesConfigurer ) . accept ( any ( RSocketStrategies . Builder . class ) ) ;
assertThat ( this . rsocketFactoryConfigurer . rsocketFactory ( ) ) . isNotNull ( ) ;
}
@Test
public void defaultDataMimeType ( ) {
RSocketRequester requester = RSocketRequester . builder ( )
. connect ( this . transport )
. block ( ) ;
assertThat ( requester . dataMimeType ( ) )
. as ( "Default data MimeType, based on the first configured Decoder" )
. isEqualTo ( MimeTypeUtils . TEXT_PLAIN ) ;
}
}
@Test
@Test
public void dataMimeType ( ) throws NoSuchFieldException {
public void defaultDataMimeTypeWithCustomDecoderRegitered ( ) {
RSocketStrategies strategies = RSocketStrategies . builder ( )
RSocketStrategies strategies = RSocketStrategies . builder ( )
. encoder ( CharSequenceEncoder . allMimeTypes ( ) )
. decoder ( new TestJsonDecoder ( MimeTypeUtils . APPLICATION_JSON ) )
. decoder ( StringDecoder . allMimeTypes ( ) )
. build ( ) ;
. build ( ) ;
RSocketRequester requester = RSocketRequester . builder ( )
RSocketRequester requester = RSocketRequester . builder ( )
. rsocketStrategies ( strategies )
. rsocketStrategies ( strategies )
. connect ( this . transport )
. block ( ) ;
assertThat ( requester . dataMimeType ( ) )
. as ( "Default data MimeType, based on the first configured, non-default Decoder" )
. isEqualTo ( MimeTypeUtils . APPLICATION_JSON ) ;
}
@Test
public void defaultDataMimeTypeWithMultipleCustomDecoderRegitered ( ) {
RSocketStrategies strategies = RSocketStrategies . builder ( )
. decoder ( new TestJsonDecoder ( MimeTypeUtils . APPLICATION_JSON ) )
. decoder ( new TestJsonDecoder ( MimeTypeUtils . APPLICATION_XML ) )
. build ( ) ;
assertThatThrownBy ( ( ) - >
RSocketRequester
. builder ( )
. rsocketStrategies ( strategies )
. connect ( this . transport )
. block ( ) )
. hasMessageContaining ( "Cannot select default data MimeType" ) ;
}
@Test
public void dataMimeTypeSet ( ) {
RSocketRequester requester = RSocketRequester . builder ( )
. dataMimeType ( MimeTypeUtils . APPLICATION_JSON )
. dataMimeType ( MimeTypeUtils . APPLICATION_JSON )
. connect ( this . transport )
. connect ( this . transport )
. block ( ) ;
. block ( ) ;
Field field = DefaultRSocketRequester . class . getDeclaredField ( "dataMimeType" ) ;
assertThat ( requester . dataMimeType ( ) ) . isEqualTo ( MimeTypeUtils . APPLICATION_JSON ) ;
}
@Test
public void frameDecoderMatchesDataBufferFactory ( ) throws Exception {
testFrameDecoder ( new NettyDataBufferFactory ( ByteBufAllocator . DEFAULT ) , PayloadDecoder . ZERO_COPY ) ;
testFrameDecoder ( new DefaultDataBufferFactory ( ) , PayloadDecoder . DEFAULT ) ;
}
private void testFrameDecoder ( DataBufferFactory bufferFactory , PayloadDecoder frameDecoder )
throws NoSuchFieldException {
RSocketStrategies strategies = RSocketStrategies . builder ( )
. dataBufferFactory ( bufferFactory )
. build ( ) ;
RSocketRequester . builder ( )
. rsocketStrategies ( strategies )
. rsocketFactory ( this . rsocketFactoryConfigurer )
. connect ( this . transport )
. block ( ) ;
RSocketFactory . ClientRSocketFactory factory = this . rsocketFactoryConfigurer . rsocketFactory ( ) ;
assertThat ( factory ) . isNotNull ( ) ;
Field field = RSocketFactory . ClientRSocketFactory . class . getDeclaredField ( "payloadDecoder" ) ;
ReflectionUtils . makeAccessible ( field ) ;
ReflectionUtils . makeAccessible ( field ) ;
MimeType dataMimeType = ( MimeType ) ReflectionUtils . getField ( field , requester ) ;
PayloadDecoder decoder = ( PayloadDecoder ) ReflectionUtils . getField ( field , factory ) ;
assertThat ( dataMimeType ) . isEqualTo ( MimeTypeUtils . APPLICATION_JSON ) ;
assertThat ( decoder ) . isSameAs ( frameDecoder ) ;
}
}
@ -135,4 +208,75 @@ public class DefaultRSocketRequesterBuilderTests {
}
}
}
}
static class TestRSocketFactoryConfigurer implements ClientRSocketFactoryConfigurer {
private RSocketStrategies strategies ;
private RSocketFactory . ClientRSocketFactory rsocketFactory ;
public RSocketStrategies rsocketStrategies ( ) {
return this . strategies ;
}
public RSocketFactory . ClientRSocketFactory rsocketFactory ( ) {
return this . rsocketFactory ;
}
@Override
public void configureWithStrategies ( RSocketStrategies strategies ) {
this . strategies = strategies ;
}
@Override
public void configure ( RSocketFactory . ClientRSocketFactory rsocketFactory ) {
this . rsocketFactory = rsocketFactory ;
}
}
static class TestJsonDecoder implements Decoder < Object > {
private final MimeType mimeType ;
TestJsonDecoder ( MimeType mimeType ) {
this . mimeType = mimeType ;
}
@Override
public List < MimeType > getDecodableMimeTypes ( ) {
return Collections . singletonList ( this . mimeType ) ;
}
@Override
public boolean canDecode ( ResolvableType elementType , MimeType mimeType ) {
return false ;
}
@Override
public Mono < Object > decodeToMono ( Publisher < DataBuffer > inputStream , ResolvableType elementType ,
MimeType mimeType , Map < String , Object > hints ) {
throw new UnsupportedOperationException ( ) ;
}
@Override
public Flux < Object > decode ( Publisher < DataBuffer > inputStream , ResolvableType elementType ,
MimeType mimeType , Map < String , Object > hints ) {
throw new UnsupportedOperationException ( ) ;
}
@Override
public Object decode ( DataBuffer buffer , ResolvableType targetType , MimeType mimeType ,
Map < String , Object > hints ) throws DecodingException {
throw new UnsupportedOperationException ( ) ;
}
}
}
}