AWS Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.
AWS Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.
mn create-app beanstalk-app-java
cd beanstalk-app-java
mn create-controller env
@Controller("/env")
public class EnvController {
private final ApplicationContext applicationContext;
public EnvController(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Get("/")
public Set<String> index() {
return applicationContext.getEnvironment().getActiveNames();
}
}
# src/main/resources/application.yml
micronaut:
application:
name: beanstalk-app-java
server:
port: 5000
plugins {
id "fi.evident.beanstalk" version "0.2.1"
}
version = '0.1-SNAPSHOT'
beanstalk {
s3Endpoint = "s3-eu-west-3.amazonaws.com"
beanstalkEndpoint = "elasticbeanstalk.eu-west-3.amazonaws.com"
deployments {
greach {
file = tasks.shadowJar
application = 'beanstalk-app-java'
environment = 'beanstalk-app-java-jar'
}
}
}
mn create-app beanstalk-app-java
cd beanstalk-app-java
mn create-controller env
@Controller("/env")
public class EnvController {
private final ApplicationContext applicationContext;
public EnvController(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Get("/")
public Set<String> index() {
return applicationContext.getEnvironment().getActiveNames();
}
}
# src/main/eb/env.config
option_settings:
aws:elasticbeanstalk:application:environment:
PORT: 8080 # application port
# src/main/eb/Procfile
web: java -jar application.jar
plugins {
id "fi.evident.beanstalk" version "0.2.1"
}
version = '0.1-SNAPSHOT'
task beanstalkArchive(type: Zip, dependsOn: jar) {
from 'src/main/eb'
from tasks.shadowJar
}
shadowJar {
archiveName = 'application.jar'
}
beanstalk {
s3Endpoint = "s3-eu-west-3.amazonaws.com"
beanstalkEndpoint = "elasticbeanstalk.eu-west-3.amazonaws.com"
deployments {
greach {
file = tasks.beanstalkArchive
application = 'beanstalk-app-java'
environment = 'beanstalk-app-java-env'
}
}
}
AWS Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.
mn create-app beanstalk-app
cd beanstalk-app
mn create-controller env
@Controller("/env")
public class EnvController {
private final ApplicationContext applicationContext;
public EnvController(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Get("/")
public Set<String> index() {
return applicationContext.getEnvironment().getActiveNames();
}
}
FROM adoptopenjdk/openjdk11-openj9:jdk-11.0.1.13-alpine-slim
EXPOSE 8080
COPY build/libs/*.jar beanstalk-app.jar
CMD java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -noverify ${JAVA_OPTS} -jar beanstalk-app.jar
plugins {
id "com.bmuschko.docker-remote-api" version "4.4.0"
id "com.patdouble.awsecr" version "0.5.1"
}
String registryHost = '1234567890.dkr.ecr.eu-west-3.amazonaws.com'
docker {
registryCredentials {
url = "https://$registryHost/"
}
}
task buildImage(type: com.bmuschko.gradle.docker.tasks.image.DockerBuildImage) {
dependsOn shadowJar
inputDir = file('.')
tags.add("$registryHost/beanstalk-app:latest")
}
task pushImage(type: com.bmuschko.gradle.docker.tasks.image.DockerPushImage) {
dependsOn buildImage
imageName = "$registryHost/beanstalk-app"
tag = 'latest'
}
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "281741939716.dkr.ecr.eu-west-3.amazonaws.com/beanstalk-app:latest",
"Update": "true"
},
"Ports": [
{
"ContainerPort": "8080",
"HostPort": "80"
}
]
}
// deployment
version = '0.1-SNAPSHOT'
// requires AmazonEC2ContainerRegistryReadOnly for aws-elasticbeanstalk-ec2-role
task buildArchive(type: Zip) {
from file('src/main/eb')
}
beanstalk {
s3Endpoint = "s3-eu-west-3.amazonaws.com"
beanstalkEndpoint = "elasticbeanstalk.eu-west-3.amazonaws.com"
deployments {
production {
file = tasks.buildArchive
application = 'beanstalk-app'
environment = "beanstalk-app"
}
}
}
AWS Lambda lets you run code without provisioning or managing servers. You pay only for the compute time you consume - there is no charge when your code is not running.
mn create-function lambda-function
cd lambda-function
@FunctionBean("lambda-function")
public class LambdaFunctionFunction implements Supplier<String> {
private final Environment environment;
public LambdaFunctionFunction(Environment environment) {
this.environment = environment;
}
@Override
public String get() {
return String.join(",", environment.getActiveNames());
}
}
if(new File("${System.getProperty("user.home")}/.aws/credentials").exists()) {
task deploy(
type: jp.classmethod.aws.gradle.lambda.AWSLambdaMigrateFunctionTask,
dependsOn: shadowJar
) {
functionName = "lambda-function"
handler = "io.micronaut.function.aws.MicronautRequestStreamHandler"
role = "arn:aws:iam::${aws.accountId}:role/lambda_basic_execution"
runtime = com.amazonaws.services.lambda.model.Runtime.Java8
zipFile = shadowJar.archivePath
memorySize = 256
timeout = 60
}
}
./gradlew shadowJar
java -jar build/libs/lambda-function-0.1.jar
curl http://localhost:8080/lambda-function
@FunctionClient
public interface LambdaFunctionClient {
@Named("lambda-function")
Single<String> index();
}
@MicronautTest
public class LambdaFunctionFunctionTest {
@Inject
LambdaFunctionClient client;
@Test
public void testFunction() throws Exception {
assertEquals(client.index().blockingGet(), "test");
}
}
./gradlew deploy
./gradlew invoke
task invoke(type: jp.classmethod.aws.gradle.lambda.AWSLambdaInvokeTask) {
functionName = "lambda-function"
invocationType = com.amazonaws.services.lambda.model.InvocationType.RequestResponse
payload = '{ }'
doLast {
println "Lambda function result: " + new String(invokeResult.payload.array(), "UTF-8")
}
}
@FunctionBean("gateway-function")
public class GatewayFunctionFunction
implements Function<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
private final ApplicationContext context;
private final ObjectMapper objectMapper;
public GatewayFunctionFunction(ApplicationContext context, ObjectMapper objectMapper) {
this.context = context;
this.objectMapper = objectMapper;
}
@Override
public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent request) {
if ("GET".equalsIgnoreCase(request.getHttpMethod()) &&
"/env".equalsIgnoreCase(request.getPath())) {
try {
Set<String> names = context.getEnvironment().getActiveNames();
String body = objectMapper.writeValueAsString(names);
return new APIGatewayProxyResponseEvent().withBody(body);
} catch (JsonProcessingException e) {
return new APIGatewayProxyResponseEvent().withStatusCode(500);
}
}
return new APIGatewayProxyResponseEvent().withStatusCode(404);
}
}
@FunctionBean("lambda-echo-java")
public class LambdaEchoJava implements Function<WebSocketRequest, WebSocketResponse> {
private final MessageSenderFactory factory;
private final TestTopicPublisher publisher;
public LambdaEchoJava(MessageSenderFactory factory, TestTopicPublisher publisher) {
this.factory = factory;
this.publisher = publisher;
}
@Override
public WebSocketResponse apply(WebSocketRequest event) {
MessageSender sender = factory.create(event.getRequestContext());
String connectionId = event.getRequestContext().getConnectionId();
switch (event.getRequestContext().getEventType()) {
case CONNECT:
case DISCONNECT:
break;
case MESSAGE:
String message = "[" + connectionId + "] " + event.getBody();
sender.send(connectionId, message);
publisher.publishMessage(connectionId, message);
break;
}
return WebSocketResponse.OK;
}
}
mn create-app gateway-app --features aws-api-gateway
cd gateway-app
mn create-controller env
@Controller("/env")
public class EnvController {
private final ApplicationContext applicationContext;
public EnvController(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Get("/")
public Set<String> index() {
return applicationContext.getEnvironment().getActiveNames();
}
}
./gradlew buildZip
npm install -g aws-sam-local
sam local start-api --template sam.yaml
curl http://127.0.0.1:3000/env
aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml \
--s3-bucket <YOUR S3 BUCKET NAME>
aws cloudformation deploy --template-file output-sam.yaml --stack-name ServerlessMicronautApi \
--capabilities CAPABILITY_IAM
aws cloudformation describe-stacks --stack-name ServerlessMicronautApi
mn create-app gateway-graal-app --features aws-api-gateway-graal
cd gateway-graal-app
docker build . -t gateway-graal-app
mkdir build
docker run --rm --entrypoint cat gateway-graal-app \
/home/application/function.zip > build/function.zip
aws lambda create-function --function-name gateway-graal-app \
--zip-file fileb://build/function.zip --handler function.handler --runtime provided \
--role arn:aws:iam::881337894647:role/lambda_basic_execution
@Controller("/conference")
public class ConferenceController {
private final String name;
public ConferenceController(@Value("${conference.name}") String name) {
this.name = name;
}
@Get("/")
public String index() {
return name;
}
}
Amazon Simple Email Service (Amazon SES) is a cloud-based email sending service designed to help digital marketers and application developers send marketing, notification, and transactional emails. It is a reliable, cost-effective service for businesses of all sizes that use email to keep in contact with their customers.
@Test
public void testSendEmail() throws IOException {
when(simpleEmailService.sendRawEmail(Mockito.any()))
.thenReturn(new SendRawEmailResult().withMessageId("foobar"));
File file = tmp.newFile("test.pdf");
Files.write(file.toPath(), Collections.singletonList("not a real PDF"));
String filepath = file.getCanonicalPath();
EmailDeliveryStatus status = service.send(e ->
e.subject("Hi Paul")
.from("subscribe@groovycalamari.com")
.to("me@sergiodelamo.com")
.htmlBody("<p>This is an example body</p>")
.attachment(a ->
a.filepath(filepath)
.filename("test.pdf")
.mimeType("application/pdf")
.description("An example pdf")
)
);
Assert.assertEquals(EmailDeliveryStatus.STATUS_DELIVERED, status);
}
void "send email"() {
given:
File file = tmp.newFile('test.pdf')
file.text = 'not a real PDF'
String thePath = file.canonicalPath
when:
EmailDeliveryStatus status = service.send {
subject 'Hi Paul'
from 'subscribe@groovycalamari.com'
to 'me@sergiodelamo.com'
htmlBody '<p>This is an example body</p>'
attachment {
filepath thePath
filename 'test.pdf'
mimeType 'application/pdf'
description 'An example pdf'
}
}
then:
status == EmailDeliveryStatus.STATUS_DELIVERED
simpleEmailService.sendRawEmail(_) >> { SendRawEmailRequest request ->
return new SendRawEmailResult().withMessageId('foobar')
}
}
Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance. This means customers of all sizes and industries can use it to store and protect any amount of data for a range of use cases, such as websites, mobile applications, backup and restore, archive, enterprise applications, IoT devices, and big data analytics.
// create bucket
service.createBucket(MY_BUCKET);
assertTrue(service.listBucketNames().contains(MY_BUCKET));
// upload file
File sampleContent = createFileWithSampleContent();
service.storeFile(TEXT_FILE_PATH, sampleContent);
assertTrue(service.exists(TEXT_FILE_PATH));
Flowable<S3ObjectSummary> summaries = service.listObjectSummaries("foo");
assertEquals(Long.valueOf(1L), summaries.count().blockingGet());
// generate downloadable URL
String url = service.generatePresignedUrl(KEY, TOMORROW);
assertEquals(SAMPLE_CONTENT, download(url));
// download file
File dir = tmp.newFolder();
File file = new File(dir, "bar.baz");
service.getFile(KEY, file);
assertTrue(file.exists());
assertEquals(SAMPLE_CONTENT, new String(Files.readAllBytes(Paths.get(file.toURI()))));
// delete file
service.deleteFile(TEXT_FILE_PATH);
assertFalse(service.exists(TEXT_FILE_PATH));
Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications. SQS eliminates the complexity and overhead associated with managing and operating message oriented middleware, and empowers developers to focus on differentiating work. Using SQS, you can send, store, and receive messages between software components at any volume, without losing messages or requiring other services to be available.
Amazon Simple Notification Service (SNS) is a highly available, durable, secure, fully managed pub/sub messaging service that enables you to decouple microservices, distributed systems, and serverless applications. Amazon SNS provides topics for high-throughput, push-based, many-to-many messaging. Using Amazon SNS topics, your publisher systems can fan out messages to a large number of subscriber endpoints for parallel processing, including Amazon SQS queues, AWS Lambda functions, and HTTP/S webhooks. Additionally, SNS can be used to fan out notifications to end users using mobile push, SMS, and email.
Amazon Kinesis makes it easy to collect, process, and analyze real-time, streaming data so you can get timely insights and react quickly to new information. Amazon Kinesis offers key capabilities to cost-effectively process streaming data at any scale, along with the flexibility to choose the tools that best suit the requirements of your application. With Amazon Kinesis, you can ingest real-time data such as video, audio, application logs, website clickstreams, and IoT telemetry data for machine learning, analytics, and other applications.
Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications. SQS eliminates the complexity and overhead associated with managing and operating message oriented middleware, and empowers developers to focus on differentiating work. Using SQS, you can send, store, and receive messages between software components at any volume, without losing messages or requiring other services to be available.
@QueueClient
interface DefaultClient {
@Queue(value = "OtherQueue", group = "SomeGroup")
String sendMessageToQueue(String message);
String sendMessage(Pogo message);
String sendMessage(byte[] record);
String sendMessage(String record);
String sendMessage(String record, int delay);
String sendMessage(String record, String group);
String sendMessage(String record, int delay, String group);
void deleteMessage(String messageId);
String OTHER_QUEUE = "OtherQueue";
}
@Testcontainers
@RestoreSystemProperties
class SimpleQueueServiceSpec extends Specification {
@Shared LocalStackContainer localstack = new LocalStackContainer('0.8.10')
.withServices(SQS)
@AutoCleanup ApplicationContext context
SimpleQueueService service
void setup() {
System.setProperty('com.amazonaws.sdk.disableCbor', 'true')
AmazonSQS sqs = AmazonSQSClient
.builder()
.withEndpointConfiguration(localstack.getEndpointConfiguration(SQS))
.withCredentials(localstack.defaultCredentialsProvider)
.build()
context = ApplicationContext.build('aws.sqs.queue': TEST_QUEUE).build()
context.registerSingleton(AmazonSQS, sqs)
context.start()
service = context.getBean(SimpleQueueService)
}
// tests
}
Amazon Simple Notification Service (SNS) is a highly available, durable, secure, fully managed pub/sub messaging service that enables you to decouple microservices, distributed systems, and serverless applications. Amazon SNS provides topics for high-throughput, push-based, many-to-many messaging. Using Amazon SNS topics, your publisher systems can fan out messages to a large number of subscriber endpoints for parallel processing, including Amazon SQS queues, AWS Lambda functions, and HTTP/S webhooks. Additionally, SNS can be used to fan out notifications to end users using mobile push, SMS, and email.
@NotificationClient
interface DefaultClient {
String OTHER_TOPIC = "OtherTopic";
@Topic("OtherTopic") String publishMessageToDifferentTopic(Pogo pogo);
String publishMessage(Pogo message);
String publishMessage(String subject, Pogo message);
String publishMessage(String message);
String publishMessage(String subject, String message);
String sendSMS(String phoneNumber, String message);
String sendSms(String phoneNumber, String message, Map attributes);
}
String appArn = service.createAndroidApplication("my-app", API_KEY);
String endpoint = service.registerAndroidDevice(appArn, DEVICE_TOKEN, DATA);
Map<String, String> notif = new LinkedHashMap<>();
notif.put("badge", "9");
notif.put("data", "{\"foo\": \"some bar\"}");
notif.put("title", "Some Title");
String msgId = service.sendAndroidAppNotification(endpoint, notif, "Welcome");
service.validateAndroidDevice(appArn, endpoint, DEVICE_TOKEN, DATA);
service.unregisterDevice(endpoint);
Amazon Kinesis makes it easy to collect, process, and analyze real-time, streaming data so you can get timely insights and react quickly to new information. Amazon Kinesis offers key capabilities to cost-effectively process streaming data at any scale, along with the flexibility to choose the tools that best suit the requirements of your application. With Amazon Kinesis, you can ingest real-time data such as video, audio, application logs, website clickstreams, and IoT telemetry data for machine learning, analytics, and other applications.
@KinesisClient
interface DefaultClient {
void putRecordString(String record);
PutRecordResult putRecord(String partitionKey, String record);
void putRecordAnno(@PartitionKey String id, String record);
void putRecord(String partitionKey, String record, String sequenceNumber);
void putRecordAnno(
@PartitionKey String id,
String record,
@SequenceNumber String sqn
);
void putRecordAnnoNumbers(
@PartitionKey Long id,
String record,
@SequenceNumber int sequenceNumber
);
}
@KinesisClient
interface DefaultClient {
void putRecordBytes(byte[] record);
void putRecordDataByteArray(@PartitionKey String id, byte[] value);
PutRecordsResult putRecords(Iterable<PutRecordsRequestEntry> entries);
PutRecordsResult putRecords(PutRecordsRequestEntry... entries);
PutRecordsResult putRecord(PutRecordsRequestEntry entry);
}
@KinesisClient
interface DefaultClient {
void putRecordObject(Pogo pogo);
PutRecordsResult putRecordObjects(Pogo... pogo);
PutRecordsResult putRecordObjects(Iterable<Pogo> pogo);
void putRecordDataObject(@PartitionKey String id, Pogo value);
}
@Singleton
public class KinesisListenerTester {
@KinesisListener
public void listenString(String string) {
String message = "EXECUTED: listenString(" + string + ")";
logExecution(message);
}
@KinesisListener
public void listenRecord(Record record) {
logExecution("EXECUTED: listenRecord(" + record + ")");
}
@KinesisListener
public void listenObject(MyEvent event) {
logExecution("EXECUTED: listenObject(" + event + ")");
}
@KinesisListener
public void listenPogoRecord(Pogo event) {
logExecution("EXECUTED: listenPogoRecord(" + event + ")");
}
}
Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale. It's a fully managed, multiregion, multimaster database with built-in security, backup and restore, and in-memory caching for internet-scale applications. DynamoDB can handle more than 10 trillion requests per day and support peaks of more than 20 million requests per second.
@DynamoDBTable(tableName = "entity")
public class DynamoDBEntity {
public static final String DATE_INDEX = "date";
public static final String RANGE_INDEX = "rangeIndex";
public static final String GLOBAL_INDEX = "globalIndex";
@DynamoDBHashKey
String parentId;
@DynamoDBRangeKey
String id;
@DynamoDBIndexRangeKey(localSecondaryIndexName = RANGE_INDEX)
String rangeIndex;
@DynamoDBIndexRangeKey(localSecondaryIndexName = DATE_INDEX)
Date date;
Integer number = 0;
// boilerplate
}
@Service(DynamoDBEntity)
interface DynamoDBItemDBService {
DynamoDBEntity get(String hash, String rangeKey)
List<DynamoDBEntity> getAll(String hash, List<String> rangeKeys)
List<DynamoDBEntity> getAll(String hash, String... rangeKeys)
DynamoDBEntity save(DynamoDBEntity entity)
List<DynamoDBEntity> saveAll(DynamoDBEntity... entities)
List<DynamoDBEntity> saveAll(Iterable<DynamoDBEntity> entities)
int count(String hashKey)
int count(String hashKey, String rangeKey)
}
@Service(DynamoDBEntity)
interface DynamoDBItemDBService {
@Query({
query(DynamoDBEntity) {
hash hashKey
range {
eq DynamoDBEntity.RANGE_INDEX, rangeKey
}
only {
rangeIndex
}
}
})
Flowable<DynamoDBEntity> queryByRangeIndex(String hashKey, String rangeKey)
@Query({
query(DynamoDBEntity) {
hash hashKey
range {
eq DynamoDBEntity.RANGE_INDEX, rangeKey
}
}
})
int countByRangeIndex(String hashKey, String rangeKey)
}
@Service(DynamoDBEntity.class)
public interface DynamoDBEntityService {
class EqRangeScan implements Function<Map<String, Object>, DetachedScan> {
public DetachedScan apply(Map<String, Object> arguments) {
return Builders.scan(DynamoDBEntity.class)
.filter(f -> f.eq(DynamoDBEntity.RANGE_INDEX, arguments.get("foo")));
}
}
@Scan(EqRangeScan.class)
Flowable<DynamoDBEntity> scanAllByRangeIndex(String foo);
}
@Service(DynamoDBEntity)
interface DynamoDBItemDBService {
@Scan({
scan(DynamoDBEntity) {
filter {
eq DynamoDBEntity.RANGE_INDEX, foo
}
}
})
Flowable<DynamoDBEntity> scanAllByRangeIndex(String foo)
}
@Service(DynamoDBEntity)
interface DynamoDBItemDBService {
@Update({
update(DynamoDBEntity) {
hash hashKey
range rangeKey
add 'number', 1
returnUpdatedNew { number }
}
})
Number increment(String hashKey, String rangeKey)
@Update({
update(DynamoDBEntity) {
hash hashKey
range rangeKey
add 'number', -1
returnUpdatedNew { number }
}
})
Number decrement(String hashKey, String rangeKey)
}