CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-amazonaws--aws-java-sdk-lambda

AWS Java SDK client for communicating with AWS Lambda Service for function management, invocation, and resource control

Pending
Overview
Eval results
Files

high-level-invocation.mddocs/

High-Level Invocation Utilities

Type-safe, annotation-driven Lambda function invocation through proxy interfaces that eliminate boilerplate code and provide compile-time safety for remote function calls.

Capabilities

LambdaInvokerFactory

Central factory class for creating type-safe proxy interfaces that enable seamless Lambda function invocation with automatic serialization and error handling.

Factory Creation and Configuration

/**
 * Factory for creating type-safe Lambda function invocation proxies
 * Enables annotation-driven function calls with automatic serialization
 * @see LambdaInvokerFactory.Builder for configuration options
 */
public class LambdaInvokerFactory {
    
    /**
     * Creates builder instance for configuring Lambda invoker factory
     * @return LambdaInvokerFactoryConfig.Builder for fluent configuration
     */
    public static LambdaInvokerFactoryConfig.Builder builder();
    
    /**
     * Creates type-safe proxy for Lambda function invocation interface
     * @param interfaceClass Interface class annotated with @LambdaFunction methods
     * @return Proxy implementation that handles Lambda invocation transparently
     * @throws LambdaSerializationException if interface cannot be proxied
     */
    public <T> T build(Class<T> interfaceClass);
}

/**
 * Configuration builder for Lambda invoker factory
 * Provides fluent API for setting up invocation behavior and serialization
 */
public static class LambdaInvokerFactoryConfig.Builder {
    
    /**
     * Sets Lambda client for function invocation
     * @param lambdaClient AWSLambda client instance (required)
     * @return Builder instance for method chaining
     */
    public Builder lambdaClient(AWSLambda lambdaClient);
    
    /**
     * Sets function name resolver for dynamic function name resolution
     * @param functionNameResolver Custom resolver implementation
     * @return Builder instance for method chaining
     */
    public Builder functionNameResolver(LambdaFunctionNameResolver functionNameResolver);
    
    /**
     * Sets object mapper for JSON serialization/deserialization
     * @param objectMapper Jackson ObjectMapper instance
     * @return Builder instance for method chaining
     */
    public Builder objectMapper(ObjectMapper objectMapper);
    
    /**
     * Sets log type for function invocation
     * @param logType LogType.None or LogType.Tail
     * @return Builder instance for method chaining
     */
    public Builder logType(LogType logType);
    
    /**
     * Builds configured Lambda invoker factory
     * @return Configured LambdaInvokerFactory instance
     * @throws IllegalArgumentException if required configuration is missing
     */
    public LambdaInvokerFactory build();
}

Function Name Resolution

Interface and implementations for resolving Lambda function names from interface methods.

LambdaFunctionNameResolver Interface

/**
 * Interface for resolving Lambda function names from method metadata
 * Enables custom function name resolution strategies beyond annotations
 */
public interface LambdaFunctionNameResolver {
    
    /**
     * Resolves Lambda function name for method invocation
     * @param method Method being invoked with potential @LambdaFunction annotation
     * @param lambdaFunction Annotation instance if present, null otherwise
     * @return Function name to invoke (name, ARN, or partial ARN)
     * @throws IllegalArgumentException if function name cannot be resolved
     */
    String getFunctionName(Method method, LambdaFunction lambdaFunction);
}

Default Function Name Resolver

/**
 * Default implementation of function name resolution
 * Uses @LambdaFunction annotation value or method name as fallback
 */
public class DefaultLambdaFunctionNameResolver implements LambdaFunctionNameResolver {
    
    /**
     * Resolves function name using annotation value or method name
     * @param method Method being invoked
     * @param lambdaFunction Annotation instance if present
     * @return Resolved function name
     */
    @Override
    public String getFunctionName(Method method, LambdaFunction lambdaFunction);
}

Annotations and Configuration

Annotation-based configuration for mapping interface methods to Lambda functions.

LambdaFunction Annotation

/**
 * Annotation for mapping interface methods to Lambda functions
 * Provides metadata for function invocation and behavior customization
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LambdaFunction {
    
    /**
     * Lambda function name, ARN, or partial ARN
     * If empty, uses method name or function name resolver
     * @return Function identifier for invocation
     */
    String functionName() default "";
    
    /**
     * Function invocation type
     * @return InvocationType.RequestResponse (synchronous) or InvocationType.Event (asynchronous)
     */
    InvocationType invocationType() default InvocationType.RequestResponse;
    
    /**
     * Log type for invocation response
     * @return LogType.None or LogType.Tail to include logs
     */
    LogType logType() default LogType.None;
    
    /**
     * Function version or alias qualifier
     * @return Version number, alias name, or empty for $LATEST
     */
    String qualifier() default "";
}

Exception Handling

Specialized exceptions for high-level invocation error scenarios.

LambdaFunctionException

/**
 * Wrapper exception for Lambda function execution errors
 * Provides access to function error details and metadata
 */
public class LambdaFunctionException extends Exception {
    
    /**
     * Creates exception with function error details
     * @param message Error message from function execution
     * @param handled Whether error was handled by function
     * @param type Error type classification
     */
    public LambdaFunctionException(String message, boolean handled, String type);
    
    /**
     * Creates exception with cause chain
     * @param message Error message
     * @param cause Underlying cause exception
     */
    public LambdaFunctionException(String message, Throwable cause);
    
    /**
     * Indicates if error was handled by function code
     * @return true if function caught and handled error, false for unhandled exceptions
     */
    public boolean isHandled();
    
    /**
     * Gets error type classification
     * @return Error type string (e.g., "User", "System")
     */
    public String getType();
}

LambdaSerializationException

/**
 * Exception for JSON serialization/deserialization errors
 * Thrown when request/response objects cannot be converted to/from JSON
 */
public class LambdaSerializationException extends RuntimeException {
    
    /**
     * Creates serialization exception with message and cause
     * @param message Descriptive error message
     * @param cause Underlying serialization error
     */
    public LambdaSerializationException(String message, Throwable cause);
}

Usage Examples

Basic Function Interface

// Define interface with Lambda function methods
public interface UserService {
    
    @LambdaFunction(functionName = "user-authentication")
    UserAuthResult authenticateUser(AuthRequest request) throws LambdaFunctionException;
    
    @LambdaFunction(functionName = "user-profile-service")
    UserProfile getUserProfile(String userId) throws LambdaFunctionException;
    
    @LambdaFunction(functionName = "user-preferences", 
                   invocationType = InvocationType.Event)
    void updatePreferencesAsync(String userId, Map<String, Object> preferences);
}

// Request/Response classes
public class AuthRequest {
    private String username;
    private String password;
    // getters and setters
}

public class UserAuthResult {
    private boolean authenticated;
    private String token;
    private long expiresAt;
    // getters and setters
}

public class UserProfile {
    private String userId;
    private String email;
    private String displayName;
    // getters and setters
}

Creating and Using Function Proxies

AWSLambda lambdaClient = AWSLambdaClientBuilder.defaultClient();

// Create factory with configuration
LambdaInvokerFactory invokerFactory = LambdaInvokerFactory.builder()
    .lambdaClient(lambdaClient)
    .logType(LogType.Tail)
    .build();

// Create type-safe proxy
UserService userService = invokerFactory.build(UserService.class);

// Use proxy like local interface
try {
    AuthRequest authRequest = new AuthRequest();
    authRequest.setUsername("john.doe");
    authRequest.setPassword("secret123");
    
    UserAuthResult result = userService.authenticateUser(authRequest);
    if (result.isAuthenticated()) {
        System.out.println("Authentication successful. Token: " + result.getToken());
        
        // Get user profile
        UserProfile profile = userService.getUserProfile("john.doe");
        System.out.println("Welcome, " + profile.getDisplayName());
        
        // Update preferences asynchronously
        Map<String, Object> preferences = new HashMap<>();
        preferences.put("theme", "dark");
        preferences.put("notifications", true);
        userService.updatePreferencesAsync("john.doe", preferences);
        
    } else {
        System.out.println("Authentication failed");
    }
    
} catch (LambdaFunctionException e) {
    System.err.println("Function error: " + e.getMessage());
    System.err.println("Error type: " + e.getType());
    System.err.println("Handled: " + e.isHandled());
}

Advanced Configuration with Custom Resolver

// Custom function name resolver for environment-based routing
public class EnvironmentFunctionNameResolver implements LambdaFunctionNameResolver {
    private final String environment;
    
    public EnvironmentFunctionNameResolver(String environment) {
        this.environment = environment;
    }
    
    @Override
    public String getFunctionName(Method method, LambdaFunction lambdaFunction) {
        String baseName = (lambdaFunction != null && !lambdaFunction.functionName().isEmpty()) 
            ? lambdaFunction.functionName() 
            : method.getName();
        
        return baseName + "-" + environment;
    }
}

// Configure factory with custom resolver
LambdaInvokerFactory factory = LambdaInvokerFactory.builder()
    .lambdaClient(lambdaClient)
    .functionNameResolver(new EnvironmentFunctionNameResolver("prod"))
    .objectMapper(createCustomObjectMapper())
    .logType(LogType.Tail)
    .build();

private ObjectMapper createCustomObjectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    return mapper;
}

Complex Service Interface

public interface OrderProcessingService {
    
    @LambdaFunction(functionName = "order-validation")
    ValidationResult validateOrder(Order order) throws LambdaFunctionException;
    
    @LambdaFunction(functionName = "payment-processing", 
                   qualifier = "PROD")
    PaymentResult processPayment(PaymentRequest payment) throws LambdaFunctionException;
    
    @LambdaFunction(functionName = "inventory-check")
    InventoryStatus checkInventory(List<String> productIds) throws LambdaFunctionException;
    
    @LambdaFunction(functionName = "order-fulfillment", 
                   invocationType = InvocationType.Event)
    void initiateFullfillment(String orderId, Address shippingAddress);
    
    @LambdaFunction(functionName = "notification-service", 
                   invocationType = InvocationType.Event,
                   logType = LogType.Tail)
    void sendOrderConfirmation(String orderId, String customerEmail);
}

// Usage in order processing workflow
public class OrderProcessor {
    private final OrderProcessingService service;
    
    public OrderProcessor(LambdaInvokerFactory factory) {
        this.service = factory.build(OrderProcessingService.class);
    }
    
    public void processOrder(Order order) {
        try {
            // Validate order synchronously
            ValidationResult validation = service.validateOrder(order);
            if (!validation.isValid()) {
                throw new OrderProcessingException("Order validation failed: " + 
                    validation.getErrors());
            }
            
            // Check inventory synchronously
            List<String> productIds = order.getItems().stream()
                .map(OrderItem::getProductId)
                .collect(Collectors.toList());
                
            InventoryStatus inventory = service.checkInventory(productIds);
            if (!inventory.isAvailable()) {
                throw new OrderProcessingException("Insufficient inventory");
            }
            
            // Process payment synchronously
            PaymentRequest paymentRequest = createPaymentRequest(order);
            PaymentResult payment = service.processPayment(paymentRequest);
            if (!payment.isSuccessful()) {
                throw new OrderProcessingException("Payment failed: " + 
                    payment.getErrorMessage());
            }
            
            // Initiate fulfillment asynchronously
            service.initiateFullfillment(order.getId(), order.getShippingAddress());
            
            // Send confirmation asynchronously
            service.sendOrderConfirmation(order.getId(), order.getCustomerEmail());
            
        } catch (LambdaFunctionException e) {
            handleFunctionError(e);
            throw new OrderProcessingException("Lambda function error", e);
        }
    }
    
    private void handleFunctionError(LambdaFunctionException e) {
        // Log error details
        System.err.println("Lambda function error: " + e.getMessage());
        System.err.println("Error type: " + e.getType());
        System.err.println("Was handled: " + e.isHandled());
        
        // Handle specific error types
        if ("User".equals(e.getType()) && e.isHandled()) {
            // Business logic error - safe to retry or handle gracefully
        } else {
            // System error - may need different handling
        }
    }
}

Error Handling and Retry Logic

public class ResilientLambdaService {
    private final OrderProcessingService service;
    private final int maxRetries = 3;
    
    public ResilientLambdaService(LambdaInvokerFactory factory) {
        this.service = factory.build(OrderProcessingService.class);
    }
    
    public ValidationResult validateOrderWithRetry(Order order) {
        int attempt = 0;
        while (attempt < maxRetries) {
            try {
                return service.validateOrder(order);
            } catch (LambdaFunctionException e) {
                attempt++;
                
                if (shouldRetry(e) && attempt < maxRetries) {
                    System.out.println("Retrying validation, attempt " + (attempt + 1));
                    try {
                        Thread.sleep(1000 * attempt); // Exponential backoff
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("Interrupted during retry", ie);
                    }
                } else {
                    throw new RuntimeException("Validation failed after " + attempt + " attempts", e);
                }
            }
        }
        throw new RuntimeException("Max retries exceeded");
    }
    
    private boolean shouldRetry(LambdaFunctionException e) {
        // Retry on system errors but not on business logic errors
        return !e.isHandled() || !"User".equals(e.getType());
    }
}

Testing Lambda Interfaces

// Mock implementation for testing
public class MockOrderProcessingService implements OrderProcessingService {
    
    @Override
    public ValidationResult validateOrder(Order order) {
        // Mock validation logic
        return new ValidationResult(true, Collections.emptyList());
    }
    
    @Override
    public PaymentResult processPayment(PaymentRequest payment) {
        // Mock payment processing
        return new PaymentResult(true, "txn_12345", null);
    }
    
    @Override
    public InventoryStatus checkInventory(List<String> productIds) {
        // Mock inventory check
        return new InventoryStatus(true, Collections.emptyMap());
    }
    
    @Override
    public void initiateFullfillment(String orderId, Address shippingAddress) {
        // Mock fulfillment - no-op for async methods
    }
    
    @Override
    public void sendOrderConfirmation(String orderId, String customerEmail) {
        // Mock notification - no-op for async methods
    }
}

// Unit test
@Test
public void testOrderProcessing() {
    OrderProcessor processor = new OrderProcessor(
        // Use mock factory that returns mock implementation
        createMockFactory()
    );
    
    Order testOrder = createTestOrder();
    processor.processOrder(testOrder);
    
    // Verify expected behavior
    // ...
}

Exception Handling

Common exceptions when using high-level invocation:

  • LambdaFunctionException: Function execution errors with details about error type and handling
  • LambdaSerializationException: JSON serialization/deserialization failures
  • IllegalArgumentException: Invalid interface class or missing required configuration
  • RuntimeException: Proxy creation failures or invocation errors

Best Practices

Interface Design

  • Keep interfaces focused and cohesive around related functionality
  • Use descriptive method names that clearly indicate the Lambda function purpose
  • Define clear input/output data models with proper validation
  • Separate synchronous and asynchronous operations logically

Configuration Management

  • Use environment-specific function name resolvers for deployment flexibility
  • Configure appropriate timeout values for synchronous invocations
  • Set up proper error handling and retry mechanisms
  • Use custom object mappers for specific serialization requirements

Error Handling

  • Distinguish between business logic errors and system errors
  • Implement appropriate retry strategies based on error types
  • Log sufficient error details for debugging and monitoring
  • Use circuit breaker patterns for resilient service communication

Performance Optimization

  • Use asynchronous invocations for non-blocking operations
  • Pool Lambda invoker factory instances to avoid repeated initialization
  • Monitor invocation performance and optimize function implementations
  • Consider batching for high-volume operations where appropriate

Install with Tessl CLI

npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-lambda

docs

account-settings.md

alias-version-management.md

client-management.md

concurrency-performance.md

event-source-mapping.md

function-invocation.md

function-management.md

function-url-management.md

high-level-invocation.md

index.md

layer-management.md

permissions-policies.md

runtime-management.md

security-code-signing.md

tagging.md

waiters-polling.md

tile.json