Skip to content
English
On this page

Ejercicio: Desarrollo y Despliegue de AWS Lambda con Java

Objetivo: Crear una aplicación Java usando AWS Lambda con API Gateway y DynamoDB para crear una API RESTful de gestión de tareas.

  1. Estructura del Proyecto:
task-manager/
├── pom.xml
├── src/
│   └── main/
│       ├── java/
│       │   └── com/
│       │       └── demo/
│       │           ├── handler/
│       │           │   ├── CreateTaskHandler.java
│       │           │   ├── GetTaskHandler.java
│       │           │   ├── ListTasksHandler.java
│       │           │   └── DeleteTaskHandler.java
│       │           ├── model/
│       │           │   └── Task.java
│       │           └── util/
│       │               └── DynamoDBUtil.java
│       └── resources/
│           └── log4j2.xml
  1. Archivo pom.xml:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo</groupId>
    <artifactId>task-manager</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-events</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-dynamodb</artifactId>
            <version>1.12.261</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j18-impl</artifactId>
            <version>2.17.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  1. Modelo Task.java:
java
package com.demo.model;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;

@DynamoDBTable(tableName = "Tasks")
public class Task {
    private String id;
    private String title;
    private String description;
    private String status;
    private String createdAt;

    @DynamoDBHashKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    @DynamoDBAttribute
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }

    @DynamoDBAttribute
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }

    @DynamoDBAttribute
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }

    @DynamoDBAttribute
    public String getCreatedAt() { return createdAt; }
    public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
}
  1. DynamoDBUtil.java:
java
package com.demo.util;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;

public class DynamoDBUtil {
    private static final AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    private static final DynamoDBMapper mapper = new DynamoDBMapper(client);

    public static DynamoDBMapper getMapper() {
        return mapper;
    }
}
  1. CreateTaskHandler.java:
java
package com.demo.handler;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.demo.model.Task;
import com.demo.util.DynamoDBUtil;
import com.google.gson.Gson;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class CreateTaskHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
    private static final Logger logger = LogManager.getLogger(CreateTaskHandler.class);
    private static final Gson gson = new Gson();

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
        logger.info("Received request: " + input.getBody());

        try {
            Task task = gson.fromJson(input.getBody(), Task.class);
            task.setId(UUID.randomUUID().toString());
            task.setCreatedAt(java.time.Instant.now().toString());

            DynamoDBUtil.getMapper().save(task);

            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "application/json");
            headers.put("X-Custom-Header", "application/json");

            return new APIGatewayProxyResponseEvent()
                    .withStatusCode(201)
                    .withHeaders(headers)
                    .withBody(gson.toJson(task));
        } catch (Exception e) {
            logger.error("Error creating task: ", e);
            return new APIGatewayProxyResponseEvent()
                    .withStatusCode(500)
                    .withBody("{\"error\":\"Error creating task\"}");
        }
    }
}
  1. GetTaskHandler.java:
java
package com.demo.handler;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.demo.model.Task;
import com.demo.util.DynamoDBUtil;
import com.google.gson.Gson;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.HashMap;
import java.util.Map;

public class GetTaskHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
    private static final Logger logger = LogManager.getLogger(GetTaskHandler.class);
    private static final Gson gson = new Gson();

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
        String taskId = input.getPathParameters().get("id");
        logger.info("Getting task with ID: " + taskId);

        try {
            Task task = DynamoDBUtil.getMapper().load(Task.class, taskId);
            
            if (task == null) {
                return new APIGatewayProxyResponseEvent()
                        .withStatusCode(404)
                        .withBody("{\"error\":\"Task not found\"}");
            }

            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "application/json");

            return new APIGatewayProxyResponseEvent()
                    .withStatusCode(200)
                    .withHeaders(headers)
                    .withBody(gson.toJson(task));
        } catch (Exception e) {
            logger.error("Error getting task: ", e);
            return new APIGatewayProxyResponseEvent()
                    .withStatusCode(500)
                    .withBody("{\"error\":\"Error getting task\"}");
        }
    }
}
  1. Pasos para el Despliegue:

a) Crear tabla en DynamoDB:

plaintext
1. Ir a DynamoDB en la consola AWS
2. Crear tabla
   - Nombre: Tasks
   - Partition key: id (String)
   - Configuración: bajo demanda

b) Crear rol IAM para Lambda:

plaintext
1. Ir a IAM
2. Crear rol
   - Tipo: Lambda
   - Políticas:
     * AWSLambdaBasicExecutionRole
     * DynamoDB Full Access (para demo, en producción usar permisos más restrictivos)

c) Crear funciones Lambda:

plaintext
1. Ir a Lambda
2. Crear función
   - Nombre: createTask
   - Runtime: Java 11
   - Handler: com.demo.handler.CreateTaskHandler::handleRequest
   - Memoria: 512 MB
   - Timeout: 30 segundos
   - Rol: usar el creado anteriormente

Repetir para getTask, listTasks y deleteTask

d) Crear API Gateway:

plaintext
1. Ir a API Gateway
2. Crear API REST
3. Crear recursos y métodos:
   POST /tasks -> createTask
   GET /tasks/{id} -> getTask
   GET /tasks -> listTasks
   DELETE /tasks/{id} -> deleteTask
  1. Pruebas:

a) Crear tarea:

bash
curl -X POST https://[API-ID].execute-api.[REGION].amazonaws.com/prod/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Mi tarea","description":"Descripción de la tarea","status":"PENDING"}'

b) Obtener tarea:

bash
curl https://[API-ID].execute-api.[REGION].amazonaws.com/prod/tasks/[TASK-ID]
  1. Monitoreo:
plaintext
1. CloudWatch Logs
   - Revisar logs de ejecución
   - Configurar métricas personalizadas

2. X-Ray (opcional)
   - Habilitar tracing
   - Analizar latencia
  1. Mejoras Sugeridas:
plaintext
1. Implementar validación de entrada
2. Agregar autenticación con Cognito
3. Implementar caché con API Gateway
4. Configurar Auto Scaling para DynamoDB
5. Implementar tests unitarios

Este ejercicio proporciona experiencia en:

  • Desarrollo de funciones Lambda con Java
  • Integración con DynamoDB
  • Configuración de API Gateway
  • Gestión de permisos IAM
  • Logging y monitoreo
  • Prácticas de desarrollo serverless

Los estudiantes pueden expandir el ejercicio:

  • Agregando más funcionalidades
  • Implementando validaciones
  • Mejorando el manejo de errores
  • Agregando tests
  • Implementando CI/CD