compile 'io.micronaut:micronaut-function-aws'
Micronaut AWS
Provides integration between Micronaut and Amazon Web Services (AWS)
Version: 1.1.0.M1
1 Introduction
This project provides various extensions to Micronaut for Amazon Web Services (AWS). The primary focus initially is on AWS Lambda, however other integrations may be included in this project in the future.
A base AWSClientConfiguration is provided which can be used as a base configuration class for any configuration that needs to configure an AWS SDK client.
2 AWS Lambda Support
Regular Micronaut functions created via mn create-function
can be deployed to Lambda directly. However if you need further integration with what AWS Lambda has to offer then this project includes a number of parent classes and utilities to simplify working with the native Lambda APIs.
2.1 Micronaut Request Handlers
The micronaut-function-aws
module includes two parent classes you can subclass to implement native AWS Lambda functionality. To get started first add the micronaut-function-aws
dependency to your build:
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-function-aws</artifactId>
</dependency>
You can then use either MicronautRequestHandler or MicronautRequestStreamHandler as your parent class to implement your Lambda handler. For example:
MicronautRequestHandler
package example;
import io.micronaut.context.env.Environment;
import javax.inject.Inject;
public class RoundHandler extends MicronautRequestHandler<Float, Integer> { (1)
@Inject
MathService mathService; (2)
@Inject
Environment env;
@Override
public Integer execute(Float input) {
return mathService.round(input); (3)
}
}
1 | The class extends MicronautRequestHandler and must have a default constructor. |
2 | You can use field injection to inject dependencies |
3 | The function implementation |
With MicronautRequestHandler it is expected that you supply generic types with the input and the output types.
If you wish to work with raw streams then subclass MicronautRequestStreamHandler instead.
You should then configure the class name as the Lambda handler when deploying to AWS. For example with SAM:
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Handler: example.RoundHandler::handleRequest
2.2 AWS API Proxy Support
An alternative approach to using Micronaut with AWS is to use Micronaut support for the AWS Serverless Java Container project.
In Micronaut 1.1 or above you can create an API proxy ready application with mn create-app myapp --features aws-api-proxy
|
In this arrangement you build your application as a regular REST application, for example:
package example;
import io.micronaut.http.annotation.*;
@Controller("/ping")
public class PingController {
@Get("/")
public String index() {
return "{\"pong\":true}";
}
}
You then need to add the micronaut-function-aws-api-proxy
dependency:
compile 'io.micronaut.aws:micronaut-function-aws-api-proxy'
<dependency>
<groupId>io.micronaut.aws</groupId>
<artifactId>micronaut-function-aws-api-proxy</artifactId>
</dependency>
You can then implement a RequestHandler
that handles the API proxy request:
package example;
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.model.*;
import com.amazonaws.services.lambda.runtime.*;
import io.micronaut.function.aws.proxy.MicronautLambdaContainerHandler;
import java.io.*;
public class StreamLambdaHandler implements RequestStreamHandler {
private static MicronautLambdaContainerHandler handler; (1)
static {
try {
handler = MicronautLambdaContainerHandler.getAwsProxyHandler();
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Micronaut", e);
}
}
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
handler.proxyStream(inputStream, outputStream, context); (2)
}
}
1 | Micronaut is initialized in the static initializer |
2 | The API Proxy Request is handled |
The MicronautLambdaContainerHandler class is used to initialization Micronaut and handle the serverless requests.
The getAwsProxyHandler()
method also accepts a ApplicationContextBuilder
instance which you can use to further customize application bootstrap.
See the Example Application and associated README for instructions on how to deploy locally using AWS SAM Local and AWS Cloud Formation for production. |
2.3 AWS Lambda Custom Runtimes
You may wish to implement a custom runtime, for example if you wish to use a different JVM than the one AWS provides.
The MicronautLambdaRuntime class provides an implementation that you can use to execute a custom runtime. You need to add the `` dependency to your build:
compile 'io.micronaut.aws:micronaut-function-aws-custom-runtime'
<dependency>
<groupId>io.micronaut.aws</groupId>
<artifactId>micronaut-function-aws-custom-runtime</artifactId>
</dependency>
Then create a bootstrap
bash script. The following is an example bootstrap
bootstrap
#!/bin/sh
set -euo pipefail
java -XX:TieredStopAtLevel=1 -noverify -cp server.jar io.micronaut.function.aws.runtime.MicronautLambdaRuntime
You then need to create a zip bundle including the bootstrap
and your application JAR filed (in the above case named server.jar
) for deployment to AWS and specify a runtime of provided
. For example:
aws lambda create-function --function-name my-function \
--zip-file fileb://function.zip --handler function.handler --runtime provided \
--role arn:aws:iam::123456789:role/lambda_basic_execution
See Publishing a Custom Runtime for more information. |
3 Alexa Skill Support
The micronaut-function-aws-alexa
module includes support for building Alexa Skills with Micronaut:
compile 'io.micronaut.aws:micronaut-function-aws-alexa'
<dependency>
<groupId>io.micronaut.aws</groupId>
<artifactId>micronaut-function-aws-alexa</artifactId>
</dependency>
When using Micronaut’s Alexa support you set your Lambda handler name to AlexaFunction class or a sublcass of this class if you plan to customize it.
The AlexaFunction will wire up your Alexa application and supports dependency injection of the following types:
-
com.amazon.ask.dispatcher.request.handler.RequestHandler
-
com.amazon.ask.dispatcher.request.interceptor.RequestInterceptor
-
com.amazon.ask.dispatcher.exception.ExceptionHandler
-
com.amazon.ask.builder.SkillBuilder
Simply declaring this types as beans will automatically configure the Alexa Skill appropriately.
You can find sample applications in Java, Kotlin and Groovy in the Examples directory of the repository. |
3.1 The IntentHandler Annotation
To simplify the programming model Micronaut AWS includes a @IntentHandler annotation that can be used on any bean method to make the method an intent handler.
The method must accept a single value of type com.amazon.ask.dispatcher.request.handler.HandlerInput and return a value of type Optional<com.amazon.ask.model.Response> otherwise a compilation error will occur.
|
A typical Alexa application written in Micronaut looks like:
import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.model.Response;
import io.micronaut.function.aws.alexa.Intents;
import io.micronaut.function.aws.alexa.annotation.IntentHandler;
import javax.inject.Singleton;
import java.util.Optional;
@Singleton (1)
public class AlexaApplication {
public static final String INTENT_NAME = "HelloWorldIntent";
private final MessageService messageService;
public AlexaApplication(MessageService messageService) { (2)
this.messageService = messageService;
}
@IntentHandler(INTENT_NAME) (3)
public Optional<Response> greet(HandlerInput input) { (4)
String speechText = messageService.sayHello();
return input.getResponseBuilder()
.withSpeech(speechText)
.withSimpleCard("HelloWorld", speechText)
.build();
}
import com.amazon.ask.dispatcher.request.handler.HandlerInput
import com.amazon.ask.model.Response
import io.micronaut.function.aws.alexa.Intents
import io.micronaut.function.aws.alexa.annotation.IntentHandler
import javax.inject.Singleton
@Singleton (1)
class AlexaApplication {
private final MessageService messageService
AlexaApplication(MessageService messageService) { (2)
this.messageService = messageService
}
@IntentHandler("HelloWorldIntent") (3)
Optional<Response> greet(HandlerInput input) { (4)
String speechText = messageService.sayHello()
return input.getResponseBuilder()
.withSpeech(speechText)
.withSimpleCard("HelloWorld", speechText)
.build()
}
import com.amazon.ask.dispatcher.request.handler.HandlerInput
import com.amazon.ask.model.Response
import io.micronaut.function.aws.alexa.Intents
import io.micronaut.function.aws.alexa.annotation.IntentHandler
import javax.inject.Singleton
import java.util.Optional
@Singleton (1)
class AlexaApplication(val messageService: MessageService) { (2)
companion object {
const val INTENT_NAME = "HelloWorldIntent"
}
@IntentHandler(INTENT_NAME) (3)
fun greet(input : HandlerInput) : Optional<Response> { (4)
val speechText = messageService.sayHello()
return input.responseBuilder
.withSpeech(speechText)
.withSimpleCard("HelloWorld", speechText)
.build()
}
1 | The javax.inject.Singleton annotation is used to define AlexApplication as a bean |
2 | Other services can be dependency injected into the constructor |
3 | The @IntentHandler is used to indicate which methods are intent handlers |
4 | The method receives a HandlerInput and returns a Response |