Migrate to servlet binder for web features
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface EnabledBindings {
|
||||
|
||||
String getInput(String output);
|
||||
|
||||
Set<String> getOutputs();
|
||||
|
||||
Set<String> getInputs();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
class HeaderUtils {
|
||||
|
||||
public static HttpHeaders fromMessage(Map<String, Object> headers,
|
||||
HttpHeaders request) {
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
for (String name : headers.keySet()) {
|
||||
Object value = headers.get(name);
|
||||
name = name.toLowerCase();
|
||||
if (MessageHeaders.ID.equals(name)) {
|
||||
continue;
|
||||
}
|
||||
if (request.containsKey(name)) {
|
||||
if (name.startsWith("x-")) {
|
||||
if (!name.startsWith("x-forwarded")) {
|
||||
Collection<?> values = multi(value);
|
||||
for (Object object : values) {
|
||||
result.set(name, object.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Collection<?> values = multi(value);
|
||||
for (Object object : values) {
|
||||
result.set(name, object.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Collection<?> multi(Object value) {
|
||||
if (value instanceof Collection) {
|
||||
Collection<?> collection = (Collection<?>) value;
|
||||
return collection;
|
||||
}
|
||||
else if (ObjectUtils.isArray(value)) {
|
||||
Object[] values = ObjectUtils.toObjectArray(value);
|
||||
return Arrays.asList(values);
|
||||
}
|
||||
return Arrays.asList(value);
|
||||
}
|
||||
|
||||
public static MessageHeaders fromHttp(HttpHeaders headers) {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
for (String name : headers.keySet()) {
|
||||
Collection<?> values = multi(headers.get(name));
|
||||
name = name.toLowerCase();
|
||||
Object value = values == null ? null
|
||||
: (values.size() == 1 ? values.iterator().next() : values);
|
||||
map.put(name, value);
|
||||
}
|
||||
return new MessageHeaders(map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Internal convenience class to help with JSON message bodies. In particular translating
|
||||
* between JSON arrays and lists of payloads.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
class JsonUtils {
|
||||
|
||||
/**
|
||||
* Split a JSON array into a list of individual objects, without parsing the objects
|
||||
* themselves..
|
||||
*/
|
||||
public static List<String> split(String body) {
|
||||
body = body.trim();
|
||||
// it's an array
|
||||
List<String> strings = new ArrayList<>();
|
||||
int index = 0;
|
||||
int open = 0;
|
||||
boolean inString = false;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (index++ < body.length() - 1) {
|
||||
char current = body.charAt(index);
|
||||
builder.append(current);
|
||||
if (body.charAt(index - 1) != '\\') {
|
||||
if (current == '"') {
|
||||
if (!inString) {
|
||||
open++;
|
||||
inString = true;
|
||||
}
|
||||
else {
|
||||
open--;
|
||||
inString = false;
|
||||
}
|
||||
}
|
||||
else if (current == '[') {
|
||||
open++;
|
||||
}
|
||||
else if (current == ']') {
|
||||
open--;
|
||||
}
|
||||
else if (current == '{') {
|
||||
open++;
|
||||
}
|
||||
else if (current == '}') {
|
||||
open--;
|
||||
}
|
||||
}
|
||||
if (open == 0) {
|
||||
if (builder.charAt(0) == '"') {
|
||||
builder.delete(0, 1);
|
||||
builder.delete(builder.length() - 1, builder.length());
|
||||
}
|
||||
strings.add(builder.toString());
|
||||
builder.setLength(0);
|
||||
while (index++ < body.length() - 1 && body.charAt(index) != ',') {
|
||||
}
|
||||
while (index++ < body.length() - 1
|
||||
&& Character.isWhitespace(body.charAt(index))) {
|
||||
}
|
||||
index--;
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.reactivestreams.Processor;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.UnicastProcessor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/${spring.cloud.stream.binder.servlet.prefix:stream}")
|
||||
public class MessageController implements RouteRegistrar {
|
||||
|
||||
public static final String ROUTE_KEY = "stream_routekey";
|
||||
|
||||
private final ConcurrentMap<String, Bridge<Message<?>>> queues = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentMap<String, Set<SseEmitter>> emitters = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, MessageChannel> inputs = new HashMap<>();
|
||||
|
||||
private final Map<String, String> outputs = new HashMap<>();
|
||||
|
||||
private final EnabledBindings bindings;
|
||||
|
||||
private final MessagingTemplate template = new MessagingTemplate();
|
||||
|
||||
private String prefix;
|
||||
|
||||
public long timeoutSeconds = 10;
|
||||
|
||||
private long receiveTimeoutMillis;
|
||||
|
||||
private Set<String> routes = new LinkedHashSet<>();
|
||||
|
||||
public MessageController(String prefix, EnabledBindings bindings) {
|
||||
if (!prefix.startsWith("/")) {
|
||||
prefix = "/" + prefix;
|
||||
}
|
||||
if (!prefix.endsWith("/")) {
|
||||
prefix = prefix + "/";
|
||||
}
|
||||
this.prefix = prefix;
|
||||
this.bindings = bindings;
|
||||
this.template.setReceiveTimeout(this.receiveTimeoutMillis);
|
||||
}
|
||||
|
||||
public void setReceiveTimeoutSeconds(long receiveTimeoutMillis) {
|
||||
this.receiveTimeoutMillis = receiveTimeoutMillis;
|
||||
this.template.setReceiveTimeout(receiveTimeoutMillis);
|
||||
}
|
||||
|
||||
public void setBufferTimeoutSeconds(long timeoutSeconds) {
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
@GetMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public ResponseEntity<SseEmitter> sse(
|
||||
@RequestAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping") String path,
|
||||
@RequestHeader HttpHeaders headers) throws IOException {
|
||||
Route route = output(path);
|
||||
String channel = route.getChannel();
|
||||
if (!bindings.getOutputs().contains(channel)) {
|
||||
return org.springframework.http.ResponseEntity.notFound().build();
|
||||
}
|
||||
Message<Collection<Object>> message = poll(route.getChannel(), route.getKey(),
|
||||
true);
|
||||
SseEmitter body = emit(route, message);
|
||||
return ResponseEntity.ok()
|
||||
.headers(HeaderUtils.fromMessage(message.getHeaders(), headers))
|
||||
.body(body);
|
||||
}
|
||||
|
||||
@GetMapping("/**")
|
||||
public ResponseEntity<Object> supplier(
|
||||
@RequestAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping") String path,
|
||||
@RequestHeader HttpHeaders headers,
|
||||
@RequestParam(required = false) boolean purge) {
|
||||
Route route = output(path);
|
||||
String channel = route.getChannel();
|
||||
if (bindings.getOutputs().contains(channel)) {
|
||||
Message<Collection<Object>> polled = poll(channel, route.getKey(), !purge);
|
||||
if (routes.contains(route.getKey()) || !polled.getPayload().isEmpty()
|
||||
|| route.getKey() == null) {
|
||||
return convert(polled, headers);
|
||||
}
|
||||
}
|
||||
route = input(path);
|
||||
channel = route.getChannel();
|
||||
if (!bindings.getInputs().contains(channel)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
String body = route.getKey();
|
||||
body = body.contains("/") ? body.substring(body.lastIndexOf("/") + 1) : body;
|
||||
path = path.replaceAll("/" + body, "");
|
||||
return string(path, body, headers);
|
||||
}
|
||||
|
||||
@PostMapping(path = "/**", consumes = MediaType.TEXT_PLAIN_VALUE)
|
||||
public ResponseEntity<Object> string(
|
||||
@RequestAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping") String path,
|
||||
@RequestBody String body, @RequestHeader HttpHeaders headers) {
|
||||
return function(path, body, headers);
|
||||
}
|
||||
|
||||
@PostMapping(path = "/**", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Object> json(
|
||||
@RequestAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping") String path,
|
||||
@RequestBody String body, @RequestHeader HttpHeaders headers) {
|
||||
return function(path, extract(body), headers);
|
||||
}
|
||||
|
||||
private Object extract(String body) {
|
||||
body = body.trim();
|
||||
Object result = body;
|
||||
if (body.startsWith("[")) {
|
||||
result = JsonUtils.split(body);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/**")
|
||||
public ResponseEntity<Object> function(
|
||||
@RequestAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping") String path,
|
||||
@RequestBody Object body, @RequestHeader HttpHeaders headers) {
|
||||
Route route = input(path);
|
||||
String channel = route.getChannel();
|
||||
if (!inputs.containsKey(channel)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
Collection<Object> collection;
|
||||
boolean single = false;
|
||||
if (body instanceof String) {
|
||||
body = extract((String) body);
|
||||
}
|
||||
if (body instanceof Collection) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> list = (Collection<Object>) body;
|
||||
collection = list;
|
||||
}
|
||||
else {
|
||||
if (ObjectUtils.isArray(body)) {
|
||||
collection = Arrays.asList(ObjectUtils.toObjectArray(body));
|
||||
}
|
||||
else {
|
||||
single = true;
|
||||
collection = Arrays.asList(body);
|
||||
}
|
||||
}
|
||||
Map<String, Object> messageHeaders = new HashMap<>(HeaderUtils.fromHttp(headers));
|
||||
if (route.getKey() != null) {
|
||||
messageHeaders.put(ROUTE_KEY, route.getKey());
|
||||
}
|
||||
MessageChannel input = inputs.get(channel);
|
||||
Map<String, Object> outputHeaders = null;
|
||||
List<Object> results = new ArrayList<>();
|
||||
HttpStatus status = HttpStatus.ACCEPTED;
|
||||
// This is a total guess. We have no way to guarantee that the user will
|
||||
// implement a Processor so that inputs always get an output, so either
|
||||
// nothing might come back or there might be multiple outputs and we only get
|
||||
// one of them.
|
||||
if (this.outputs.containsKey(channel)) {
|
||||
for (Object payload : collection) {
|
||||
Message<?> result = template.sendAndReceive(input, MessageBuilder
|
||||
.withPayload(payload).copyHeadersIfAbsent(messageHeaders)
|
||||
.setHeader(MessageHeaders.REPLY_CHANNEL, outputs.get(channel))
|
||||
.build());
|
||||
if (result != null) {
|
||||
if (outputHeaders == null) {
|
||||
outputHeaders = new LinkedHashMap<>(result.getHeaders());
|
||||
}
|
||||
results.add(result.getPayload());
|
||||
}
|
||||
}
|
||||
status = HttpStatus.OK;
|
||||
if (results.isEmpty()) {
|
||||
// If nothing came back, just assume it was intentional, and say that
|
||||
// we accepted the inputs.
|
||||
status = HttpStatus.ACCEPTED;
|
||||
results.addAll(collection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (Object payload : collection) {
|
||||
template.send(input, MessageBuilder.withPayload(payload)
|
||||
.copyHeadersIfAbsent(messageHeaders).build());
|
||||
}
|
||||
outputHeaders = messageHeaders;
|
||||
results.addAll(collection);
|
||||
}
|
||||
if (outputHeaders == null) {
|
||||
outputHeaders = new LinkedHashMap<>();
|
||||
}
|
||||
outputHeaders.put(ROUTE_KEY, route.getKey());
|
||||
if (single && results.size() == 1) {
|
||||
body = results.get(0);
|
||||
}
|
||||
else {
|
||||
body = results;
|
||||
}
|
||||
if (headers.getContentType() != null
|
||||
&& headers.getContentType().includes(MediaType.APPLICATION_JSON)
|
||||
&& body.toString().contains("\"")) {
|
||||
body = body.toString();
|
||||
}
|
||||
return convert(status, MessageBuilder.withPayload(body)
|
||||
.copyHeadersIfAbsent(outputHeaders).build(), headers);
|
||||
}
|
||||
|
||||
private ResponseEntity<Object> convert(Message<?> message, HttpHeaders request) {
|
||||
return convert(HttpStatus.OK, message, request);
|
||||
}
|
||||
|
||||
private ResponseEntity<Object> convert(HttpStatus status, Message<?> message,
|
||||
HttpHeaders request) {
|
||||
return ResponseEntity.status(status)
|
||||
.headers(HeaderUtils.fromMessage(message.getHeaders(), request))
|
||||
.body(message.getPayload());
|
||||
}
|
||||
|
||||
private SseEmitter emit(Route route, Message<Collection<Object>> message)
|
||||
throws IOException {
|
||||
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
|
||||
String path = route.getPath();
|
||||
if (!emitters.containsKey(path)) {
|
||||
emitters.putIfAbsent(path, new HashSet<>());
|
||||
}
|
||||
emitters.get(path).add(emitter);
|
||||
emitter.onCompletion(() -> emitters.get(path).remove(emitter));
|
||||
emitter.onTimeout(() -> emitters.get(path).remove(emitter));
|
||||
for (Object body : message.getPayload()) {
|
||||
emitter.send(body);
|
||||
}
|
||||
return emitter;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
queues.clear();
|
||||
}
|
||||
|
||||
private Message<Collection<Object>> poll(String channel, String key,
|
||||
boolean requeue) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
List<Message<?>> messages = new ArrayList<>();
|
||||
Bridge<Message<?>> queue = queues.get(new Route(key, channel).getPath());
|
||||
if (queue != null) {
|
||||
queue.receive().subscribe(message -> {
|
||||
messages.add(message);
|
||||
list.add(message.getPayload());
|
||||
});
|
||||
if (!requeue) {
|
||||
queue.reset();
|
||||
}
|
||||
}
|
||||
MessageBuilder<Collection<Object>> builder = MessageBuilder.withPayload(list);
|
||||
if (!messages.isEmpty()) {
|
||||
builder.copyHeadersIfAbsent(messages.get(0).getHeaders());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public void subscribe(String name, SubscribableChannel outboundBindTarget) {
|
||||
this.outputs.put(bindings.getInput(name), name);
|
||||
outboundBindTarget.subscribe(message -> this.append(name, message));
|
||||
}
|
||||
|
||||
private void append(String name, Message<?> message) {
|
||||
String key = (String) message.getHeaders().get(ROUTE_KEY);
|
||||
if (message.getHeaders().getReplyChannel() instanceof MessageChannel) {
|
||||
MessageChannel replyChannel = (MessageChannel) message.getHeaders()
|
||||
.getReplyChannel();
|
||||
replyChannel.send(message);
|
||||
return;
|
||||
}
|
||||
Route route = new Route(key, name);
|
||||
String path = route.getPath();
|
||||
if (!queues.containsKey(path)) {
|
||||
Bridge<Message<?>> flux = new Bridge<>();
|
||||
queues.putIfAbsent(path, flux);
|
||||
}
|
||||
queues.get(path).send(message);
|
||||
if (emitters.containsKey(path)) {
|
||||
Set<SseEmitter> list = new HashSet<>(emitters.get(path));
|
||||
for (SseEmitter emitter : list) {
|
||||
try {
|
||||
emitter.send(message.getPayload());
|
||||
}
|
||||
catch (IOException e) {
|
||||
emitters.get(path).remove(emitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void bind(String name, String group, MessageChannel inputTarget) {
|
||||
this.inputs.put(name, inputTarget);
|
||||
}
|
||||
|
||||
public Route output(String path) {
|
||||
return new Route(prefix, path,
|
||||
bindings.getOutputs().size() == 1
|
||||
? bindings.getOutputs().iterator().next()
|
||||
: "output");
|
||||
}
|
||||
|
||||
public Route input(String path) {
|
||||
return new Route(prefix, path,
|
||||
bindings.getInputs().size() == 1 ? bindings.getInputs().iterator().next()
|
||||
: "input");
|
||||
}
|
||||
|
||||
private class Route {
|
||||
private String key;
|
||||
private String channel;
|
||||
private String path;
|
||||
|
||||
private Route(String prefix, String path, String defaultChannel) {
|
||||
String channel;
|
||||
String route = null;
|
||||
// Strip the prefix first
|
||||
if (path.length() > prefix.length()) {
|
||||
path = path.substring(prefix.length());
|
||||
}
|
||||
else {
|
||||
path = "";
|
||||
}
|
||||
// Then extract the first segment of the path, and call it a "channel"
|
||||
String[] paths = path.split("/");
|
||||
if (paths.length > 1) {
|
||||
channel = paths[0];
|
||||
route = path.substring(channel.length() + 1, path.length());
|
||||
}
|
||||
else {
|
||||
channel = path;
|
||||
}
|
||||
// If it's not actually a channel we know about, use the default, and call the
|
||||
// whole path a "route"
|
||||
if (!bindings.getInputs().contains(channel)
|
||||
& !bindings.getOutputs().contains(channel)) {
|
||||
channel = defaultChannel;
|
||||
route = path.length() > 0 ? path : null;
|
||||
}
|
||||
this.channel = channel;
|
||||
this.key = route;
|
||||
this.path = key != null ? key + "/" + channel : channel;
|
||||
}
|
||||
|
||||
public Route(String key, String channel) {
|
||||
this.key = key;
|
||||
this.channel = channel;
|
||||
this.path = key != null ? key + "/" + channel : channel;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
|
||||
private class Bridge<T> {
|
||||
|
||||
private Processor<T, T> emitter;
|
||||
private Flux<T> sink;
|
||||
|
||||
public Bridge() {
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.emitter = UnicastProcessor.<T>create().serialize();
|
||||
this.sink = Flux.from(emitter).replay().autoConnect()
|
||||
.take(Duration.ofSeconds(timeoutSeconds));
|
||||
}
|
||||
|
||||
public void send(T item) {
|
||||
emitter.onNext(item);
|
||||
}
|
||||
|
||||
public Flux<T> receive() {
|
||||
return sink;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRoutes(Set<String> routes) {
|
||||
this.routes.addAll(routes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterRoutes(Set<String> routes) {
|
||||
this.routes.removeAll(routes);
|
||||
for (String path : routes) {
|
||||
queues.remove(output(prefix + path).getPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface RouteRegistrar {
|
||||
|
||||
void registerRoutes(Set<String> routes);
|
||||
|
||||
void unregisterRoutes(Set<String> routes);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface RouteRegistry {
|
||||
|
||||
Set<String> routes();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2014-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.binder.servlet;
|
||||
|
||||
import org.springframework.cloud.stream.binder.AbstractBinder;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.cloud.stream.binder.ConsumerProperties;
|
||||
import org.springframework.cloud.stream.binder.DefaultBinding;
|
||||
import org.springframework.cloud.stream.binder.ProducerProperties;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.cloud.stream.binder.Binder} implementation backed by HTTP.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ServletMessageChannelBinder
|
||||
extends AbstractBinder<MessageChannel, ConsumerProperties, ProducerProperties> {
|
||||
|
||||
private MessageController controller;
|
||||
|
||||
public ServletMessageChannelBinder(MessageController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Binding<MessageChannel> doBindConsumer(String name, String group,
|
||||
MessageChannel inputTarget, ConsumerProperties properties) {
|
||||
controller.bind(name, group, inputTarget);
|
||||
return new DefaultBinding<MessageChannel>(name, group, inputTarget, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Binding<MessageChannel> doBindProducer(String name,
|
||||
MessageChannel outboundBindTarget, ProducerProperties properties) {
|
||||
controller.subscribe(name, (SubscribableChannel) outboundBindTarget);
|
||||
return new DefaultBinding<MessageChannel>(name, null, outboundBindTarget, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
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.binder.servlet.EnabledBindings;
|
||||
import org.springframework.cloud.stream.binding.BindingBeanDefinitionRegistryUtils;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BeanFactoryEnabledBindings implements EnabledBindings {
|
||||
|
||||
private final ConfigurableListableBeanFactory beanFactory;
|
||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
private final Map<String, String> outputsToInputs = new HashMap<>();
|
||||
private final Set<String> outputs = new HashSet<>();
|
||||
private final Set<String> inputs = new HashSet<>();
|
||||
private final BindingServiceProperties binding;
|
||||
|
||||
public BeanFactoryEnabledBindings(ConfigurableListableBeanFactory beanFactory,
|
||||
BindingServiceProperties binding) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getInputs() {
|
||||
init();
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getOutputs() {
|
||||
init();
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInput(String output) {
|
||||
init();
|
||||
return outputsToInputs.get(output);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
String[] names = beanFactory.getBeanNamesForAnnotation(EnableBinding.class);
|
||||
for (String bean : names) {
|
||||
Class<?> type = beanFactory.getType(bean);
|
||||
MultiValueMap<String, Object> attrs = AnnotatedElementUtils
|
||||
.getAllAnnotationAttributes(type, EnableBinding.class.getName());
|
||||
List<Object> list = attrs.get("value");
|
||||
if (list != null) {
|
||||
for (Object object : list) {
|
||||
Class<?>[] bindings = (Class<?>[]) object;
|
||||
for (Class<?> binding : bindings) {
|
||||
List<String> inputs = new ArrayList<>();
|
||||
List<String> outputs = new ArrayList<>();
|
||||
ReflectionUtils.doWithMethods(binding, method -> {
|
||||
Input input = AnnotationUtils.findAnnotation(method,
|
||||
Input.class);
|
||||
Output output = AnnotationUtils.findAnnotation(method,
|
||||
Output.class);
|
||||
if (input != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils
|
||||
.getBindingTargetName(input, method);
|
||||
inputs.add(BeanFactoryEnabledBindings.this.binding
|
||||
.getBindingDestination(name));
|
||||
}
|
||||
if (output != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils
|
||||
.getBindingTargetName(output, method);
|
||||
outputs.add(BeanFactoryEnabledBindings.this.binding
|
||||
.getBindingDestination(name));
|
||||
}
|
||||
});
|
||||
BeanFactoryEnabledBindings.this.outputs.addAll(outputs);
|
||||
BeanFactoryEnabledBindings.this.inputs.addAll(inputs);
|
||||
if (inputs.size() == 1 && outputs.size() == 1) {
|
||||
BeanFactoryEnabledBindings.this.outputsToInputs
|
||||
.put(outputs.get(0), inputs.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2015-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.binder.servlet.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.stream.binder.servlet.EnabledBindings;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.binder.servlet.RouteRegistry;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore({ WebMvcAutoConfiguration.class })
|
||||
@ConfigurationProperties("spring.cloud.stream.binder.servlet")
|
||||
@ConditionalOnProperty(name = "spring.cloud.stream.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class MessageHandlingAutoConfiguration {
|
||||
|
||||
/**
|
||||
* The prefix for the HTTP endpoint.
|
||||
*/
|
||||
private String prefix = "stream";
|
||||
|
||||
/**
|
||||
* The buffer timeout for messages sent to output channels, and accessed via GET.
|
||||
*/
|
||||
private long bufferTimeoutSeconds = 10;
|
||||
|
||||
/**
|
||||
* The receive timeout for messages in a send-and-receive from a linked output via
|
||||
* POST.
|
||||
*/
|
||||
private long receiveTimeoutMillis = 100;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<RouteRegistry> registries;
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public long getBufferTimeoutSeconds() {
|
||||
return bufferTimeoutSeconds;
|
||||
}
|
||||
|
||||
public void setBufferTimeoutSeconds(long bufferTimeoutSeconds) {
|
||||
this.bufferTimeoutSeconds = bufferTimeoutSeconds;
|
||||
}
|
||||
|
||||
public long getReceiveTimeoutMillis() {
|
||||
return receiveTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setReceiveTimeoutMillis(long receiveTimeoutMillis) {
|
||||
this.receiveTimeoutMillis = receiveTimeoutMillis;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageController messageController(EnabledBindings bindings) {
|
||||
MessageController controller = new MessageController(prefix, bindings);
|
||||
controller.setBufferTimeoutSeconds(bufferTimeoutSeconds);
|
||||
controller.setReceiveTimeoutSeconds(receiveTimeoutMillis);
|
||||
if (registries != null) {
|
||||
for (RouteRegistry registry : registries) {
|
||||
controller.registerRoutes(registry.routes());
|
||||
}
|
||||
}
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanFactoryEnabledBindings enabledBindings(
|
||||
ConfigurableListableBeanFactory beanFactory,
|
||||
BindingServiceProperties binding) {
|
||||
return new BeanFactoryEnabledBindings(beanFactory, binding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2015-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.binder.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.cloud.stream.binder.Binder;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.binder.servlet.ServletMessageChannelBinder;
|
||||
import org.springframework.cloud.stream.config.codec.kryo.KryoCodecAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.integration.codec.Codec;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(Binder.class)
|
||||
@AutoConfigureBefore({ WebMvcAutoConfiguration.class })
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class, KryoCodecAutoConfiguration.class })
|
||||
public class ServletServiceAutoConfiguration {
|
||||
@Autowired
|
||||
private Codec codec;
|
||||
|
||||
@Bean
|
||||
public ServletMessageChannelBinder servletMessageChannelBinder(
|
||||
MessageController controller) {
|
||||
|
||||
ServletMessageChannelBinder messageChannelBinder = new ServletMessageChannelBinder(
|
||||
controller);
|
||||
messageChannelBinder.setCodec(this.codec);
|
||||
return messageChannelBinder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* This package contains an implementation of the {@link org.springframework.cloud.stream.binder.Binder} for Redis.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.binder.servlet;
|
||||
@@ -0,0 +1,2 @@
|
||||
servlet:\
|
||||
org.springframework.cloud.stream.binder.servlet.config.ServletServiceAutoConfiguration
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.stream.binder.servlet.config.MessageHandlingAutoConfiguration
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JsonUtilsTests {
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Test
|
||||
public void empty() {
|
||||
assertThat(JsonUtils.split("[]")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void strings() {
|
||||
assertThat(JsonUtils.split("[\"foo\", \"bar\"]")).hasSize(2).contains("foo",
|
||||
"bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void objects() throws Exception {
|
||||
assertThat(JsonUtils.split(
|
||||
mapper.writeValueAsString(Arrays.asList(new Foo("foo"), new Foo("bar")))))
|
||||
.hasSize(2)
|
||||
.contains("{\"value\":\"foo\"}", "{\"value\":\"bar\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrays() throws Exception {
|
||||
assertThat(JsonUtils.split(mapper.writeValueAsString(
|
||||
Arrays.asList(Arrays.asList(new Foo("foo"), new Foo("bar")))))).hasSize(1)
|
||||
.contains("[{\"value\":\"foo\"},{\"value\":\"bar\"}]");
|
||||
}
|
||||
|
||||
protected static class Foo {
|
||||
private String value;
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
|
||||
public Foo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class DoubleSinkMessageChannelBinderTests implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private Sink sink;
|
||||
|
||||
@Autowired
|
||||
private Custom custom;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
@Test
|
||||
public void string() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(
|
||||
post("/stream/input").contentType(MediaType.TEXT_PLAIN).content("hello"))
|
||||
.andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void custom() throws Exception {
|
||||
custom.input().subscribe(this);
|
||||
mockMvc.perform(
|
||||
post("/stream/custom").contentType(MediaType.TEXT_PLAIN).content("hello"))
|
||||
.andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
custom.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding({ Sink.class, Custom.class })
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
interface Custom {
|
||||
@Input("custom")
|
||||
SubscribableChannel input();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class HeaderProcessorMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isOk())
|
||||
.andExpect(header().string("x-foo", "bar"))
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public Message<String> uppercase(Message<String> input) {
|
||||
return MessageBuilder.withPayload(input.getPayload().toUpperCase())
|
||||
.copyHeadersIfAbsent(input.getHeaders()).setHeader("x-foo", "bar")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class HeadersDroppedRoutedProcessorMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(post("/stream/input/words")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(MessageController.ROUTE_KEY, "words"))
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public String uppercase(String input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
import org.junit.Before;
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest("spring.cloud.stream.bindings.input.destination:words")
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class NamedProcessorMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private MessageController controller;
|
||||
|
||||
@Autowired
|
||||
private Processor processor;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
controller.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(post("/stream/words").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicit() throws Exception {
|
||||
mockMvc.perform(post("/stream").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() throws Exception {
|
||||
mockMvc.perform(get("/stream/output").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("[]")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void output() throws Exception {
|
||||
processor.input().send(MessageBuilder.withPayload("hello").build());
|
||||
mockMvc.perform(get("/stream/output").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputWithRoute() throws Exception {
|
||||
processor.input().send(MessageBuilder.withPayload("hello")
|
||||
.setHeader(MessageController.ROUTE_KEY, "uppercase").build());
|
||||
mockMvc.perform(
|
||||
get("/stream/output/uppercase").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public String uppercase(String input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class NamedSinkMessageChannelBinderTests implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private Sink sink;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
@Test
|
||||
public void consumer() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Sink.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest("spring.cloud.stream.bindings.output.destination:words")
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class NamedSourceMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private Source source;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void supplier() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello").build());
|
||||
mockMvc.perform(get("/stream/words")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Source.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties = "logging.level.org.springframework.web=DEBUG")
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class PojoProcessorMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void json() throws Exception {
|
||||
mockMvc.perform(post("/stream").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"value\":\"hello\"}")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void text() throws Exception {
|
||||
mockMvc.perform(post("/stream").contentType(MediaType.TEXT_PLAIN)
|
||||
.content("[{\"value\":\"hello\"}]")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void single() throws Exception {
|
||||
mockMvc.perform(post("/stream").contentType(MediaType.TEXT_PLAIN)
|
||||
.content("{\"value\":\"hello\"}")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public Foo uppercase(Foo input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Foo {
|
||||
private String value;
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
|
||||
public Foo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Foo toUpperCase() {
|
||||
return new Foo(value.toUpperCase());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class PojoSinkMessageChannelBinderTests implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private Sink sink;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
@Test
|
||||
public void consumer() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"value\":\"hello\"}")).andExpect(status().isAccepted())
|
||||
.andExpect(content().string("{\"value\":\"hello\"}"));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multi() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[{\"value\":\"hello\"},{\"value\":\"world\"}]"))
|
||||
.andExpect(status().isAccepted()).andExpect(content()
|
||||
.string("[{\"value\":\"hello\"}, {\"value\":\"world\"}]"));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Sink.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
protected static class Foo {
|
||||
private String value;
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
|
||||
public Foo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Foo toUpperCase() {
|
||||
return new Foo(value.toUpperCase());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest("spring.cloud.stream.binder.servlet.prefix:awesome")
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class PrefixMessageChannelBinderTests implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private Sink sink;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
@Test
|
||||
public void consumer() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/awesome/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Sink.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class ProcessorMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private Processor processor;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supplier() throws Exception {
|
||||
processor.output().send(MessageBuilder.withPayload("hello").build());
|
||||
mockMvc.perform(get("/stream/output")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missing() throws Exception {
|
||||
// Missing route is not found if channel is explicit
|
||||
mockMvc.perform(get("/stream/output/missing")).andExpect(status().isNotFound())
|
||||
.andExpect(content().string(equalTo("")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() throws Exception {
|
||||
// Missing route where channel can be inferred (there is an input channel) is
|
||||
// going to be passed on as a body
|
||||
mockMvc.perform(get("/stream/missing")).andExpect(status().isOk())
|
||||
.andExpect(content().string(equalTo("MISSING")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyed() throws Exception {
|
||||
mockMvc.perform(get("/stream/hello")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicit() throws Exception {
|
||||
mockMvc.perform(post("/stream").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void string() throws Exception {
|
||||
mockMvc.perform(
|
||||
post("/stream/input").contentType(MediaType.TEXT_PLAIN).content("hello"))
|
||||
.andExpect(status().isOk()).andExpect(content().string(equalTo("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multi() throws Exception {
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[\"hello\",\"world\"]")).andExpect(status().isOk())
|
||||
.andExpect(content().string("[\"HELLO\",\"WORLD\"]"));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public String uppercase(String input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(
|
||||
ProcessorMessageChannelBinderTests.TestConfiguration.class,
|
||||
"--logging.level.root=INFO");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class RoutedProcessorMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(post("/stream/input/words")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(MessageController.ROUTE_KEY, "words"))
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyed() throws Exception {
|
||||
mockMvc.perform(get("/stream/words/hello")).andExpect(status().isOk())
|
||||
.andExpect(header().string(MessageController.ROUTE_KEY, "words"))
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void channelAndKeyed() throws Exception {
|
||||
mockMvc.perform(get("/stream/input/words/hello")).andExpect(status().isOk())
|
||||
.andExpect(header().string(MessageController.ROUTE_KEY, "words"))
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicit() throws Exception {
|
||||
mockMvc.perform(post("/stream/words").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isOk())
|
||||
.andExpect(header().string(MessageController.ROUTE_KEY, "words"))
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@StreamListener(Processor.INPUT)
|
||||
@SendTo(Processor.OUTPUT)
|
||||
public Message<String> uppercase(Message<String> input) {
|
||||
return MessageBuilder.withPayload(input.getPayload().toUpperCase())
|
||||
.copyHeadersIfAbsent(input.getHeaders()).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class RoutedSinkMessageChannelBinderTests implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private Sink sink;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
@Test
|
||||
public void consumer() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/stream/input/words")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
assertThat(this.message.getHeaders().get(MessageController.ROUTE_KEY))
|
||||
.isEqualTo("words");
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Sink.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.binder.servlet.MessageController;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class RoutedSourceMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private Source source;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void supplier() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello")
|
||||
.setHeader(MessageController.ROUTE_KEY, "words").build());
|
||||
mockMvc.perform(get("/stream/output/words")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicit() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello")
|
||||
.setHeader(MessageController.ROUTE_KEY, "words").build());
|
||||
mockMvc.perform(get("/stream/words")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Source.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class SinkAndProcessorMessageChannelBinderTests {
|
||||
|
||||
protected static Log log = LogFactory
|
||||
.getLog(SinkAndProcessorMessageChannelBinderTests.class);
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
mockMvc.perform(post("/stream/input/words")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumer() throws Exception {
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
mockMvc.perform(post("/stream/input/accept")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@Autowired
|
||||
private Processor processor;
|
||||
|
||||
@StreamListener(value = Processor.INPUT, condition = "headers['stream_routekey']=='words'")
|
||||
public void uppercase(Message<String> input) {
|
||||
processor.output()
|
||||
.send(MessageBuilder.withPayload(input.getPayload().toUpperCase())
|
||||
.copyHeaders(input.getHeaders()).build());
|
||||
}
|
||||
|
||||
@StreamListener(value = Processor.INPUT, condition = "headers['stream_routekey']=='accept'")
|
||||
public void accept(String input) {
|
||||
log.warn("Processed: " + input);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class SinkMessageChannelBinderTests implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private Sink sink;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private Message<?> message;
|
||||
|
||||
@Test
|
||||
public void consumer() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("\"hello\"")).andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multi() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(post("/stream/input").contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[\"hello\",\"world\"]")).andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("[\"hello\",\"world\"]")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void string() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
mockMvc.perform(
|
||||
post("/stream/input").contentType(MediaType.TEXT_PLAIN).content("hello"))
|
||||
.andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missing() throws Exception {
|
||||
sink.input().subscribe(this);
|
||||
// It gets routed to "input" channel with key "missing"
|
||||
mockMvc.perform(post("/stream/missing").contentType(MediaType.TEXT_PLAIN)
|
||||
.content("hello")).andExpect(status().isAccepted())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
assertThat(this.message).isNotNull();
|
||||
sink.input().unsubscribe(this);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Sink.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class SinkWithResponseMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(post("/stream/input/words")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@Autowired
|
||||
private Processor processor;
|
||||
|
||||
@StreamListener(value = Processor.INPUT, condition = "headers['stream_routekey']=='words'")
|
||||
public void uppercase(Message<String> input) {
|
||||
// TODO: this won't work without the Message<?> wrapper for the input because
|
||||
// the headers don't get copied.
|
||||
processor.output()
|
||||
.send(MessageBuilder.withPayload(input.getPayload().toUpperCase())
|
||||
.copyHeaders(input.getHeaders()).build());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.cloud.stream.binder.servlet.RouteRegistry;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class SourceAndProcessorMessageChannelBinderTests {
|
||||
|
||||
protected static Log log = LogFactory
|
||||
.getLog(SourceAndProcessorMessageChannelBinderTests.class);
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void function() throws Exception {
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
mockMvc.perform(post("/stream/input/words")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("\"hello\""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("HELLO")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missing() throws Exception {
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
mockMvc.perform(get("/stream/output/missing")).andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() throws Exception {
|
||||
// An explicit route registration prevents the implicit conversion of route into
|
||||
// body
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
mockMvc.perform(get("/stream/empty")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("[]")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Processor.class)
|
||||
protected static class TestConfiguration {
|
||||
@Autowired
|
||||
private Processor processor;
|
||||
|
||||
@StreamListener(Processor.INPUT)
|
||||
public void uppercase(Message<String> input) {
|
||||
processor.output()
|
||||
.send(MessageBuilder.withPayload(input.getPayload().toUpperCase())
|
||||
.copyHeaders(input.getHeaders()).build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteRegistry routeRegistry() {
|
||||
return () -> Collections.singleton("empty");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
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.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@DirtiesContext
|
||||
public class SourceMessageChannelBinderTests {
|
||||
|
||||
@Autowired
|
||||
private Source source;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void supplier() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello").build());
|
||||
mockMvc.perform(get("/stream/output")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicit() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello").build());
|
||||
mockMvc.perform(get("/stream")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trailing() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello").build());
|
||||
mockMvc.perform(get("/stream/")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("hello")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() throws Exception {
|
||||
mockMvc.perform(get("/stream/output?purge=true")).andReturn();
|
||||
mockMvc.perform(get("/stream/output")).andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("[]")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missing() throws Exception {
|
||||
// Missing route is just empty
|
||||
mockMvc.perform(get("/stream/missing")).andExpect(status().isNotFound())
|
||||
.andExpect(content().string(equalTo("")));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Source.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2016-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.stream.binder.servlet.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
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.context.embedded.LocalServerPort;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class SseSourceMessageChannelBinderTests {
|
||||
|
||||
private static Log log = LogFactory.getLog(SseSourceMessageChannelBinderTests.class);
|
||||
|
||||
@Autowired
|
||||
private Source source;
|
||||
|
||||
private CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
private RestTemplate rest = new RestTemplate();
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String message = null;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
rest.getForEntity(
|
||||
new URI("http://localhost:" + port + "/stream/output?purge=true"),
|
||||
String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supplier() throws Exception {
|
||||
source.output().send(MessageBuilder.withPayload("hello").build());
|
||||
rest.getInterceptors().add(new NonClosingInterceptor());
|
||||
ResponseEntity<String> response = rest.execute(
|
||||
new URI("http://localhost:" + port + "/stream/output"), HttpMethod.GET,
|
||||
request -> request.getHeaders()
|
||||
.setAccept(Arrays.asList(MediaType.TEXT_EVENT_STREAM)),
|
||||
this::extract);
|
||||
assertThat(response.getHeaders().getContentType())
|
||||
.isGreaterThan(MediaType.TEXT_EVENT_STREAM);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isEqualTo("data:hello\n\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lateSending() throws Exception {
|
||||
message = "world";
|
||||
rest.getInterceptors().add(new NonClosingInterceptor());
|
||||
ResponseEntity<String> response = rest.execute(
|
||||
new URI("http://localhost:" + port + "/stream/output"), HttpMethod.GET,
|
||||
request -> request.getHeaders()
|
||||
.setAccept(Arrays.asList(MediaType.TEXT_EVENT_STREAM)),
|
||||
this::extract);
|
||||
assertThat(response.getHeaders().getContentType())
|
||||
.isGreaterThan(MediaType.TEXT_EVENT_STREAM);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isEqualTo("data:world\n\n");
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableBinding(Source.class)
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
private ResponseEntity<String> extract(ClientHttpResponse response)
|
||||
throws IOException {
|
||||
if (message != null) {
|
||||
// Once there is an incoming request we can send a message to it
|
||||
source.output().send(MessageBuilder.withPayload(message).build());
|
||||
}
|
||||
byte[] bytes = new byte[1024];
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int read = 0;
|
||||
while (read >= 0
|
||||
&& StringUtils.countOccurrencesOf(builder.toString(), "\n") < 2) {
|
||||
read = response.getBody().read(bytes, 0, bytes.length);
|
||||
if (read > 0) {
|
||||
latch.countDown();
|
||||
builder.append(new String(bytes, 0, read));
|
||||
}
|
||||
log.debug("Building: " + builder);
|
||||
}
|
||||
log.debug("Done: " + builder);
|
||||
return ResponseEntity.status(response.getStatusCode())
|
||||
.headers(response.getHeaders()).body(builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Special interceptor that prevents the response from being closed and allows us to
|
||||
* assert on the contents of an event stream.
|
||||
*/
|
||||
private class NonClosingInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private class NonClosingResponse implements ClientHttpResponse {
|
||||
|
||||
private ClientHttpResponse delegate;
|
||||
|
||||
public NonClosingResponse(ClientHttpResponse delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return delegate.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return delegate.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return delegate.getStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return delegate.getRawStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return delegate.getStatusText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
return new NonClosingResponse(execution.execute(request, body));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
spring.main.banner-mode=off
|
||||
#logging.level.org.springframework.cloud.stream.binder.servlet=DEBUG
|
||||
Reference in New Issue
Block a user