Skip to content

Commit c30b197

Browse files
committed
Address PR comments and add sample which also addresses most of the PR comments from the sample PR that I have closed
Update jacoco exclusions
1 parent fa819bc commit c30b197

File tree

33 files changed

+1302
-33
lines changed

33 files changed

+1302
-33
lines changed

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AsyncInitializationWrapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class AsyncInitializationWrapper extends InitializationWrapper {
4747
private static final boolean ASYNC_INIT_DISABLED = !INITIALIZATION_TYPE.equals(INITIALIZATION_TYPE_ON_DEMAND);
4848
private static final int INIT_GRACE_TIME_MS = Integer.parseInt(System.getenv().getOrDefault(
4949
INIT_GRACE_TIME_ENVIRONMENT_VARIABLE_NAME, Integer.toString(DEFAULT_INIT_GRACE_TIME_MS)));
50-
private static final int LAMBDA_MAX_INIT_TIME_MS = 10_000;
50+
public static final int LAMBDA_MAX_INIT_TIME_MS = 10_000;
5151

5252
private CountDownLatch initializationLatch;
5353
private final long actualStartTime;

aws-serverless-java-container-springboot3/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@
203203
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
204204
<excludes>
205205
<!-- Native AOT implementation is currently not covered (due to complexity of the test setup) -->
206-
<exclude>com/amazonaws/serverless/proxy/spring/AWSWebRuntimeEventLoop*</exclude>
207-
<exclude>com/amazonaws/serverless/proxy/spring/AWSTypesProcessor*</exclude>
206+
<exclude>com/amazonaws/serverless/proxy/spring/AwsSpringWebCustomRuntimeEventLoop*</exclude>
207+
<exclude>com/amazonaws/serverless/proxy/spring/AwsSpringAotTypesProcessor*</exclude>
208208
</excludes>
209209
</configuration>
210210
<executions>

aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AWSHttpUtils.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.springframework.util.MultiValueMapAdapter;
1515
import org.springframework.util.StringUtils;
1616

17+
import com.amazonaws.serverless.proxy.AsyncInitializationWrapper;
1718
import com.amazonaws.serverless.proxy.AwsHttpApiV2SecurityContextWriter;
1819
import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
1920
import com.amazonaws.serverless.proxy.RequestReader;
@@ -29,18 +30,21 @@
2930
import jakarta.servlet.ServletContext;
3031
import jakarta.servlet.http.HttpServletRequest;
3132

32-
public class AWSHttpUtils {
33+
class AWSHttpUtils {
3334

34-
private static Log logger = LogFactory.getLog(AWSWebRuntimeEventLoop.class);
35+
private static Log logger = LogFactory.getLog(AWSHttpUtils.class);
3536

37+
private AWSHttpUtils() {
38+
39+
}
3640

37-
public static AwsProxyResponse serviceAWS(String gatewayEvent, ServerlessMVC mvc, ObjectMapper mapper, AwsProxyHttpServletResponseWriter responseWriter) {
41+
public static AwsProxyResponse processRequest(String gatewayEvent, ServerlessMVC mvc, ObjectMapper mapper, AwsProxyHttpServletResponseWriter responseWriter) {
3842
HttpServletRequest request = AWSHttpUtils.generateHttpServletRequest(gatewayEvent, null, mvc.getServletContext(), mapper);
3943
CountDownLatch latch = new CountDownLatch(1);
4044
AwsHttpServletResponse response = new AwsHttpServletResponse(request, latch);
4145
try {
4246
mvc.service(request, response);
43-
latch.await(10, TimeUnit.SECONDS);
47+
latch.await(AsyncInitializationWrapper.LAMBDA_MAX_INIT_TIME_MS, TimeUnit.SECONDS);
4448
AwsProxyResponse awsResponse = responseWriter.writeResponse(response, null);
4549
return awsResponse;
4650
}

aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AWSWebRuntimeInitializer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public void initialize(GenericApplicationContext context) {
4545
}
4646

4747
if (context instanceof ServletWebServerApplicationContext && isCustomRuntime(environment)) {
48-
if (context.getBeanFactory().getBeanNamesForType(AWSWebRuntimeEventLoop.class, false, false).length == 0) {
49-
context.registerBean(StringUtils.uncapitalize(AWSWebRuntimeEventLoop.class.getSimpleName()),
50-
SmartLifecycle.class, () -> new AWSWebRuntimeEventLoop((ServletWebServerApplicationContext) context));
48+
if (context.getBeanFactory().getBeanNamesForType(AwsSpringWebCustomRuntimeEventLoop.class, false, false).length == 0) {
49+
context.registerBean(StringUtils.uncapitalize(AwsSpringWebCustomRuntimeEventLoop.class.getSimpleName()),
50+
SmartLifecycle.class, () -> new AwsSpringWebCustomRuntimeEventLoop((ServletWebServerApplicationContext) context));
5151
}
5252
}
5353
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.amazonaws.serverless.proxy.model.ApiGatewayRequestIdentity;
2929
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
3030
import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
31+
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
3132
import com.amazonaws.serverless.proxy.model.Headers;
3233
import com.amazonaws.serverless.proxy.model.MultiValuedTreeMap;
3334
import com.amazonaws.serverless.proxy.model.SingleValueHeaders;
@@ -40,7 +41,7 @@
4041
*
4142
* @author Oleg Zhurakousky
4243
*/
43-
public class AWSTypesProcessor implements BeanFactoryInitializationAotProcessor {
44+
public class AwsSpringAotTypesProcessor implements BeanFactoryInitializationAotProcessor {
4445

4546
@Override
4647
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
@@ -55,6 +56,8 @@ public void applyTo(GenerationContext generationContext, BeanFactoryInitializati
5556

5657
runtimeHints.reflection().registerType(AwsProxyRequest.class,
5758
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS, MemberCategory.DECLARED_CLASSES);
59+
runtimeHints.reflection().registerType(AwsProxyResponse.class,
60+
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS, MemberCategory.DECLARED_CLASSES);
5861
runtimeHints.reflection().registerType(SingleValueHeaders.class,
5962
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS, MemberCategory.DECLARED_CLASSES);
6063
runtimeHints.reflection().registerType(JsonToken.class,
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
* @author Mark Sailes
4949
*
5050
*/
51-
public final class AWSWebRuntimeEventLoop implements SmartLifecycle {
51+
public final class AwsSpringWebCustomRuntimeEventLoop implements SmartLifecycle {
5252

53-
private static Log logger = LogFactory.getLog(AWSWebRuntimeEventLoop.class);
53+
private static Log logger = LogFactory.getLog(AwsSpringWebCustomRuntimeEventLoop.class);
5454

5555
static final String LAMBDA_VERSION_DATE = "2018-06-01";
5656
private static final String LAMBDA_ERROR_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/error";
@@ -65,7 +65,7 @@ public final class AWSWebRuntimeEventLoop implements SmartLifecycle {
6565

6666
private ExecutorService executor = Executors.newSingleThreadExecutor();
6767

68-
public AWSWebRuntimeEventLoop(ServletWebServerApplicationContext applicationContext) {
68+
public AwsSpringWebCustomRuntimeEventLoop(ServletWebServerApplicationContext applicationContext) {
6969
this.applicationContext = applicationContext;
7070
}
7171

@@ -110,7 +110,7 @@ private void eventLoop(ServletWebServerApplicationContext context) {
110110
RequestEntity<Void> requestEntity = RequestEntity.get(URI.create(eventUri))
111111
.header("User-Agent", USER_AGENT_VALUE).build();
112112
RestTemplate rest = new RestTemplate();
113-
ObjectMapper mapper = context.getBean(ObjectMapper.class);
113+
ObjectMapper mapper = new ObjectMapper();//.getBean(ObjectMapper.class);
114114
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
115115
AwsProxyHttpServletResponseWriter responseWriter = new AwsProxyHttpServletResponseWriter();
116116

@@ -128,16 +128,23 @@ private void eventLoop(ServletWebServerApplicationContext context) {
128128
try {
129129
logger.debug("Submitting request to the user's web application");
130130

131-
AwsProxyResponse awsResponse = AWSHttpUtils.serviceAWS(incomingEvent.getBody(), mvc, mapper, responseWriter);
132-
131+
AwsProxyResponse awsResponse = AWSHttpUtils.processRequest(incomingEvent.getBody(), mvc, mapper, responseWriter);
133132
if (logger.isDebugEnabled()) {
134-
logger.debug("Received response - body:" + awsResponse.getBody() +
135-
"; status:" + awsResponse.getStatusCode() + "; headers:" + awsResponse.getHeaders());
133+
logger.debug("Received response - body: " + awsResponse.getBody() +
134+
"; status: " + awsResponse.getStatusCode() + "; headers: " + awsResponse.getHeaders());
136135
}
136+
137137
String invocationUrl = MessageFormat.format(LAMBDA_INVOCATION_URL_TEMPLATE, runtimeApi,
138138
LAMBDA_VERSION_DATE, requestId);
139-
ResponseEntity<Object> result = rest.exchange(RequestEntity.post(URI.create(invocationUrl))
140-
.header("User-Agent", USER_AGENT_VALUE).body(awsResponse, AwsProxyResponse.class), Object.class);
139+
140+
ResponseEntity<byte[]> result = rest.exchange(RequestEntity.post(URI.create(invocationUrl))
141+
.header("User-Agent", USER_AGENT_VALUE).body(awsResponse), byte[].class);
142+
if (logger.isDebugEnabled()) {
143+
logger.debug("Response sent: body: " + result.getBody() +
144+
"; status: " + result.getStatusCode() + "; headers: " + result.getHeaders());
145+
}
146+
System.out.println("==> status " + result.getStatusCode());
147+
System.out.println("==> body " + result.getBody());
141148
logger.debug("Response submitted back to the AWS Gateway");
142149
if (logger.isInfoEnabled()) {
143150
logger.info("Result POST status: " + result);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=com.amazonaws.serverless.proxy.spring.AWSTypesProcessor
1+
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=com.amazonaws.serverless.proxy.spring.AwsSpringAotTypesProcessor

aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/AWSHttpUtilsTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public void validateRequestResponse(String jsonEvent) throws Exception {
221221
try (ConfigurableApplicationContext context = SpringApplication.run(EmptyApplication.class);) {
222222
ServerlessMVC mvc = ServerlessMVC.INSTANCE((ServletWebServerApplicationContext) context);
223223
AwsProxyHttpServletResponseWriter responseWriter = new AwsProxyHttpServletResponseWriter();
224-
AwsProxyResponse awsResponse = AWSHttpUtils.serviceAWS(jsonEvent, mvc, mapper, responseWriter);
224+
AwsProxyResponse awsResponse = AWSHttpUtils.processRequest(jsonEvent, mvc, mapper, responseWriter);
225225
assertEquals("hello", awsResponse.getBody());
226226
assertEquals(200, awsResponse.getStatusCode());
227227
}

aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/AWSWebRuntimeTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ public void after() {
1919
@Test
2020
public void testWebRuntimeInitialization() throws Exception {
2121
try (ConfigurableApplicationContext context = SpringApplication.run(EmptyApplication.class);) {
22-
assertFalse(context.getBeansOfType(AWSWebRuntimeEventLoop.class).size() > 0);
22+
assertFalse(context.getBeansOfType(AwsSpringWebCustomRuntimeEventLoop.class).size() > 0);
2323
}
2424
System.setProperty("_HANDLER", "foo");
25-
AWSWebRuntimeEventLoop loop = null;
25+
AwsSpringWebCustomRuntimeEventLoop loop = null;
2626
try (ConfigurableApplicationContext context = SpringApplication.run(EmptyApplication.class);) {
2727
Thread.sleep(100);
28-
assertTrue(context.getBeansOfType(AWSWebRuntimeEventLoop.class).size() > 0);
29-
loop = context.getBean(AWSWebRuntimeEventLoop.class);
28+
assertTrue(context.getBeansOfType(AwsSpringWebCustomRuntimeEventLoop.class).size() > 0);
29+
loop = context.getBean(AwsSpringWebCustomRuntimeEventLoop.class);
3030
assertTrue(loop.isRunning());
3131
}
3232
assertFalse(loop.isRunning());

samples/springboot3/alt-pet-store/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@
3232
<dependency>
3333
<groupId>org.springframework.boot</groupId>
3434
<artifactId>spring-boot-starter</artifactId>
35-
<!-- <exclusions>-->
36-
<!-- <exclusion>-->
37-
<!-- <groupId>org.springframework.boot</groupId>-->
38-
<!-- <artifactId>spring-boot-starter-tomcat</artifactId>-->
39-
<!-- </exclusion>-->
40-
<!-- </exclusions>-->
4135
</dependency>
4236

4337
<dependency>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
HELP.md
2+
target/
3+
!.mvn/wrapper/maven-wrapper.jar
4+
!**/src/main/**/target/
5+
!**/src/test/**/target/
6+
7+
### STS ###
8+
.apt_generated
9+
.classpath
10+
.factorypath
11+
.project
12+
.settings
13+
.springBeans
14+
.sts4-cache
15+
16+
### IntelliJ IDEA ###
17+
.idea
18+
*.iws
19+
*.iml
20+
*.ipr
21+
22+
### NetBeans ###
23+
/nbproject/private/
24+
/nbbuild/
25+
/dist/
26+
/nbdist/
27+
/.nb-gradle/
28+
build/
29+
!**/src/main/**/build/
30+
!**/src/test/**/build/
31+
32+
### VS Code ###
33+
.vscode/
Binary file not shown.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
18+
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
FROM arm64v8/amazonlinux:2
2+
3+
RUN yum -y update \
4+
&& yum install -y tar unzip gzip bzip2-devel ed gcc gcc-c++ gcc-gfortran \
5+
less libcurl-devel openssl openssl-devel readline-devel xz-devel \
6+
zlib-devel glibc-static libcxx libcxx-devel llvm-toolset-7 zlib-static \
7+
&& rm -rf /var/cache/yum
8+
9+
ENV GRAAL_VERSION 21.0.2
10+
ENV GRAAL_FOLDERNAME graalvm-community-openjdk-${GRAAL_VERSION}
11+
ENV ARCHITECTURE aarch64
12+
RUN curl -4 -L https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${GRAAL_VERSION}/graalvm-community-jdk-${GRAAL_VERSION}_linux-${ARCHITECTURE}_bin.tar.gz | tar -xvz
13+
RUN mv graalvm-community-openjdk-${GRAAL_VERSION}* /usr/lib/graalvm
14+
RUN rm -rf $GRAAL_FOLDERNAME
15+
16+
# Graal maven plugin requires Maven 3.3.x
17+
ENV MVN_VERSION 3.6.3
18+
ENV MVN_FOLDERNAME apache-maven-${MVN_VERSION}
19+
ENV MVN_FILENAME apache-maven-${MVN_VERSION}-bin.tar.gz
20+
RUN curl -4 -L https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/${MVN_VERSION}/${MVN_FILENAME} | tar -xvz
21+
RUN mv $MVN_FOLDERNAME /usr/lib/maven
22+
RUN rm -rf $MVN_FOLDERNAME
23+
24+
# Gradle
25+
ENV GRADLE_VERSION 7.4.1
26+
ENV GRADLE_FOLDERNAME gradle-${GRADLE_VERSION}
27+
ENV GRADLE_FILENAME gradle-${GRADLE_VERSION}-bin.zip
28+
RUN curl -LO https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip
29+
RUN unzip gradle-${GRADLE_VERSION}-bin.zip
30+
RUN mv $GRADLE_FOLDERNAME /usr/lib/gradle
31+
RUN rm -rf $GRADLE_FOLDERNAME
32+
33+
VOLUME /project
34+
WORKDIR /project
35+
36+
#RUN /usr/lib/graalvm/bin/gu install native-image
37+
#RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
38+
RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn
39+
RUN ln -s /usr/lib/gradle/bin/gradle /usr/bin/gradle
40+
41+
ENV JAVA_HOME /usr/lib/graalvm
42+
43+
WORKDIR /pet-store-native
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
In this sample, you'll build a native GraalVM image for running web workloads in AWS Lambda.
2+
3+
4+
## To build the sample on macOS (Apple silicon arm64)
5+
6+
You first need to build the function, then you will deploy it to AWS Lambda.
7+
8+
### Step 1 - Build the native image
9+
10+
Before starting the build, you must clone or download the code in **function-sample-aws-native**.
11+
12+
1. Change into the project directory: `samples/springboot3/function-sample-aws-serverless-web-native`
13+
2. Run the following to build a Docker container image which will be used to create the Lambda function zip file.
14+
```
15+
docker build -t "al2-graalvm21:native-web" .
16+
```
17+
3. Start the container
18+
```
19+
docker run -dit -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 al2-graalvm21:native-web
20+
```
21+
4. In Docker, open the image terminal.
22+
23+
> Your working directory should default to the project root. Verify by running `ls` to view the files.
24+
25+
6. From inside the container, build the Lambda function:
26+
```
27+
./mvnw clean -Pnative native:compile -DskipTests
28+
```
29+
30+
After the build finishes, you need to deploy the function.
31+
You can do it manually or you can use SAM (AWS Serverless Application Model) with the included template.yaml file.
32+
If you chose SAM simply execute the following command.
33+
```
34+
sam deploy --guided
35+
```
36+
This will deploy your application and will attach an AWS API Gateway
37+
Once the deployment is finished you shouild see the following:
38+
```
39+
Key ServerlessWebNativeApi
40+
Description URL for application
41+
Value https://xxxxxxxx.execute-api.us-east-2.amazonaws.com/pets
42+
```
43+
44+
You can now simply execute GET on this URL and see the listing fo all pets.

0 commit comments

Comments
 (0)