Add a sample app with just beans that are Functions
Make it deployable via its maven coordinates in spring-cloud-function-deployer (it is deployed by default on start up right now, but that's just a demo)
This commit is contained in:
@@ -13,7 +13,8 @@
|
||||
== Run a REST Microservice using that Function:
|
||||
|
||||
----
|
||||
./web.sh -p /words -f uppercase
|
||||
./web.sh
|
||||
curl -H "Content-Type=text/plain" localhost:8080/uppercase -d foo
|
||||
----
|
||||
|
||||
== Compose Functions:
|
||||
@@ -23,7 +24,7 @@
|
||||
----
|
||||
./registerFunction.sh -n pluralize -f "f->f.map(s->s+\"S\")"
|
||||
|
||||
./web.sh -p /words -f uppercase,pluralize
|
||||
curl -H "Content-Type=text/plain" localhost:8080/uppercase,pluralize -d foo
|
||||
----
|
||||
|
||||
== Run a Task Microservice using a Supplier, Function, and Consumer:
|
||||
|
||||
4
pom.xml
4
pom.xml
@@ -19,6 +19,7 @@
|
||||
<reactor.version>3.0.4.RELEASE</reactor.version>
|
||||
<spring-cloud-stream.version>1.1.0.BUILD-SNAPSHOT</spring-cloud-stream.version>
|
||||
<wrapper.version>0.0.1.BUILD-SNAPSHOT</wrapper.version>
|
||||
<spring-boot.version>1.5.0.BUILD-SNAPSHOT</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -39,9 +40,12 @@
|
||||
<modules>
|
||||
<module>spring-cloud-function-compiler</module>
|
||||
<module>spring-cloud-function-core</module>
|
||||
<module>spring-cloud-function-context</module>
|
||||
<module>spring-cloud-function-stream</module>
|
||||
<module>spring-cloud-function-task</module>
|
||||
<module>spring-cloud-function-web</module>
|
||||
<module>spring-cloud-function-samples</module>
|
||||
<module>spring-cloud-function-deployer</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,17 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
while getopts ":p:f:" opt; do
|
||||
case $opt in
|
||||
p)
|
||||
WEBPATH=$OPTARG
|
||||
;;
|
||||
f)
|
||||
FUNC=$OPTARG
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
java -noverify -XX:TieredStopAtLevel=1 -Xss256K -Xms16M -Xmx256M -XX:MaxMetaspaceSize=128M -jar ../spring-cloud-function-web/target/spring-cloud-function-web-1.0.0.BUILD-SNAPSHOT.jar\
|
||||
--web.path=$WEBPATH\
|
||||
--function.name=$FUNC
|
||||
java -jar ../spring-cloud-function-web/target/spring-cloud-function-web-1.0.0.BUILD-SNAPSHOT.jar ${@}
|
||||
|
||||
|
||||
0
spring-cloud-function-context/.jdk8
Normal file
0
spring-cloud-function-context/.jdk8
Normal file
58
spring-cloud-function-context/pom.xml
Normal file
58
spring-cloud-function-context/pom.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-function-context</name>
|
||||
<description>Spring Cloud Function Web Support</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>1.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>0.0.1.BUILD-SNAPSHOT</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-core</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.function.context;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.function.registry.FunctionCatalog;
|
||||
|
||||
public class ApplicationContextFunctionCatalog implements FunctionCatalog {
|
||||
|
||||
private final Map<String, Function<?, ?>> functions;
|
||||
private final Map<String, Consumer<?>> consumers;
|
||||
private final Map<String, Supplier<?>> suppliers;
|
||||
|
||||
public ApplicationContextFunctionCatalog(Map<String, Function<?, ?>> functions,
|
||||
Map<String, Consumer<?>> consumers, Map<String, Supplier<?>> suppliers) {
|
||||
this.functions = functions;
|
||||
this.consumers = consumers;
|
||||
this.suppliers = suppliers;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Consumer<T> lookupConsumer(String name) {
|
||||
return (Consumer<T>) consumers.get(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T, R> Function<T, R> lookupFunction(String name) {
|
||||
return (Function<T, R>) functions.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, R> Function<T, R> composeFunction(String... functionNames) {
|
||||
Function<T, R> function = this.lookupFunction(functionNames[0]);
|
||||
for (int i = 1; i < functionNames.length; i++) {
|
||||
function = function.andThen(this.lookupFunction(functionNames[i]));
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Supplier<T> lookupSupplier(String name) {
|
||||
return (Supplier<T>) suppliers.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.function.context;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.function.registry.DefaultFunctionRegistryAutoConfiguration;
|
||||
import org.springframework.cloud.function.registry.FunctionCatalog;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ApplicationContextFunctionCatalog.class)
|
||||
@ConditionalOnMissingBean(FunctionCatalog.class)
|
||||
@AutoConfigureBefore(DefaultFunctionRegistryAutoConfiguration.class)
|
||||
public class ContextFunctionCatalogAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Map<String, Function<?, ?>> functions = Collections.emptyMap();
|
||||
@Autowired(required = false)
|
||||
private Map<String, Consumer<?>> consumers = Collections.emptyMap();
|
||||
@Autowired(required = false)
|
||||
private Map<String, Supplier<?>> suppliers = Collections.emptyMap();
|
||||
|
||||
@Bean
|
||||
public FunctionCatalog functionCatalog() {
|
||||
return new ApplicationContextFunctionCatalog(functions, consumers, suppliers);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.context.ContextFunctionCatalogAutoConfiguration
|
||||
@@ -18,6 +18,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-compiler</artifactId>
|
||||
@@ -46,6 +50,13 @@
|
||||
<configuration>
|
||||
<classifier>registrar</classifier>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-thin-launcher</artifactId>
|
||||
<version>${wrapper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.function.registry;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(FileSystemFunctionRegistry.class)
|
||||
@ConditionalOnMissingBean(FunctionCatalog.class)
|
||||
public class DefaultFunctionRegistryAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FunctionRegistry functionRegistry() {
|
||||
return new FileSystemFunctionRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.registry;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public interface FunctionCatalog {
|
||||
|
||||
<T> Consumer<T> lookupConsumer(String name);
|
||||
|
||||
<T, R> Function<T, R> lookupFunction(String name);
|
||||
|
||||
<T, R> Function<T, R> composeFunction(String... functionNames);
|
||||
|
||||
<T> Supplier<T> lookupSupplier(String name);
|
||||
}
|
||||
@@ -16,14 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.function.registry;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface FunctionRegistry {
|
||||
public interface FunctionRegistry extends FunctionCatalog {
|
||||
|
||||
void registerConsumer(String name, String consumer);
|
||||
|
||||
@@ -31,11 +27,4 @@ public interface FunctionRegistry {
|
||||
|
||||
void registerSupplier(String name, String supplier);
|
||||
|
||||
<T> Consumer<T> lookupConsumer(String name);
|
||||
|
||||
<T, R> Function<T, R> lookupFunction(String name);
|
||||
|
||||
<T, R> Function<T, R> composeFunction(String... functionNames);
|
||||
|
||||
<T> Supplier<T> lookupSupplier(String name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.registry.DefaultFunctionRegistryAutoConfiguration
|
||||
0
spring-cloud-function-deployer/.jdk8
Normal file
0
spring-cloud-function-deployer/.jdk8
Normal file
96
spring-cloud-function-deployer/pom.xml
Normal file
96
spring-cloud-function-deployer/pom.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-function-deployer</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-function-deployer</name>
|
||||
<description>Spring Cloud Function Web Support</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-deployer-thin.version>0.0.1.BUILD-SNAPSHOT</spring-cloud-deployer-thin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-starter-web-reactive</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-deployer-thin</artifactId>
|
||||
<version>${spring-cloud-deployer-thin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-dependencies-web-reactive</artifactId>
|
||||
<version>0.1.0.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Dalston.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.graph.Dependency;
|
||||
import org.eclipse.aether.resolution.ArtifactResolutionException;
|
||||
|
||||
import org.springframework.boot.Banner.Mode;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
|
||||
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.thin.AetherEngine;
|
||||
import org.springframework.boot.loader.thin.ArchiveUtils;
|
||||
import org.springframework.cloud.deployer.thin.ContextRunner;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
// NOT a @Component (to prevent it from being scanned by the "main" application).
|
||||
public class ApplicationRunner implements CommandLineRunner {
|
||||
|
||||
private static final String DEFAULT_REACTOR_VERSION = "3.0.3.RELEASE";
|
||||
|
||||
private static Log logger = LogFactory.getLog(ApplicationRunner.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
new ApplicationRunner().start(args);
|
||||
}
|
||||
|
||||
public ConfigurableApplicationContext start(String... args) {
|
||||
return new SpringApplicationBuilder(ApplicationRunner.class).web(false)
|
||||
.contextClass(AnnotationConfigApplicationContext.class)
|
||||
.bannerMode(Mode.OFF).properties("spring.main.applicationContextClass="
|
||||
+ AnnotationConfigApplicationContext.class.getName())
|
||||
.run(args);
|
||||
}
|
||||
|
||||
private Object app;
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
ClassLoader classLoader = createClassLoader();
|
||||
ClassUtils.overrideThreadContextClassLoader(classLoader);
|
||||
Class<?> cls = classLoader.loadClass(ContextRunner.class.getName());
|
||||
this.app = cls.newInstance();
|
||||
runContext(DeployedFunctionApplication.class.getName(),
|
||||
Collections.emptyMap(), args);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Cannot deploy", e);
|
||||
}
|
||||
finally {
|
||||
ClassUtils.overrideThreadContextClassLoader(contextLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
closeContext();
|
||||
}
|
||||
|
||||
private void runContext(String mainClass, Map<String, String> properties,
|
||||
String... args) {
|
||||
Method method = ReflectionUtils.findMethod(this.app.getClass(), "run",
|
||||
String.class, Map.class, String[].class);
|
||||
ReflectionUtils.invokeMethod(method, this.app, mainClass, properties, args);
|
||||
}
|
||||
|
||||
private void closeContext() {
|
||||
Method method = ReflectionUtils.findMethod(this.app.getClass(), "close");
|
||||
ReflectionUtils.invokeMethod(method, this.app);
|
||||
}
|
||||
|
||||
private ClassLoader createClassLoader() {
|
||||
ClassLoader base = getClass().getClassLoader();
|
||||
if (!(base instanceof URLClassLoader)) {
|
||||
throw new IllegalStateException("Need a URL class loader, found: " + base);
|
||||
}
|
||||
@SuppressWarnings("resource")
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) base;
|
||||
URL[] urls = urlClassLoader.getURLs();
|
||||
List<URL> child = new ArrayList<>();
|
||||
List<URL> parent = new ArrayList<>();
|
||||
for (URL url : urls) {
|
||||
child.add(url);
|
||||
}
|
||||
String reactor = getReactorCoordinates();
|
||||
DependencyResolutionContext context = new DependencyResolutionContext();
|
||||
AetherEngine engine = AetherEngine.create(
|
||||
RepositoryConfigurationFactory.createDefaultRepositoryConfiguration(),
|
||||
context);
|
||||
try {
|
||||
List<File> resolved = engine.resolve(Arrays
|
||||
.asList(new Dependency(new DefaultArtifact(reactor), "runtime")));
|
||||
for (File archive : resolved) {
|
||||
try {
|
||||
URL url = archive.toURI().toURL();
|
||||
parent.add(url);
|
||||
child.remove(url);
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new IllegalStateException("Cannot locate jar for: " + archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArtifactResolutionException e) {
|
||||
throw new IllegalStateException("Cannot resolve archive for " + reactor, e);
|
||||
}
|
||||
logger.info("Parent: " + parent);
|
||||
logger.info("Child: " + child);
|
||||
if (!parent.isEmpty()) {
|
||||
base = new URLClassLoader(parent.toArray(new URL[0]), base.getParent());
|
||||
}
|
||||
return new URLClassLoader(child.toArray(new URL[0]), base);
|
||||
}
|
||||
|
||||
private String getReactorCoordinates() {
|
||||
Package pkg = Flux.class.getPackage();
|
||||
String version = null;
|
||||
version = (pkg != null ? pkg.getImplementationVersion()
|
||||
: DEFAULT_REACTOR_VERSION);
|
||||
if (version == null) {
|
||||
Archive archive = ArchiveUtils.getArchive(Flux.class);
|
||||
try {
|
||||
String path = archive.getUrl().toString();
|
||||
if (path.endsWith("!/")) {
|
||||
path = path.substring(0, path.length() - 2);
|
||||
}
|
||||
path = StringUtils.getFilename(path);
|
||||
if (path.startsWith("reactor-core-")) {
|
||||
path = path.substring("reactor-core-".length());
|
||||
}
|
||||
if (path.endsWith(".jar")) {
|
||||
path = path.substring(0, path.length() - ".jar".length());
|
||||
}
|
||||
version = path;
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (version == null) {
|
||||
version = DEFAULT_REACTOR_VERSION;
|
||||
}
|
||||
return "io.projectreactor:reactor-core:" + version;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class DeployedFunctionApplication {
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
public class DeployedFunctionController {
|
||||
|
||||
private final FunctionExtractingAppDeployer deployer;
|
||||
|
||||
@Autowired
|
||||
public DeployedFunctionController(FunctionExtractingAppDeployer deployer) {
|
||||
this.deployer = deployer;
|
||||
}
|
||||
|
||||
@PostMapping(path = "/{name}", consumes = MediaType.TEXT_PLAIN_VALUE)
|
||||
public Flux<String> function(@PathVariable String name,
|
||||
@RequestBody Flux<String> body) {
|
||||
Function<Object, Object> function;
|
||||
if (name.contains(",")) {
|
||||
function = deployer.composeFunction(name.split(","));
|
||||
}
|
||||
else {
|
||||
function = deployer.lookupFunction(name);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Flux<String> result = (Flux<String>) function.apply(body);
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/{name}")
|
||||
public Flux<String> supplier(@PathVariable String name) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Flux<String> result = (Flux<String>) deployer.lookupSupplier(name).get();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.loader.thin.ArchiveUtils;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDefinition;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
public class FunctionAdminController implements CommandLineRunner {
|
||||
|
||||
private final FunctionExtractingAppDeployer deployer;
|
||||
|
||||
private Map<String, String> deployed = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, String> names = new LinkedHashMap<>();
|
||||
|
||||
@Autowired
|
||||
public FunctionAdminController(FunctionExtractingAppDeployer deployer) {
|
||||
this.deployer = deployer;
|
||||
}
|
||||
|
||||
@PostMapping(path = "/{name}")
|
||||
public Map<String, Object> push(@PathVariable String name, @RequestParam String path)
|
||||
throws Exception {
|
||||
String id = deploy(name, path);
|
||||
return Collections.singletonMap("id", id);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/{name}")
|
||||
public Map<String, Object> undeploy(@PathVariable String name,
|
||||
@RequestParam String path) throws Exception {
|
||||
String id = names.get(name);
|
||||
if (id == null) {
|
||||
throw new IllegalStateException("No such app");
|
||||
}
|
||||
deployer.undeploy(id);
|
||||
names.remove(name);
|
||||
deployed.remove(id);
|
||||
return Collections.singletonMap("id", id);
|
||||
}
|
||||
|
||||
@GetMapping({ "", "/" })
|
||||
public Map<String, Object> deployed() {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (String name : names.keySet()) {
|
||||
result.put(name, new DeployedArtifact(name, names.get(name),
|
||||
deployed.get(names.get(name))));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
deploy("sample", "maven://com.example:function-sample:1.0.0.BUILD-SNAPSHOT");
|
||||
}
|
||||
|
||||
private String deploy(String name, String path, String... args) throws Exception {
|
||||
Resource resource = new FileSystemResource(
|
||||
ArchiveUtils.getArchiveRoot(ArchiveUtils.getArchive(path)));
|
||||
AppDefinition definition = new AppDefinition(resource.getFilename(),
|
||||
Collections.emptyMap());
|
||||
AppDeploymentRequest request = new AppDeploymentRequest(definition, resource,
|
||||
Collections.emptyMap(), Arrays.asList(args));
|
||||
String deployed = deployer.deploy(request);
|
||||
this.deployed.put(deployed, path);
|
||||
this.names.put(deployed, name);
|
||||
return deployed;
|
||||
}
|
||||
}
|
||||
|
||||
class DeployedArtifact {
|
||||
|
||||
private String name;
|
||||
private String id;
|
||||
private String path;
|
||||
|
||||
public DeployedArtifact() {
|
||||
}
|
||||
|
||||
public DeployedArtifact(String name, String id, String path) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
|
||||
import org.springframework.cloud.deployer.thin.ThinJarAppDeployer;
|
||||
import org.springframework.cloud.deployer.thin.ThinJarAppWrapper;
|
||||
import org.springframework.cloud.function.registry.FunctionCatalog;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
public class FunctionExtractingAppDeployer extends ThinJarAppDeployer
|
||||
implements FunctionCatalog {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(FunctionExtractingAppDeployer.class);
|
||||
|
||||
private final Map<String, Function<?, ?>> functions = new HashMap<>();
|
||||
private final Map<String, Consumer<?>> consumers = new HashMap<>();
|
||||
private final Map<String, Supplier<?>> suppliers = new HashMap<>();
|
||||
|
||||
public FunctionExtractingAppDeployer() {
|
||||
this("thin", "slim");
|
||||
}
|
||||
|
||||
public FunctionExtractingAppDeployer(String name, String... profiles) {
|
||||
super(name, profiles);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Consumer<T> lookupConsumer(String name) {
|
||||
return (Consumer<T>) consumers.get(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T, R> Function<T, R> lookupFunction(String name) {
|
||||
return (Function<T, R>) functions.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, R> Function<T, R> composeFunction(String... functionNames) {
|
||||
Function<T, R> function = this.lookupFunction(functionNames[0]);
|
||||
for (int i = 1; i < functionNames.length; i++) {
|
||||
function = function.andThen(this.lookupFunction(functionNames[i]));
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Supplier<T> lookupSupplier(String name) {
|
||||
return (Supplier<T>) suppliers.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deploy(AppDeploymentRequest request) {
|
||||
String id = super.deploy(request);
|
||||
functions.putAll(functions(id));
|
||||
suppliers.putAll(suppliers(id));
|
||||
consumers.putAll(consumers(id));
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undeploy(String id) {
|
||||
super.undeploy(id);
|
||||
for (String name : functions(id).keySet()) {
|
||||
functions.remove(name);
|
||||
}
|
||||
for (String name : suppliers(id).keySet()) {
|
||||
suppliers.remove(name);
|
||||
}
|
||||
for (String name : consumers(id).keySet()) {
|
||||
consumers.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Function<?, ?>> functions(String id) {
|
||||
Map<String, Function<?, ?>> map = new HashMap<>();
|
||||
ThinJarAppWrapper wrapper = getWrapper(id);
|
||||
if (wrapper == null) {
|
||||
return map;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ? extends Function<?, ?>> result = (Map<String, ? extends Function<?, ?>>) getBeans(
|
||||
wrapper, Function.class);
|
||||
map.putAll(result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot extract functions", e);
|
||||
}
|
||||
logger.info("Loaded functions: " + map.keySet());
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Consumer<?>> consumers(String id) {
|
||||
Map<String, Consumer<?>> map = new HashMap<>();
|
||||
ThinJarAppWrapper wrapper = getWrapper(id);
|
||||
if (wrapper == null) {
|
||||
return map;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ? extends Consumer<?>> result = (Map<String, ? extends Consumer<?>>) getBeans(
|
||||
wrapper, Consumer.class);
|
||||
map.putAll(result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot extract consumers", e);
|
||||
}
|
||||
logger.info("Loaded consumers: " + map.keySet());
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Supplier<?>> suppliers(String id) {
|
||||
Map<String, Supplier<?>> map = new HashMap<>();
|
||||
ThinJarAppWrapper wrapper = getWrapper(id);
|
||||
if (wrapper == null) {
|
||||
return map;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ? extends Supplier<?>> result = (Map<String, ? extends Supplier<?>>) getBeans(
|
||||
wrapper, Supplier.class);
|
||||
map.putAll(result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot extract suppliers", e);
|
||||
}
|
||||
logger.info("Loaded suppliers: " + map.keySet());
|
||||
return map;
|
||||
}
|
||||
|
||||
private <T> Map<String, ? extends T> getBeans(ThinJarAppWrapper wrapper,
|
||||
Class<T> type) throws IllegalAccessException, ClassNotFoundException,
|
||||
NoSuchMethodException, InvocationTargetException {
|
||||
Object app = findContext(wrapper);
|
||||
MethodInvoker invoker = new MethodInvoker();
|
||||
invoker.setTargetObject(app);
|
||||
invoker.setTargetMethod("getBeansOfType");
|
||||
invoker.setArguments(new Object[] { type });
|
||||
invoker.prepare();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, T> result = (Map<String, T>) invoker.invoke();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object findContext(ThinJarAppWrapper wrapper) {
|
||||
Object app = wrapper.getApp();
|
||||
Field field = ReflectionUtils.findField(app.getClass(), "context");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
app = ReflectionUtils.getField(field, app);
|
||||
return app;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.function.registry.DefaultFunctionRegistryAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(FunctionExtractingAppDeployer.class)
|
||||
@AutoConfigureBefore(DefaultFunctionRegistryAutoConfiguration.class)
|
||||
public class FunctionExtractingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FunctionExtractingAppDeployer functionCatalog() {
|
||||
return new FunctionExtractingAppDeployer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.deployer.FunctionExtractingAutoConfiguration
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.deployer;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class FunctionExtractingAppDeployerIntegrationTests {
|
||||
|
||||
private static ConfigurableApplicationContext context;
|
||||
private static int port;
|
||||
|
||||
@BeforeClass
|
||||
public static void open() {
|
||||
port = SocketUtils.findAvailableTcpPort();
|
||||
context = new ApplicationRunner().start("--server.port=" + port);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void close() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
assertThat(new TestRestTemplate()
|
||||
.getForObject("http://localhost:" + port + "/words", String.class))
|
||||
.isEqualTo("foobar");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.function.deployer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.loader.thin.ArchiveUtils;
|
||||
import org.springframework.boot.loader.tools.LogbackInitializer;
|
||||
import org.springframework.cloud.deployer.spi.app.DeploymentState;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDefinition;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class FunctionExtractingAppDeployerTests {
|
||||
|
||||
private static String id;
|
||||
|
||||
static {
|
||||
LogbackInitializer.initialize();
|
||||
}
|
||||
|
||||
private static FunctionExtractingAppDeployer deployer = new FunctionExtractingAppDeployer();
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
if (id == null) {
|
||||
id = deploy("maven://com.example:function-sample:1.0.0.BUILD-SNAPSHOT");
|
||||
// "--debug");
|
||||
}
|
||||
assertThat(deployer.status(id).getState()).isEqualTo(DeploymentState.deployed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployAndExtractFunctions() throws Exception {
|
||||
// This one can only work if you change the boot classpath to contain reactor-core
|
||||
// and reactive-streams
|
||||
expected.expect(ClassCastException.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Flux<String> result = (Flux<String>) deployer.lookupFunction("uppercase")
|
||||
.apply(Flux.just("foo"));
|
||||
assertThat(result.blockFirst()).isEqualTo("FOO");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployAndExtractConsumers() throws Exception {
|
||||
assertThat(deployer.lookupConsumer("sink")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployAndExtractSuppliers() throws Exception {
|
||||
assertThat(deployer.lookupSupplier("words")).isNotNull();
|
||||
}
|
||||
|
||||
private String deploy(String jarName, String... args) throws Exception {
|
||||
Resource resource = new FileSystemResource(
|
||||
ArchiveUtils.getArchiveRoot(ArchiveUtils.getArchive(jarName)));
|
||||
AppDefinition definition = new AppDefinition(resource.getFilename(),
|
||||
Collections.emptyMap());
|
||||
AppDeploymentRequest request = new AppDeploymentRequest(definition, resource,
|
||||
Collections.emptyMap(), Arrays.asList(args));
|
||||
String deployed = deployer.deploy(request);
|
||||
return deployed;
|
||||
}
|
||||
|
||||
}
|
||||
13
spring-cloud-function-deployer/thin-slim.properties
Normal file
13
spring-cloud-function-deployer/thin-slim.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
exclusions.spring-web-reactive: org.springframework:spring-web-reactive
|
||||
exclusions.spring-web: org.springframework:spring-web
|
||||
exclusions.reator-netty: io.projectreactor.ipc:reactor-netty
|
||||
exclusions.spring-cloud-stream: org.springframework.cloud:spring-cloud-stream
|
||||
exclusions.spring-cloud-stream-reactive: org.springframework.cloud:spring-cloud-stream-reactive
|
||||
exclusions.spring-cloud-stream-binder-rabbit: org.springframework.cloud:spring-cloud-stream-binder-rabbit
|
||||
exclusions.spring-cloud-stream-binder-kafka: org.springframework.cloud:spring-cloud-stream-binder-kafka
|
||||
exclusions.spring-boot-starter-web: org.springframework.boot:spring-boot-starter-web
|
||||
exclusions.spring-boot-starter-stream: org.springframework.boot:spring-boot-starter-stream
|
||||
exclusions.spring-boot-starter-actuator: org.springframework.boot:spring-boot-starter-actuator
|
||||
dependencies.spring-boot-starter: org.springframework.boot:spring-boot-starter
|
||||
dependencies.spring-cloud-function-context: org.springframework.cloud:spring-cloud-function-context:1.0.0.BUILD-SNAPSHOT
|
||||
|
||||
19
spring-cloud-function-samples/pom.xml
Normal file
19
spring-cloud-function-samples/pom.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-function-samples</artifactId>
|
||||
<name>Spring Cloud Function Samples</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-function-sample</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>function-sample</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-function-sample</name>
|
||||
<description>Spring Cloud Function Web Support</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>1.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>0.0.1.BUILD-SNAPSHOT</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-web</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>1.5.0.BUILD-SNAPSHOT</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-thin-launcher</artifactId>
|
||||
<version>${wrapper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013-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 com.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleApplication {
|
||||
|
||||
@Bean
|
||||
public Function<Flux<String>, Flux<String>> uppercase() {
|
||||
return flux -> flux.map(value -> value.toUpperCase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Supplier<Flux<String>> words() {
|
||||
return () -> Flux.fromArray(new String[] { "foo", "bar" });
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Flux<String>, Flux<String>> lowercase() {
|
||||
return flux -> flux.map(value -> value.toLowerCase());
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(SampleApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,6 +52,13 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-thin-launcher</artifactId>
|
||||
<version>${wrapper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -19,12 +19,13 @@ package org.springframework.cloud.function.stream;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.invoker.AbstractFunctionInvoker;
|
||||
import org.springframework.cloud.function.registry.FileSystemFunctionRegistry;
|
||||
import org.springframework.cloud.function.registry.FunctionRegistry;
|
||||
import org.springframework.cloud.function.registry.FunctionCatalog;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.binder.Binder;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -36,23 +37,20 @@ import reactor.core.publisher.Flux;
|
||||
*/
|
||||
@EnableBinding(Processor.class)
|
||||
@EnableConfigurationProperties(FunctionConfigurationProperties.class)
|
||||
@ConditionalOnClass({ Binder.class, AbstractFunctionInvoker.class })
|
||||
public class StreamConfiguration {
|
||||
|
||||
@Autowired
|
||||
private FunctionConfigurationProperties properties;
|
||||
|
||||
@Bean
|
||||
public FunctionRegistry registry() {
|
||||
return new FileSystemFunctionRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("spring.cloud.stream.bindings.input.destination")
|
||||
public AbstractFunctionInvoker<?,?> invoker(FunctionRegistry registry) {
|
||||
public AbstractFunctionInvoker<?, ?> invoker(FunctionCatalog registry) {
|
||||
String name = properties.getName();
|
||||
Function<Flux<Object>, Flux<Object>> function = (name.indexOf(',') == -1)
|
||||
? registry.lookupFunction(name)
|
||||
: registry.composeFunction(StringUtils.commaDelimitedListToStringArray(name));
|
||||
: registry.composeFunction(
|
||||
StringUtils.commaDelimitedListToStringArray(name));
|
||||
return new StreamListeningFunctionInvoker(function);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.stream.StreamConfiguration
|
||||
@@ -43,6 +43,13 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-thin-launcher</artifactId>
|
||||
<version>${wrapper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -24,9 +24,9 @@ import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.function.registry.FileSystemFunctionRegistry;
|
||||
import org.springframework.cloud.function.registry.FunctionRegistry;
|
||||
import org.springframework.cloud.function.registry.FunctionCatalog;
|
||||
import org.springframework.cloud.task.configuration.EnableTask;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -40,24 +40,23 @@ import reactor.core.publisher.Flux;
|
||||
@Configuration
|
||||
@EnableTask
|
||||
@EnableConfigurationProperties(LambdaConfigurationProperties.class)
|
||||
@ConditionalOnClass({ EnableTask.class })
|
||||
public class TaskConfiguration {
|
||||
|
||||
@Autowired
|
||||
private LambdaConfigurationProperties properties;
|
||||
|
||||
@Bean
|
||||
public FunctionRegistry registry() {
|
||||
return new FileSystemFunctionRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(FunctionRegistry registry) {
|
||||
final Supplier<Flux<Object>> supplier = registry.lookupSupplier(properties.getSupplier());
|
||||
public CommandLineRunner commandLineRunner(FunctionCatalog registry) {
|
||||
final Supplier<Flux<Object>> supplier = registry
|
||||
.lookupSupplier(properties.getSupplier());
|
||||
String functionName = properties.getFunction();
|
||||
Function<Flux<Object>, Flux<Object>> function = (functionName.indexOf(',') == -1)
|
||||
? registry.lookupFunction(functionName)
|
||||
: registry.composeFunction(StringUtils.commaDelimitedListToStringArray(functionName));
|
||||
final Consumer<Object> consumer = registry.lookupConsumer(properties.getConsumer());
|
||||
: registry.composeFunction(
|
||||
StringUtils.commaDelimitedListToStringArray(functionName));
|
||||
final Consumer<Object> consumer = registry
|
||||
.lookupConsumer(properties.getConsumer());
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean status = new AtomicBoolean();
|
||||
CommandLineRunner runner = new CommandLineRunner() {
|
||||
@@ -81,7 +80,8 @@ public class TaskConfiguration {
|
||||
|
||||
private final boolean value;
|
||||
|
||||
private CompletionConsumer(CountDownLatch latch, AtomicBoolean status, boolean value) {
|
||||
private CompletionConsumer(CountDownLatch latch, AtomicBoolean status,
|
||||
boolean value) {
|
||||
this.latch = latch;
|
||||
this.status = status;
|
||||
this.value = value;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.task.TaskConfiguration
|
||||
@@ -58,6 +58,13 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-thin-launcher</artifactId>
|
||||
<version>${wrapper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -17,15 +17,16 @@
|
||||
package org.springframework.cloud.function.web;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.function.registry.FileSystemFunctionRegistry;
|
||||
import org.springframework.cloud.function.registry.FunctionRegistry;
|
||||
import org.springframework.cloud.function.registry.FunctionCatalog;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -50,33 +51,22 @@ import reactor.ipc.netty.http.server.HttpServer;
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({ FunctionConfigurationProperties.class,
|
||||
WebConfigurationProperties.class })
|
||||
@EnableConfigurationProperties({ WebConfigurationProperties.class })
|
||||
@ConditionalOnClass({ HttpHandler.class, NettyContext.class })
|
||||
public class RestConfiguration {
|
||||
|
||||
@Autowired
|
||||
private FunctionConfigurationProperties functionProperties;
|
||||
|
||||
@Autowired
|
||||
private WebConfigurationProperties webProperties;
|
||||
|
||||
@Bean
|
||||
public FunctionRegistry registry() {
|
||||
return new FileSystemFunctionRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHandler(FunctionRegistry registry) {
|
||||
String name = functionProperties.getName();
|
||||
Function<Flux<String>, Flux<String>> function = (name.indexOf(',') == -1)
|
||||
? registry.lookupFunction(name)
|
||||
: registry.composeFunction(
|
||||
StringUtils.commaDelimitedListToStringArray(name));
|
||||
FunctionInvokingHandler handler = new FunctionInvokingHandler(function);
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route(
|
||||
RequestPredicates.POST(webProperties.getPath())
|
||||
public HttpHandler httpHandler(FunctionCatalog registry) {
|
||||
FunctionInvokingHandler handler = new FunctionInvokingHandler(registry);
|
||||
RouterFunction<?> route = RouterFunctions
|
||||
.route(RequestPredicates.POST(webProperties.getPath() + "/{name}")
|
||||
.and(RequestPredicates.contentType(MediaType.TEXT_PLAIN)),
|
||||
handler::handleText);
|
||||
handler::handlePost)
|
||||
.andRoute(RequestPredicates.GET(webProperties.getPath() + "/{name}"),
|
||||
handler::handleGet);
|
||||
return RouterFunctions.toHttpHandler(route);
|
||||
}
|
||||
|
||||
@@ -87,15 +77,27 @@ public class RestConfiguration {
|
||||
|
||||
private static class FunctionInvokingHandler {
|
||||
|
||||
private final Function<Flux<String>, Flux<String>> function;
|
||||
private final FunctionCatalog registry;
|
||||
|
||||
private FunctionInvokingHandler(Function<Flux<String>, Flux<String>> function) {
|
||||
this.function = function;
|
||||
private FunctionInvokingHandler(FunctionCatalog registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleText(ServerRequest request) {
|
||||
private Mono<ServerResponse> handlePost(ServerRequest request) {
|
||||
Flux<String> input = request.body(toFlux(String.class));
|
||||
Publisher<String> output = this.function.apply(input);
|
||||
String name = request.pathVariable("name");
|
||||
Function<Flux<String>, Flux<String>> function = (name.indexOf(',') == -1)
|
||||
? registry.lookupFunction(name)
|
||||
: registry.composeFunction(
|
||||
StringUtils.commaDelimitedListToStringArray(name));
|
||||
Publisher<String> output = function.apply(input);
|
||||
return ServerResponse.ok().body(fromPublisher(output, String.class));
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleGet(ServerRequest request) {
|
||||
String name = request.pathVariable("name");
|
||||
Supplier<Flux<String>> function = registry.lookupSupplier(name);
|
||||
Publisher<String> output = function.get();
|
||||
return ServerResponse.ok().body(fromPublisher(output, String.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* 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
|
||||
* 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,
|
||||
@@ -13,20 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.web;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "web")
|
||||
@ConfigurationProperties("server")
|
||||
public class WebConfigurationProperties {
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
private String path;
|
||||
private String path = "";
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
@@ -41,6 +37,9 @@ public class WebConfigurationProperties {
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
while (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.web.RestConfiguration
|
||||
Reference in New Issue
Block a user