Add support for Message<Foo> in stream apps

The FunctionInspector needs to be able to distinguish between a
function of Flux<Foo> and a function of Flux<Message<Foo>>. Then
it can do the conversion a level below.

Only supports Message->Message or POJO->POJO (no mixtures), so there
is only one new method in FunctionInspector.

No support in the web endpoints yet. But it's probably not so hard
to add.
This commit is contained in:
Dave Syer
2017-06-15 12:32:46 +01:00
parent 797936fd0c
commit 0756dc3394
9 changed files with 307 additions and 21 deletions

View File

@@ -93,7 +93,10 @@ public class StreamListeningConsumerInvoker implements SmartInitializingSingleto
private Function<Message<?>, Object> convertInput(String name) {
Class<?> inputType = functionInspector.getInputType(name);
return m -> {
if (inputType.isAssignableFrom(m.getPayload().getClass())) {
if (Message.class.isAssignableFrom(inputType)) {
return m;
}
else if (inputType.isAssignableFrom(m.getPayload().getClass())) {
return m.getPayload();
}
else {

View File

@@ -28,7 +28,7 @@ import org.springframework.cloud.stream.converter.CompositeMessageConverterFacto
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.util.Assert;
import org.springframework.messaging.support.MessageBuilder;
import reactor.core.publisher.Flux;
@@ -96,12 +96,22 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
private Function<Message<?>, Object> convertInput(String name) {
Class<?> inputType = functionInspector.getInputType(name);
return m -> {
if (inputType.isAssignableFrom(m.getPayload().getClass())) {
return m.getPayload();
if (functionInspector.isMessage(name)) {
return MessageBuilder.withPayload(convertPayload(name, inputType, m))
.copyHeaders(m.getHeaders()).build();
}
else {
return this.converter.fromMessage(m, inputType);
return convertPayload(name, inputType, m);
}
};
}
private Object convertPayload(String name, Class<?> inputType, Message<?> m) {
if (inputType.isAssignableFrom(m.getPayload().getClass())) {
return m.getPayload();
}
else {
return this.converter.fromMessage(m, inputType);
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2017 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.function.stream.function;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.cloud.stream.test.binder.MessageCollector;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import reactor.core.publisher.Flux;
/**
* @author Marius Bogoevici
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FluxMessagePojoStreamingFunctionTests.StreamingFunctionApplication.class)
public class FluxMessagePojoStreamingFunctionTests {
@Autowired
Processor processor;
@Autowired
MessageCollector messageCollector;
@Test
public void test() throws Exception {
processor.input().send(
MessageBuilder.withPayload(new String("{\"name\":\"foo\"}")).build());
Message<?> result = messageCollector.forChannel(processor.output()).poll(1000,
TimeUnit.MILLISECONDS);
assertThat(result.getPayload()).isInstanceOf(Foo.class);
}
@SpringBootApplication
public static class StreamingFunctionApplication {
@Bean
public Function<Flux<Message<Foo>>, Flux<Message<Foo>>> uppercase() {
return flux -> flux.map(f -> MessageBuilder
.withPayload(new Foo(f.getPayload().getName().toUpperCase()))
.build());
}
}
protected static class Foo {
private String name;
Foo() {
}
public Foo(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2017 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.function.stream.function;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.cloud.stream.test.binder.MessageCollector;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marius Bogoevici
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MessagePojoStreamingFunctionTests.StreamingFunctionApplication.class)
public class MessagePojoStreamingFunctionTests {
@Autowired
Processor processor;
@Autowired
MessageCollector messageCollector;
@Test
public void test() throws Exception {
processor.input().send(
MessageBuilder.withPayload(new String("{\"name\":\"foo\"}")).build());
Message<?> result = messageCollector.forChannel(processor.output()).poll(1000,
TimeUnit.MILLISECONDS);
assertThat(result.getPayload()).isInstanceOf(Foo.class);
}
@SpringBootApplication
public static class StreamingFunctionApplication {
@Bean
public Function<Message<Foo>, Message<Foo>> uppercase() {
return f -> MessageBuilder
.withPayload(new Foo(f.getPayload().getName().toUpperCase())).build();
}
}
protected static class Foo {
private String name;
Foo() {
}
public Foo(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}