diff --git a/README.adoc b/README.adoc
index 579d70b3c..73425850e 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,7 +1,7 @@
== Register a Function:
----
-./register.sh -n uppercase -f "f->f.map(s->s.toString().toUpperCase())"
+./registerFunction.sh -n uppercase -f "f->f.map(s->s.toString().toUpperCase())"
----
== Run a Stream Processing Microservice using that Function:
@@ -21,9 +21,21 @@
(assuming the `uppercase` function was already registered as above)
----
-./register.sh -n pluralize -f "f->f.map(s->s+\"S\")"
+./registerFunction.sh -n pluralize -f "f->f.map(s->s+\"S\")"
./web.sh -p /words -f uppercase,pluralize
----
+== Run a Task Microservice using a Supplier, Function, and Consumer:
+
+(assuming the `uppercase` function was already registered as above)
+
+----
+./registerSupplier.sh -n words -f "()->Flux.just(\"foo\",\"bar\")"
+
+./registerConsumer.sh -n print -f "System.out::println"
+
+./task.sh -s words -f uppercase -c print
+----
+
(more docs soon)
diff --git a/pom.xml b/pom.xml
index 68f68f908..b014487a7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
spring-cloud-function-compilerspring-cloud-function-corespring-cloud-function-stream
+ spring-cloud-function-taskspring-cloud-function-web
diff --git a/scripts/register.sh b/scripts/registerConsumer.sh
similarity index 80%
rename from scripts/register.sh
rename to scripts/registerConsumer.sh
index a365fc9dc..8b46d514a 100755
--- a/scripts/register.sh
+++ b/scripts/registerConsumer.sh
@@ -11,6 +11,6 @@ while getopts ":n:f:" opt; do
esac
done
-java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar\
+java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar consumer\
$NAME\
$FUNC
diff --git a/scripts/registerFunction.sh b/scripts/registerFunction.sh
new file mode 100755
index 000000000..aa95fb5d3
--- /dev/null
+++ b/scripts/registerFunction.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+while getopts ":n:f:" opt; do
+ case $opt in
+ n)
+ NAME=$OPTARG
+ ;;
+ f)
+ FUNC=$OPTARG
+ ;;
+ esac
+done
+
+java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar function\
+ $NAME\
+ $FUNC
diff --git a/scripts/registerSupplier.sh b/scripts/registerSupplier.sh
new file mode 100755
index 000000000..242c47d97
--- /dev/null
+++ b/scripts/registerSupplier.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+while getopts ":n:f:" opt; do
+ case $opt in
+ n)
+ NAME=$OPTARG
+ ;;
+ f)
+ FUNC=$OPTARG
+ ;;
+ esac
+done
+
+java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar supplier\
+ $NAME\
+ $FUNC
diff --git a/scripts/task.sh b/scripts/task.sh
new file mode 100755
index 000000000..8fbcb5591
--- /dev/null
+++ b/scripts/task.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+while getopts ":s:f:c:" opt; do
+ case $opt in
+ s)
+ SUPP=$OPTARG
+ ;;
+ f)
+ FUNC=$OPTARG
+ ;;
+ c)
+ CONS=$OPTARG
+ ;;
+ esac
+done
+
+java -jar ../spring-cloud-function-task/target/spring-cloud-function-task-1.0.0.BUILD-SNAPSHOT.jar\
+ --lambda.supplier=$SUPP --lambda.function=$FUNC --lambda.consumer=$CONS
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java
new file mode 100644
index 000000000..7471306e0
--- /dev/null
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java
@@ -0,0 +1,143 @@
+/*
+ * 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.compiler;
+
+import java.util.List;
+import java.util.regex.Matcher;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.cloud.function.compiler.java.CompilationFailedException;
+import org.springframework.cloud.function.compiler.java.CompilationMessage;
+import org.springframework.cloud.function.compiler.java.CompilationResult;
+import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
+
+/**
+ * @author Andy Clement
+ * @author Mark Fisher
+ */
+abstract class AbstractFunctionCompiler {
+
+ private static Logger logger = LoggerFactory.getLogger(AbstractFunctionCompiler.class);
+
+ // Newlines in the property are escaped
+ private static final String NEWLINE_ESCAPE = Matcher.quoteReplacement("\\n");
+
+ // Individual double-quote characters are represented by two double quotes in the DSL
+ private static final String DOUBLE_DOUBLE_QUOTE = Matcher.quoteReplacement("\"\"");
+
+ /**
+ * The user supplied code snippet is inserted into the template and then the result is compiled
+ */
+ private static String SOURCE_CODE_TEMPLATE =
+ "package " + AbstractFunctionCompiler.class.getPackage().getName() + ";\n" +
+ "import java.util.*;\n" + // Helpful to include this
+ "import java.util.function.*;\n" +
+ "import reactor.core.publisher.Flux;\n" +
+ "public class %s implements %sFactory {\n" +
+ " public %s<%s> getResult() {\n" +
+ " %s\n" +
+ " }\n" +
+ "}\n";
+
+ static enum ResultType { Consumer, Function, Supplier }
+
+ private final ResultType resultType;
+
+ private final String parameterizedTypes;
+
+ private final RuntimeJavaCompiler compiler = new RuntimeJavaCompiler();
+
+ AbstractFunctionCompiler(ResultType type, String parameterizedTypes) {
+ this.resultType = type;
+ this.parameterizedTypes = parameterizedTypes;
+ }
+
+ /**
+ * Produce a factory instance by:
+ *
Decoding the code String to process any newlines/double-double-quotes
+ *
Insert the code into the source code template for a class
+ *
Compiling the class using the JDK provided Java Compiler
+ *
Loading the compiled class
+ *
Invoking a well known method on the factory class to produce a Consumer, Function, or Supplier instance
+ *
Returning that instance.
+ *
+ *
+ * @return a factory instance
+ */
+ public CompiledFunctionFactory compile(String name, String code) {
+ if (name == null || name.length() == 0) {
+ throw new IllegalArgumentException("name must not be empty");
+ }
+ logger.info("Initial code property value :'{}'", code);
+ code = decode(code);
+ if (code.startsWith("\"") && code.endsWith("\"")) {
+ code = code.substring(1,code.length()-1);
+ }
+ if (!code.startsWith("return ") && !code.endsWith(";")) {
+ code = String.format("return (%s<%s> & java.io.Serializable) %s;", resultType, this.parameterizedTypes, code);
+ }
+ logger.info("Processed code property value :\n{}\n", code);
+ String firstLetter = name.substring(0, 1).toUpperCase();
+ name = (name.length() > 1) ? firstLetter + name.substring(1) : firstLetter;
+ String className = String.format("%s.%s%sFactory", this.getClass().getPackage().getName(), name, resultType);
+ CompilationResult compilationResult = buildAndCompileSourceCode(className, code);
+ if (compilationResult.wasSuccessful()) {
+ return new CompiledFunctionFactory(className, compilationResult);
+ }
+ List compilationMessages = compilationResult.getCompilationMessages();
+ throw new CompilationFailedException(compilationMessages);
+ }
+
+ /**
+ * Create the source for and then compile and load a class that embodies
+ * the supplied methodBody. The methodBody is inserted into a class template that
+ * returns a Function<Flux<Object>,Flux<Object>>.
+ * This method can return more than one class if the method body includes local class
+ * declarations. An example methodBody would be return input -> input.buffer(5).map(list->list.get(0));.
+ *
+ * @param className the name of the class
+ * @param methodBody the source code for a method that should return a
+ * Function<Flux<Object>,Flux<Object>>
+ * @return the list of Classes produced by compiling and then loading the snippet of code
+ */
+ private CompilationResult buildAndCompileSourceCode(String className, String methodBody) {
+ String sourceCode = makeSourceClassDefinition(className, methodBody);
+ return compiler.compile(className, sourceCode);
+ }
+
+ private static String decode(String input) {
+ return input.replaceAll(NEWLINE_ESCAPE, "\n").replaceAll(DOUBLE_DOUBLE_QUOTE, "\"");
+ }
+
+ /**
+ * Make a full source code definition for a class by applying the specified method body
+ * to the Reactive template.
+ *
+ * @param className the name of the class
+ * @param methodBody the code to insert into the Reactive source class template
+ * @return a complete Java Class definition
+ */
+ private String makeSourceClassDefinition(String className, String methodBody) {
+ String shortClassName = className.substring(className.lastIndexOf('.') + 1);
+ String s = String.format(SOURCE_CODE_TEMPLATE, shortClassName, resultType, resultType, this.parameterizedTypes, methodBody);
+ System.out.println(s);
+ return s;
+ }
+
+}
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java
new file mode 100644
index 000000000..af3946a91
--- /dev/null
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.compiler;
+
+/**
+ * @author Mark Fisher
+ */
+public interface CompilationResultFactory {
+
+ T getResult();
+
+}
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java
index 26938862e..22de0bd39 100644
--- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java
@@ -17,43 +17,42 @@
package org.springframework.cloud.function.compiler;
import java.util.List;
-import java.util.function.Function;
import org.springframework.cloud.function.compiler.java.CompilationResult;
/**
* @author Mark Fisher
*/
-public class CompiledFunctionFactory implements FunctionFactory {
+public class CompiledFunctionFactory implements CompilationResultFactory {
- private final Function function;
+ private final T result;
private final byte[] generatedClassBytes;
public CompiledFunctionFactory(String className, CompilationResult compilationResult) {
List> clazzes = compilationResult.getCompiledClasses();
- Function function = null;
+ T result = null;
for (Class> clazz: clazzes) {
if (clazz.getName().equals(className)) {
try {
@SuppressWarnings("unchecked")
- FunctionFactory functionFactory = (FunctionFactory) clazz.newInstance();
- function = functionFactory.getFunction();
+ CompilationResultFactory factory = (CompilationResultFactory) clazz.newInstance();
+ result = factory.getResult();
}
catch (Exception e) {
throw new IllegalArgumentException("Unexpected problem during retrieval of Function from compiled class", e);
}
}
}
- if (function == null) {
- throw new IllegalArgumentException("Failed to extract Function from compilation result.");
+ if (result == null) {
+ throw new IllegalArgumentException("Failed to extract compilation result.");
}
- this.function = function;
+ this.result = result;
this.generatedClassBytes = compilationResult.getClassBytes(className);
}
- public Function getFunction() {
- return function;
+ public T getResult() {
+ return result;
}
public byte[] getGeneratedClassBytes() {
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java
new file mode 100644
index 000000000..0b79ee495
--- /dev/null
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.compiler;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Mark Fisher
+ */
+public class ConsumerCompiler extends AbstractFunctionCompiler> {
+
+ public ConsumerCompiler() {
+ super(ResultType.Consumer, "Object");
+ }
+}
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerFactory.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerFactory.java
new file mode 100644
index 000000000..babe13f9a
--- /dev/null
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.compiler;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Mark Fisher
+ */
+public interface ConsumerFactory extends CompilationResultFactory> {
+
+}
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/Example.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/Example.java
new file mode 100644
index 000000000..2fd1bd0e4
--- /dev/null
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/Example.java
@@ -0,0 +1,43 @@
+/*
+ * 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.compiler;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import reactor.core.publisher.Flux;
+
+/**
+ * @author Mark Fisher
+ */
+public class Example {
+
+ public static void main(String[] args) {
+ SupplierCompiler> supplierCompiler = new SupplierCompiler<>();
+ CompiledFunctionFactory>> supplierFactory = supplierCompiler.compile("s", "return ()->Flux.just(\"foo\");");
+ Flux input = supplierFactory.getResult().get();
+
+ FunctionCompiler, Flux> functionCompiler = new FunctionCompiler<>();
+ CompiledFunctionFactory,Flux>> functionFactory = functionCompiler.compile("f", "f->f.map(s->s.toString().toUpperCase())");
+ Flux output = functionFactory.getResult().apply(input);
+
+ ConsumerCompiler> consumerCompiler = new ConsumerCompiler<>();
+ CompiledFunctionFactory>> consumerFactory = consumerCompiler.compile("c", "f->f.subscribe(System.out::println)");
+ consumerFactory.getResult().accept(output);
+ }
+}
diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java
index 19e81f148..a896e90b2 100644
--- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java
+++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java
@@ -1,12 +1,12 @@
/*
* 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
- *
+ *
+ * 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.
@@ -16,115 +16,14 @@
package org.springframework.cloud.function.compiler;
-import java.util.List;
-import java.util.regex.Matcher;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.cloud.function.compiler.java.CompilationFailedException;
-import org.springframework.cloud.function.compiler.java.CompilationMessage;
-import org.springframework.cloud.function.compiler.java.CompilationResult;
-import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
+import java.util.function.Function;
/**
- * @author Andy Clement
* @author Mark Fisher
*/
-public class FunctionCompiler {
+public class FunctionCompiler extends AbstractFunctionCompiler> {
- private static Logger logger = LoggerFactory.getLogger(FunctionCompiler.class);
-
- // Newlines in the property are escaped
- private static final String NEWLINE_ESCAPE = Matcher.quoteReplacement("\\n");
-
- // Individual double-quote characters are represented by two double quotes in the DSL
- private static final String DOUBLE_DOUBLE_QUOTE = Matcher.quoteReplacement("\"\"");
-
- /**
- * The user supplied code snippet is inserted into the template and then the result is compiled
- */
- private static String SOURCE_CODE_TEMPLATE =
- "package " + FunctionCompiler.class.getPackage().getName() + ";\n" +
- "import java.util.*;\n" + // Helpful to include this
- "import java.util.function.*;\n" +
- "import reactor.core.publisher.Flux;\n" +
- "public class %s implements FunctionFactory {\n" +
- " public Function, Flux