Skip to content

Cloud-Native-Webapp/webapp

Repository files navigation

webapp: A Cloud Native Web Application

This repository contains a production-ready, cloud-native RESTful API web application built with Spring Boot for user management, product catalog, and image storage. The application is designed for deployment on AWS with comprehensive monitoring, automated infrastructure provisioning, and CI/CD pipelines.

Table of Contents

Overview

This is a stateless, horizontally-scalable REST API that provides:

  • User Management: Secure user registration and profile management
  • Product Catalog: Full CRUD operations with ownership-based access control
  • Image Storage: AWS S3-backed image upload and management for products
  • Health Monitoring: Application and database health checks
  • Auto-Scaling Architecture: Deployed with AWS Auto Scaling Groups (3-5 instances) behind Application Load Balancer
  • High Availability: Multi-AZ deployment with automatic failover and dynamic scaling
  • Cloud-Native Design: Fully automated infrastructure provisioning via Terraform

The application follows microservice best practices with comprehensive logging, metrics, and security measures.

Features

Core Functionality

  • User Management: User registration, authentication, profile retrieval and updates
  • Product Management: Complete CRUD operations on products with ownership-based authorization
  • Image Management: Upload, retrieve, list, and delete product images stored in AWS S3
  • Health Checks: Database connectivity verification and application health monitoring

Security & Validation

  • Authentication: HTTP Basic Authentication with BCrypt password hashing (strength 10)
  • Authorization: Ownership-based access control for products and user data
  • Password Policy: Enforced complexity requirements (8-15 chars, uppercase, lowercase, digit, special char)
  • Input Validation: Comprehensive validation for email format, SKU uniqueness, quantity constraints
  • Stateless Design: No session storage, horizontally scalable architecture
  • Security Headers: Cache-control and X-Content-Type-Options headers on all responses

Cloud Integration

  • Auto Scaling: Dynamic instance management with AWS Auto Scaling Groups
    • Minimum: 3 instances
    • Maximum: 5 instances
    • Scale up when CPU > 5%, scale down when CPU < 3%
    • Spans multiple availability zones for high availability
  • Load Balancing: Application Load Balancer for traffic distribution
    • HTTP traffic on port 80 forwarded to application port 8080
    • Health checks via /healthz endpoint
    • Provides static DNS endpoint for dynamic instance pool
  • AWS S3: Persistent image storage with organized path structure (users/{userId}/products/{productId}/{uuid-filename})
  • CloudWatch: Centralized logging and metrics aggregation
  • Route53: DNS management with subdomain delegation (dev/demo environments)
  • Packer: Automated AMI builds with application pre-installed
  • Terraform: Infrastructure as Code for reproducible deployments

Observability

  • Structured Logging: JSON-formatted logs with request ID tracking via Logstash encoder
  • Metrics Export: Micrometer integration with StatsD protocol for CloudWatch
  • Request Tracing: Unique request IDs assigned via MDC for distributed tracing
  • API Monitoring: Automatic timing and call counting for all endpoints
  • Database Monitoring: Query performance tracking via Hibernate statistics

Quality Assurance

  • Testing: 91% code coverage with 160 comprehensive integration and unit tests
  • CI/CD: Automated testing on every pull request with branch protection
  • Code Quality: JaCoCo coverage reports and Maven quality checks

Architecture

System Architecture

┌─────────────┐     ┌──────────────┐     ┌──────────────────────────┐
│   Client    │────▶│   Route53    │────▶│  Application Load        │
│  (HTTP)     │     │   (DNS)      │     │  Balancer (Port 80)      │
└─────────────┘     └──────────────┘     └──────────┬───────────────┘
                                                     │
                                    ┌────────────────┼────────────────┐
                                    │                │                │
                            ┌───────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
                            │ Application  │ │ Application │ │ Application │
                            │  Instance 1  │ │  Instance 2 │ │  Instance 3 │
                            │  (Spring)    │ │  (Spring)   │ │  (Spring)   │
                            └───────┬──────┘ └──────┬──────┘ └──────┬──────┘
                                    │                │                │
                                    └────────────────┼────────────────┘
                                                     │
                                    ┌────────────────┼────────────────┐
                                    │                │                │
                             ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
                             │    MySQL    │ │   AWS S3    │ │ CloudWatch  │
                             │   Database  │ │  (Images)   │ │(Logs/Metrics│
                             └─────────────┘ └─────────────┘ └─────────────┘

Application Layers

  1. Controller Layer: REST endpoint handlers (HealthController, UserController, ProductController, ImageController)
  2. Service Layer: Business logic (UserService, ProductService, ImageService, S3Service, MetricsService)
  3. Repository Layer: Data access via JPA repositories
  4. Entity Layer: JPA entities (User, Product, Image, HealthCheck)
  5. DTO Layer: Data transfer objects for request/response mapping
  6. Filter Layer: MetricsFilter for API timing, RequestLoggingFilter for request tracing
  7. Configuration Layer: Security, S3, and application configuration

Design Patterns

  • Repository Pattern: Data access abstraction via Spring Data JPA
  • DTO Pattern: Separation of internal entities from API contracts
  • Service Layer Pattern: Business logic encapsulation
  • Filter Chain Pattern: Request processing pipeline for metrics and logging
  • Dependency Injection: Spring Framework IoC container
  • Builder Pattern: Entity construction and updates

Tech Stack

Core Framework

  • Spring Boot: 3.5.6
  • Java: 21 (JRE for production, JDK for development)
  • Maven: 3.6+ for dependency management and builds

Database

  • MySQL: 8.0+
  • JPA/Hibernate: ORM with automatic schema management
  • HikariCP: Connection pooling (default with Spring Boot)

Security

  • Spring Security: Authentication and authorization framework
  • BCrypt: Password hashing with strength 10

Cloud & Storage

  • AWS SDK v2: S3 client for image storage
  • AWS CloudWatch: Logging and metrics
  • Packer: AMI image building
  • Systemd: Service management on Ubuntu

Testing

  • JUnit 5: Test framework
  • Mockito: Mocking framework
  • REST Assured: HTTP API testing
  • Spring Boot Test: Integration test support
  • JaCoCo: Code coverage reporting

Monitoring

  • Micrometer: Metrics collection facade
  • StatsD: Metrics aggregation protocol
  • Logback: Logging framework
  • Logstash Encoder: JSON-formatted logging for CloudWatch

CI/CD

  • GitHub Actions: Automated workflows for testing and AMI builds
  • Maven Surefire: Test execution during builds

Prerequisites

Local Development

  • Java 21 or higher
  • Maven 3.6+ for dependency management and building
  • MySQL 8.0+ database server
  • Git for version control

Cloud Deployment

  • AWS Account with permissions for EC2, S3, and CloudWatch
  • Packer 1.8+ for AMI builds
  • AWS CLI configured with credentials

Database Setup

  1. Install and start MySQL server
  2. Create a database user with privileges:
CREATE USER 'csye6225'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON webapp.* TO 'csye6225'@'localhost';
FLUSH PRIVILEGES;
  1. The application will automatically create the webapp database on first run if it doesn't exist

Development Setup

1. Clone the Repository

git clone git@github.com:dugganite-cloud-computing/webapp.git
cd webapp

2. Configure Database

Set environment variables for your local MySQL instance:

export DB_HOST=localhost
export DB_PORT=3306
export DB_NAME=webapp
export DB_USER=csye6225
export DB_PASSWORD=your_password

3. Configure AWS (Optional, for S3 features)

export AWS_REGION=us-east-1
export S3_BUCKET_NAME=your-bucket-name
# Or configure AWS credentials via ~/.aws/credentials

4. Build the Application

mvn clean compile

5. Run Tests

# Run all tests
mvn test

# Run specific test class
mvn test -Dtest=UserIntegrationTest

# Run tests with coverage
mvn clean test jacoco:report

6. Generate Coverage Report

mvn jacoco:report

Coverage report will be available at target/site/jacoco/index.html

7. Package the Application

mvn clean package

This creates target/webapp-0.0.1-SNAPSHOT.jar

8. Run the Application

# Using Maven
mvn spring-boot:run

# Or run the JAR directly
java -jar target/webapp-0.0.1-SNAPSHOT.jar

The application will:

  • Start on http://localhost:8080
  • Connect to MySQL using configured credentials
  • Create the database and tables automatically if they don't exist
  • Start exporting metrics to StatsD on localhost:8125 (if available)

API Documentation

Base URL

http://localhost:8080

Authentication

Most endpoints require HTTP Basic Authentication:

Authorization: Basic <base64-encoded-credentials>
  • Username must be a valid email address
  • Credentials format: email:password

Common Response Headers

All responses include:

  • Cache-Control: no-cache, no-store, must-revalidate
  • Pragma: no-cache
  • X-Content-Type-Options: nosniff

Endpoint Summary

Endpoint Method Auth Description
/healthz GET No Health check
/v1/user POST No Create user
/v1/user/{userId} GET Yes Get user (own data)
/v1/user/{userId} PUT Yes Update user (own data)
/v1/product POST Yes Create product
/v1/product/{productId} GET No Get product
/v1/product/{productId} PATCH Yes Partial update (owner)
/v1/product/{productId} PUT Yes Full update (owner)
/v1/product/{productId} DELETE Yes Delete product (owner)
/v1/product/{productId}/image POST Yes Upload image (owner)
/v1/product/{productId}/image GET No List images
/v1/product/{productId}/image/{imageId} GET No Get image details
/v1/product/{productId}/image/{imageId} DELETE Yes Delete image (owner)

Health Check

GET /healthz

Check application and database health.

Request:

  • No authentication required
  • No query parameters allowed
  • No request body allowed

Response:

  • 200 OK - Application healthy, database connected
  • 400 Bad Request - Query parameters or request body present
  • 405 Method Not Allowed - Method not supported (only GET allowed)
  • 503 Service Unavailable - Database unreachable

Example:

curl -X GET http://localhost:8080/healthz

User Management

POST /v1/user

Create a new user account.

Request:

  • No authentication required
  • No query parameters allowed

Request Body:

{
  "first_name": "Johnny",
  "last_name": "Test",
  "username": "johnny@email.com",
  "password": "Password123!"
}

Validation Rules:

  • username: Valid email format, unique, required
  • password: 8-15 characters with uppercase, lowercase, digit, special char (!@#$%^&*()_+-=[]{}|;:',.<>?/~`)
  • first_name: Required, max 255 characters
  • last_name: Required, max 255 characters
  • Read-only fields (id, account_created, account_updated) cannot be provided

Response:

  • 201 Created - User created successfully
{
  "id": 1,
  "first_name": "Johnny",
  "last_name": "Test",
  "username": "johnny@email.com",
  "account_created": "2025-11-04T10:30:00",
  "account_updated": "2025-11-04T10:30:00"
}
  • 400 Bad Request - Validation failed or duplicate username
  • 503 Service Unavailable - Database unreachable

Example:

curl -X POST http://localhost:8080/v1/user \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Johnny",
    "last_name": "Test",
    "username": "johnny@email.com",
    "password": "Password123!"
  }'

GET /v1/user/{userId}

Retrieve user information.

Request:

  • Authentication required (user can only access their own data)
  • No query parameters allowed
  • No request body allowed

Path Parameters:

  • userId: User ID (must match authenticated user's ID)

Response:

  • 200 OK - User retrieved successfully
{
  "id": 1,
  "first_name": "Johnny",
  "last_name": "Test",
  "username": "johnny@email.com",
  "account_created": "2025-11-04T10:30:00",
  "account_updated": "2025-11-04T10:30:00"
}
  • 400 Bad Request - Query parameters or request body present
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Trying to access another user's data
  • 404 Not Found - User doesn't exist
  • 503 Service Unavailable - Database unreachable

Example:

curl -X GET http://localhost:8080/v1/user/1 \
  -u "johnny@email.com:Password123!"

PUT /v1/user/{userId}

Update user information.

Request:

  • Authentication required (user can only update their own data)
  • No query parameters allowed

Path Parameters:

  • userId: User ID (must match authenticated user's ID)

Request Body (all fields optional):

{
  "first_name": "John",
  "last_name": "Doe",
  "password": "NewPassword123!"
}

Validation Rules:

  • password: Same requirements as registration if provided
  • first_name: Max 255 characters, cannot be empty if provided
  • last_name: Max 255 characters, cannot be empty if provided
  • Read-only fields (id, username, email, account_created, account_updated) cannot be provided

Response:

  • 204 No Content - User updated successfully
  • 400 Bad Request - Validation failed or read-only fields provided
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Trying to update another user's data
  • 404 Not Found - User doesn't exist
  • 503 Service Unavailable - Database unreachable

Example:

curl -X PUT http://localhost:8080/v1/user/1 \
  -u "johnny@email.com:Password123!" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "John",
    "password": "NewPassword456!"
  }'

Product Management

POST /v1/product

Create a new product.

Request:

  • Authentication required
  • No query parameters allowed

Request Body:

{
  "name": "Obsidian",
  "description": "Nature's sharpest blade. Volcanic glass sharper than surgical scalpels",
  "sku": "UNIQUE-SKU-123",
  "manufacturer": "Fantasia Materials",
  "quantity": 5
}

Validation Rules:

  • name: Required, max 255 characters, cannot be empty
  • description: Required, cannot be empty
  • sku: Required, unique across all products, cannot be empty
  • manufacturer: Required, max 255 characters, cannot be empty
  • quantity: Required, must be >= 0
  • Read-only fields (id, date_added, date_last_updated, owner_user_id) cannot be provided

Response:

  • 201 Created - Product created successfully
{
  "id": 1,
  "name": "Obsidian",
  "description": "Nature's sharpest blade. Volcanic glass sharper than surgical scalpels",
  "sku": "UNIQUE-SKU-123",
  "manufacturer": "Fantasia Materials",
  "quantity": 5,
  "date_added": "2025-11-04T10:35:00",
  "date_last_updated": "2025-11-04T10:35:00",
  "owner_user_id": 1
}
  • 400 Bad Request - Validation failed or duplicate SKU
  • 401 Unauthorized - No authentication provided
  • 503 Service Unavailable - Database unreachable

Example:

curl -X POST http://localhost:8080/v1/product \
  -u "johnny@email.com:Password123!" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Obsidian",
    "description": "Volcanic glass",
    "sku": "UNIQUE-SKU-123",
    "manufacturer": "Fantasia Materials",
    "quantity": 5
  }'

GET /v1/product/{productId}

Retrieve product information (public endpoint).

Request:

  • No authentication required
  • No query parameters allowed
  • No request body allowed

Path Parameters:

  • productId: Product ID

Response:

  • 200 OK - Product retrieved successfully
{
  "id": 1,
  "name": "Obsidian",
  "description": "Volcanic glass sharper than surgical scalpels",
  "sku": "UNIQUE-SKU-123",
  "manufacturer": "Fantasia Materials",
  "quantity": 5,
  "date_added": "2025-11-04T10:35:00",
  "date_last_updated": "2025-11-04T10:35:00",
  "owner_user_id": 1
}
  • 400 Bad Request - Query parameters or request body present
  • 404 Not Found - Product doesn't exist
  • 503 Service Unavailable - Database unreachable

Example:

curl -X GET http://localhost:8080/v1/product/1

PATCH /v1/product/{productId}

Partially update product (owner only).

Request:

  • Authentication required (must be product owner)
  • No query parameters allowed

Path Parameters:

  • productId: Product ID

Request Body (all fields optional):

{
  "name": "Updated Obsidian",
  "quantity": 10
}

Validation Rules:

  • name: Max 255 characters, cannot be empty if provided
  • description: Cannot be empty if provided
  • sku: Must be unique if changed
  • manufacturer: Max 255 characters, cannot be empty if provided
  • quantity: Must be >= 0 if provided
  • Read-only fields cannot be provided

Response:

  • 204 No Content - Product updated successfully
  • 400 Bad Request - Validation failed
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Not the product owner
  • 404 Not Found - Product doesn't exist
  • 503 Service Unavailable - Database unreachable

PUT /v1/product/{productId}

Fully replace product (owner only).

Request:

  • Authentication required (must be product owner)
  • No query parameters allowed
  • All fields required in request body

Path Parameters:

  • productId: Product ID

Request Body:

{
  "name": "New Obsidian",
  "description": "Updated description",
  "sku": "NEW-SKU-456",
  "manufacturer": "New Manufacturer",
  "quantity": 20
}

Response:

  • 204 No Content - Product updated successfully
  • 400 Bad Request - Validation failed or missing required fields
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Not the product owner
  • 404 Not Found - Product doesn't exist
  • 503 Service Unavailable - Database unreachable

DELETE /v1/product/{productId}

Delete product and all associated images (owner only).

Request:

  • Authentication required (must be product owner)
  • No query parameters allowed
  • No request body allowed

Path Parameters:

  • productId: Product ID

Response:

  • 204 No Content - Product deleted successfully (also deletes all images from S3)
  • 400 Bad Request - Query parameters or request body present
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Not the product owner
  • 404 Not Found - Product doesn't exist
  • 503 Service Unavailable - Database unreachable

Example:

curl -X DELETE http://localhost:8080/v1/product/1 \
  -u "johnny@email.com:Password123!"

Image Management

POST /v1/product/{productId}/image

Upload an image for a product (owner only).

Request:

  • Authentication required (must be product owner)
  • Content-Type: multipart/form-data
  • Form field name: file

Path Parameters:

  • productId: Product ID

File Requirements:

  • Allowed types: JPEG, JPG, PNG only
  • Validated by Content-Type header

Response:

  • 201 Created - Image uploaded successfully
{
  "image_id": 1,
  "product_id": 1,
  "file_name": "product-image.jpg",
  "date_created": "2025-11-04T10:40:00",
  "s3_bucket_path": "users/1/products/1/uuid-product-image.jpg"
}
  • 400 Bad Request - Invalid file type or no file provided
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Not the product owner
  • 404 Not Found - Product doesn't exist
  • 503 Service Unavailable - Database or S3 unreachable

S3 Path Structure:

users/{userId}/products/{productId}/{uuid-filename}

Example:

curl -X POST http://localhost:8080/v1/product/1/image \
  -u "johnny@email.com:Password123!" \
  -F "file=@/path/to/image.jpg"

GET /v1/product/{productId}/image

List all images for a product (public endpoint).

Request:

  • No authentication required
  • Query parameters allowed (unlike other endpoints)

Path Parameters:

  • productId: Product ID

Response:

  • 200 OK - List of images
[
  {
    "image_id": 1,
    "product_id": 1,
    "file_name": "image1.jpg",
    "date_created": "2025-11-04T10:40:00",
    "s3_bucket_path": "users/1/products/1/uuid-image1.jpg"
  },
  {
    "image_id": 2,
    "product_id": 1,
    "file_name": "image2.png",
    "date_created": "2025-11-04T10:45:00",
    "s3_bucket_path": "users/1/products/1/uuid-image2.png"
  }
]
  • 404 Not Found - Product doesn't exist
  • 503 Service Unavailable - Database unreachable

Example:

curl -X GET http://localhost:8080/v1/product/1/image

GET /v1/product/{productId}/image/{imageId}

Get specific image details (public endpoint).

Request:

  • No authentication required
  • Query parameters allowed

Path Parameters:

  • productId: Product ID
  • imageId: Image ID

Response:

  • 200 OK - Image details
{
  "image_id": 1,
  "product_id": 1,
  "file_name": "product-image.jpg",
  "date_created": "2025-11-04T10:40:00",
  "s3_bucket_path": "users/1/products/1/uuid-product-image.jpg"
}
  • 404 Not Found - Product or image doesn't exist
  • 503 Service Unavailable - Database unreachable

Example:

curl -X GET http://localhost:8080/v1/product/1/image/1

DELETE /v1/product/{productId}/image/{imageId}

Delete an image (owner only).

Request:

  • Authentication required (must be product owner)
  • Query parameters allowed

Path Parameters:

  • productId: Product ID
  • imageId: Image ID

Response:

  • 204 No Content - Image deleted successfully (removed from both database and S3)
  • 401 Unauthorized - No authentication provided
  • 403 Forbidden - Not the product owner
  • 404 Not Found - Product or image doesn't exist
  • 503 Service Unavailable - Database or S3 unreachable

Example:

curl -X DELETE http://localhost:8080/v1/product/1/image/1 \
  -u "johnny@email.com:Password123!"

Database Schema

The application uses MySQL with JPA/Hibernate for ORM. Schema is automatically created and updated via spring.jpa.hibernate.ddl-auto=update.

Entity Relationship Diagram

┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│     User     │1    *│    Product   │1    *│    Image     │
│──────────────│───────│──────────────│───────│──────────────│
│ user_id (PK) │       │ id (PK)      │       │ image_id(PK) │
│ email        │       │ name         │       │ product_id   │
│ username     │       │ description  │       │ file_name    │
│ password     │       │ sku (UNIQUE) │       │ s3_bucket_   │
│ first_name   │       │ manufacturer │       │   path       │
│ last_name    │       │ quantity     │       │ date_created │
│ account_     │       │ date_added   │       └──────────────┘
│   created    │       │ date_last_   │
│ account_     │       │   updated    │       ┌──────────────┐
│   updated    │       │ owner_user_  │       │ HealthCheck  │
└──────────────┘       │   id (FK)    │       │──────────────│
                       └──────────────┘       │ check_id(PK) │
                                              │ check_       │
                                              │   datetime   │
                                              └──────────────┘

User Table

CREATE TABLE user (
  user_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) NOT NULL UNIQUE,
  username VARCHAR(255) NOT NULL UNIQUE,
  password VARCHAR(255) NOT NULL,
  first_name VARCHAR(255) NOT NULL,
  last_name VARCHAR(255) NOT NULL,
  account_created DATETIME(6),
  account_updated DATETIME(6)
);

Constraints:

  • email and username are unique (username must equal email)
  • password is hashed with BCrypt
  • Timestamps auto-managed via JPA @PrePersist and @PreUpdate

Product Table

CREATE TABLE product (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  description TEXT NOT NULL,
  sku VARCHAR(255) NOT NULL UNIQUE,
  manufacturer VARCHAR(255) NOT NULL,
  quantity INT NOT NULL CHECK (quantity >= 0),
  date_added DATETIME(6),
  date_last_updated DATETIME(6),
  owner_user_id BIGINT NOT NULL,
  FOREIGN KEY (owner_user_id) REFERENCES user(user_id) ON DELETE CASCADE
);

Constraints:

  • sku must be unique across all products
  • quantity must be >= 0
  • owner_user_id is a foreign key to user.user_id
  • Cascading delete: Deleting a user deletes all their products

Image Table

CREATE TABLE image (
  image_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  product_id BIGINT NOT NULL,
  file_name VARCHAR(255) NOT NULL,
  date_created DATETIME(6),
  s3_bucket_path VARCHAR(500) NOT NULL,
  FOREIGN KEY (product_id) REFERENCES product(id) ON DELETE CASCADE
);

Constraints:

  • product_id is a foreign key to product.id
  • Cascading delete: Deleting a product deletes all associated images from database (application also deletes from S3)
  • s3_bucket_path stores full path in S3 bucket

HealthCheck Table

CREATE TABLE health_check (
  check_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  check_datetime DATETIME(6) NOT NULL
);

Purpose:

  • Records each health check request to verify database connectivity
  • Accumulates audit trail of health check requests

Security

Authentication

  • Type: HTTP Basic Authentication
  • Stateless: No sessions or cookies, credentials required with each request
  • Username Format: Must be a valid email address
  • Password Hashing: BCrypt with strength 10 (configured via BCryptPasswordEncoder bean)

Authorization Model

  1. Public Endpoints (no auth required):

    • POST /v1/user - User registration
    • GET /v1/product/{id} - View products
    • GET /v1/product/{id}/image* - View product images
    • GET /healthz - Health checks
  2. Authenticated Endpoints:

    • All user operations (GET, PUT)
    • Product creation (POST)
    • Product modifications (PATCH, PUT, DELETE)
    • Image operations (POST, DELETE)
  3. Ownership Validation:

    • Users can only access/modify their own user data
    • Users can only modify products they own (owner_user_id check)
    • Product owners can manage product images

Password Requirements

Enforced via @Pattern annotation on UserRequestDTO:

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{}|;:',.<>?/~`]).{8,15}$
  • 8-15 characters in length
  • At least one uppercase letter (A-Z)
  • At least one lowercase letter (a-z)
  • At least one digit (0-9)
  • At least one special character from: `!@#$%^&*()_+-=[]{}|;:',.<>?/~``

Security Configuration Highlights

From SecurityConfig.java:

  • CSRF disabled (stateless API)
  • Session management: STATELESS
  • HTTP Basic authentication with UserDetailsService integration
  • Authorization rules defined via SecurityFilterChain
  • All passwords stored as BCrypt hashes, never in plaintext

Additional Security Measures

  • Query Parameter Rejection: Most endpoints reject unexpected query parameters (400 Bad Request)
  • Request Body Validation: GET and DELETE endpoints reject request bodies
  • SQL Injection Prevention: JPA/Hibernate parameterized queries
  • XSS Prevention: X-Content-Type-Options: nosniff header
  • Cache Control: Response headers prevent caching of sensitive data
  • Read-Only Fields Protection: DTOs enforce that auto-generated fields cannot be set by users

Cloud Infrastructure

Packer AMI Build

The application is packaged as a custom Amazon Machine Image (AMI) using Packer.

Configuration File: aws-ubuntu.pkr.hcl

AMI Specifications:

  • Base Image: Ubuntu 24.04 LTS Minimal (ubuntu/images/hbm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*)
  • Instance Type: t2.micro
  • Storage: 25GB GP2 EBS volume
  • Region: Configurable via variables

Provisioning Steps (via scripts/setup.sh):

  1. Update system packages (apt-get update && upgrade)
  2. Install OpenJDK 21 JRE Headless
  3. Create system user csye6225 (non-login, system account)
  4. Create application directory: /opt/csye6225
  5. Copy application JAR to /opt/csye6225/webapp.jar
  6. Copy configuration file: /opt/csye6225/application.properties
  7. Install CloudWatch Agent (via scripts/install-cloudwatch.sh)
  8. Copy CloudWatch config: /opt/cloudwatch-config.json
  9. Copy systemd service file: /etc/systemd/system/webapp.service
  10. Create logs directory: /opt/csye6225/logs
  11. Set proper ownership (csye6225:csye6225) and permissions
  12. Enable systemd service (auto-start on boot)

Build Command:

packer build \
  -var "aws_region=us-east-1" \
  -var "subnet_id=subnet-xxxxx" \
  aws-ubuntu.pkr.hcl

Systemd Service

Service File: webapp.service

[Unit]
Description=Webapp Spring Boot Application
After=network.target

[Service]
Type=simple
User=csye6225
Group=csye6225
WorkingDirectory=/opt/csye6225
EnvironmentFile=/opt/csye6225/app.env
ExecStart=/usr/bin/java -jar /opt/csye6225/webapp.jar
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target

Features:

  • Runs as non-root user (csye6225)
  • Auto-restart on failure with 10-second delay
  • Loads environment variables from /opt/csye6225/app.env
  • Enabled on system boot

Management Commands:

sudo systemctl start webapp      # Start application
sudo systemctl stop webapp       # Stop application
sudo systemctl status webapp     # Check status
sudo systemctl restart webapp    # Restart application
sudo journalctl -u webapp -f     # View logs

AWS S3 Integration

Configuration (in application.properties):

aws.region=${AWS_REGION:us-east-1}
aws.s3.bucket.name=${S3_BUCKET_NAME:}

S3 Client Setup (S3Config.java):

  • Uses AWS SDK v2
  • Instance role-based authentication (no hardcoded credentials)
  • Regional endpoint configuration
  • Conditional bean creation (only if S3_BUCKET_NAME configured)

Storage Path Structure:

s3://bucket-name/
└── users/
    └── {userId}/
        └── products/
            └── {productId}/
                ├── {uuid}-image1.jpg
                ├── {uuid}-image2.png
                └── {uuid}-image3.jpeg

Operations:

  • Upload: PutObjectRequest with automatic UUID prefix to prevent filename collisions
  • Delete: DeleteObjectRequest with full S3 key path
  • Metadata: Content-Type preserved from original upload

Monitoring and Observability

CloudWatch Integration

CloudWatch Agent Configuration (cloudwatch-config.json):

{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/opt/csye6225/logs/application.log",
            "log_group_name": "/aws/ec2/webapp",
            "log_stream_name": "{instance_id}-application",
            "timezone": "UTC"
          }
        ]
      }
    }
  },
  "metrics": {
    "namespace": "WebApp",
    "metrics_collected": {
      "statsd": {
        "service_address": ":8125",
        "metrics_collection_interval": 60,
        "metrics_aggregation_interval": 60
      }
    }
  }
}

Features:

  • Log Group: /aws/ec2/webapp
  • Log Stream: {instance_id}-application (unique per EC2 instance)
  • Metrics Namespace: WebApp
  • StatsD Port: 8125 (UDP)
  • Collection Interval: 60 seconds

Structured Logging

Configuration (logback-spring.xml):

  • Console Appender: For development (stdout)
  • File Appender: For production (/opt/csye6225/logs/application.log)
  • Format: JSON via Logstash Encoder
  • Rolling Policy:
    • Max file size: 10MB
    • Max history: 7 days
    • Total size cap: 100MB
    • Compression: .gz

Log Fields:

  • timestamp: ISO-8601 format
  • level: DEBUG, INFO, WARN, ERROR
  • logger: Fully qualified class name
  • message: Log message
  • requestId: Unique UUID per request (via MDC)
  • thread: Thread name
  • exception: Stack trace if error

Example Log Entry:

{
  "timestamp": "2025-11-04T10:50:32.123Z",
  "level": "INFO",
  "logger": "com.example.webapp.controller.ProductController",
  "message": "Product created with ID: 1",
  "requestId": "a3f8b2c4-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
  "thread": "http-nio-8080-exec-1"
}

Metrics Collection

Framework: Micrometer with StatsD export

Configuration (application.properties):

management.metrics.export.statsd.enabled=true
management.metrics.export.statsd.flavor=etsy
management.metrics.export.statsd.host=localhost
management.metrics.export.statsd.port=8125
management.metrics.export.statsd.protocol=udp

Metrics Tracked (via MetricsService and MetricsFilter):

  1. API Call Counts:

    • api.calls - Tagged with endpoint and method
    • Examples: api.calls,endpoint=/v1/user,method=POST
  2. Response Times:

    • api.response.time - Tagged with endpoint and method
    • Measured in milliseconds
  3. Database Query Times:

    • db.query.time - Tagged with operation
    • Measured in milliseconds
  4. S3 Operation Times:

    • s3.operation.time - Tagged with operation type (upload, delete)
    • Measured in milliseconds
  5. Health Check Metrics:

    • health.check - Count of health check requests
    • health.check.time - Response time

Metrics Format (StatsD):

api.calls,endpoint=/v1/product,method=POST:1|c
api.response.time,endpoint=/v1/product,method=POST:45|ms
db.query.time,operation=save:12|ms
s3.operation.time,operation=upload:230|ms

Request Tracing

Implementation (RequestLoggingFilter):

  • Generates unique UUID for each request
  • Stored in MDC (Mapped Diagnostic Context) as requestId
  • Automatically included in all log entries during request lifecycle
  • Cleared after request completion
  • Enables distributed tracing across logs

Usage:

  1. Request arrives → UUID generated → Added to MDC
  2. All log statements include requestId in JSON
  3. Search CloudWatch Logs by requestId to trace full request flow

CI/CD Pipelines

GitHub Actions Workflows

1. Integration Tests Workflow

File: .github/workflows/test.yml

Triggers:

  • Pull requests to main branch

Jobs:

test:
  runs-on: ubuntu-latest
  services:
    mysql:
      image: mysql:8.0
      env:
        MYSQL_ROOT_PASSWORD: root
        MYSQL_DATABASE: webapp
      options: >-
        --health-cmd="mysqladmin ping"
        --health-interval=10s
        --health-timeout=5s
        --health-retries=3
  steps:
    - Checkout code
    - Setup Java 21
    - Create DB user with privileges
    - Build with Maven (skip tests)
    - Run all 160 tests
    - Generate JaCoCo coverage report
    - Upload coverage artifacts

Environment:

  • MySQL 8.0 service container
  • Java 21
  • Maven 3.x

Test Execution:

  • Runs all integration and unit tests
  • Validates 91% code coverage
  • Blocks PR merge if tests fail (branch protection)

2. Packer Build Workflow

File: .github/workflows/packer-build.yml

Triggers:

  • Push to main branch (after PR merge)

Jobs:

packer-build:
  runs-on: ubuntu-latest
  steps:
    - Checkout code
    - Setup Java 21
    - Build JAR with Maven (skip tests - already validated)
    - Configure AWS credentials
    - Setup Packer
    - Initialize Packer
    - Build AMI with custom configuration
    - Share AMI with demo account

Secrets Required:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_REGION
  • SUBNET_ID
  • DEMO_ACCOUNT_ID

Workflow Variables:

  • Packer variables injected via -var flags
  • AMI shared with specified AWS account for deployment

3. Packer Validation Workflow

File: .github/workflows/packer-status-check.yml

Triggers:

  • Pull requests to main branch

Jobs:

packer-validate:
  runs-on: ubuntu-latest
  steps:
    - Checkout code
    - Setup Java 21
    - Build JAR (skip tests)
    - Setup Packer
    - Run packer fmt -check (format validation)
    - Run packer validate (template validation)

Purpose:

  • Validates Packer template syntax before merge
  • Ensures AMI builds will succeed when merged to main
  • Part of required status checks for PR approval

Branch Protection Rules

Main Branch Protection:

  • ✅ Require pull request before merging
  • ✅ Require status checks to pass:
    • Integration tests (160 tests)
    • Packer validation
    • All checks must be up-to-date
  • ✅ No direct pushes to main
  • ✅ Require conversation resolution before merging
  • ✅ Linear history enforced

Testing

Test Coverage: 91%

Total Tests: 160

  • Integration Tests: 95 tests across 3 test classes
  • Unit Tests: 65 tests across 9 test classes

Integration Test Suites

1. UserIntegrationTest (34 tests)

Coverage: src/main/java/com/example/webapp/controller/UserController.java

Test Categories:

  • User registration validation (email format, password complexity, duplicate prevention)
  • Authentication flows (Basic Auth, invalid credentials, missing credentials)
  • Authorization checks (access control, forbidden resource access)
  • User retrieval (successful GET, 403 for other users, 404 for non-existent)
  • User updates (partial updates, password changes, validation errors)
  • Query parameter rejection
  • Request body validation on GET requests
  • Read-only field protection

Key Test Methods:

testCreateUser_Success()
testCreateUser_InvalidEmail()
testCreateUser_WeakPassword()
testCreateUser_DuplicateEmail()
testGetUser_Success()
testGetUser_Unauthorized()
testGetUser_Forbidden()
testUpdateUser_Success()
testUpdateUser_PartialUpdate()
testUpdateUser_InvalidPassword()

2. ProductIntegrationTest (49 tests)

Coverage: src/main/java/com/example/webapp/controller/ProductController.java

Test Categories:

  • Product creation (all fields, validation, SKU uniqueness)
  • Product retrieval (public access, 404 handling)
  • Partial updates (PATCH - individual fields, ownership validation)
  • Full updates (PUT - all required fields, validation)
  • Product deletion (ownership, cascading to images)
  • Quantity validation (>= 0 constraint)
  • SKU uniqueness enforcement
  • Owner-based authorization
  • Read-only field protection
  • Query parameter rejection

Key Test Methods:

testCreateProduct_Success()
testCreateProduct_InvalidQuantity()
testCreateProduct_DuplicateSku()
testGetProduct_Success()
testGetProduct_NotFound()
testPatchProduct_Success()
testPatchProduct_Forbidden()
testPutProduct_Success()
testPutProduct_MissingFields()
testDeleteProduct_Success()
testDeleteProduct_Forbidden()

3. HealthCheckIntegrationTest (12 tests)

Coverage: src/main/java/com/example/webapp/controller/HealthController.java

Test Categories:

  • Successful health checks (200 OK)
  • Database connectivity verification
  • Query parameter rejection (400 Bad Request)
  • Request body rejection (400 Bad Request)
  • Unsupported HTTP methods (405 Method Not Allowed)
  • Database unavailability handling (503 Service Unavailable)
  • HealthCheck record creation

Key Test Methods:

testHealthCheck_Success()
testHealthCheck_WithQueryParams_Returns400()
testHealthCheck_WithRequestBody_Returns400()
testHealthCheck_PostMethod_Returns405()
testHealthCheck_DatabaseConnectivity()

Unit Test Coverage

Entity Tests:

  • User entity validation
  • Product entity validation
  • Image entity relationships
  • Timestamp auto-generation

DTO Tests:

  • UserRequestDTO validation rules
  • UserResponseDTO field exclusions
  • ProductRequestDTO constraints
  • ImageResponseDTO mapping

Service Layer Tests:

  • UserService business logic
  • ProductService CRUD operations
  • ImageService S3 integration (mocked)
  • S3Service operations (mocked)
  • MetricsService recording

Repository Tests:

  • Custom query methods
  • JPA findBy methods
  • Unique constraint validation

Utility Tests:

  • Password validation logic
  • Email format validation
  • SKU uniqueness checks

Running Tests

All Tests:

mvn test

Specific Test Class:

mvn test -Dtest=UserIntegrationTest

Specific Test Method:

mvn test -Dtest=UserIntegrationTest#testCreateUser_Success

With Coverage Report:

mvn clean test jacoco:report

View report: target/site/jacoco/index.html

Integration Tests Only:

mvn test -Dtest=*IntegrationTest

Unit Tests Only:

mvn test -Dtest=*Test,!*IntegrationTest

Test Configuration

Test Profile (application-test.properties):

spring.datasource.url=jdbc:mysql://localhost:3306/webapp_test
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=false
aws.s3.bucket.name=test-bucket

Test Annotations:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@TestMethodOrder(OrderAnnotation.class)
@Sql(executionPhase = BEFORE_TEST_METHOD, scripts = "classpath:cleanup.sql")

Key Testing Libraries:

  • REST Assured: Fluent HTTP API testing
  • Mockito: Mocking S3 service, repositories
  • AssertJ: Fluent assertions
  • Spring Boot Test: Application context loading
  • JUnit 5: Test framework with parameterized tests

Configuration

Environment Variables

Database Configuration:

DB_HOST=localhost              # MySQL host (default: localhost)
DB_PORT=3306                   # MySQL port (default: 3306)
DB_NAME=webapp                 # Database name (default: webapp)
DB_USER=csye6225               # Database user (default: csye6225)
DB_PASSWORD=your_password      # Database password (required)

AWS Configuration:

AWS_REGION=us-east-1          # AWS region (default: us-east-1)
S3_BUCKET_NAME=your-bucket    # S3 bucket for images (required for image features)

Application Configuration:

SPRING_PROFILES_ACTIVE=prod   # Spring profile (default: none)
SERVER_PORT=8080              # Application port (default: 8080)

Application Properties

File: src/main/resources/application.properties

Database Settings:

spring.application.name=webapp
spring.datasource.url=jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:webapp}
spring.datasource.username=${DB_USER:csye6225}
spring.datasource.password=${DB_PASSWORD:}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

JPA/Hibernate Settings:

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=false
logging.level.org.hibernate.SQL=INFO

Server Settings:

server.port=8080
server.error.whitelabel.enabled=false

AWS Settings:

aws.region=${AWS_REGION:us-east-1}
aws.s3.bucket.name=${S3_BUCKET_NAME:}

Metrics Settings:

management.metrics.export.statsd.enabled=true
management.metrics.export.statsd.flavor=etsy
management.metrics.export.statsd.host=localhost
management.metrics.export.statsd.port=8125
management.metrics.export.statsd.protocol=udp
management.metrics.distribution.percentiles-histogram.http.server.requests=false

# Disable Spring Data repository metrics to prevent UNKNOWN values in CloudWatch
management.metrics.enable.data.repository.invocations=false

Production Configuration

Environment File (on EC2): /opt/csye6225/app.env

DB_HOST=your-rds-endpoint.rds.amazonaws.com
DB_PORT=3306
DB_NAME=webapp
DB_USER=admin
DB_PASSWORD=your_secure_password
AWS_REGION=us-east-1
S3_BUCKET_NAME=your-production-bucket
SPRING_PROFILES_ACTIVE=prod

Loaded by systemd service via:

EnvironmentFile=/opt/csye6225/app.env

Deployment

Production Deployment (Auto-Scaling with Load Balancer)

Prerequisites

  1. Custom AMI built via Packer (see CI/CD Pipelines)
  2. AWS infrastructure provisioned via Terraform:
    • VPC with 3 public and 3 private subnets across availability zones
    • RDS MySQL instance (8.0+) in private subnets
    • S3 bucket for image storage
    • Application Load Balancer
    • Auto Scaling Group (min: 3, max: 5 instances)
    • Route53 hosted zone for domain
  3. IAM instance role with permissions:
    • s3:PutObject, s3:GetObject, s3:DeleteObject on image bucket
    • cloudwatch:PutMetricData, logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents

Architecture Overview

The application is deployed using AWS Auto Scaling Groups behind an Application Load Balancer:

  1. Route53Application Load BalancerAuto Scaling Group (3-5 instances)RDS/S3
  2. Instances are automatically launched/terminated based on CPU utilization
  3. Load balancer distributes traffic across healthy instances
  4. Access application via domain: http://dev.yourdomain.tld or http://demo.yourdomain.tld

Note: Direct instance IP access is disabled. Application only accessible via load balancer.

Deployment Steps

The infrastructure is fully automated via Terraform. Manual deployment is NOT required.

1. Build Custom AMI (Automated via GitHub Actions):

# Triggered automatically on PR merge to main branch
# See .github/workflows/packer-build.yml
# Creates AMI with application pre-installed

2. Deploy Infrastructure via Terraform:

cd tf-aws-infra-02
terraform init
terraform plan -var-file="dev.tfvars"  # or demo.tfvars
terraform apply -var-file="dev.tfvars"

3. Verify Deployment:

# Get load balancer DNS name
terraform output

# Test application via domain (wait 2-3 minutes for DNS propagation)
curl http://dev.yourdomain.tld/healthz
# Expected: 200 OK

4. Monitor Auto Scaling:

# View Auto Scaling Group status
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names csye6225-asg

# View running instances
aws ec2 describe-instances --filters "Name=tag:Name,Values=*asg-instance*" --query 'Reservations[*].Instances[*].[InstanceId,State.Name,PrivateIpAddress]'

Environment Configuration

Environment variables are automatically configured via Launch Template user data:

  • DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD - From RDS instance
  • AWS_REGION - From Terraform variable
  • S3_BUCKET_NAME - From S3 bucket resource

File location: /opt/csye6225/app.env (created automatically on instance launch)


Manual EC2 Deployment (Legacy - For Development Only)

For local development or testing, you can deploy a single EC2 instance manually. This approach is NOT recommended for production.

Launch EC2 Instance:

aws ec2 run-instances \
  --image-id ami-xxxxx \
  --instance-type t2.micro \
  --key-name your-key-pair \
  --security-group-ids sg-xxxxx \
  --subnet-id subnet-xxxxx \
  --iam-instance-profile Name=WebAppInstanceRole \
  --user-data file://user-data.sh

Configure and Start:

# SSH into instance
ssh -i your-key.pem ubuntu@instance-ip

# Environment variables are created by user-data script
# Verify application is running
sudo systemctl status webapp
curl http://localhost:8080/healthz

Load Balancer & Auto Scaling Details

Application Load Balancer Configuration

  • Name: csye6225-lb
  • Scheme: Internet-facing
  • Target Group: Port 8080, HTTP protocol
  • Health Check Path: /healthz
  • Health Check Interval: 30 seconds
  • Healthy Threshold: 2 consecutive successes
  • Unhealthy Threshold: 2 consecutive failures
  • Timeout: 5 seconds
  • Success Codes: 200
  • Listener: HTTP port 80 → Forward to target group

Auto Scaling Group Configuration

  • Name: csye6225-asg
  • Launch Template: csye6225_asg (latest version)
  • IAM instance profile: WebAppInstanceRole
  • User data: Script to configure environment variables
  • Security groups: Application security group

Auto Scaling Group:

  • Min size: 3 instances (ensures high availability)
  • Max size: 5 instances
  • Desired capacity: 1 instance (scales up as needed)
  • Cooldown period: 60 seconds
  • Deployment: Spans 3 availability zones
  • Health check type: ELB (via load balancer target group)
  • Registered with: csye6225-lb-tg target group

Scaling Policies:

  • Scale Up Policy:
    • Trigger: Average CPU Utilization > 5%
    • Action: Add 1 instance
    • Cooldown: 60 seconds
  • Scale Down Policy:
    • Trigger: Average CPU Utilization < 3%
    • Action: Remove 1 instance
    • Cooldown: 60 seconds

CloudWatch Alarms:

  • csye6225-cpu-high: Monitors ASG average CPU > 5%
  • csye6225-cpu-low: Monitors ASG average CPU < 3%

Database Migration

Initial Schema Setup: Schema is automatically created by Hibernate DDL on first run.

Manual Schema Inspection:

USE webapp;
SHOW TABLES;
DESCRIBE user;
DESCRIBE product;
DESCRIBE image;
DESCRIBE health_check;

Backup Strategy:

# Backup
mysqldump -h your-rds-endpoint -u admin -p webapp > backup.sql

# Restore
mysql -h your-rds-endpoint -u admin -p webapp < backup.sql

Monitoring Deployment

Check Application Logs:

# Systemd logs
sudo journalctl -u webapp -f

# Application log file
sudo tail -f /opt/csye6225/logs/application.log

Check CloudWatch Logs:

  • Log Group: /aws/ec2/webapp
  • Log Stream: {instance-id}-application

Check CloudWatch Metrics:

  • Namespace: WebApp
  • Metrics: api.calls, api.response.time, db.query.time, s3.operation.time

Verify S3 Bucket:

aws s3 ls s3://your-webapp-images-bucket/users/

Error Handling

The application uses a centralized exception handler (GlobalExceptionHandler) that returns consistent error responses with appropriate HTTP status codes.

HTTP Status Codes

Code Meaning When It Occurs
200 OK Success Successful GET requests
201 Created Resource created Successful POST for user, product, image
204 No Content Success, no body Successful PUT, PATCH, DELETE
400 Bad Request Invalid input Validation failures, malformed JSON, unexpected query params/body, duplicate SKU/email
401 Unauthorized Authentication required Missing or invalid credentials
403 Forbidden Authorization failed Attempting to access/modify another user's resources
404 Not Found Resource not found User, product, or image doesn't exist
405 Method Not Allowed Unsupported method Using POST on /healthz, etc.
503 Service Unavailable Service failure Database connection lost, S3 unreachable

Error Response Format

All errors return JSON with consistent structure:

Example 400 Bad Request:

{
  "timestamp": "2025-11-04T10:55:30.123Z",
  "status": 400,
  "error": "Bad Request",
  "message": "Password must be 8-15 characters with uppercase, lowercase, digit, and special character",
  "path": "/v1/user"
}

Example 401 Unauthorized:

{
  "timestamp": "2025-11-04T10:56:00.456Z",
  "status": 401,
  "error": "Unauthorized",
  "message": "Full authentication is required to access this resource",
  "path": "/v1/product"
}

Example 403 Forbidden:

{
  "timestamp": "2025-11-04T10:57:00.789Z",
  "status": 403,
  "error": "Forbidden",
  "message": "You don't have permission to access this resource",
  "path": "/v1/user/2"
}

Common Error Scenarios

Authentication Errors

Missing Credentials:

curl -X GET http://localhost:8080/v1/user/1
# Response: 401 Unauthorized

Invalid Credentials:

curl -X GET http://localhost:8080/v1/user/1 \
  -u "user@example.com:wrongpassword"
# Response: 401 Unauthorized

Authorization Errors

Accessing Another User's Data:

curl -X GET http://localhost:8080/v1/user/2 \
  -u "user1@example.com:password123"
# Response: 403 Forbidden (if user1 has ID 1, not 2)

Modifying Another User's Product:

curl -X DELETE http://localhost:8080/v1/product/5 \
  -u "user1@example.com:password123"
# Response: 403 Forbidden (if product 5 is owned by user 2)

Validation Errors

Weak Password:

curl -X POST http://localhost:8080/v1/user \
  -H "Content-Type: application/json" \
  -d '{"username":"test@example.com","password":"weak","first_name":"Test","last_name":"User"}'
# Response: 400 Bad Request - Password validation failed

Negative Quantity:

curl -X POST http://localhost:8080/v1/product \
  -u "user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{"name":"Product","description":"Desc","sku":"SKU1","manufacturer":"Mfg","quantity":-5}'
# Response: 400 Bad Request - Quantity must be >= 0

Duplicate SKU:

curl -X POST http://localhost:8080/v1/product \
  -u "user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{"name":"Product","description":"Desc","sku":"EXISTING-SKU","manufacturer":"Mfg","quantity":10}'
# Response: 400 Bad Request - SKU already exists

Query Parameter Errors

Unexpected Query Parameters:

curl -X GET "http://localhost:8080/healthz?debug=true"
# Response: 400 Bad Request - Query parameters not allowed

curl -X GET "http://localhost:8080/v1/product/1?details=full"
# Response: 400 Bad Request - Query parameters not allowed

Request Body Errors

Request Body on GET:

curl -X GET http://localhost:8080/v1/user/1 \
  -u "user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{"extra":"data"}'
# Response: 400 Bad Request - Request body not allowed

Database Connectivity Errors

Database Down:

# If MySQL is unavailable
curl -X GET http://localhost:8080/healthz
# Response: 503 Service Unavailable

curl -X POST http://localhost:8080/v1/user \
  -H "Content-Type: application/json" \
  -d '{"username":"test@example.com","password":"Password123!","first_name":"Test","last_name":"User"}'
# Response: 503 Service Unavailable - Database connection error

S3 Errors

S3 Upload Failure:

# If S3 bucket doesn't exist or permissions are wrong
curl -X POST http://localhost:8080/v1/product/1/image \
  -u "owner@example.com:password" \
  -F "file=@image.jpg"
# Response: 503 Service Unavailable - Failed to upload image to S3

Error Headers

All error responses include security headers:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
X-Content-Type-Options: nosniff

Project Structure

webapp-02/
├── src/
│   ├── main/
│   │   ├── java/com/example/webapp/
│   │   │   ├── WebappApplication.java          # Main entry point
│   │   │   ├── config/
│   │   │   │   ├── AppConfig.java              # BCrypt bean
│   │   │   │   ├── S3Config.java               # AWS S3 client config
│   │   │   │   └── SecurityConfig.java         # Spring Security config
│   │   │   ├── controller/
│   │   │   │   ├── HealthController.java       # /healthz endpoint
│   │   │   │   ├── UserController.java         # /v1/user endpoints
│   │   │   │   ├── ProductController.java      # /v1/product endpoints
│   │   │   │   └── ImageController.java        # /v1/product/{id}/image endpoints
│   │   │   ├── entity/
│   │   │   │   ├── User.java                   # User JPA entity
│   │   │   │   ├── Product.java                # Product JPA entity
│   │   │   │   ├── Image.java                  # Image JPA entity
│   │   │   │   └── HealthCheck.java            # HealthCheck JPA entity
│   │   │   ├── dto/
│   │   │   │   ├── UserRequestDTO.java         # User input DTO
│   │   │   │   ├── UserResponseDTO.java        # User response DTO
│   │   │   │   ├── ProductRequestDTO.java      # Product input DTO
│   │   │   │   ├── ProductResponseDTO.java     # Product response DTO
│   │   │   │   └── ImageResponseDTO.java       # Image response DTO
│   │   │   ├── service/
│   │   │   │   ├── UserService.java            # User business logic + UserDetailsService
│   │   │   │   ├── ProductService.java         # Product business logic
│   │   │   │   ├── ImageService.java           # Image management logic
│   │   │   │   ├── S3Service.java              # AWS S3 operations
│   │   │   │   ├── HealthCheckService.java     # Health check logic
│   │   │   │   └── MetricsService.java         # Metrics recording
│   │   │   ├── repository/
│   │   │   │   ├── UserRepository.java         # User data access
│   │   │   │   ├── ProductRepository.java      # Product data access
│   │   │   │   ├── ImageRepository.java        # Image data access
│   │   │   │   └── HealthCheckRepository.java  # HealthCheck data access
│   │   │   ├── filter/
│   │   │   │   ├── MetricsFilter.java          # API metrics collection
│   │   │   │   └── RequestLoggingFilter.java   # Request ID tracking
│   │   │   └── exception/
│   │   │       └── GlobalExceptionHandler.java # Centralized error handling
│   │   └── resources/
│   │       ├── application.properties          # Application configuration
│   │       └── logback-spring.xml              # Logging configuration
│   └── test/
│       └── java/com/example/webapp/
│           ├── UserIntegrationTest.java        # User endpoint tests (34 tests)
│           ├── ProductIntegrationTest.java     # Product endpoint tests (49 tests)
│           ├── HealthCheckIntegrationTest.java # Health endpoint tests (12 tests)
│           └── [9 more unit test files]        # Unit tests (65 tests)
├── scripts/
│   ├── setup.sh                                # Main deployment script
│   └── install-cloudwatch.sh                  # CloudWatch agent installer
├── .github/
│   └── workflows/
│       ├── test.yml                            # Integration tests workflow
│       ├── packer-build.yml                    # AMI build workflow
│       └── packer-status-check.yml             # Packer validation workflow
├── aws-ubuntu.pkr.hcl                          # Packer AMI template
├── cloudwatch-config.json                      # CloudWatch agent config
├── webapp.service                              # Systemd service file
├── pom.xml                                     # Maven configuration
├── README.md                                   # This file
├── HELP.md                                     # Spring Boot help
└── .gitignore                                  # Git ignore rules

Key Files Reference

Application Entry Point

  • WebappApplication.java: Main Spring Boot application class with @SpringBootApplication

Configuration Files

  • application.properties: Database, AWS, metrics, and JPA configuration
  • logback-spring.xml: Logging configuration with Logstash encoder
  • SecurityConfig.java: HTTP Basic Auth, authorization rules, stateless session
  • S3Config.java: AWS S3 client bean configuration
  • AppConfig.java: BCrypt password encoder bean

Infrastructure Files

  • aws-ubuntu.pkr.hcl: Packer template for AMI builds
  • cloudwatch-config.json: CloudWatch Agent configuration for logs and metrics
  • webapp.service: Systemd service definition
  • setup.sh: Main provisioning script (installs Java, creates user, deploys app)
  • install-cloudwatch.sh: CloudWatch Agent installation script

Build Files

  • pom.xml: Maven POM with dependencies, plugins, and build configuration

CI/CD Files

  • .github/workflows/test.yml: Runs 160 tests on PR
  • .github/workflows/packer-build.yml: Builds AMI on merge to main
  • .github/workflows/packer-status-check.yml: Validates Packer template on PR

Dependencies (Key Libraries)

From pom.xml:

Spring Boot Starters:

  • spring-boot-starter-web - REST API framework
  • spring-boot-starter-data-jpa - JPA/Hibernate ORM
  • spring-boot-starter-security - Authentication/authorization
  • spring-boot-starter-validation - Input validation
  • spring-boot-starter-actuator - Metrics and health endpoints

Database:

  • mysql-connector-j - MySQL JDBC driver

Cloud:

  • software.amazon.awssdk:s3 - AWS SDK v2 for S3

Monitoring:

  • micrometer-registry-statsd - Metrics export to StatsD
  • logstash-logback-encoder - JSON logging for CloudWatch

Testing:

  • spring-boot-starter-test - Test framework
  • rest-assured - HTTP API testing
  • jacoco-maven-plugin - Code coverage

Conclusion

This webapp is a production-ready cloud-native application demonstrating:

  • Modern Architecture: RESTful API with Spring Boot 3.x and Java 21
  • Security Best Practices: BCrypt hashing, stateless auth, ownership-based authorization
  • Cloud Integration: AWS S3 storage, CloudWatch monitoring, Packer AMI builds
  • High Quality: 91% test coverage with comprehensive integration tests
  • DevOps Excellence: CI/CD pipelines, automated testing, infrastructure as code
  • Observability: Structured logging, metrics export, request tracing
  • Scalability: Stateless design, load balancer ready, horizontal scaling support

Author

Arundhati Bandopadhyaya NUID: 002313855 Northeastern University-- Network Structures & Cloud Computing Course


License

This project is part of an academic course at Northeastern University.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors