Migrate to servlet binder for web features

This commit is contained in:
Dave Syer
2017-08-08 08:27:04 +01:00
parent 540b4d378e
commit 1af0d451cf
107 changed files with 4055 additions and 2010 deletions

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.function.stream;
import java.util.function.Supplier;
import org.springframework.cloud.function.core.FunctionCatalog;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
/**
* @author Mark Fisher
*/
public class SupplierInvokingMessageProducer<T> extends MessageProducerSupport {
private final FunctionCatalog functionCatalog;
public SupplierInvokingMessageProducer(FunctionCatalog registry) {
this.functionCatalog = registry;
this.setOutputChannelName(Source.OUTPUT);
}
@Override
protected void doStart() {
supplier().subscribeOn(Schedulers.elastic()).subscribe(m -> this.sendMessage(m));
}
private Flux<Message<?>> supplier() {
Supplier<Flux<?>> supplier = null;
Flux<Message<?>> result = Flux.empty();
for (String name : functionCatalog.getSupplierNames()) {
supplier = functionCatalog.lookupSupplier(name);
Assert.notNull(supplier, "Supplier must not be null");
result = Flux.merge(result,
supplier.get().map(payload -> MessageBuilder.withPayload(payload)
.setHeader(StreamConfigurationProperties.ROUTE_KEY, name)
.build()));
}
return result;
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.function.stream.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
import org.springframework.cloud.function.core.FunctionCatalog;
import org.springframework.cloud.stream.binder.servlet.RouteRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Dave Syer
*
*/
@Configuration
@ConditionalOnBean(FunctionCatalog.class)
@ConditionalOnClass(RouteRegistry.class)
@AutoConfigureAfter(ContextFunctionCatalogAutoConfiguration.class)
public class RouteRegistryAutoConfiguration {
@Bean
public RouteRegistry supplierRoutes(FunctionCatalog registry) {
return () -> registry.getSupplierNames();
}
}

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package org.springframework.cloud.function.stream;
package org.springframework.cloud.function.stream.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.function.context.FunctionInspector;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.core.FunctionCatalog;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.binder.Binder;
@@ -39,7 +39,7 @@ import org.springframework.context.annotation.Lazy;
@ConditionalOnBean(FunctionCatalog.class)
@ConditionalOnProperty(name = "spring.cloud.stream.enabled", havingValue = "true", matchIfMissing = true)
@EnableBinding(Processor.class)
public class StreamConfiguration {
public class StreamAutoConfiguration {
@Autowired
private StreamConfigurationProperties properties;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.function.stream;
package org.springframework.cloud.function.stream.config;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.function.stream;
package org.springframework.cloud.function.stream.config;
import java.util.ArrayList;
import java.util.HashMap;
@@ -26,7 +26,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cloud.function.context.FunctionInspector;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.core.FunctionCatalog;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
@@ -101,7 +101,11 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
}
private Message<?> message(Object result, Map<String, Object> headers) {
return result instanceof Message ? (Message<?>) result
return result instanceof Message
// TODO: why do we have to do this? The headers should have come with the
// result.
? MessageBuilder.fromMessage((Message<?>) result)
.copyHeadersIfAbsent(headers).build()
: MessageBuilder.withPayload(result).copyHeadersIfAbsent(headers).build();
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.function.stream.config;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.cloud.function.core.FunctionCatalog;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
/**
* @author Mark Fisher
*/
public class SupplierInvokingMessageProducer<T> extends MessageProducerSupport {
private final FunctionCatalog functionCatalog;
private final Set<String> suppliers = new HashSet<>();
private final Map<String, Disposable> disposables = new HashMap<>();
public SupplierInvokingMessageProducer(FunctionCatalog registry) {
this.functionCatalog = registry;
this.setOutputChannelName(Source.OUTPUT);
}
@Override
protected void doStart() {
for (String name : functionCatalog.getSupplierNames()) {
start(name);
}
}
@Override
protected void doStop() {
for (String name : new HashSet<>(suppliers)) {
stop(name);
}
}
public void stop(String name) {
if (disposables.containsKey(name)) {
synchronized (disposables) {
if (disposables.containsKey(name)) {
try {
disposables.get(name).dispose();
}
finally {
disposables.remove(name);
suppliers.remove(name);
}
}
}
}
}
public void start(String name) {
if (!disposables.containsKey(name)) {
synchronized (disposables) {
if (!disposables.containsKey(name)) {
Supplier<Flux<?>> supplier = functionCatalog.lookupSupplier(name);
if (supplier != null) {
suppliers.add(name);
disposables.put(name,
supplier.get().subscribeOn(Schedulers.elastic()).subscribe(m -> send(name, m)));
}
}
}
}
}
private void send(String name, Object payload) {
Message<?> message;
if (payload instanceof Message) {
message = MessageBuilder.fromMessage((Message<?>) payload)
.setHeaderIfAbsent(StreamConfigurationProperties.ROUTE_KEY, name)
.build();
}
else {
message = MessageBuilder.withPayload(payload)
.setHeader(StreamConfigurationProperties.ROUTE_KEY, name).build();
}
getOutputChannel().send(message);
}
}

View File

@@ -1,2 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.function.stream.StreamConfiguration
org.springframework.cloud.function.stream.config.StreamAutoConfiguration,\
org.springframework.cloud.function.stream.config.RouteRegistryAutoConfiguration