Initial support for Reactive API
Fixes #520 Fixes #458 (for non-reactive binders) Adds spring-cloud-stream-reactive module. Introduces support for declarative @StreamListener and @Input and @Output annotated parameters. Add StreamListenerArgumentAdapter and StreamListenerResultAdapter for wrapping bindable inputs and outputs when passing arguments to declarative @StreamListener. Adds support for using reactive types (Flux/Observable) with traditional binders. Introduce FluxSender and ObservableSender for handling multiple streaming outputs per method. Ensure that errors are caught and logged. Fixing constructor assertions and Javadoc Addressing PR comments - rework @Input/@Output parameter validation - renamed StreamListenerArgumentAdapter to StreamListenerParameterAdapter - ensure that parameter direction is accounted for in the current adapters
This commit is contained in:
committed by
Mark Fisher
parent
55b9aa75dd
commit
08d65a92bb
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.stream.config;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.cloud.stream.test.binder.MessageCollector;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class StreamListenerWithAnnotatedArgsTests {
|
||||
|
||||
@Test
|
||||
public void testInputOutputArgs() throws Exception {
|
||||
ConfigurableApplicationContext context = SpringApplication.run(TestInputOutputArgs.class, "--server.port=0");
|
||||
sendMessageAndValidate(context);
|
||||
}
|
||||
|
||||
private void sendMessageAndValidate(ConfigurableApplicationContext context) throws InterruptedException {
|
||||
@SuppressWarnings("unchecked")
|
||||
Processor processor = context.getBean(Processor.class);
|
||||
processor.input().send(MessageBuilder.withPayload("hello").setHeader("contentType", "text/plain").build());
|
||||
MessageCollector messageCollector = context.getBean(MessageCollector.class);
|
||||
Message<?> result = messageCollector.forChannel(processor.output()).poll(1000, TimeUnit.MILLISECONDS);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getPayload()).isEqualTo("HELLO");
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
@EnableBinding(Processor.class)
|
||||
@EnableAutoConfiguration
|
||||
public static class TestInputOutputArgs {
|
||||
|
||||
@StreamListener
|
||||
public void receive(@Input(Processor.INPUT) SubscribableChannel input, @Output(Processor.OUTPUT) final MessageChannel output) {
|
||||
input.subscribe(new MessageHandler() {
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
output.send(MessageBuilder.withPayload(message.getPayload().toString().toUpperCase()).build());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,7 @@ import static org.junit.Assert.fail;
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class StreamListenerTests {
|
||||
public class StreamListenerWithHandlerTests {
|
||||
|
||||
@Test
|
||||
public void testContentTypeConversion() throws Exception {
|
||||
@@ -240,8 +240,8 @@ public class StreamListenerTests {
|
||||
|
||||
@StreamListener(Sink.INPUT)
|
||||
public void receive(FooPojo fooPojo) {
|
||||
receivedArguments.add(fooPojo);
|
||||
latch.countDown();
|
||||
this.receivedArguments.add(fooPojo);
|
||||
this.latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class StreamListenerTests {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public String receive(FooPojo fooPojo) {
|
||||
receivedPojos.add(fooPojo);
|
||||
this.receivedPojos.add(fooPojo);
|
||||
return fooPojo.getBar();
|
||||
}
|
||||
}
|
||||
@@ -268,7 +268,7 @@ public class StreamListenerTests {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public BazPojo receive(FooPojo fooPojo) {
|
||||
receivedPojos.add(fooPojo);
|
||||
this.receivedPojos.add(fooPojo);
|
||||
BazPojo bazPojo = new BazPojo();
|
||||
bazPojo.setQux(fooPojo.getBar());
|
||||
return bazPojo;
|
||||
@@ -285,9 +285,9 @@ public class StreamListenerTests {
|
||||
public void receive(@Payload FooPojo fooPojo,
|
||||
@Headers Map<String, Object> headers,
|
||||
@Header(MessageHeaders.CONTENT_TYPE) String contentType) {
|
||||
receivedArguments.add(fooPojo);
|
||||
receivedArguments.add(headers);
|
||||
receivedArguments.add(contentType);
|
||||
this.receivedArguments.add(fooPojo);
|
||||
this.receivedArguments.add(headers);
|
||||
this.receivedArguments.add(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ public class StreamListenerTests {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public Message<?> receive(FooPojo fooPojo) {
|
||||
receivedPojos.add(fooPojo);
|
||||
this.receivedPojos.add(fooPojo);
|
||||
BazPojo bazPojo = new BazPojo();
|
||||
bazPojo.setQux(fooPojo.getBar());
|
||||
return MessageBuilder.withPayload(bazPojo).setHeader("foo", "bar").build();
|
||||
@@ -316,7 +316,7 @@ public class StreamListenerTests {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public BazPojo receive(Message<String> fooMessage) {
|
||||
receivedMessages.add(fooMessage);
|
||||
this.receivedMessages.add(fooMessage);
|
||||
BazPojo bazPojo = new BazPojo();
|
||||
bazPojo.setQux(fooMessage.getPayload());
|
||||
return bazPojo;
|
||||
@@ -353,7 +353,7 @@ public class StreamListenerTests {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public BazPojo receive(FooPojo fooMessage) {
|
||||
receivedPojos.add(fooMessage);
|
||||
this.receivedPojos.add(fooMessage);
|
||||
BazPojo bazPojo = new BazPojo();
|
||||
bazPojo.setQux(fooMessage.getBar());
|
||||
return bazPojo;
|
||||
@@ -365,7 +365,7 @@ public class StreamListenerTests {
|
||||
private String bar;
|
||||
|
||||
public String getBar() {
|
||||
return bar;
|
||||
return this.bar;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
@@ -378,7 +378,7 @@ public class StreamListenerTests {
|
||||
private String qux;
|
||||
|
||||
public String getQux() {
|
||||
return qux;
|
||||
return this.qux;
|
||||
}
|
||||
|
||||
public void setQux(String qux) {
|
||||
Reference in New Issue
Block a user