Skip to content

Commit 494f60b

Browse files
committed
Add support for native web workloads for AWS
Fix Azure web support after refactoring classes
1 parent bd15562 commit 494f60b

File tree

26 files changed

+385
-270
lines changed

26 files changed

+385
-270
lines changed

spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.cloud.function.json.JacksonMapper;
2222
import org.springframework.cloud.function.json.JsonMapper;
2323
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
2425
import org.springframework.context.support.GenericApplicationContext;
2526
import org.springframework.util.CollectionUtils;
2627

@@ -31,7 +32,8 @@
3132
* @since 3.2
3233
*
3334
*/
34-
public class AWSCompanionAutoConfiguration {
35+
@Configuration
36+
public class AWSCompanionAutoConfiguration {
3537

3638
@Bean
3739
public AWSTypesMessageConverter awsTypesMessageConverter(GenericApplicationContext applicationContext) {

spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -495,44 +495,44 @@ public class FunctionInvokerTests {
495495
" \"isBase64Encoded\": false\n" +
496496
"}";
497497

498-
String s3Event = "{\n" +
499-
" \"Records\":[\n" +
500-
" {\n" +
501-
" \"eventVersion\":\"2.1\",\n" +
502-
" \"eventSource\":\"aws:s3\",\n" +
503-
" \"awsRegion\":\"us-east-2\",\n" +
504-
" \"eventTime\":\"2020-07-15T21:29:41.365Z\",\n" +
505-
" \"eventName\":\"ObjectCreated:Put\",\n" +
506-
" \"userIdentity\":{\n" +
507-
" \"principalId\":\"AWS:AIxxx\"\n" +
508-
" },\n" +
509-
" \"requestParameters\":{\n" +
510-
" \"sourceIPAddress\":\"xxxx\"\n" +
511-
" },\n" +
512-
" \"responseElements\":{\n" +
513-
" \"x-amz-request-id\":\"xxxx\",\n" +
514-
" \"x-amz-id-2\":\"xxx/=\"\n" +
515-
" },\n" +
516-
" \"s3\":{\n" +
517-
" \"s3SchemaVersion\":\"1.0\",\n" +
518-
" \"configurationId\":\"New Data Delivery\",\n" +
519-
" \"bucket\":{\n" +
520-
" \"name\":\"bucket\",\n" +
521-
" \"ownerIdentity\":{\n" +
522-
" \"principalId\":\"xxx\"\n" +
523-
" },\n" +
524-
" \"arn\":\"arn:aws:s3:::bucket\"\n" +
525-
" },\n" +
526-
" \"object\":{\n" +
527-
" \"key\":\"test/file.geojson\",\n" +
528-
" \"size\":32711,\n" +
529-
" \"eTag\":\"aaaa\",\n" +
530-
" \"sequencer\":\"aaaa\"\n" +
531-
" }\n" +
532-
" }\n" +
533-
" }\n" +
534-
" ]\n" +
535-
"}";
498+
String s3Event = "{\n"
499+
+ " \"Records\": [\n"
500+
+ " {\n"
501+
+ " \"eventVersion\": \"2.1\",\n"
502+
+ " \"eventSource\": \"aws:s3\",\n"
503+
+ " \"awsRegion\": \"eu-central-1\",\n"
504+
+ " \"eventTime\": \"2023-11-04T23:44:23.905Z\",\n"
505+
+ " \"eventName\": \"ObjectCreated:Put\",\n"
506+
+ " \"userIdentity\": {\n"
507+
+ " \"principalId\": \"AWS:xxxxxxxxxxxxxxxxxxx\"\n"
508+
+ " },\n"
509+
+ " \"requestParameters\": {\n"
510+
+ " \"sourceIPAddress\": \"x.x.x.x\"\n"
511+
+ " },\n"
512+
+ " \"responseElements\": {\n"
513+
+ " \"x-amz-request-id\": \"xxxxxxxxxxxxxxxx\",\n"
514+
+ " \"x-amz-id-2\": \"xxxxxxxxxxxxxxxxxxxx\"\n"
515+
+ " },\n"
516+
+ " \"s3\": {\n"
517+
+ " \"s3SchemaVersion\": \"1.0\",\n"
518+
+ " \"configurationId\": \"xxxxxxxxxxxxxxxxxxxxxxxx\",\n"
519+
+ " \"bucket\": {\n"
520+
+ " \"name\": \"xxxxxxxxxxxxxxx\",\n"
521+
+ " \"ownerIdentity\": {\n"
522+
+ " \"principalId\": \"xxxxxxxxxxxxxxxxxx\"\n"
523+
+ " },\n"
524+
+ " \"arn\": \"arn:aws:s3:::xxxxxxxxxxxxxxxxx\"\n"
525+
+ " },\n"
526+
+ " \"object\": {\n"
527+
+ " \"key\": \"xxxxxxxxxxxxxxxx\",\n"
528+
+ " \"size\": 6064,\n"
529+
+ " \"eTag\": \"xxxxxxxxxxxxx\",\n"
530+
+ " \"sequencer\": \"xxxxxxxxxxxxxx\"\n"
531+
+ " }\n"
532+
+ " }\n"
533+
+ " }\n"
534+
+ " ]\n"
535+
+ "}";
536536

537537
String apiGatewayEventWithStructuredBody = "{\n" +
538538
" \"resource\": \"/uppercase2\",\n" +
@@ -1187,6 +1187,7 @@ public void handleRequest(InputStream input, OutputStream output, Context contex
11871187
@SuppressWarnings("rawtypes")
11881188
@Test
11891189
public void testApiGatewayV2Event() throws Exception {
1190+
System.out.println(this.apiGatewayV2Event);
11901191
System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName());
11911192
System.setProperty("spring.cloud.function.definition", "inputApiV2Event");
11921193
FunctionInvoker invoker = new FunctionInvoker();

spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,10 @@
5757
<groupId>com.fasterxml.jackson.core</groupId>
5858
<artifactId>jackson-databind</artifactId>
5959
</dependency>
60-
<dependency>
61-
<groupId>org.springframework</groupId>
62-
<artifactId>spring-webmvc</artifactId>
63-
</dependency>
6460
<dependency>
6561
<groupId>org.springframework.boot</groupId>
6662
<artifactId>spring-boot-starter-test</artifactId>
6763
<scope>test</scope>
6864
</dependency>
69-
<dependency>
70-
<groupId>org.springframework.boot</groupId>
71-
<artifactId>spring-boot-starter-web</artifactId>
72-
<scope>test</scope>
73-
</dependency>
7465
</dependencies>
7566
</project>

spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
import org.apache.commons.logging.Log;
3838
import org.apache.commons.logging.LogFactory;
3939

40-
import org.springframework.cloud.function.serverless.web.ProxyHttpServletRequest;
41-
import org.springframework.cloud.function.serverless.web.ProxyHttpServletResponse;
42-
import org.springframework.cloud.function.serverless.web.ProxyMvc;
40+
import org.springframework.cloud.function.serverless.web.ServerlessHttpServletRequest;
41+
import org.springframework.cloud.function.serverless.web.ServerlessHttpServletResponse;
42+
import org.springframework.cloud.function.serverless.web.ServerlessMVC;
4343
import org.springframework.cloud.function.utils.FunctionClassUtils;
4444
import org.springframework.util.CollectionUtils;
4545
import org.springframework.util.StringUtils;
@@ -58,10 +58,11 @@ public class AzureWebProxyInvoker implements FunctionInstanceInjector {
5858
private static final String AZURE_WEB_ADAPTER_ROUTE = AZURE_WEB_ADAPTER_NAME
5959
+ "/{e?}/{e2?}/{e3?}/{e4?}/{e5?}/{e6?}/{e7?}/{e8?}/{e9?}/{e10?}/{e11?}/{e12?}/{e13?}/{e14?}/{e15?}";
6060

61-
private ProxyMvc mvc;
61+
private ServerlessMVC mvc;
6262

6363
private ServletContext servletContext;
6464

65+
@SuppressWarnings("unchecked")
6566
@Override
6667
public <T> T getInstance(Class<T> functionClass) throws Exception {
6768
this.initialize();
@@ -77,7 +78,7 @@ private void initialize() throws ServletException {
7778
synchronized (AzureWebProxyInvoker.class.getName()) {
7879
if (mvc == null) {
7980
Class<?> startClass = FunctionClassUtils.getStartClass();
80-
this.mvc = ProxyMvc.INSTANCE(startClass);
81+
this.mvc = ServerlessMVC.INSTANCE(startClass);
8182
}
8283
}
8384
}
@@ -88,7 +89,7 @@ private HttpServletRequest prepareRequest(HttpRequestMessage<Optional<String>> r
8889

8990
String path = request.getUri().getPath().substring(pathOffset);
9091

91-
ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(servletContext,
92+
ServerlessHttpServletRequest httpRequest = new ServerlessHttpServletRequest(servletContext,
9293
request.getHttpMethod().toString(), path);
9394

9495

@@ -122,7 +123,7 @@ public HttpResponseMessage execute(
122123

123124
HttpServletRequest httpRequest = this.prepareRequest(request);
124125

125-
ProxyHttpServletResponse httpResponse = new ProxyHttpServletResponse();
126+
ServerlessHttpServletResponse httpResponse = new ServerlessHttpServletResponse();
126127
try {
127128
this.mvc.service(httpRequest, httpResponse);
128129

spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import jakarta.servlet.ServletRequest;
2525
import jakarta.servlet.ServletResponse;
2626

27+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2728
import org.springframework.context.annotation.Bean;
2829
import org.springframework.context.annotation.Configuration;
2930
import org.springframework.context.annotation.Import;
@@ -32,6 +33,7 @@
3233
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
3334
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
3435

36+
@EnableAutoConfiguration
3537
@Configuration
3638
@Import({ PetsController.class })
3739
public class PetStoreSpringAppConfig {

spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@
4848
<dependency>
4949
<groupId>org.springframework.boot</groupId>
5050
<artifactId>spring-boot-starter-web</artifactId>
51+
<exclusions>
52+
<exclusion>
53+
<groupId>org.springframework.boot</groupId>
54+
<artifactId>spring-boot-starter-tomcat</artifactId>
55+
</exclusion>
56+
</exclusions>
5157
<scope>test</scope>
5258
</dependency>
5359
</dependencies>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2024-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.function.serverless.web;
18+
19+
import org.springframework.aot.generate.GenerationContext;
20+
import org.springframework.aot.hint.MemberCategory;
21+
import org.springframework.aot.hint.RuntimeHints;
22+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
23+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
24+
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
25+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
26+
import org.springframework.http.HttpEntity;
27+
import org.springframework.http.ResponseEntity;
28+
29+
/**
30+
* Ensure that Function/Consumer input types are reflectively available.
31+
*
32+
* @author Oleg Zhurakousky
33+
*/
34+
public class AWSTypesProcessor implements BeanFactoryInitializationAotProcessor {
35+
36+
@Override
37+
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
38+
return new ReflectiveProcessorBeanFactoryInitializationAotContribution();
39+
}
40+
41+
private static final class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution {
42+
@Override
43+
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
44+
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
45+
// known static types
46+
runtimeHints.reflection().registerType(HttpEntity.class,
47+
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
48+
runtimeHints.reflection().registerType(ResponseEntity.class,
49+
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
50+
}
51+
52+
}
53+
}

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyErrorController.java

Lines changed: 0 additions & 81 deletions
This file was deleted.

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyAsyncContext.java renamed to spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAsyncContext.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
import org.springframework.web.util.WebUtils;
3737

3838
/**
39-
* Implementation of Async context for {@link ProxyMvc}.
39+
* Implementation of Async context for {@link ServerlessMVC}.
4040
*
4141
* @author Oleg Zhurakousky
4242
*/
43-
public class ProxyAsyncContext implements AsyncContext {
43+
public class ServerlessAsyncContext implements AsyncContext {
4444
private final HttpServletRequest request;
4545

4646
@Nullable
@@ -56,7 +56,7 @@ public class ProxyAsyncContext implements AsyncContext {
5656
private final List<Runnable> dispatchHandlers = new ArrayList<>();
5757

5858

59-
public ProxyAsyncContext(ServletRequest request, @Nullable ServletResponse response) {
59+
public ServerlessAsyncContext(ServletRequest request, @Nullable ServletResponse response) {
6060
this.request = (HttpServletRequest) request;
6161
this.response = (HttpServletResponse) response;
6262
}
@@ -87,7 +87,7 @@ public ServletResponse getResponse() {
8787

8888
@Override
8989
public boolean hasOriginalRequestAndResponse() {
90-
return (this.request instanceof ProxyHttpServletRequest && this.response instanceof ProxyHttpServletResponse);
90+
return (this.request instanceof ServerlessHttpServletRequest && this.response instanceof ServerlessHttpServletResponse);
9191
}
9292

9393
@Override
@@ -115,7 +115,7 @@ public String getDispatchedPath() {
115115

116116
@Override
117117
public void complete() {
118-
ProxyHttpServletRequest mockRequest = WebUtils.getNativeRequest(this.request, ProxyHttpServletRequest.class);
118+
ServerlessHttpServletRequest mockRequest = WebUtils.getNativeRequest(this.request, ServerlessHttpServletRequest.class);
119119
if (mockRequest != null) {
120120
mockRequest.setAsyncStarted(false);
121121
}

0 commit comments

Comments
 (0)