docs
0
# High-Level Invocation Utilities
1
2
Type-safe, annotation-driven Lambda function invocation through proxy interfaces that eliminate boilerplate code and provide compile-time safety for remote function calls.
3
4
## Capabilities
5
6
### LambdaInvokerFactory
7
8
Central factory class for creating type-safe proxy interfaces that enable seamless Lambda function invocation with automatic serialization and error handling.
9
10
#### Factory Creation and Configuration
11
12
```java { .api }
13
/**
14
* Factory for creating type-safe Lambda function invocation proxies
15
* Enables annotation-driven function calls with automatic serialization
16
* @see LambdaInvokerFactory.Builder for configuration options
17
*/
18
public class LambdaInvokerFactory {
19
20
/**
21
* Creates builder instance for configuring Lambda invoker factory
22
* @return LambdaInvokerFactoryConfig.Builder for fluent configuration
23
*/
24
public static LambdaInvokerFactoryConfig.Builder builder();
25
26
/**
27
* Creates type-safe proxy for Lambda function invocation interface
28
* @param interfaceClass Interface class annotated with @LambdaFunction methods
29
* @return Proxy implementation that handles Lambda invocation transparently
30
* @throws LambdaSerializationException if interface cannot be proxied
31
*/
32
public <T> T build(Class<T> interfaceClass);
33
}
34
35
/**
36
* Configuration builder for Lambda invoker factory
37
* Provides fluent API for setting up invocation behavior and serialization
38
*/
39
public static class LambdaInvokerFactoryConfig.Builder {
40
41
/**
42
* Sets Lambda client for function invocation
43
* @param lambdaClient AWSLambda client instance (required)
44
* @return Builder instance for method chaining
45
*/
46
public Builder lambdaClient(AWSLambda lambdaClient);
47
48
/**
49
* Sets function name resolver for dynamic function name resolution
50
* @param functionNameResolver Custom resolver implementation
51
* @return Builder instance for method chaining
52
*/
53
public Builder functionNameResolver(LambdaFunctionNameResolver functionNameResolver);
54
55
/**
56
* Sets object mapper for JSON serialization/deserialization
57
* @param objectMapper Jackson ObjectMapper instance
58
* @return Builder instance for method chaining
59
*/
60
public Builder objectMapper(ObjectMapper objectMapper);
61
62
/**
63
* Sets log type for function invocation
64
* @param logType LogType.None or LogType.Tail
65
* @return Builder instance for method chaining
66
*/
67
public Builder logType(LogType logType);
68
69
/**
70
* Builds configured Lambda invoker factory
71
* @return Configured LambdaInvokerFactory instance
72
* @throws IllegalArgumentException if required configuration is missing
73
*/
74
public LambdaInvokerFactory build();
75
}
76
```
77
78
### Function Name Resolution
79
80
Interface and implementations for resolving Lambda function names from interface methods.
81
82
#### LambdaFunctionNameResolver Interface
83
84
```java { .api }
85
/**
86
* Interface for resolving Lambda function names from method metadata
87
* Enables custom function name resolution strategies beyond annotations
88
*/
89
public interface LambdaFunctionNameResolver {
90
91
/**
92
* Resolves Lambda function name for method invocation
93
* @param method Method being invoked with potential @LambdaFunction annotation
94
* @param lambdaFunction Annotation instance if present, null otherwise
95
* @return Function name to invoke (name, ARN, or partial ARN)
96
* @throws IllegalArgumentException if function name cannot be resolved
97
*/
98
String getFunctionName(Method method, LambdaFunction lambdaFunction);
99
}
100
```
101
102
#### Default Function Name Resolver
103
104
```java { .api }
105
/**
106
* Default implementation of function name resolution
107
* Uses @LambdaFunction annotation value or method name as fallback
108
*/
109
public class DefaultLambdaFunctionNameResolver implements LambdaFunctionNameResolver {
110
111
/**
112
* Resolves function name using annotation value or method name
113
* @param method Method being invoked
114
* @param lambdaFunction Annotation instance if present
115
* @return Resolved function name
116
*/
117
@Override
118
public String getFunctionName(Method method, LambdaFunction lambdaFunction);
119
}
120
```
121
122
### Annotations and Configuration
123
124
Annotation-based configuration for mapping interface methods to Lambda functions.
125
126
#### LambdaFunction Annotation
127
128
```java { .api }
129
/**
130
* Annotation for mapping interface methods to Lambda functions
131
* Provides metadata for function invocation and behavior customization
132
*/
133
@Target(ElementType.METHOD)
134
@Retention(RetentionPolicy.RUNTIME)
135
public @interface LambdaFunction {
136
137
/**
138
* Lambda function name, ARN, or partial ARN
139
* If empty, uses method name or function name resolver
140
* @return Function identifier for invocation
141
*/
142
String functionName() default "";
143
144
/**
145
* Function invocation type
146
* @return InvocationType.RequestResponse (synchronous) or InvocationType.Event (asynchronous)
147
*/
148
InvocationType invocationType() default InvocationType.RequestResponse;
149
150
/**
151
* Log type for invocation response
152
* @return LogType.None or LogType.Tail to include logs
153
*/
154
LogType logType() default LogType.None;
155
156
/**
157
* Function version or alias qualifier
158
* @return Version number, alias name, or empty for $LATEST
159
*/
160
String qualifier() default "";
161
}
162
```
163
164
### Exception Handling
165
166
Specialized exceptions for high-level invocation error scenarios.
167
168
#### LambdaFunctionException
169
170
```java { .api }
171
/**
172
* Wrapper exception for Lambda function execution errors
173
* Provides access to function error details and metadata
174
*/
175
public class LambdaFunctionException extends Exception {
176
177
/**
178
* Creates exception with function error details
179
* @param message Error message from function execution
180
* @param handled Whether error was handled by function
181
* @param type Error type classification
182
*/
183
public LambdaFunctionException(String message, boolean handled, String type);
184
185
/**
186
* Creates exception with cause chain
187
* @param message Error message
188
* @param cause Underlying cause exception
189
*/
190
public LambdaFunctionException(String message, Throwable cause);
191
192
/**
193
* Indicates if error was handled by function code
194
* @return true if function caught and handled error, false for unhandled exceptions
195
*/
196
public boolean isHandled();
197
198
/**
199
* Gets error type classification
200
* @return Error type string (e.g., "User", "System")
201
*/
202
public String getType();
203
}
204
```
205
206
#### LambdaSerializationException
207
208
```java { .api }
209
/**
210
* Exception for JSON serialization/deserialization errors
211
* Thrown when request/response objects cannot be converted to/from JSON
212
*/
213
public class LambdaSerializationException extends RuntimeException {
214
215
/**
216
* Creates serialization exception with message and cause
217
* @param message Descriptive error message
218
* @param cause Underlying serialization error
219
*/
220
public LambdaSerializationException(String message, Throwable cause);
221
}
222
```
223
224
## Usage Examples
225
226
### Basic Function Interface
227
228
```java
229
// Define interface with Lambda function methods
230
public interface UserService {
231
232
@LambdaFunction(functionName = "user-authentication")
233
UserAuthResult authenticateUser(AuthRequest request) throws LambdaFunctionException;
234
235
@LambdaFunction(functionName = "user-profile-service")
236
UserProfile getUserProfile(String userId) throws LambdaFunctionException;
237
238
@LambdaFunction(functionName = "user-preferences",
239
invocationType = InvocationType.Event)
240
void updatePreferencesAsync(String userId, Map<String, Object> preferences);
241
}
242
243
// Request/Response classes
244
public class AuthRequest {
245
private String username;
246
private String password;
247
// getters and setters
248
}
249
250
public class UserAuthResult {
251
private boolean authenticated;
252
private String token;
253
private long expiresAt;
254
// getters and setters
255
}
256
257
public class UserProfile {
258
private String userId;
259
private String email;
260
private String displayName;
261
// getters and setters
262
}
263
```
264
265
### Creating and Using Function Proxies
266
267
```java
268
AWSLambda lambdaClient = AWSLambdaClientBuilder.defaultClient();
269
270
// Create factory with configuration
271
LambdaInvokerFactory invokerFactory = LambdaInvokerFactory.builder()
272
.lambdaClient(lambdaClient)
273
.logType(LogType.Tail)
274
.build();
275
276
// Create type-safe proxy
277
UserService userService = invokerFactory.build(UserService.class);
278
279
// Use proxy like local interface
280
try {
281
AuthRequest authRequest = new AuthRequest();
282
authRequest.setUsername("john.doe");
283
authRequest.setPassword("secret123");
284
285
UserAuthResult result = userService.authenticateUser(authRequest);
286
if (result.isAuthenticated()) {
287
System.out.println("Authentication successful. Token: " + result.getToken());
288
289
// Get user profile
290
UserProfile profile = userService.getUserProfile("john.doe");
291
System.out.println("Welcome, " + profile.getDisplayName());
292
293
// Update preferences asynchronously
294
Map<String, Object> preferences = new HashMap<>();
295
preferences.put("theme", "dark");
296
preferences.put("notifications", true);
297
userService.updatePreferencesAsync("john.doe", preferences);
298
299
} else {
300
System.out.println("Authentication failed");
301
}
302
303
} catch (LambdaFunctionException e) {
304
System.err.println("Function error: " + e.getMessage());
305
System.err.println("Error type: " + e.getType());
306
System.err.println("Handled: " + e.isHandled());
307
}
308
```
309
310
### Advanced Configuration with Custom Resolver
311
312
```java
313
// Custom function name resolver for environment-based routing
314
public class EnvironmentFunctionNameResolver implements LambdaFunctionNameResolver {
315
private final String environment;
316
317
public EnvironmentFunctionNameResolver(String environment) {
318
this.environment = environment;
319
}
320
321
@Override
322
public String getFunctionName(Method method, LambdaFunction lambdaFunction) {
323
String baseName = (lambdaFunction != null && !lambdaFunction.functionName().isEmpty())
324
? lambdaFunction.functionName()
325
: method.getName();
326
327
return baseName + "-" + environment;
328
}
329
}
330
331
// Configure factory with custom resolver
332
LambdaInvokerFactory factory = LambdaInvokerFactory.builder()
333
.lambdaClient(lambdaClient)
334
.functionNameResolver(new EnvironmentFunctionNameResolver("prod"))
335
.objectMapper(createCustomObjectMapper())
336
.logType(LogType.Tail)
337
.build();
338
339
private ObjectMapper createCustomObjectMapper() {
340
ObjectMapper mapper = new ObjectMapper();
341
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
342
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
343
return mapper;
344
}
345
```
346
347
### Complex Service Interface
348
349
```java
350
public interface OrderProcessingService {
351
352
@LambdaFunction(functionName = "order-validation")
353
ValidationResult validateOrder(Order order) throws LambdaFunctionException;
354
355
@LambdaFunction(functionName = "payment-processing",
356
qualifier = "PROD")
357
PaymentResult processPayment(PaymentRequest payment) throws LambdaFunctionException;
358
359
@LambdaFunction(functionName = "inventory-check")
360
InventoryStatus checkInventory(List<String> productIds) throws LambdaFunctionException;
361
362
@LambdaFunction(functionName = "order-fulfillment",
363
invocationType = InvocationType.Event)
364
void initiateFullfillment(String orderId, Address shippingAddress);
365
366
@LambdaFunction(functionName = "notification-service",
367
invocationType = InvocationType.Event,
368
logType = LogType.Tail)
369
void sendOrderConfirmation(String orderId, String customerEmail);
370
}
371
372
// Usage in order processing workflow
373
public class OrderProcessor {
374
private final OrderProcessingService service;
375
376
public OrderProcessor(LambdaInvokerFactory factory) {
377
this.service = factory.build(OrderProcessingService.class);
378
}
379
380
public void processOrder(Order order) {
381
try {
382
// Validate order synchronously
383
ValidationResult validation = service.validateOrder(order);
384
if (!validation.isValid()) {
385
throw new OrderProcessingException("Order validation failed: " +
386
validation.getErrors());
387
}
388
389
// Check inventory synchronously
390
List<String> productIds = order.getItems().stream()
391
.map(OrderItem::getProductId)
392
.collect(Collectors.toList());
393
394
InventoryStatus inventory = service.checkInventory(productIds);
395
if (!inventory.isAvailable()) {
396
throw new OrderProcessingException("Insufficient inventory");
397
}
398
399
// Process payment synchronously
400
PaymentRequest paymentRequest = createPaymentRequest(order);
401
PaymentResult payment = service.processPayment(paymentRequest);
402
if (!payment.isSuccessful()) {
403
throw new OrderProcessingException("Payment failed: " +
404
payment.getErrorMessage());
405
}
406
407
// Initiate fulfillment asynchronously
408
service.initiateFullfillment(order.getId(), order.getShippingAddress());
409
410
// Send confirmation asynchronously
411
service.sendOrderConfirmation(order.getId(), order.getCustomerEmail());
412
413
} catch (LambdaFunctionException e) {
414
handleFunctionError(e);
415
throw new OrderProcessingException("Lambda function error", e);
416
}
417
}
418
419
private void handleFunctionError(LambdaFunctionException e) {
420
// Log error details
421
System.err.println("Lambda function error: " + e.getMessage());
422
System.err.println("Error type: " + e.getType());
423
System.err.println("Was handled: " + e.isHandled());
424
425
// Handle specific error types
426
if ("User".equals(e.getType()) && e.isHandled()) {
427
// Business logic error - safe to retry or handle gracefully
428
} else {
429
// System error - may need different handling
430
}
431
}
432
}
433
```
434
435
### Error Handling and Retry Logic
436
437
```java
438
public class ResilientLambdaService {
439
private final OrderProcessingService service;
440
private final int maxRetries = 3;
441
442
public ResilientLambdaService(LambdaInvokerFactory factory) {
443
this.service = factory.build(OrderProcessingService.class);
444
}
445
446
public ValidationResult validateOrderWithRetry(Order order) {
447
int attempt = 0;
448
while (attempt < maxRetries) {
449
try {
450
return service.validateOrder(order);
451
} catch (LambdaFunctionException e) {
452
attempt++;
453
454
if (shouldRetry(e) && attempt < maxRetries) {
455
System.out.println("Retrying validation, attempt " + (attempt + 1));
456
try {
457
Thread.sleep(1000 * attempt); // Exponential backoff
458
} catch (InterruptedException ie) {
459
Thread.currentThread().interrupt();
460
throw new RuntimeException("Interrupted during retry", ie);
461
}
462
} else {
463
throw new RuntimeException("Validation failed after " + attempt + " attempts", e);
464
}
465
}
466
}
467
throw new RuntimeException("Max retries exceeded");
468
}
469
470
private boolean shouldRetry(LambdaFunctionException e) {
471
// Retry on system errors but not on business logic errors
472
return !e.isHandled() || !"User".equals(e.getType());
473
}
474
}
475
```
476
477
### Testing Lambda Interfaces
478
479
```java
480
// Mock implementation for testing
481
public class MockOrderProcessingService implements OrderProcessingService {
482
483
@Override
484
public ValidationResult validateOrder(Order order) {
485
// Mock validation logic
486
return new ValidationResult(true, Collections.emptyList());
487
}
488
489
@Override
490
public PaymentResult processPayment(PaymentRequest payment) {
491
// Mock payment processing
492
return new PaymentResult(true, "txn_12345", null);
493
}
494
495
@Override
496
public InventoryStatus checkInventory(List<String> productIds) {
497
// Mock inventory check
498
return new InventoryStatus(true, Collections.emptyMap());
499
}
500
501
@Override
502
public void initiateFullfillment(String orderId, Address shippingAddress) {
503
// Mock fulfillment - no-op for async methods
504
}
505
506
@Override
507
public void sendOrderConfirmation(String orderId, String customerEmail) {
508
// Mock notification - no-op for async methods
509
}
510
}
511
512
// Unit test
513
@Test
514
public void testOrderProcessing() {
515
OrderProcessor processor = new OrderProcessor(
516
// Use mock factory that returns mock implementation
517
createMockFactory()
518
);
519
520
Order testOrder = createTestOrder();
521
processor.processOrder(testOrder);
522
523
// Verify expected behavior
524
// ...
525
}
526
```
527
528
## Exception Handling
529
530
Common exceptions when using high-level invocation:
531
532
- **LambdaFunctionException**: Function execution errors with details about error type and handling
533
- **LambdaSerializationException**: JSON serialization/deserialization failures
534
- **IllegalArgumentException**: Invalid interface class or missing required configuration
535
- **RuntimeException**: Proxy creation failures or invocation errors
536
537
## Best Practices
538
539
### Interface Design
540
- Keep interfaces focused and cohesive around related functionality
541
- Use descriptive method names that clearly indicate the Lambda function purpose
542
- Define clear input/output data models with proper validation
543
- Separate synchronous and asynchronous operations logically
544
545
### Configuration Management
546
- Use environment-specific function name resolvers for deployment flexibility
547
- Configure appropriate timeout values for synchronous invocations
548
- Set up proper error handling and retry mechanisms
549
- Use custom object mappers for specific serialization requirements
550
551
### Error Handling
552
- Distinguish between business logic errors and system errors
553
- Implement appropriate retry strategies based on error types
554
- Log sufficient error details for debugging and monitoring
555
- Use circuit breaker patterns for resilient service communication
556
557
### Performance Optimization
558
- Use asynchronous invocations for non-blocking operations
559
- Pool Lambda invoker factory instances to avoid repeated initialization
560
- Monitor invocation performance and optimize function implementations
561
- Consider batching for high-volume operations where appropriate