diff --git a/pom.xml b/pom.xml index 7a6dbdd2a..53201fe76 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ spring-cloud-function-web spring-cloud-function-samples spring-cloud-function-deployer + spring-cloud-function-adapters diff --git a/spring-cloud-function-adapters/pom.xml b/spring-cloud-function-adapters/pom.xml new file mode 100644 index 000000000..177fa3dbb --- /dev/null +++ b/spring-cloud-function-adapters/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-function-adapter-parent + 1.0.0.BUILD-SNAPSHOT + pom + + spring-cloud-function-adapter-parent + + + + + org.springframework.cloud + spring-cloud-dependencies + Dalston.BUILD-SNAPSHOT + pom + import + + + + + + spring-cloud-function-adapter-aws + spring-cloud-function-adapter-sample + + + diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/.jdk8 new file mode 100644 index 000000000..e69de29bb diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml new file mode 100644 index 000000000..57b2ac90a --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-function-adapter-aws + 1.0.0.BUILD-SNAPSHOT + jar + + spring-cloud-function-adapter-aws + AWS Lambda Adapter for Spring Cloud Function + + + org.springframework.boot + spring-boot-starter-parent + 1.5.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 1.2.1 + 3.0.7.RELEASE + + + + + org.springframework.cloud + spring-cloud-function-context + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.boot + spring-boot-starter + + + com.amazonaws + aws-lambda-java-events + ${aws-lambda-events.version} + provided + + + com.amazonaws + aws-lambda-java-log4j + 1.0.0 + provided + + + io.projectreactor + reactor-core + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Dalston.BUILD-SNAPSHOT + pom + import + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvokingKinesisEventHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvokingKinesisEventHandler.java new file mode 100644 index 000000000..444408db5 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvokingKinesisEventHandler.java @@ -0,0 +1,52 @@ +/* + * 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.adapter.aws; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Date; + +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; + +/** + * @author Mark Fisher + */ +public class FunctionInvokingKinesisEventHandler extends SpringBootRequestHandler { + + public FunctionInvokingKinesisEventHandler(Class configurationClass) { + super(configurationClass); + } + + @Override + protected String convertEvent(KinesisEvent event) { + StringBuilder result = new StringBuilder(); + for (KinesisEventRecord record : event.getRecords()) { + String id = record.getEventID(); + String name = record.getEventName(); + String source = record.getEventSource(); + Date timestamp = record.getKinesis().getApproximateArrivalTimestamp(); + String partitionKey = record.getKinesis().getPartitionKey(); + String sequenceNumber = record.getKinesis().getSequenceNumber(); + ByteBuffer data = record.getKinesis().getData(); + String dataString = new String(data.array(), Charset.forName("UTF-8")); + result.append(String.format("id=%s,name=%s,source=%s,timestamp=%s,partitionKey=%s,sequenceNumber=%s,data=%s", + id, name, source, timestamp, partitionKey, sequenceNumber, dataString)); + } + return result.toString(); + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvokingS3EventHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvokingS3EventHandler.java new file mode 100644 index 000000000..0b3db676a --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvokingS3EventHandler.java @@ -0,0 +1,34 @@ +/* + * 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.adapter.aws; + +import com.amazonaws.services.lambda.runtime.events.S3Event; + +/** + * @author Mark Fisher + */ +public class FunctionInvokingS3EventHandler extends SpringBootRequestHandler { + + public FunctionInvokingS3EventHandler(Class configurationClass) { + super(configurationClass); + } + + @Override + protected String convertEvent(S3Event event) { + return event.toJson(); + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootRequestHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootRequestHandler.java new file mode 100644 index 000000000..c405f5181 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootRequestHandler.java @@ -0,0 +1,73 @@ +/* + * 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.adapter.aws; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import reactor.core.publisher.Flux; + +/** + * @author Mark Fisher + */ +public class SpringBootRequestHandler extends SpringFunctionInitializer implements RequestHandler> { + + public SpringBootRequestHandler(Class configurationClass) { + super(configurationClass); + } + + public SpringBootRequestHandler() { + super(); + } + + @Override + public List handleRequest(E event, Context context) { + initialize(); + Object input = convertEvent(event); + Flux output = apply(extract(input)); + return result(output); + } + + private List result(Flux output) { + List result = new ArrayList<>(); + for (Object value : output.toIterable()) { + result.add(value); + } + return convertResult(result); + } + + @SuppressWarnings("unchecked") + protected List convertResult(List value) { + return (List) value; + } + + private Flux extract(Object input) { + if (input instanceof Collection) { + return Flux.fromIterable((Iterable) input); + } + return Flux.just(input); + } + + protected Object convertEvent(E event) { + return event; + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java new file mode 100644 index 000000000..3598f2299 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java @@ -0,0 +1,83 @@ +/* + * 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.adapter.aws; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.springframework.beans.factory.annotation.Autowired; + +import reactor.core.publisher.Flux; + +/** + * @author Dave Syer + */ +public class SpringBootStreamHandler extends SpringFunctionInitializer + implements RequestStreamHandler { + + @Autowired + private ObjectMapper mapper; + + public SpringBootStreamHandler() { + super(); + } + + public SpringBootStreamHandler(Class configurationClass) { + super(configurationClass); + } + + @Override + public void handleRequest(InputStream input, OutputStream output, Context context) + throws IOException { + initialize(); + Object value = convertStream(input); + Flux flux = apply(extract(value)); + mapper.writeValue(output, result(flux)); + } + + private Object result(Flux flux) { + List result = new ArrayList<>(); + for (Object value : flux.toIterable()) { + result.add(value); + } + return result; + } + + private Flux extract(Object input) { + if (input instanceof Collection) { + return Flux.fromIterable((Iterable) input); + } + return Flux.just(input); + } + + private Object convertStream(InputStream input) { + try { + return mapper.readValue(input, getInputType()); + } + catch (Exception e) { + throw new IllegalStateException("Cannot convert event", e); + } + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java new file mode 100644 index 000000000..8072439f4 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java @@ -0,0 +1,191 @@ +/* + * 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.adapter.aws; + +import java.io.Closeable; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.jar.Manifest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.function.context.FunctionInspector; +import org.springframework.cloud.function.registry.FunctionCatalog; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.ClassUtils; + +import reactor.core.publisher.Flux; + +/** + * @author Dave Syer + */ +public class SpringFunctionInitializer implements Closeable { + + private static Log logger = LogFactory.getLog(SpringFunctionInitializer.class); + + private final Class configurationClass; + + private Function, Flux> function; + + private Consumer> consumer; + + private Supplier> supplier; + + private AtomicBoolean initialized = new AtomicBoolean(); + + @Autowired(required = false) + private FunctionInspector inspector; + + @Autowired(required = false) + private FunctionCatalog catalog; + + private String name; + + private ConfigurableApplicationContext context; + + public SpringFunctionInitializer(Class configurationClass) { + this.configurationClass = configurationClass; + } + + public SpringFunctionInitializer() { + this(getStartClass()); + } + + @Override + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @SuppressWarnings("unchecked") + protected void initialize() { + if (!this.initialized.compareAndSet(false, true)) { + return; + } + logger.info("Initializing: " + configurationClass); + SpringApplicationBuilder builder = new SpringApplicationBuilder( + configurationClass); + ConfigurableApplicationContext context = builder.web(false).run(); + context.getAutowireCapableBeanFactory().autowireBean(this); + String name = context.getEnvironment().getProperty("function.name"); + boolean defaultName = false; + if (name == null) { + name = "function"; + defaultName = true; + } + if (this.catalog == null) { + this.function = context.getBean(name, Function.class); + } + else { + this.function = this.catalog.lookupFunction(name); + this.name = name; + if (this.function == null) { + if (defaultName) { + name = "consumer"; + } + this.consumer = this.catalog.lookupConsumer(name); + this.name = name; + if (this.consumer == null) { + if (defaultName) { + name = "supplier"; + } + this.supplier = this.catalog.lookupSupplier(name); + this.name = name; + } + } + } + this.context = context; + } + + protected Class getInputType() { + if (inspector != null) { + return inspector.getInputType(this.name); + } + return Object.class; + } + + protected Flux apply(Flux input) { + if (this.function != null) { + return function.apply(input); + } + if (this.consumer != null) { + this.consumer.accept(input); + return Flux.empty(); + } + if (this.supplier != null) { + return this.supplier.get(); + } + throw new IllegalStateException("No function defined"); + } + + private static Class getStartClass() { + ClassLoader classLoader = SpringFunctionInitializer.class.getClassLoader(); + if (System.getenv("MAIN_CLASS") != null) { + return ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader); + } + try { + Class result = getStartClass( + Collections.list(classLoader.getResources("META-INF/MANIFEST.MF"))); + if (result == null) { + result = getStartClass(Collections + .list(classLoader.getResources("meta-inf/manifest.mf"))); + } + logger.info("Main class: " + result); + return result; + } + catch (Exception ex) { + logger.error("Failed to find main class", ex); + return null; + } + } + + private static Class getStartClass(List list) { + logger.info("Searching manifests: " + list); + for (URL url : list) { + try { + logger.info("Searching manifest: " + url); + InputStream inputStream = url.openStream(); + try { + Manifest manifest = new Manifest(inputStream); + String startClass = manifest.getMainAttributes() + .getValue("Start-Class"); + if (startClass != null) { + return ClassUtils.forName(startClass, + SpringFunctionInitializer.class.getClassLoader()); + } + } + finally { + inputStream.close(); + } + } + catch (Exception ex) { + } + } + return null; + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java new file mode 100644 index 000000000..77065d3a8 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java @@ -0,0 +1,148 @@ +/* + * 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.adapter.aws; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.cloud.function.context.ContextFunctionCatalogAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.assertj.core.api.Assertions.assertThat; + +import reactor.core.publisher.Flux; + +/** + * @author Dave Syer + * + */ +public class SpringFunctionInitializerTests { + + private SpringFunctionInitializer initializer; + + @After + public void after() { + System.clearProperty("function.name"); + if (initializer!=null) { + initializer.close(); + } + } + + @Test + public void functionBean() { + initializer = new SpringFunctionInitializer( + FluxFunctionConfig.class); + initializer.initialize(); + Flux result = initializer.apply(Flux.just(new Foo())); + assertThat(result.blockFirst()).isInstanceOf(Bar.class); + } + + @Test + public void functionCatalog() { + initializer = new SpringFunctionInitializer( + FunctionConfig.class); + initializer.initialize(); + Flux result = initializer.apply(Flux.just(new Foo())); + assertThat(result.blockFirst()).isInstanceOf(Bar.class); + } + + @Test + public void namedFunctionCatalog() { + initializer = new SpringFunctionInitializer( + NamedFunctionConfig.class); + System.setProperty("function.name", "other"); + initializer.initialize(); + Flux result = initializer.apply(Flux.just(new Foo())); + assertThat(result.blockFirst()).isInstanceOf(Bar.class); + } + + @Test + public void consumerCatalog() { + initializer = new SpringFunctionInitializer( + ConsumerConfig.class); + initializer.initialize(); + Flux result = initializer.apply(Flux.just(new Foo())); + assertThat(result.toStream().collect(Collectors.toList())).isEmpty(); + } + + @Test + public void supplierCatalog() { + initializer = new SpringFunctionInitializer( + SupplierConfig.class); + initializer.initialize(); + Flux result = initializer.apply(Flux.empty()); + assertThat(result.blockFirst()).isInstanceOf(Bar.class); + } + + @Configuration + protected static class FluxFunctionConfig { + @Bean + public Function, Flux> function() { + return flux -> flux.map(foo -> new Bar()); + } + } + + @Configuration + @Import(ContextFunctionCatalogAutoConfiguration.class) + protected static class FunctionConfig { + @Bean + public Function function() { + return foo -> new Bar(); + } + } + + @Configuration + @Import(ContextFunctionCatalogAutoConfiguration.class) + protected static class NamedFunctionConfig { + @Bean + public Function other() { + return foo -> new Bar(); + } + } + + @Configuration + @Import(ContextFunctionCatalogAutoConfiguration.class) + protected static class SupplierConfig { + @Bean + public Supplier supplier() { + return () -> new Bar(); + } + } + + @Configuration + @Import(ContextFunctionCatalogAutoConfiguration.class) + protected static class ConsumerConfig { + @Bean + public Consumer consumer() { + return foo -> { + }; + } + } + + protected static class Foo { + } + + protected static class Bar { + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/.jdk8 new file mode 100644 index 000000000..e69de29bb diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/pom.xml new file mode 100644 index 000000000..e916c244d --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/pom.xml @@ -0,0 +1,146 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-function-adapter-sample + 1.0.0.BUILD-SNAPSHOT + jar + + spring-cloud-function-adapter-sample + Spring Cloud Function Sample + + + org.springframework.boot + spring-boot-starter-parent + 1.5.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 1.0.3.BUILD-SNAPSHOT + 1.2.1 + 3.0.7.RELEASE + 1.0.0.BUILD-SNAPSHOT + example.Config + + + + + org.springframework.cloud + spring-cloud-function-adapter-aws + ${project.version} + + + org.springframework.cloud + spring-cloud-function-web + provided + + + com.amazonaws + aws-lambda-java-events + ${aws-lambda-events.version} + provided + + + com.amazonaws + aws-lambda-java-log4j + 1.0.0 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Dalston.BUILD-SNAPSHOT + pom + import + + + org.springframework.cloud + spring-cloud-function-web + ${spring-cloud-function.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.springframework.boot.experimental + spring-boot-thin-layout + ${wrapper.version} + + + + + org.apache.maven.plugins + maven-shade-plugin + + false + true + aws + + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Config.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Config.java new file mode 100644 index 000000000..152e13868 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Config.java @@ -0,0 +1,97 @@ +/* + * 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 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.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableConfigurationProperties(Properties.class) +public class Config { + + private Properties props; + + @Autowired + public Config(Properties props) { + this.props = props; + } + + @Bean + public Function function() { + return value -> new Bar(value.uppercase() + + (props.getFoo() != null ? "-" + props.getFoo() : "")); + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(Config.class, args); + } + +} + +class Foo { + + private String value; + + Foo() { + } + + public String lowercase() { + return value.toLowerCase(); + } + + public Foo(String value) { + this.value = value; + } + + public String uppercase() { + return value.toUpperCase(); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} + +class Bar { + + private String value; + + Bar() { + } + + public Bar(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java new file mode 100644 index 000000000..27d12f183 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java @@ -0,0 +1,27 @@ +/* + * 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 example; + +import org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler; + +/** + * @author Dave Syer + * + */ +public class Handler extends SpringBootRequestHandler { + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java new file mode 100644 index 000000000..c58b0850e --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java @@ -0,0 +1,33 @@ +/* + * 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 example; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("app") +public class Properties { + + public String foo; + + public String getFoo() { + return foo; + } + + public void setFoo(String foo) { + this.foo = foo; + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties new file mode 100644 index 000000000..cd3c738e9 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties @@ -0,0 +1 @@ +dependencies.spring-cloud-function-web: org.springframework.cloud:spring-cloud-function-web \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java new file mode 100644 index 000000000..aec63a84e --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java @@ -0,0 +1,44 @@ +/* + * 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 example; + +import org.junit.Test; + +import org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Dave Syer + * + */ +public class MapTests { + + @Test + public void test() { + Bar result = new Config(new Properties()).function().apply(new Foo("foo")); + assertThat(result.getValue()).isEqualTo("FOO"); + } + + @Test + public void start() throws Exception { + SpringBootRequestHandler handler = new SpringBootRequestHandler<>(Config.class); + handler.handleRequest(new Foo("foo"), null); + handler.close(); + } + +}