add Function compiling webapp and update scripts
This commit is contained in:
78
README.adoc
78
README.adoc
@@ -1,42 +1,56 @@
|
|||||||
|
To run these examples, change into the `scripts` directory:
|
||||||
|
|
||||||
|
----
|
||||||
|
cd scripts
|
||||||
|
----
|
||||||
|
|
||||||
|
== Start the Function Registry Service:
|
||||||
|
|
||||||
|
----
|
||||||
|
./function-registry.sh
|
||||||
|
----
|
||||||
|
|
||||||
== Register a Function:
|
== Register a Function:
|
||||||
|
|
||||||
----
|
----
|
||||||
./registerFunction.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:
|
== Run a REST Microservice using that Function:
|
||||||
|
|
||||||
|
----
|
||||||
|
./web.sh -f uppercase -p 9000
|
||||||
|
curl -H "Content-Type: text/plain" -H "Accept: text/plain" :9000/function -d foo
|
||||||
|
----
|
||||||
|
|
||||||
|
== Register a Supplier:
|
||||||
|
|
||||||
|
----
|
||||||
|
./registerSupplier.sh -n words -f "()->Flux.just(\"foo\",\"bar\")"
|
||||||
|
----
|
||||||
|
|
||||||
|
== Run a REST Microservice using that Supplier:
|
||||||
|
|
||||||
|
----
|
||||||
|
./web.sh -s words -p 9001
|
||||||
|
curl -H "Accept: application/json" :9001/supplier
|
||||||
|
----
|
||||||
|
|
||||||
|
== Register a Consumer:
|
||||||
|
|
||||||
|
----
|
||||||
|
./registerConsumer.sh -n print -f "System.out::println"
|
||||||
|
----
|
||||||
|
|
||||||
|
== Run a REST Microservice using that Consumer:
|
||||||
|
|
||||||
|
----
|
||||||
|
./web.sh -c print -p 9002
|
||||||
|
curl -X POST -H "Content-Type: text/plain" -d foo :9002/consumer
|
||||||
|
----
|
||||||
|
|
||||||
|
== Run a Stream Processing Microservice using the Function:
|
||||||
|
|
||||||
----
|
----
|
||||||
./stream.sh -i words -o uppercaseWords -f uppercase
|
./stream.sh -i words -o uppercaseWords -f uppercase
|
||||||
----
|
----
|
||||||
|
|
||||||
== Run a REST Microservice using that Function:
|
|
||||||
|
|
||||||
----
|
|
||||||
./web.sh
|
|
||||||
curl -H "Content-Type=text/plain" localhost:8080/uppercase -d foo
|
|
||||||
----
|
|
||||||
|
|
||||||
== Compose Functions:
|
|
||||||
|
|
||||||
(assuming the `uppercase` function was already registered as above)
|
|
||||||
|
|
||||||
----
|
|
||||||
./registerFunction.sh -n pluralize -f "f->f.map(s->s+\"S\")"
|
|
||||||
|
|
||||||
curl -H "Content-Type=text/plain" localhost:8080/uppercase,pluralize -d foo
|
|
||||||
----
|
|
||||||
|
|
||||||
== 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)
|
|
||||||
|
|||||||
3
scripts/function-registry.sh
Executable file
3
scripts/function-registry.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
java -jar ../spring-cloud-function-compiler/target/spring-cloud-function-compiler-1.0.0.BUILD-SNAPSHOT.jar
|
||||||
@@ -11,6 +11,4 @@ while getopts ":n:f:" opt; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar consumer\
|
curl -X POST -H "Content-Type: text/plain" -d $FUNC :8080/consumer/$NAME
|
||||||
$NAME\
|
|
||||||
$FUNC
|
|
||||||
|
|||||||
@@ -11,6 +11,4 @@ while getopts ":n:f:" opt; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar function\
|
curl -X POST -H "Content-Type: text/plain" -d $FUNC :8080/function/$NAME
|
||||||
$NAME\
|
|
||||||
$FUNC
|
|
||||||
|
|||||||
@@ -11,6 +11,4 @@ while getopts ":n:f:" opt; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
java -jar ../spring-cloud-function-core/target/spring-cloud-function-core-1.0.0.BUILD-SNAPSHOT-registrar.jar supplier\
|
curl -X POST -H "Content-Type: text/plain" -d $FUNC :8080/supplier/$NAME
|
||||||
$NAME\
|
|
||||||
$FUNC
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ while getopts ":i:f:o:" opt; do
|
|||||||
;;
|
;;
|
||||||
f)
|
f)
|
||||||
FUNC=$OPTARG
|
FUNC=$OPTARG
|
||||||
|
TYPE=function
|
||||||
;;
|
;;
|
||||||
o)
|
o)
|
||||||
OUT=$OPTARG
|
OUT=$OPTARG
|
||||||
@@ -14,7 +15,11 @@ while getopts ":i:f:o:" opt; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
java -noverify -XX:TieredStopAtLevel=1 -Xss256K -Xms16M -Xmx256M -XX:MaxMetaspaceSize=128M -jar ../spring-cloud-function-stream/target/spring-cloud-function-stream-1.0.0.BUILD-SNAPSHOT.jar\
|
java -jar ../spring-cloud-function-samples/spring-cloud-function-sample-bytecode/target/function-sample-bytecode-1.0.0.BUILD-SNAPSHOT.jar\
|
||||||
|
--management.security.enabled=false\
|
||||||
|
--server.port=9999\
|
||||||
--spring.cloud.stream.bindings.input.destination=$IN\
|
--spring.cloud.stream.bindings.input.destination=$IN\
|
||||||
--spring.cloud.stream.bindings.output.destination=$OUT\
|
--spring.cloud.stream.bindings.output.destination=$OUT\
|
||||||
--function.name=$FUNC
|
--function.name=$TYPE\
|
||||||
|
--function.type=$TYPE\
|
||||||
|
--function.resource=file:///tmp/function-registry/$TYPE's'/$FUNC.fun
|
||||||
|
|||||||
@@ -1,4 +1,26 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
java -jar ../spring-cloud-function-web/target/spring-cloud-function-web-1.0.0.BUILD-SNAPSHOT.jar ${@}
|
while getopts ":s:f:c:p:" opt; do
|
||||||
|
case $opt in
|
||||||
|
s)
|
||||||
|
FUNC=$OPTARG
|
||||||
|
TYPE=supplier
|
||||||
|
;;
|
||||||
|
f)
|
||||||
|
FUNC=$OPTARG
|
||||||
|
TYPE=function
|
||||||
|
;;
|
||||||
|
c)
|
||||||
|
FUNC=$OPTARG
|
||||||
|
TYPE=consumer
|
||||||
|
;;
|
||||||
|
p)
|
||||||
|
PORT=$OPTARG
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
java -jar ../spring-cloud-function-samples/spring-cloud-function-sample-bytecode/target/function-sample-bytecode-1.0.0.BUILD-SNAPSHOT.jar \
|
||||||
|
--function.type=$TYPE \
|
||||||
|
--function.resource=file:///tmp/function-registry/$TYPE's'/$FUNC.fun \
|
||||||
|
--server.port=$PORT
|
||||||
|
|||||||
@@ -38,12 +38,24 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-logging</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-context</artifactId>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<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>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.app;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
|
||||||
|
import org.springframework.cloud.function.compiler.ConsumerCompiler;
|
||||||
|
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
||||||
|
import org.springframework.cloud.function.compiler.SupplierCompiler;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*/
|
||||||
|
public class CompiledFunctionRegistry {
|
||||||
|
|
||||||
|
private static final String SUPPLIER_DIRECTORY = "suppliers";
|
||||||
|
|
||||||
|
private static final String FUNCTION_DIRECTORY = "functions";
|
||||||
|
|
||||||
|
private static final String CONSUMER_DIRECTORY = "consumers";
|
||||||
|
|
||||||
|
private final File supplierDirectory;
|
||||||
|
|
||||||
|
private final File functionDirectory;
|
||||||
|
|
||||||
|
private final File consumerDirectory;
|
||||||
|
|
||||||
|
private final SupplierCompiler<Flux<?>> supplierCompiler = new SupplierCompiler<>();
|
||||||
|
|
||||||
|
private final FunctionCompiler<Flux<?>, Flux<?>> functionCompiler = new FunctionCompiler<>();
|
||||||
|
|
||||||
|
private final ConsumerCompiler<?> consumerCompiler = new ConsumerCompiler<>();
|
||||||
|
|
||||||
|
public CompiledFunctionRegistry() {
|
||||||
|
this(new File("/tmp/function-registry"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompiledFunctionRegistry(File directory) {
|
||||||
|
Assert.notNull(directory, "Directory must not be null");
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkdirs();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert.isTrue(directory.isDirectory(),
|
||||||
|
String.format("%s is not a directory.", directory.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
this.supplierDirectory = new File(directory, SUPPLIER_DIRECTORY);
|
||||||
|
this.functionDirectory = new File(directory, FUNCTION_DIRECTORY);
|
||||||
|
this.consumerDirectory = new File(directory, CONSUMER_DIRECTORY);
|
||||||
|
this.supplierDirectory.mkdir();
|
||||||
|
this.functionDirectory.mkdir();
|
||||||
|
this.consumerDirectory.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerSupplier(String name, String supplier) {
|
||||||
|
CompiledFunctionFactory<?> factory = this.supplierCompiler.compile(name, supplier);
|
||||||
|
File file = new File(this.supplierDirectory, fileName(name));
|
||||||
|
try {
|
||||||
|
FileCopyUtils.copy(factory.getGeneratedClassBytes(), file);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(String.format("failed to register Supplier: %s", name), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerFunction(String name, String function) {
|
||||||
|
CompiledFunctionFactory<?> factory = this.functionCompiler.compile(name, function);
|
||||||
|
File file = new File(this.functionDirectory, fileName(name));
|
||||||
|
try {
|
||||||
|
FileCopyUtils.copy(factory.getGeneratedClassBytes(), file);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(String.format("failed to register Function: %s", name), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerConsumer(String name, String consumer) {
|
||||||
|
CompiledFunctionFactory<?> factory = this.consumerCompiler.compile(name, consumer);
|
||||||
|
File file = new File(this.consumerDirectory, fileName(name));
|
||||||
|
try {
|
||||||
|
FileCopyUtils.copy(factory.getGeneratedClassBytes(), file);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(String.format("failed to register Consumer: %s", name), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fileName(String functionName) {
|
||||||
|
return String.format("%s.%s", functionName, "fun");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.app;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class CompilerApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(CompilerApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.app;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class CompilerController {
|
||||||
|
|
||||||
|
private final CompiledFunctionRegistry registry = new CompiledFunctionRegistry();
|
||||||
|
|
||||||
|
@PostMapping(path = "/{type}/{name}")
|
||||||
|
public void registerFunction(@PathVariable String type, @PathVariable String name, @RequestBody String lambda) {
|
||||||
|
switch (type) {
|
||||||
|
case "supplier":
|
||||||
|
registry.registerSupplier(name, lambda);
|
||||||
|
break;
|
||||||
|
case "function":
|
||||||
|
registry.registerFunction(name, lambda);
|
||||||
|
break;
|
||||||
|
case "consumer":
|
||||||
|
registry.registerConsumer(name, lambda);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.proxy;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.cloud.function.compiler.CompilationResultFactory;
|
||||||
|
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
||||||
|
import org.springframework.cloud.function.compiler.java.SimpleClassLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*/
|
||||||
|
public abstract class AbstractByteCodeLoadingProxy<T> implements InitializingBean {
|
||||||
|
|
||||||
|
private final Resource resource;
|
||||||
|
|
||||||
|
private final Class<?> type;
|
||||||
|
|
||||||
|
private T target;
|
||||||
|
|
||||||
|
private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader());
|
||||||
|
|
||||||
|
public AbstractByteCodeLoadingProxy(Resource resource, Class<?> type) {
|
||||||
|
this.resource = resource;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
byte[] bytes = FileCopyUtils.copyToByteArray(this.resource.getInputStream());
|
||||||
|
String functionName = this.resource.getFilename().replaceAll(".fun$", "");
|
||||||
|
String firstLetter = functionName.substring(0, 1).toUpperCase();
|
||||||
|
String upperCasedName = (functionName.length() > 1) ? firstLetter + functionName.substring(1) : firstLetter;
|
||||||
|
String className = String.format("%s.%s%sFactory", FunctionCompiler.class.getPackage().getName(), upperCasedName, this.type.getSimpleName());
|
||||||
|
Class<?> factoryClass = this.classLoader.defineClass(className, bytes);
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
CompilationResultFactory<T> factory = (CompilationResultFactory<T>) factoryClass.newInstance();
|
||||||
|
this.target = factory.getResult();
|
||||||
|
}
|
||||||
|
catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
throw new IllegalArgumentException("failed to load Function byte code", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final T getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.proxy;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*
|
||||||
|
* @param <T> type
|
||||||
|
*/
|
||||||
|
public class ByteCodeLoadingConsumer<T> extends AbstractByteCodeLoadingProxy<Consumer<T>> implements Consumer<T> {
|
||||||
|
|
||||||
|
public ByteCodeLoadingConsumer(Resource resource) {
|
||||||
|
super(resource, Consumer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(T t) {
|
||||||
|
this.getTarget().accept(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.proxy;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*
|
||||||
|
* @param <T> Function input type
|
||||||
|
* @param <R> Function result type
|
||||||
|
*/
|
||||||
|
public class ByteCodeLoadingFunction<T, R> extends AbstractByteCodeLoadingProxy<Function<T, R>> implements Function<T, R>, InitializingBean {
|
||||||
|
|
||||||
|
public ByteCodeLoadingFunction(Resource resource) {
|
||||||
|
super(resource, Function.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R apply(T input) {
|
||||||
|
return this.getTarget().apply(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.function.compiler.proxy;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*
|
||||||
|
* @param <T> type
|
||||||
|
*/
|
||||||
|
public class ByteCodeLoadingSupplier<T> extends AbstractByteCodeLoadingProxy<Supplier<T>> implements Supplier<T> {
|
||||||
|
|
||||||
|
public ByteCodeLoadingSupplier(Resource resource) {
|
||||||
|
super(resource, Supplier.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
return this.getTarget().get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.compiler;
|
package org.springframework.cloud.function.compiler.proxy;
|
||||||
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>spring-cloud-function-sample</module>
|
<module>spring-cloud-function-sample</module>
|
||||||
<module>spring-cloud-function-sample-pojo</module>
|
<module>spring-cloud-function-sample-pojo</module>
|
||||||
|
<module>spring-cloud-function-sample-compiler</module>
|
||||||
|
<module>spring-cloud-function-sample-bytecode</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?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-bytecode</artifactId>
|
||||||
|
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>spring-cloud-function-sample-bytecode</name>
|
||||||
|
<description>Spring Cloud Function ByteCode Loading Support</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>1.5.0.BUILD-SNAPSHOT</version>
|
||||||
|
<relativePath/>
|
||||||
|
</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>
|
||||||
|
<reactor.version>3.0.4.RELEASE</reactor.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<!-- TODO: this shouldn't be needed (bug in thin launcher) -->
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
<version>${reactor.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<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-stream</artifactId>
|
||||||
|
<version>${spring-cloud-function.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-function-compiler</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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-releases</id>
|
||||||
|
<name>Spring Releases</name>
|
||||||
|
<url>https://repo.spring.io/release</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
</pluginRepository>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-releases</id>
|
||||||
|
<name>Spring Releases</name>
|
||||||
|
<url>https://repo.spring.io/libs-release-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
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.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.cloud.function.compiler.proxy.ByteCodeLoadingConsumer;
|
||||||
|
import org.springframework.cloud.function.compiler.proxy.ByteCodeLoadingFunction;
|
||||||
|
import org.springframework.cloud.function.compiler.proxy.ByteCodeLoadingSupplier;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties(FunctionProperties.class)
|
||||||
|
public class SampleApplication {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FunctionProperties properties;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@ConditionalOnProperty(name="function.type", havingValue="supplier")
|
||||||
|
public Supplier<Flux<String>> supplier() {
|
||||||
|
return new ByteCodeLoadingSupplier(properties.getResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@ConditionalOnProperty(name="function.type", havingValue="function")
|
||||||
|
public Function<Flux<String>, Flux<String>> function() {
|
||||||
|
return new ByteCodeLoadingFunction(properties.getResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@ConditionalOnProperty(name="function.type", havingValue="consumer")
|
||||||
|
public Consumer<String> consumer() {
|
||||||
|
return new ByteCodeLoadingConsumer(properties.getResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SpringApplication.run(SampleApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigurationProperties("function")
|
||||||
|
class FunctionProperties {
|
||||||
|
|
||||||
|
private String type = "function";
|
||||||
|
|
||||||
|
private Resource resource;
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResource(Resource resource) {
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = "function.resource=file:///tmp/function-registry/functions/uppercase.fun")
|
||||||
|
public class SampleApplicationTests {
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void uppercase() {
|
||||||
|
assertThat(new TestRestTemplate().postForObject(
|
||||||
|
"http://localhost:" + port + "/function", "{\"value\":\"foo\"}",
|
||||||
|
String.class)).isEqualTo("{\"VALUE\":\"FOO\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
<?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-compiler</artifactId>
|
||||||
|
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>spring-cloud-function-sample-compiler</name>
|
||||||
|
<description>Spring Cloud Function Lambda Compiling Support</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>1.5.0.BUILD-SNAPSHOT</version>
|
||||||
|
<relativePath/>
|
||||||
|
</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>
|
||||||
|
<reactor.version>3.0.4.RELEASE</reactor.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<!-- TODO: this shouldn't be needed (bug in thin launcher) -->
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
<version>${reactor.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<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-compiler</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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-releases</id>
|
||||||
|
<name>Spring Releases</name>
|
||||||
|
<url>https://repo.spring.io/release</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
</pluginRepository>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-releases</id>
|
||||||
|
<name>Spring Releases</name>
|
||||||
|
<url>https://repo.spring.io/libs-release-local</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
||||||
|
import org.springframework.cloud.function.compiler.proxy.LambdaCompilingFunction;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties(FunctionProperties.class)
|
||||||
|
public class SampleApplication {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FunctionProperties properties;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public Function<Flux<String>, Flux<String>> function(FunctionCompiler<?, ?> compiler) {
|
||||||
|
ByteArrayResource resource = new ByteArrayResource(properties.getLambda().getBytes());
|
||||||
|
return new LambdaCompilingFunction(resource, compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FunctionCompiler<?, ?> compiler() {
|
||||||
|
return new FunctionCompiler<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SpringApplication.run(SampleApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigurationProperties("function")
|
||||||
|
class FunctionProperties {
|
||||||
|
|
||||||
|
private String lambda;
|
||||||
|
|
||||||
|
public String getLambda() {
|
||||||
|
return lambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLambda(String lambda) {
|
||||||
|
this.lambda = lambda;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Fisher
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = "function.lambda=f->f.map(s->s.toString().toLowerCase())")
|
||||||
|
public class SampleApplicationTests {
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lowercase() {
|
||||||
|
assertThat(new TestRestTemplate().postForObject(
|
||||||
|
"http://localhost:" + port + "/function", "{\"VALUE\":\"FOO\"}",
|
||||||
|
String.class)).isEqualTo("{\"value\":\"foo\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.example;
|
package com.example;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -21,7 +22,7 @@ import java.util.function.Supplier;
|
|||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
||||||
import org.springframework.cloud.function.compiler.LambdaCompilingFunction;
|
import org.springframework.cloud.function.compiler.proxy.LambdaCompilingFunction;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import org.springframework.context.annotation.ConditionContext;
|
|||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.ConfigurationCondition;
|
import org.springframework.context.annotation.ConfigurationCondition;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
@@ -134,6 +135,9 @@ public class StreamConfiguration {
|
|||||||
@Override
|
@Override
|
||||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||||
String functionName = context.getEnvironment().getProperty("function.name");
|
String functionName = context.getEnvironment().getProperty("function.name");
|
||||||
|
if (!StringUtils.hasText(functionName)) {
|
||||||
|
return ConditionOutcome.noMatch("no function name available");
|
||||||
|
}
|
||||||
Class<?> beanType = context.getBeanFactory().getType(functionName);
|
Class<?> beanType = context.getBeanFactory().getType(functionName);
|
||||||
if (type.isAssignableFrom(beanType)) {
|
if (type.isAssignableFrom(beanType)) {
|
||||||
return ConditionOutcome.match(String.format("bean '%s' is a %s", functionName, type));
|
return ConditionOutcome.match(String.format("bean '%s' is a %s", functionName, type));
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.function.web;
|
package org.springframework.cloud.function.web;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -31,7 +33,7 @@ import reactor.core.publisher.Flux;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@ConditionalOnClass(RestController.class)
|
@ConditionalOnClass(RestController.class)
|
||||||
@@ -51,9 +53,14 @@ public class FunctionController {
|
|||||||
public Flux<String> function(@PathVariable String name,
|
public Flux<String> function(@PathVariable String name,
|
||||||
@RequestBody Flux<String> body) {
|
@RequestBody Flux<String> body) {
|
||||||
Function<Object, Object> function = functions.lookupFunction(name);
|
Function<Object, Object> function = functions.lookupFunction(name);
|
||||||
@SuppressWarnings("unchecked")
|
if (function != null) {
|
||||||
Flux<String> result = (Flux<String>) function.apply(body);
|
@SuppressWarnings("unchecked")
|
||||||
return debug ? result.log() : result;
|
Flux<String> result = (Flux<String>) function.apply(body);
|
||||||
|
return debug ? result.log() : result;
|
||||||
|
}
|
||||||
|
Consumer<String> consumer = functions.lookupConsumer(name);
|
||||||
|
body.subscribe(consumer::accept);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/{name}")
|
@GetMapping(path = "/{name}")
|
||||||
@@ -62,5 +69,4 @@ public class FunctionController {
|
|||||||
Flux<String> result = (Flux<String>) functions.lookupSupplier(name).get();
|
Flux<String> result = (Flux<String>) functions.lookupSupplier(name).get();
|
||||||
return debug ? result.log() : result;
|
return debug ? result.log() : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user