Add Supplier Exporter sample
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
AWS Lambda custom runtime.
|
||||
|
||||
```
|
||||
$ ./build.sh
|
||||
$ ./mvnw package -P native
|
||||
```
|
||||
|
||||
builds a native-zip ZIP file in target. Upload it to AWS and set the handler to "foobar".
|
||||
|
||||
To test locally, run the `TestServer` and then the `DemoApplication` (either in a JVM or natively). Then POST some data into the test server:
|
||||
|
||||
```
|
||||
$ curl localhost:8000/add -d world -H "Content-Type: text/plain"
|
||||
```
|
||||
|
||||
There is a unit test that does the same thing. Also the `build.sh` script orchestrates the same test for the native image.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Useful for running locally (hence not in src/main/resources)
|
||||
spring.cloud.function.web.export.source.url=http://localhost:8000/home
|
||||
spring.cloud.function.web.export.sink.url=http://localhost:8000/echo
|
||||
spring.cloud.function.web.export.debug=true
|
||||
logging.level.org.springframework=DEBUG
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
printf "=== ${BLUE}Building %s sample${NC} ===\n" "${PWD##*/}"
|
||||
|
||||
./compile.sh || exit 1
|
||||
|
||||
JARDIR=target/native-image
|
||||
java -cp $JARDIR/BOOT-INF/lib/*:$JARDIR/BOOT-INF/classes:$JARDIR:target/test-classes com.example.test.TestServer &
|
||||
SPID=$!
|
||||
sleep 5
|
||||
|
||||
${PWD%/*samples/*}/scripts/test.sh --spring.cloud.function.web.export.source.url=http://localhost:8000/home --spring.cloud.function.web.export.sink.url=http://localhost:8000/echo
|
||||
|
||||
kill $SPID
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ARTIFACT=function-aws
|
||||
MAINCLASS=com.example.demo.DemoApplication
|
||||
VERSION=0.0.1-SNAPSHOT
|
||||
FEATURE=../../../../spring-graal-native/target/spring-graal-native-0.7.0.BUILD-SNAPSHOT.jar
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
rm -rf target
|
||||
mkdir -p target/native-image
|
||||
|
||||
echo "Packaging $ARTIFACT with Maven"
|
||||
mvn -DskipTests package > target/native-image/output.txt
|
||||
|
||||
JAR="$ARTIFACT-$VERSION.jar"
|
||||
rm -f $ARTIFACT
|
||||
echo "Unpacking $JAR"
|
||||
cd target/native-image
|
||||
jar -xvf ../$JAR >/dev/null 2>&1
|
||||
cp -R META-INF BOOT-INF/classes
|
||||
|
||||
LIBPATH=`find BOOT-INF/lib | tr '\n' ':'`
|
||||
CP=BOOT-INF/classes:$LIBPATH:$FEATURE
|
||||
|
||||
if [ ! -f "$FEATURE" ]; then
|
||||
printf "${RED}FAILURE${NC}: $FEATURE does not exist, please build the root project before building this sample.\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GRAALVM_VERSION=`native-image --version`
|
||||
echo "Compiling $ARTIFACT with $GRAALVM_VERSION"
|
||||
{ time native-image \
|
||||
--verbose \
|
||||
--no-server \
|
||||
--no-fallback \
|
||||
--initialize-at-build-time \
|
||||
-H:+PrintMethodHistogram \
|
||||
-H:+TraceClassInitialization \
|
||||
-H:Name=$ARTIFACT \
|
||||
-H:+ReportExceptionStackTraces \
|
||||
-Dspring.graal.remove-unused-autoconfig=true \
|
||||
-Dspring.graal.remove-yaml-support=true \
|
||||
-cp $CP $MAINCLASS >> output.txt ; } 2>> output.txt
|
||||
|
||||
if [[ -f $ARTIFACT ]]
|
||||
then
|
||||
printf "${GREEN}SUCCESS${NC}\n"
|
||||
mv ./$ARTIFACT ..
|
||||
exit 0
|
||||
else
|
||||
cat output.txt
|
||||
printf "${RED}FAILURE${NC}: an error occurred when compiling the native-image.\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.0.M4</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>function-sample-aws-supplier-exporter</artifactId>
|
||||
<version>1.0.0.RELEASE</version>
|
||||
<name>function-sample-aws-supplier-exporter</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.1.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<reactor-bom.version>Dysprosium-SR6</reactor-bom.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http2</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-adapter-aws</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-dependencies</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-release</id>
|
||||
<name>Spring release</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshot</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestone</id>
|
||||
<name>Spring Milestone</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-release</id>
|
||||
<name>Spring release</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<name>Spring Milestone</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.FunctionType;
|
||||
import org.springframework.cloud.function.context.FunctionalSpringApplication;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
@SpringBootConfiguration(proxyBeanMethods = false)
|
||||
public class DemoApplication
|
||||
implements ApplicationContextInitializer<GenericApplicationContext> {
|
||||
|
||||
public static void main(String[] args) {
|
||||
FunctionalSpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(GenericApplicationContext context) {
|
||||
context.registerBean("foobar", FunctionRegistration.class,
|
||||
() -> new FunctionRegistration<>(new Foobar())
|
||||
.type(FunctionType.from(Foo.class).to(Foo.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Foo {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Foo(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Foo() {
|
||||
}
|
||||
}
|
||||
|
||||
class Foobar implements Function<Foo, Foo> {
|
||||
|
||||
@Override
|
||||
public Foo apply(Foo input) {
|
||||
System.err.println("HI: " + input.getName());
|
||||
return new Foo("hi " + input.getName() + "!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "com.example.demo.Foo",
|
||||
"allDeclaredConstructors": true,
|
||||
"allDeclaredMethods": true
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,4 @@
|
||||
spring.cloud.function.web.export.enabled=true
|
||||
spring.cloud.function.web.export.debug=true
|
||||
spring.main.web-application-type=none
|
||||
logging.level.org.springframework.cloud=DEBUG
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.example.demo;
|
||||
|
||||
import com.example.test.TestServer;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.cloud.function.context.test.FunctionalSpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@FunctionalSpringBootTest({"spring.cloud.function.web.export.source.url=http://localhost:${export.port}/home",
|
||||
"spring.cloud.function.web.export.sink.url=http://localhost:${export.port}/echo",
|
||||
"logging.level.reactor=OFF",
|
||||
"logging.level.io.netty=OFF"})
|
||||
public class DemoApplicationTests {
|
||||
|
||||
static ConfigurableApplicationContext context;
|
||||
|
||||
@Value("${export.port}")
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private WebClient.Builder builder;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
WebClient client = builder.baseUrl("http://localhost:" + port).build();
|
||||
client.post().uri("/add").bodyValue("{\"name\":\"Fred\"}").exchange().block();
|
||||
Thread.sleep(1000L);
|
||||
String response = client.get().uri("/take").exchange().block().bodyToMono(String.class).block();
|
||||
assertThat(response).isEqualTo("{\"name\":\"hi Fred!\"}");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
static void before() {
|
||||
int port = SocketUtils.findAvailableTcpPort();
|
||||
System.setProperty("export.port", "" + port);
|
||||
context = SpringApplication.run(TestServer.class, "--server.port="+port, "--spring.cloud.function.web.export.enabled=false", "--spring.main.web-application-type=reactive");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.example.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.function.json.JsonMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
|
||||
@SpringBootApplication(proxyBeanMethods = false)
|
||||
public class TestServer {
|
||||
|
||||
private MonoProcessor<String> output = MonoProcessor.<String>create();
|
||||
|
||||
private String response = "";
|
||||
|
||||
public static void main(String[] args) {
|
||||
Set<String> list = new LinkedHashSet<>(Arrays.asList(args));
|
||||
list.addAll(Arrays.asList("--server.port=8000", "--spring.cloud.function.web.export.enabled=false", "--spring.main.web-application-type=reactive"));
|
||||
SpringApplication.run(TestServer.class, list.toArray(new String[0]));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Supplier<Mono<String>> home() {
|
||||
return () -> output;
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public Function<String, String> echo(JsonMapper mapper) {
|
||||
// return input -> {
|
||||
// response = input;
|
||||
// return "Echo: " + response;
|
||||
// };
|
||||
// }
|
||||
|
||||
@Bean
|
||||
public Function<Foo, String> echo(JsonMapper mapper) {
|
||||
return input -> {
|
||||
System.out.println("===> POJO " + input);
|
||||
response = new String(mapper.toJson(input));
|
||||
return "Echo: " + response;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<String, String> add() {
|
||||
return input -> {
|
||||
System.err.println("Add: " + input);
|
||||
output.onNext(input);
|
||||
output = MonoProcessor.<String>create();
|
||||
return "Added: " + input;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Supplier<String> take() {
|
||||
return () -> response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Foo {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Foo(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Foo() {}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl -s localhost:8000/add -d '{"name":"world"}' -H "Content-Type: text/plain"
|
||||
echo
|
||||
echo Waiting...
|
||||
sleep 1
|
||||
RESPONSE=`curl -s localhost:8000/take`
|
||||
echo Got response: $RESPONSE
|
||||
if [[ "$RESPONSE" == '{"name":"hi world!"}' ]]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
@@ -19,6 +19,7 @@
|
||||
<module>function-sample-pojo</module>
|
||||
<module>function-sample-aws</module>
|
||||
<module>function-sample-aws-custom</module>
|
||||
<module>function-sample-supplier-exporter</module>
|
||||
<module>function-sample-azure</module>
|
||||
<!--><module>function-sample-spring-integration</module>-->
|
||||
<module>function-sample-gcp</module>
|
||||
|
||||
Reference in New Issue
Block a user