From 9d0cf75bff69b84b2968e0dd1a653aeb2086f83f Mon Sep 17 00:00:00 2001 From: markfisher Date: Wed, 7 Jun 2017 09:49:52 -0400 Subject: [PATCH] initial commit for openwhisk support --- README.adoc | 3 +- spring-cloud-function-adapters/pom.xml | 3 +- .../.jdk8 | 0 .../README.md | 79 ++++++++++++ .../pom.xml | 115 ++++++++++++++++++ .../adapter/openwhisk/ActionApplication.java | 33 +++++ .../adapter/openwhisk/ActionController.java | 91 ++++++++++++++ .../adapter/openwhisk/ActionRequest.java | 90 ++++++++++++++ .../openwhisk/FunctionInitializer.java | 96 +++++++++++++++ .../adapter/openwhisk/FunctionProperties.java | 46 +++++++ .../adapter/openwhisk/InitRequest.java | 53 ++++++++ .../pom.xml | 15 +++ .../.jdk8 | 0 .../pom.xml | 0 .../src/main/java/example/Config.java | 0 .../src/main/java/example/Handler.java | 0 .../src/main/java/example/Properties.java | 0 .../main/resources/META-INF/thin.properties | 0 .../src/main/resources/log4j.properties | 0 .../src/test/java/example/MapTests.java | 0 20 files changed, 621 insertions(+), 3 deletions(-) rename spring-cloud-function-adapters/{spring-cloud-function-adapter-sample => spring-cloud-function-adapter-openwhisk}/.jdk8 (100%) create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/README.md create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/pom.xml create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionProperties.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-samples/pom.xml create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/.jdk8 rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/pom.xml (100%) rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/src/main/java/example/Config.java (100%) rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java (100%) rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java (100%) rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties (100%) rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/src/main/resources/log4j.properties (100%) rename spring-cloud-function-adapters/{ => spring-cloud-function-adapter-samples}/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java (100%) diff --git a/README.adoc b/README.adoc index 83e450fc8..065e623a2 100644 --- a/README.adoc +++ b/README.adoc @@ -41,8 +41,7 @@ then turning them into `@Beans` that can be wrapped as above. isolated classloader, so that you can pack them together in a single JVM. -4. TBD: adapters for AWS Lambda, and possibly other "serverless" -service providers. +4. Adapters for https://github.com/markfisher/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/markfisher/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] and possibly other "serverless" service providers. == Getting Started diff --git a/spring-cloud-function-adapters/pom.xml b/spring-cloud-function-adapters/pom.xml index 62644e781..04f254bc1 100644 --- a/spring-cloud-function-adapters/pom.xml +++ b/spring-cloud-function-adapters/pom.xml @@ -28,7 +28,8 @@ spring-cloud-function-adapter-aws - spring-cloud-function-adapter-sample + spring-cloud-function-adapter-openwhisk + spring-cloud-function-adapter-samples diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/.jdk8 similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/.jdk8 rename to spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/.jdk8 diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/README.md b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/README.md new file mode 100644 index 000000000..3f3cd5da3 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/README.md @@ -0,0 +1,79 @@ +Implement a POF: + +``` +package functions; + +import java.util.function.Function; + +public class Uppercase implements Function { + + public String apply(String input) { + return input.toUpperCase(); + } +} +``` + +Install it into your local Maven repository: + +``` +./mvnw clean install +``` + +Create a `function.properties` file that provides its Maven coordinates. For example: + +``` +dependencies.function: io.spring.sample:uppercase-function:0.0.1-SNAPSHOT +``` + +Copy the openwhisk runner JAR to the working directory (same directory as the properties file): + +``` +cp spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/target/spring-cloud-function-adapter-openwhisk-1.0.0.BUILD-SNAPSHOT.jar runner.jar +``` + +Generate a m2 repo from the `--thin.dryrun` of the runner JAR with the above properties file: + +``` +java -jar runner.jar --thin.root=m2 --thin.name=function --thin.dryrun +``` + +Use the following Dockerfile: + +``` +FROM openjdk:8 + +COPY m2 /m2 +ADD runner.jar . +ADD function.properties . + +ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom -jar runner.jar --thin.root=/m2 --thin.name=function --function.name=uppercase" ] + +EXPOSE 8080 +``` + +Build the Docker image: + +``` +docker build -t [username/appname] . +``` + +Push the Docker image: + +``` +docker push [username/appname] +``` + +Use the OpenWhisk CLI (e.g. after `vagrant ssh`) to create the action: + +``` +wsk action create --docker example [username/appname] +``` + +Invoke the action: + +``` +wsk action invoke --result example --param payload foo +{ + "result": "FOO" +} +``` diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/pom.xml new file mode 100644 index 000000000..1da62b80f --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-function-adapter-openwhisk + 1.0.0.BUILD-SNAPSHOT + jar + + spring-cloud-function-adapter-openwhisk + Apache OpenWhisk Adapter for Spring Cloud Function + + + org.springframework.boot + spring-boot-starter-parent + 1.5.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 3.0.7.RELEASE + 1.0.4.BUILD-SNAPSHOT + + + + + org.springframework.cloud + spring-cloud-function-context + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-web + + + io.projectreactor + reactor-core + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Dalston.BUILD-SNAPSHOT + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.springframework.boot.experimental + spring-boot-thin-layout + ${wrapper.version} + + + + + + + + + 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-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.java new file mode 100644 index 000000000..6af423e43 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.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 org.springframework.cloud.function.adapter.openwhisk; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author Mark Fisher + */ +@SpringBootApplication +@EnableConfigurationProperties(FunctionProperties.class) +public class ActionApplication { + + public static void main(String[] args) { + SpringApplication.run(ActionApplication.class, args); + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java new file mode 100644 index 000000000..6da8b0c0a --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java @@ -0,0 +1,91 @@ +/* + * 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.openwhisk; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.function.context.FunctionScan; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import reactor.core.publisher.Flux; + +/** + * @author Mark Fisher + */ +@FunctionScan +@RestController +public class ActionController extends FunctionInitializer { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public ActionController() { + super(); + } + + @PostMapping("/init") + public void init(@RequestBody InitRequest request) { + initialize(); + } + + @PostMapping(value="/run", consumes="application/json", produces="application/json") + public Object run(@RequestBody ActionRequest request) { + Object input = convertEvent(request.getValue()); + Flux output = apply(extract(input)); + Object result = result(input, output); + try { + return "{\"result\":" + this.objectMapper.writeValueAsString(result) + "}"; + } + catch (JsonProcessingException e) { + throw new IllegalStateException("failed to write JSON response", e); + } + } + + private Object result(Object input, Flux output) { + List result = new ArrayList<>(); + for (Object value : output.toIterable()) { + result.add(value); + } + if (isSingleValue(input) && result.size()==1) { + return result.get(0); + } + return result; + } + + private boolean isSingleValue(Object input) { + return !(input instanceof Collection); + } + + private Flux extract(Object input) { + if (input instanceof Collection) { + return Flux.fromIterable((Iterable) input); + } + return Flux.just(input); + } + + protected Object convertEvent(Map event) { + // just expecting "payload" for now + return event.get("payload"); + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java new file mode 100644 index 000000000..635cce709 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java @@ -0,0 +1,90 @@ +/* + * 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.openwhisk; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author Mark Fisher + */ +public class ActionRequest { + + @JsonProperty("action_name") + private String actionName; + + @JsonProperty("activation_id") + private String activationId; + + @JsonProperty("api_key") + private String apiKey; + + private String deadline; + + private String namespace; + + private Map value; + + public String getActionName() { + return actionName; + } + + public void setActionName(String actionName) { + this.actionName = actionName; + } + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getDeadline() { + return deadline; + } + + public void setDeadline(String deadline) { + this.deadline = deadline; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public Map getValue() { + return value; + } + + public void setValue(Map value) { + this.value = value; + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java new file mode 100644 index 000000000..8e6fd09b8 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java @@ -0,0 +1,96 @@ +/* + * 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.openwhisk; + +import java.util.concurrent.atomic.AtomicBoolean; +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.cloud.function.context.FunctionInspector; +import org.springframework.cloud.function.registry.FunctionCatalog; +import org.springframework.cloud.function.support.FluxFunction; +import org.springframework.cloud.function.support.FunctionUtils; + +import reactor.core.publisher.Flux; + +/** + * @author Dave Syer + * @author Mark Fisher + */ +public class FunctionInitializer { + + private Function, Flux> function; + + private Consumer> consumer; + + private Supplier> supplier; + + private AtomicBoolean initialized = new AtomicBoolean(); + + @Autowired(required = false) + private FunctionInspector inspector; + + @Autowired + private FunctionCatalog catalog; + + @Autowired + private FunctionProperties properties; + + @SuppressWarnings("unchecked") + protected void initialize() { + if (!this.initialized.compareAndSet(false, true)) { + return; + } + String name = this.properties.getName(); + String type = this.properties.getType(); + if ("function".equals(type)) { + this.function = this.catalog.lookupFunction(name); + if (this.function != null && !FunctionUtils.isFluxFunction(this.function)) { + this.function = new FluxFunction(this.function); + } + } + else if ("consumer".equals(type)) { + this.consumer = this.catalog.lookupConsumer(name); + } + else if ("supplier".equals(type)) { + this.supplier = this.catalog.lookupSupplier(name); + } + } + + protected Class getInputType() { + if (inspector != null) { + return inspector.getInputType(this.properties.getName()); + } + 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"); + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionProperties.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionProperties.java new file mode 100644 index 000000000..b3aeeef17 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionProperties.java @@ -0,0 +1,46 @@ +/* + * 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.openwhisk; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author Mark Fisher + */ +@ConfigurationProperties(prefix = "function") +public class FunctionProperties { + + private String name = "function"; + + private String type = "function"; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java new file mode 100644 index 000000000..31f0af399 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java @@ -0,0 +1,53 @@ +/* + * 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.openwhisk; + +/** + * @author Mark Fisher + */ +public class InitRequest { + + private String name; + + private boolean binary; + + private String main; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isBinary() { + return binary; + } + + public void setBinary(boolean binary) { + this.binary = binary; + } + + public String getMain() { + return main; + } + + public void setMain(String main) { + this.main = main; + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-samples/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-samples/pom.xml new file mode 100644 index 000000000..1dd7acd30 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-samples/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-function-adapter-samples + 1.0.0.BUILD-SNAPSHOT + pom + + + spring-cloud-function-adapter-sample + + + diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-adapter-samples/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-samples/spring-cloud-function-adapter-sample/pom.xml similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/pom.xml rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/pom.xml 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-samples/spring-cloud-function-adapter-sample/src/main/java/example/Config.java similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Config.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/main/java/example/Config.java 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-samples/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/main/java/example/Handler.java 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-samples/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/main/java/example/Properties.java 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-samples/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/main/resources/META-INF/thin.properties diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/resources/log4j.properties b/spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/main/resources/log4j.properties similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/main/resources/log4j.properties rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/main/resources/log4j.properties 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-samples/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java similarity index 100% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-samples/spring-cloud-function-adapter-sample/src/test/java/example/MapTests.java