Skip to content

Commit 7045ddd

Browse files
committed
feat: added spring cloud function sample
1 parent 8c2189a commit 7045ddd

11 files changed

Lines changed: 610 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Spring Cloud Function AWS Demo
2+
3+
This project demonstrates how to use Spring Cloud Function with AWS Lambda, showcasing function routing, and deployment to AWS Lambda using AWS SAM.
4+
5+
## Overview
6+
7+
Spring Cloud Function is a framework that promotes the implementation of business logic via functions. It abstracts away the underlying runtime platform, allowing the same code to run as a web endpoint, a stream processor, or a task. This project demonstrates:
8+
9+
- Function definition and routing
10+
- Custom message routing
11+
- Deployment to AWS Lambda
12+
- Integration with AWS services (API Gateway, SQS)
13+
14+
## Project Structure
15+
16+
- `SpringCloudFunctionDemo.java`: Main application class with function definitions
17+
- `Unicorn.java`: Simple data model class
18+
- `template.yaml`: AWS SAM template for deployment
19+
- `TestHttp.http`: HTTP request examples for local testing
20+
21+
## Functions Implemented
22+
23+
This demo includes several function implementations:
24+
25+
- `lowerCase`: Converts input string to lowercase
26+
- `upperCase`: Converts input string to uppercase
27+
- `reverse`: Reverses the input string
28+
- `helloUnicorn`: Processes a Unicorn object and returns a greeting
29+
- `asyncProcessor`: Consumes SQS events and logs the number of messages processed
30+
- `noOpFunction`: Default function when no proper routing is found
31+
32+
## Custom Routing
33+
34+
The application demonstrates custom routing using `MessageRoutingCallback`. The router inspects the `x-routing-key` header and routes to the appropriate function:
35+
36+
- `uppercase``upperCase` function
37+
- `lowercase``lowerCase` function
38+
- `reverse``reverse` function
39+
- `unicorn``helloUnicorn` function
40+
41+
## Local Development
42+
43+
### Prerequisites
44+
45+
- Java 21
46+
- Maven
47+
- AWS SAM CLI (for deployment)
48+
49+
### Building the Application
50+
51+
```bash
52+
mvn clean package
53+
```
54+
55+
### Running Locally
56+
57+
```bash
58+
mvn spring-boot:run
59+
```
60+
61+
Once running, you can test the functions using the provided `TestHttp.http` file or with curl:
62+
63+
```bash
64+
# Test lowercase function
65+
curl -X POST http://localhost:8080/lowerCase -H "Content-Type: text/plain" -d "HELLO WORLD"
66+
67+
# Test uppercase function
68+
curl -X POST http://localhost:8080/upperCase -H "Content-Type: text/plain" -d "hello world"
69+
70+
# Test custom routing
71+
curl -X POST http://localhost:8080/functionRouter -H "Content-Type: text/plain" -H "x-routing-key: uppercase" -d "hello world"
72+
```
73+
74+
## AWS Deployment
75+
76+
This project uses AWS SAM for deployment to AWS Lambda.
77+
78+
### Prerequisites
79+
80+
- AWS CLI configured with appropriate credentials
81+
- AWS SAM CLI installed
82+
83+
### Deployment Steps
84+
85+
1. Build the application:
86+
```bash
87+
mvn clean package
88+
```
89+
90+
2. Deploy using SAM:
91+
```bash
92+
sam deploy --guided
93+
```
94+
95+
3. Follow the prompts to complete the deployment.
96+
97+
### AWS Resources Created
98+
99+
The SAM template creates the following resources:
100+
101+
- **API Gateway**: Exposes the Spring Cloud Function as a REST API
102+
- **Lambda Functions**:
103+
- `SpringCloudFunction`: Handles API requests
104+
- `MessageProcessor`: Processes SQS messages
105+
- **SQS Queue**: For asynchronous message processing
106+
107+
## Testing in AWS
108+
109+
After deployment, you can test the API using the endpoint URL provided in the outputs:
110+
111+
```bash
112+
# Test the deployed API
113+
curl -X POST https://{api-id}.execute-api.{region}.amazonaws.com/Prod/uppercase/ -H "Content-Type: text/plain" -d "hello world"
114+
```
115+
116+
To test the SQS integration, send a message to the created queue:
117+
118+
```bash
119+
aws sqs send-message --queue-url {QueueURL} --message-body "Test message"
120+
```
121+
122+
## Function Composition
123+
124+
Spring Cloud Function supports function composition. For example:
125+
126+
```bash
127+
# Apply reverse then uppercase
128+
curl -X POST http://localhost:8080/reverse,upperCase -H "Content-Type: text/plain" -d "hello world"
129+
```
130+
131+
This will first reverse "hello world" to "dlrow olleh" and then convert it to uppercase: "DLROW OLLEH".
132+
133+
## Additional Resources
134+
135+
- [Spring Cloud Function Documentation](https://spring.io/projects/spring-cloud-function)
136+
- [AWS Lambda Java Runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-java.html)
137+
- [AWS Serverless Application Model (SAM)](https://aws.amazon.com/serverless/sam/)
138+
139+
## License
140+
141+
This project is licensed under the MIT License - see the LICENSE file for details.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
### Test lowerCase function
2+
POST http://localhost:8080/lowerCase
3+
Content-Type: text/plain
4+
5+
HELLO SPRING I/O
6+
7+
### Test upperCase function
8+
POST http://localhost:8080/upperCase
9+
Content-Type: text/plain
10+
11+
hello spring i/o
12+
13+
### Test reverse function
14+
POST http://localhost:8080/reverse
15+
Content-Type: text/plain
16+
17+
hello world
18+
19+
### Test helloUnicorn function
20+
POST http://localhost:8080/helloUnicorn
21+
Content-Type: application/json
22+
23+
{
24+
"name": "Sparkles",
25+
"age": 5
26+
}
27+
28+
### Test function composition (upperCase after reverse)
29+
POST http://localhost:8080/reverse,upperCase
30+
Content-Type: text/plain
31+
32+
hello world
33+
34+
35+
### Test routing to lowerCase function using custom router
36+
POST http://localhost:8080/functionRouter
37+
Content-Type: application/json
38+
x-routing-key: lowercase
39+
40+
HELLO WORLD
41+
42+
### Test routing to upperCase function using custom router
43+
POST http://localhost:8080/functionRouter
44+
Content-Type: application/json
45+
x-routing-key: uppercase
46+
47+
hello world
48+
49+
50+
### Test routing to upperCase function using custom router
51+
POST http://localhost:8080/functionRouter
52+
Content-Type: application/json
53+
54+
hello world
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>org.springframework.boot</groupId>
6+
<artifactId>spring-boot-starter-parent</artifactId>
7+
<version>3.4.5</version>
8+
</parent>
9+
<groupId>com.amazonaws.demo</groupId>
10+
<artifactId>spring-cloud-function-demo</artifactId>
11+
<version>1.0</version>
12+
<packaging>jar</packaging>
13+
<name>Example application to route to different Spring Cloud Functions</name>
14+
<properties>
15+
<maven.compiler.target>21</maven.compiler.target>
16+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
18+
<spring-cloud-function.version>4.2.2</spring-cloud-function.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.cloud</groupId>
28+
<artifactId>spring-cloud-function-context</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.cloud</groupId>
32+
<artifactId>spring-cloud-function-adapter-aws</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.cloud</groupId>
36+
<artifactId>spring-cloud-starter-function-web</artifactId>
37+
</dependency>
38+
<dependency>
39+
<groupId>com.amazonaws</groupId>
40+
<artifactId>aws-lambda-java-events</artifactId>
41+
<version>3.15.0</version>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.slf4j</groupId>
45+
<artifactId>slf4j-api</artifactId>
46+
<version>2.0.17</version>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.junit.jupiter</groupId>
50+
<artifactId>junit-jupiter</artifactId>
51+
<version>5.10.2</version>
52+
<scope>test</scope>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-starter-test</artifactId>
57+
<scope>test</scope>
58+
</dependency>
59+
</dependencies>
60+
61+
<dependencyManagement>
62+
<dependencies>
63+
<dependency>
64+
<groupId>org.springframework.cloud</groupId>
65+
<artifactId>spring-cloud-function-dependencies</artifactId>
66+
<version>${spring-cloud-function.version}</version>
67+
<type>pom</type>
68+
<scope>import</scope>
69+
</dependency>
70+
71+
</dependencies>
72+
</dependencyManagement>
73+
74+
<build>
75+
<plugins>
76+
<plugin>
77+
<groupId>org.apache.maven.plugins</groupId>
78+
<artifactId>maven-deploy-plugin</artifactId>
79+
<configuration>
80+
<skip>true</skip>
81+
</configuration>
82+
</plugin>
83+
<plugin>
84+
<groupId>org.springframework.boot</groupId>
85+
<artifactId>spring-boot-maven-plugin</artifactId>
86+
<dependencies>
87+
<dependency>
88+
<groupId>org.springframework.boot.experimental</groupId>
89+
<artifactId>spring-boot-thin-layout</artifactId>
90+
<version>1.0.31.RELEASE</version>
91+
</dependency>
92+
</dependencies>
93+
</plugin>
94+
<plugin>
95+
<groupId>org.apache.maven.plugins</groupId>
96+
<artifactId>maven-shade-plugin</artifactId>
97+
<version>3.6.0</version>
98+
<configuration>
99+
<createDependencyReducedPom>false</createDependencyReducedPom>
100+
<shadedArtifactAttached>true</shadedArtifactAttached>
101+
<shadedClassifierName>aws</shadedClassifierName>
102+
</configuration>
103+
</plugin>
104+
</plugins>
105+
</build>
106+
107+
</project>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.amazonaws.demo;
2+
3+
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
import org.springframework.cloud.function.context.MessageRoutingCallback;
7+
import org.springframework.cloud.function.context.config.RoutingFunction;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.messaging.Message;
10+
11+
import java.util.function.Consumer;
12+
import java.util.function.Function;
13+
14+
@SpringBootApplication
15+
public class SpringCloudFunctionDemo {
16+
17+
public static void main(String[] args) {
18+
SpringApplication.run(SpringCloudFunctionDemo.class, args);
19+
}
20+
21+
@Bean
22+
public Function<String, String> lowerCase() {return String::toLowerCase;}
23+
24+
@Bean
25+
public Function<String, String> upperCase(){
26+
return String::toUpperCase;
27+
}
28+
29+
@Bean
30+
public Function<String, String> reverse(){
31+
return value -> new StringBuilder(value).reverse().toString();
32+
}
33+
34+
@Bean
35+
public Function<com.amazonaws.demo.Unicorn, String> helloUnicorn(){
36+
return value -> "Hello %s! You are %d years old!".formatted(value.name(), value.age());
37+
}
38+
39+
@Bean
40+
public Consumer<SQSEvent> asyncProcessor(){
41+
return value -> System.out.printf("Processed %d messages!", value.getRecords().size());
42+
}
43+
44+
@Bean
45+
public Function<String, String> noOpFunction(){
46+
return value -> "No proper function found!";
47+
}
48+
49+
@Bean
50+
public MessageRoutingCallback customRouter() {
51+
return new MessageRoutingCallback() {
52+
@Override
53+
public String routingResult(Message<?> message) {
54+
System.out.println("Hello from MessageRoutingCallback");
55+
var routingKey = message.getHeaders().getOrDefault("x-routing-key", "").toString();
56+
return switch (routingKey) {
57+
case "uppercase" -> "upperCase";
58+
case "lowercase" -> "lowerCase";
59+
case "reverse" -> "reverse";
60+
case "unicorn" -> "helloUnicorn";
61+
default -> "noOpFunction";
62+
};
63+
}
64+
};
65+
}
66+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.amazonaws.demo;
2+
3+
public record Unicorn(String name, int age) { }
4+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Function configuration
2+
spring.cloud.function.routing.enabled=true
3+
4+
# Web configuration
5+
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
6+
7+
# Function URL mappings
8+
spring.cloud.function.web.path=/
9+
10+
11+
spring.application.name=agents

0 commit comments

Comments
 (0)