diff --git a/README.adoc b/README.adoc
index 73425850e..7780b0b2f 100644
--- a/README.adoc
+++ b/README.adoc
@@ -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:
diff --git a/pom.xml b/pom.xml
index bb6ea0e29..cb83c05de 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,7 @@
3.0.4.RELEASE
1.1.0.BUILD-SNAPSHOT
0.0.1.BUILD-SNAPSHOT
+ 1.5.0.BUILD-SNAPSHOT
@@ -39,9 +40,12 @@
spring-cloud-function-compiler
spring-cloud-function-core
+ spring-cloud-function-context
spring-cloud-function-stream
spring-cloud-function-task
spring-cloud-function-web
+ spring-cloud-function-samples
+ spring-cloud-function-deployer
diff --git a/scripts/web.sh b/scripts/web.sh
index 6a2ad038e..4a1ed9d14 100755
--- a/scripts/web.sh
+++ b/scripts/web.sh
@@ -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 ${@}
diff --git a/spring-cloud-function-context/.jdk8 b/spring-cloud-function-context/.jdk8
new file mode 100644
index 000000000..e69de29bb
diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml
new file mode 100644
index 000000000..d182403c7
--- /dev/null
+++ b/spring-cloud-function-context/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+
+ spring-cloud-function-context
+ jar
+ spring-cloud-function-context
+ Spring Cloud Function Web Support
+
+
+ org.springframework.cloud
+ spring-cloud-function-parent
+ 1.0.0.BUILD-SNAPSHOT
+
+
+
+ 1.8
+ 1.0.0.BUILD-SNAPSHOT
+ 0.0.1.BUILD-SNAPSHOT
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-function-core
+ ${spring-cloud-function.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 2.0.0.BUILD-SNAPSHOT
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.0
+
+
+
+
+
diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ApplicationContextFunctionCatalog.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ApplicationContextFunctionCatalog.java
new file mode 100644
index 000000000..230ef7dfb
--- /dev/null
+++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ApplicationContextFunctionCatalog.java
@@ -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> functions;
+ private final Map> consumers;
+ private final Map> suppliers;
+
+ public ApplicationContextFunctionCatalog(Map> functions,
+ Map> consumers, Map> suppliers) {
+ this.functions = functions;
+ this.consumers = consumers;
+ this.suppliers = suppliers;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Consumer lookupConsumer(String name) {
+ return (Consumer) consumers.get(name);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Function lookupFunction(String name) {
+ return (Function) functions.get(name);
+ }
+
+ @Override
+ public Function composeFunction(String... functionNames) {
+ Function 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 Supplier lookupSupplier(String name) {
+ return (Supplier) suppliers.get(name);
+ }
+
+}
diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java
new file mode 100644
index 000000000..ac1911c97
--- /dev/null
+++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java
@@ -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> functions = Collections.emptyMap();
+ @Autowired(required = false)
+ private Map> consumers = Collections.emptyMap();
+ @Autowired(required = false)
+ private Map> suppliers = Collections.emptyMap();
+
+ @Bean
+ public FunctionCatalog functionCatalog() {
+ return new ApplicationContextFunctionCatalog(functions, consumers, suppliers);
+ }
+
+}
diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring.factories b/spring-cloud-function-context/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..d00ef6707
--- /dev/null
+++ b/spring-cloud-function-context/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.springframework.cloud.function.context.ContextFunctionCatalogAutoConfiguration
\ No newline at end of file
diff --git a/spring-cloud-function-core/pom.xml b/spring-cloud-function-core/pom.xml
index 8b882d82e..cbf02fd29 100644
--- a/spring-cloud-function-core/pom.xml
+++ b/spring-cloud-function-core/pom.xml
@@ -18,6 +18,10 @@
+
+ org.springframework.boot
+ spring-boot-starter
+
org.springframework.cloud
spring-cloud-function-compiler
@@ -46,6 +50,13 @@
registrar
+
+
+ org.springframework.boot.experimental
+ spring-boot-thin-launcher
+ ${wrapper.version}
+
+
diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/DefaultFunctionRegistryAutoConfiguration.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/DefaultFunctionRegistryAutoConfiguration.java
new file mode 100644
index 000000000..2ff70c9d9
--- /dev/null
+++ b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/DefaultFunctionRegistryAutoConfiguration.java
@@ -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();
+ }
+
+}
diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionCatalog.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionCatalog.java
new file mode 100644
index 000000000..f1a48519e
--- /dev/null
+++ b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionCatalog.java
@@ -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 {
+
+ Consumer lookupConsumer(String name);
+
+ Function lookupFunction(String name);
+
+ Function composeFunction(String... functionNames);
+
+ Supplier lookupSupplier(String name);
+}
diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionRegistry.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionRegistry.java
index 8fc8bc685..e58e7639f 100644
--- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionRegistry.java
+++ b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/registry/FunctionRegistry.java
@@ -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);
- Consumer lookupConsumer(String name);
-
- Function lookupFunction(String name);
-
- Function composeFunction(String... functionNames);
-
- Supplier lookupSupplier(String name);
}
diff --git a/spring-cloud-function-core/src/main/resources/META-INF/spring.factories b/spring-cloud-function-core/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..106bdaa49
--- /dev/null
+++ b/spring-cloud-function-core/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.springframework.cloud.function.registry.DefaultFunctionRegistryAutoConfiguration
\ No newline at end of file
diff --git a/spring-cloud-function-deployer/.jdk8 b/spring-cloud-function-deployer/.jdk8
new file mode 100644
index 000000000..e69de29bb
diff --git a/spring-cloud-function-deployer/pom.xml b/spring-cloud-function-deployer/pom.xml
new file mode 100644
index 000000000..f7853a206
--- /dev/null
+++ b/spring-cloud-function-deployer/pom.xml
@@ -0,0 +1,96 @@
+
+
+ 4.0.0
+
+ spring-cloud-function-deployer
+ jar
+ spring-cloud-function-deployer
+ Spring Cloud Function Web Support
+
+
+ org.springframework.cloud
+ spring-cloud-function-parent
+ 1.0.0.BUILD-SNAPSHOT
+
+
+
+ 1.8
+ 0.0.1.BUILD-SNAPSHOT
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-function-core
+ ${project.version}
+
+
+ org.springframework.boot.experimental
+ spring-boot-starter-web-reactive
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-deployer-thin
+ ${spring-cloud-deployer-thin.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 2.0.0.BUILD-SNAPSHOT
+ pom
+ import
+
+
+ org.springframework.boot.experimental
+ spring-boot-dependencies-web-reactive
+ 0.1.0.BUILD-SNAPSHOT
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-function-parent
+ ${project.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ Dalston.BUILD-SNAPSHOT
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.0
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/ApplicationRunner.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/ApplicationRunner.java
new file mode 100644
index 000000000..b120a3e7c
--- /dev/null
+++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/ApplicationRunner.java
@@ -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 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 child = new ArrayList<>();
+ List 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 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;
+ }
+
+}
diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployedFunctionApplication.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployedFunctionApplication.java
new file mode 100644
index 000000000..e31b19aa2
--- /dev/null
+++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployedFunctionApplication.java
@@ -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 {
+}
diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployedFunctionController.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployedFunctionController.java
new file mode 100644
index 000000000..dc2cf1c42
--- /dev/null
+++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployedFunctionController.java
@@ -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 function(@PathVariable String name,
+ @RequestBody Flux body) {
+ Function