Skip to content

Commit 2dbaab7

Browse files
committed
Add reflective metadata for powertools-cloudformation. Add sam-graalvm infra example.
1 parent 0ac151c commit 2dbaab7

File tree

19 files changed

+889
-76
lines changed

19 files changed

+889
-76
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build-HelloWorldFunction:
2+
chmod +x target/hello-world
3+
cp target/hello-world $(ARTIFACTS_DIR) # (ARTIFACTS_DIR --> https://github.com/aws/aws-lambda-builders/blob/develop/aws_lambda_builders/workflows/custom_make/DESIGN.md#implementation)
4+
chmod +x src/main/config/bootstrap
5+
cp src/main/config/bootstrap $(ARTIFACTS_DIR)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Use the official AWS SAM base image for Java 21
2+
FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461
3+
4+
# Install GraalVM dependencies
5+
RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz
6+
RUN mv graalvm-jdk-21.* /usr/lib/graalvm
7+
8+
# Make native image and mvn available on CLI
9+
RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
10+
RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn
11+
12+
# Set GraalVM as default
13+
ENV JAVA_HOME=/usr/lib/graalvm
14+
ENV PATH=/usr/lib/graalvm/bin:$PATH
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Powertools for AWS Lambda (Java) - CloudFormation Custom Resource Example with SAM on GraalVM
2+
3+
This project contains an example of a Lambda function using the CloudFormation module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda-java/utilities/custom_resources/).
4+
5+
In this example you pass in a bucket name as a parameter and upon CloudFormation events a call is made to a lambda. That lambda attempts to create the bucket on CREATE events, create a new bucket if the name changes with an UPDATE event and delete the bucket upon DELETE events.
6+
7+
Have a look at [App.java](../../src/main/java/helloworld/App.java) for the full details.
8+
9+
## Build the sample application
10+
11+
> [!NOTE]
12+
> Building AWS Lambda packages on macOS (ARM64/Intel) for deployment on AWS Lambda (Linux x86_64 or ARM64) will result in incompatible binary dependencies that cause import errors at runtime.
13+
14+
Choose the appropriate build method based on your operating system:
15+
16+
### Build locally using Docker
17+
18+
Recommended for macOS and Windows users: Cross-compile using Docker to match target platform of Lambda:
19+
20+
```shell
21+
docker build --platform linux/amd64 . -t powertools-examples-cloudformation-sam-graalvm
22+
docker run --platform linux/amd64 -it -v `pwd`/../..:`pwd`/../.. -w `pwd`/../.. -v ~/.m2:/root/.m2 powertools-examples-cloudformation-sam-graalvm mvn clean -Pnative-image package -DskipTests
23+
sam build --use-container --build-image powertools-examples-cloudformation-sam-graalvm
24+
```
25+
26+
**Note**: The Docker run command mounts your local Maven cache (`~/.m2`) and builds the native binary with SNAPSHOT support, then SAM packages the pre-built binary.
27+
28+
### Build on native OS
29+
30+
For Linux users with GraalVM installed:
31+
32+
```shell
33+
export JAVA_HOME=<path to GraalVM>
34+
cd ../..
35+
mvn clean -Pnative-image package -DskipTests
36+
cd infra/sam-graalvm
37+
sam build
38+
```
39+
40+
## Deploy the sample application
41+
42+
```shell
43+
sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.3.0718
44+
```
45+
46+
This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../../README.md)
47+
48+
## Test the application
49+
50+
The CloudFormation custom resource will be triggered automatically during stack deployment. You can monitor the Lambda function execution in CloudWatch Logs to see the custom resource handling CREATE, UPDATE, and DELETE events for the S3 bucket.
51+
52+
Check out [App.java](../../src/main/java/helloworld/App.java) to see how it works!
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Description: >
4+
powertools-examples-cloudformation-graalvm
5+
6+
Sample SAM Template for powertools-examples-cloudformation with GraalVM native image
7+
8+
Globals:
9+
Function:
10+
Timeout: 20
11+
12+
Parameters:
13+
BucketNameParam:
14+
Type: String
15+
16+
Resources:
17+
HelloWorldCustomResource:
18+
Type: AWS::CloudFormation::CustomResource
19+
Properties:
20+
ServiceToken: !GetAtt HelloWorldFunction.Arn
21+
BucketName: !Ref BucketNameParam
22+
23+
HelloWorldFunction:
24+
Type: AWS::Serverless::Function
25+
Properties:
26+
CodeUri: ../../
27+
Handler: helloworld.App::handleRequest
28+
Runtime: provided.al2023
29+
Architectures:
30+
- x86_64
31+
MemorySize: 512
32+
Policies:
33+
- Statement:
34+
- Sid: bucketaccess1
35+
Effect: Allow
36+
Action:
37+
- s3:GetLifecycleConfiguration
38+
- s3:PutLifecycleConfiguration
39+
- s3:CreateBucket
40+
- s3:ListBucket
41+
- s3:DeleteBucket
42+
Resource: '*'
43+
Metadata:
44+
BuildMethod: makefile
45+
46+
Outputs:
47+
HelloWorldFunction:
48+
Description: "Hello World Lambda Function ARN"
49+
Value: !GetAtt HelloWorldFunction.Arn

examples/powertools-examples-cloudformation/pom.xml

Lines changed: 108 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
<version>${lambda.core.version}</version>
3838
</dependency>
3939
<dependency>
40-
<groupId>com.amazonaws</groupId>
41-
<artifactId>aws-lambda-java-events</artifactId>
42-
<version>${lambda.events.version}</version>
40+
<groupId>com.amazonaws</groupId>
41+
<artifactId>aws-lambda-java-events</artifactId>
42+
<version>${lambda.events.version}</version>
4343
</dependency>
4444
<dependency>
4545
<groupId>software.amazon.lambda</groupId>
@@ -73,82 +73,114 @@
7373
<dependency>
7474
<groupId>software.amazon.awssdk</groupId>
7575
<artifactId>apache-client</artifactId>
76-
<exclusions>
77-
<exclusion>
78-
<groupId>commons-logging</groupId>
79-
<artifactId>commons-logging</artifactId>
80-
</exclusion>
81-
</exclusions>
76+
</dependency>
77+
<dependency>
78+
<groupId>com.amazonaws</groupId>
79+
<artifactId>aws-lambda-java-runtime-interface-client</artifactId>
80+
<version>2.8.3</version>
8281
</dependency>
8382
</dependencies>
8483

8584
<build>
86-
<plugins>
87-
<plugin>
88-
<groupId>dev.aspectj</groupId>
89-
<artifactId>aspectj-maven-plugin</artifactId>
90-
<version>1.14.1</version>
91-
<configuration>
92-
<source>${maven.compiler.source}</source>
93-
<target>${maven.compiler.target}</target>
94-
<complianceLevel>${maven.compiler.target}</complianceLevel>
95-
<aspectLibraries>
96-
<aspectLibrary>
97-
<groupId>software.amazon.lambda</groupId>
98-
<artifactId>powertools-logging</artifactId>
99-
</aspectLibrary>
100-
</aspectLibraries>
101-
</configuration>
102-
<executions>
103-
<execution>
104-
<goals>
105-
<goal>compile</goal>
106-
</goals>
107-
</execution>
108-
</executions>
109-
<dependencies>
110-
<dependency>
111-
<groupId>org.aspectj</groupId>
112-
<artifactId>aspectjtools</artifactId>
113-
<version>${aspectj.version}</version>
114-
</dependency>
115-
</dependencies>
116-
</plugin>
117-
<plugin>
118-
<groupId>org.apache.maven.plugins</groupId>
119-
<artifactId>maven-shade-plugin</artifactId>
120-
<version>3.6.0</version>
121-
<executions>
122-
<execution>
123-
<phase>package</phase>
124-
<goals>
125-
<goal>shade</goal>
126-
</goals>
127-
<configuration>
128-
<createDependencyReducedPom>false</createDependencyReducedPom>
129-
<transformers>
130-
<transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/>
131-
</transformers>
132-
</configuration>
133-
</execution>
134-
</executions>
135-
<dependencies>
136-
<dependency>
137-
<groupId>org.apache.logging.log4j</groupId>
138-
<artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId>
139-
<version>0.2.0</version>
140-
</dependency>
141-
</dependencies>
142-
</plugin>
143-
<!-- Don't deploy the example -->
144-
<plugin>
145-
<groupId>org.apache.maven.plugins</groupId>
146-
<artifactId>maven-deploy-plugin</artifactId>
147-
<version>3.1.4</version>
148-
<configuration>
149-
<skip>true</skip>
150-
</configuration>
151-
</plugin>
152-
</plugins>
85+
<plugins>
86+
<plugin>
87+
<groupId>dev.aspectj</groupId>
88+
<artifactId>aspectj-maven-plugin</artifactId>
89+
<version>1.14.1</version>
90+
<configuration>
91+
<source>${maven.compiler.source}</source>
92+
<target>${maven.compiler.target}</target>
93+
<complianceLevel>${maven.compiler.target}</complianceLevel>
94+
<aspectLibraries>
95+
<aspectLibrary>
96+
<groupId>software.amazon.lambda</groupId>
97+
<artifactId>powertools-logging</artifactId>
98+
</aspectLibrary>
99+
</aspectLibraries>
100+
</configuration>
101+
<executions>
102+
<execution>
103+
<goals>
104+
<goal>compile</goal>
105+
</goals>
106+
</execution>
107+
</executions>
108+
<dependencies>
109+
<dependency>
110+
<groupId>org.aspectj</groupId>
111+
<artifactId>aspectjtools</artifactId>
112+
<version>${aspectj.version}</version>
113+
</dependency>
114+
</dependencies>
115+
</plugin>
116+
<plugin>
117+
<groupId>org.apache.maven.plugins</groupId>
118+
<artifactId>maven-shade-plugin</artifactId>
119+
<version>3.6.0</version>
120+
<executions>
121+
<execution>
122+
<phase>package</phase>
123+
<goals>
124+
<goal>shade</goal>
125+
</goals>
126+
<configuration>
127+
<createDependencyReducedPom>false</createDependencyReducedPom>
128+
<transformers>
129+
<transformer
130+
implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" />
131+
</transformers>
132+
</configuration>
133+
</execution>
134+
</executions>
135+
<dependencies>
136+
<dependency>
137+
<groupId>org.apache.logging.log4j</groupId>
138+
<artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId>
139+
<version>0.2.0</version>
140+
</dependency>
141+
</dependencies>
142+
</plugin>
143+
<!-- Don't deploy the example -->
144+
<plugin>
145+
<groupId>org.apache.maven.plugins</groupId>
146+
<artifactId>maven-deploy-plugin</artifactId>
147+
<version>3.1.4</version>
148+
<configuration>
149+
<skip>true</skip>
150+
</configuration>
151+
</plugin>
152+
</plugins>
153153
</build>
154+
<profiles>
155+
<profile>
156+
<id>native-image</id>
157+
<build>
158+
<plugins>
159+
<plugin>
160+
<groupId>org.graalvm.buildtools</groupId>
161+
<artifactId>native-maven-plugin</artifactId>
162+
<version>0.11.0</version>
163+
<extensions>true</extensions>
164+
<executions>
165+
<execution>
166+
<id>build-native</id>
167+
<goals>
168+
<goal>build</goal>
169+
</goals>
170+
<phase>package</phase>
171+
</execution>
172+
</executions>
173+
<configuration>
174+
<imageName>hello-world</imageName>
175+
<mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass>
176+
<buildArgs>
177+
<arg>--enable-url-protocols=http</arg>
178+
<arg>--add-opens java.base/java.util=ALL-UNNAMED</arg>
179+
</buildArgs>
180+
</configuration>
181+
</plugin>
182+
</plugins>
183+
</build>
184+
</profile>
185+
</profiles>
154186
</project>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
set -e
3+
4+
./hello-world $_HANDLER
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[
2+
{
3+
"name":"com.amazonaws.services.lambda.runtime.LambdaRuntime",
4+
"methods":[{"name":"<init>","parameterTypes":[] }],
5+
"fields":[{"name":"logger"}],
6+
"allPublicMethods":true
7+
},
8+
{
9+
"name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal",
10+
"methods":[{"name":"<init>","parameterTypes":[] }],
11+
"allPublicMethods":true
12+
}
13+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[
2+
{
3+
"name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent",
4+
"allDeclaredFields": true,
5+
"allDeclaredMethods": true,
6+
"allDeclaredConstructors": true
7+
},
8+
{
9+
"name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext",
10+
"allDeclaredFields": true,
11+
"allDeclaredMethods": true,
12+
"allDeclaredConstructors": true
13+
},
14+
{
15+
"name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity",
16+
"allDeclaredFields": true,
17+
"allDeclaredMethods": true,
18+
"allDeclaredConstructors": true
19+
},
20+
{
21+
"name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent",
22+
"allDeclaredFields": true,
23+
"allDeclaredMethods": true,
24+
"allDeclaredConstructors": true
25+
},
26+
{
27+
"name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent",
28+
"allDeclaredConstructors": true,
29+
"allPublicConstructors": true,
30+
"allDeclaredMethods": true,
31+
"allPublicMethods": true,
32+
"allDeclaredClasses": true,
33+
"allPublicClasses": true
34+
}
35+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[
2+
{
3+
"name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException",
4+
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }]
5+
},
6+
{
7+
"name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest",
8+
"fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}],
9+
"allPublicMethods":true
10+
}
11+
]

0 commit comments

Comments
 (0)