Skip to content

Commit 26ef1fa

Browse files
committed
GH-1187 Fix AWS Context initialization for Custom Runtime
Updated sample to show that Context is not null Resolves #1187
1 parent a0405d3 commit 26ef1fa

File tree

5 files changed

+114
-9
lines changed

5 files changed

+114
-9
lines changed

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

+88-3
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@
2828
import java.util.concurrent.ExecutorService;
2929
import java.util.concurrent.Executors;
3030

31+
import com.amazonaws.services.lambda.runtime.ClientContext;
32+
import com.amazonaws.services.lambda.runtime.CognitoIdentity;
33+
import com.amazonaws.services.lambda.runtime.Context;
34+
import com.amazonaws.services.lambda.runtime.LambdaLogger;
35+
import com.amazonaws.services.lambda.runtime.LambdaRuntime;
3136
import org.apache.commons.logging.Log;
3237
import org.apache.commons.logging.LogFactory;
3338

34-
3539
import org.springframework.cloud.function.context.FunctionCatalog;
3640
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
3741
import org.springframework.cloud.function.context.config.RoutingFunction;
@@ -130,6 +134,8 @@ private void eventLoop(ConfigurableApplicationContext context) {
130134
logger.debug("Attempting to get new event");
131135
ResponseEntity<String> response = this.pollForData(rest, requestEntity);
132136

137+
Context clientContext = generateClientContext(response.getHeaders());
138+
133139
if (logger.isDebugEnabled()) {
134140
logger.debug("New Event received: " + response);
135141
}
@@ -140,9 +146,9 @@ private void eventLoop(ConfigurableApplicationContext context) {
140146
FunctionInvocationWrapper function = locateFunction(environment, functionCatalog, response.getHeaders());
141147

142148
ByteArrayInputStream is = new ByteArrayInputStream(response.getBody().getBytes(StandardCharsets.UTF_8));
143-
Message<?> requestMessage = AWSLambdaUtils.generateMessage(is, function.getInputType(), function.isSupplier(), mapper, null);
144-
149+
Message<?> requestMessage = AWSLambdaUtils.generateMessage(is, function.getInputType(), function.isSupplier(), mapper, clientContext);
145150
Object functionResponse = function.apply(requestMessage);
151+
146152
byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, functionResponse, mapper, function.getOutputType());
147153

148154
String invocationUrl = MessageFormat
@@ -157,12 +163,91 @@ private void eventLoop(ConfigurableApplicationContext context) {
157163
}
158164
}
159165
catch (Exception e) {
166+
e.printStackTrace();
160167
this.propagateAwsError(requestId, e, mapper, runtimeApi, rest);
161168
}
162169
}
163170
}
164171
}
165172

173+
private Context generateClientContext(HttpHeaders headers) {
174+
175+
Map<String, String> environment = System.getenv();
176+
177+
Context context = new Context() {
178+
179+
@Override
180+
public int getRemainingTimeInMillis() {
181+
long now = System.currentTimeMillis();
182+
if (!headers.containsKey("Lambda-Runtime-Deadline-Ms")) {
183+
return 0;
184+
}
185+
int delta = (int) (Long.parseLong(headers.getFirst("Lambda-Runtime-Deadline-Ms")) - now);
186+
return delta > 0 ? delta : 0;
187+
}
188+
189+
@Override
190+
public int getMemoryLimitInMB() {
191+
if (!environment.containsKey("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")) {
192+
return 128;
193+
}
194+
return Integer.parseInt(environment.getOrDefault("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128"));
195+
}
196+
197+
@Override
198+
public LambdaLogger getLogger() {
199+
return LambdaRuntime.getLogger();
200+
}
201+
202+
@Override
203+
public String getLogStreamName() {
204+
return environment.get("LOG_STREAM_NAME");
205+
}
206+
207+
@Override
208+
public String getLogGroupName() {
209+
return environment.get("LOG_GROUP_NAME");
210+
}
211+
212+
@Override
213+
public String getInvokedFunctionArn() {
214+
return headers.getFirst("Lambda-Runtime-Invoked-Function-Arn");
215+
}
216+
217+
@Override
218+
public CognitoIdentity getIdentity() {
219+
return null;
220+
}
221+
222+
@Override
223+
public String getFunctionVersion() {
224+
return environment.get("FUNCTION_VERSION");
225+
}
226+
227+
@Override
228+
public String getFunctionName() {
229+
return environment.get("FUNCTION_NAME");
230+
}
231+
232+
@Override
233+
public ClientContext getClientContext() {
234+
return null;
235+
}
236+
237+
@Override
238+
public String getAwsRequestId() {
239+
return headers.getFirst("Lambda-Runtime-Aws-Request-Id");
240+
}
241+
242+
public String toString() {
243+
return "FUNCTION NAME: " + getFunctionName() + ", FUNCTION VERSION: " + getFunctionVersion()
244+
+ ", FUNCTION ARN: " + getInvokedFunctionArn() + ", FUNCTION MEM LIMIT: " + getMemoryLimitInMB()
245+
+ ", FUNCTION DEADLINE: " + getRemainingTimeInMillis();
246+
}
247+
};
248+
return context;
249+
}
250+
166251
private void propagateAwsError(String requestId, Exception e, JsonMapper mapper, String runtimeApi, RestTemplate rest) {
167252
String errorMessage = e.getMessage();
168253
String errorType = e.getClass().getSimpleName();

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
3232
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
3333
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
34+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
35+
import org.springframework.boot.web.server.Ssl;
36+
import org.springframework.boot.web.server.Ssl.ServerNameSslBundle;
3437
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
3538
import org.springframework.cloud.function.context.config.FunctionContextUtils;
3639
import org.springframework.cloud.function.context.message.MessageUtils;
@@ -105,6 +108,15 @@ public void applyTo(GenerationContext generationContext, BeanFactoryInitializati
105108
// known static types
106109
runtimeHints.reflection().registerType(MessageUtils.MessageStructureWithCaseInsensitiveHeaderKeys.class,
107110
MemberCategory.INVOKE_PUBLIC_METHODS);
111+
112+
113+
// temporary due to bug in boot
114+
runtimeHints.reflection().registerType(ClientHttpRequestFactorySettings.class,
115+
MemberCategory.INVOKE_PUBLIC_METHODS);
116+
runtimeHints.reflection().registerType(Ssl.class,
117+
MemberCategory.INVOKE_PUBLIC_METHODS);
118+
runtimeHints.reflection().registerType(ServerNameSslBundle.class,
119+
MemberCategory.INVOKE_PUBLIC_METHODS);
108120
}
109121

110122
}

spring-cloud-function-samples/function-sample-aws-custom/pom.xml

+5-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@
4242
<groupId>org.springframework.boot</groupId>
4343
<artifactId>spring-boot-starter</artifactId>
4444
</dependency>
45-
45+
<dependency>
46+
<groupId>com.amazonaws</groupId>
47+
<artifactId>aws-lambda-java-core</artifactId>
48+
<version>1.1.0</version>
49+
</dependency>
4650
<dependency>
4751
<groupId>org.springframework.boot</groupId>
4852
<artifactId>spring-boot-starter-test</artifactId>

spring-cloud-function-samples/function-sample-aws-native/pom.xml

-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
<groupId>com.amazonaws</groupId>
4848
<artifactId>aws-lambda-java-core</artifactId>
4949
<version>1.1.0</version>
50-
<scope>provided</scope>
5150
</dependency>
5251

5352
<dependency>

spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeFunctionApplication.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
import org.apache.commons.logging.LogFactory;
88
import org.springframework.boot.SpringApplication;
99
import org.springframework.boot.autoconfigure.SpringBootApplication;
10+
import org.springframework.cloud.function.adapter.aws.AWSLambdaUtils;
1011
import org.springframework.context.annotation.Bean;
1112
// import org.springframework.cloud.function.context.DefaultMessageRoutingHandler;
1213
// import org.springframework.cloud.function.context.MessageRoutingCallback;
1314
// import org.springframework.messaging.Message;
15+
import org.springframework.messaging.Message;
16+
17+
import com.amazonaws.services.lambda.runtime.Context;
1418

1519
@SpringBootApplication
1620
public class NativeFunctionApplication {
@@ -33,10 +37,11 @@ public static void main(String[] args) {
3337
// }
3438

3539
@Bean
36-
public Function<String, String> uppercase() {
37-
return v -> {
38-
System.out.println("Uppercasing " + v);
39-
return v.toUpperCase(Locale.ROOT);
40+
public Function<Message<String>, String> uppercase() {
41+
return message -> {
42+
System.out.println("AWS Context: " + message.getHeaders().get(AWSLambdaUtils.AWS_CONTEXT));
43+
System.out.println("Uppercasing " + message.getPayload());
44+
return message.getPayload().toUpperCase(Locale.ROOT);
4045
};
4146
}
4247

0 commit comments

Comments
 (0)