Skip to content

drag0sd0g/MultipartUploadService

Repository files navigation

ζ—₯本θͺžγ§θͺ­γ‚€ (Japanese)

Simple File Storage Server & CLI

Clean Build CodeQL Docker Build Java Version Quarkus Version License

A modern, containerized file storage service with REST API, built with Quarkus and Java 21.

πŸ“‹ Table of Contents

  1. Features
  2. Prerequisites
  3. Quick Start with Docker
  4. Build & Packaging Instructions
  5. Running the Server
  6. Running the Client
  7. Observability
  8. Development
  9. Testing

✨ Features

  • πŸš€ Modern Stack: Built with Quarkus 3.17.4 and Java 21
  • 🐳 Containerized: Docker images for both server and client
  • πŸ“Š Observability: Prometheus metrics and structured logging
  • πŸ”’ Security: CodeQL scanning and dependency updates via Dependabot
  • βœ… Quality: Checkstyle, PMD, and SpotBugs static analysis
  • πŸ§ͺ Tested: 70% code coverage + comprehensive integration tests + PIT mutation testing
  • πŸ“– API Documentation: OpenAPI/Swagger UI included
  • 🌐 REST API: Simple, well-documented file storage API

Prerequisites

For Running with Docker (Recommended)

  • Docker 20.10+
  • Docker Compose 2.0+

For Building from Source

  • JDK 21 or newer (download from Adoptium)
  • JAVA_HOME environment variable set to the JDK installation directory

πŸš€ Quick Start with Docker

The fastest way to get started is using Docker Compose:

# Clone the repository
git clone https://github.com/drag0sd0g/MultipartUploadService.git
cd MultipartUploadService

# Start both server and client
docker-compose up -d

# Check server health
curl http://localhost:8080/q/health

# View logs
docker-compose logs -f server

# Stop services
docker-compose down

The server will be available at http://localhost:8080 with:


Build & Packaging Instructions

Run one of the following commands from the repository root directory to build the binaries of both client and server.

On Windows

gradlew clean build distClient distServer

On Unix

chmod +x gradlew && ./gradlew clean build distClient distServer

Client and server jars will be deployed in the build folder under fsclient/ and fsserver/ directories.

Building Docker Images

# Build server image
docker build -f file-storage-server/Dockerfile -t file-storage-server:latest .

# Build client image
docker build -f file-storage-client/Dockerfile -t file-storage-client:latest .

Running the Server

Using Docker (Recommended)

docker run -p 8080:8080 -v $(pwd)/data-server:/app/data-server file-storage-server:latest

Using Java

To run the file storage server navigate to build/fsserver and then run:

java -jar file-storage-server-1.0.0-SNAPSHOT.jar

Properties are kept in the application.properties file under src/main/resources but there are also plenty of default properties assumed by Quarkus. If you wish to override some of these (for example the port number or host), you can do so by passing the override with -D args to the jar e.g.

java -Dquarkus.http.host="192.168.11.7" -Dquarkus.http.port=8085 -jar file-storage-server-1.0.0-SNAPSHOT.jar

Server Notes

  • By default, the server will start up at http://127.0.0.1:8080
  • REST API documentation available at http://127.0.0.1:8080/q/swagger-ui/
  • OpenAPI spec available at http://127.0.0.1:8080/q/openapi
  • Quarkus framework stores multipart/form-data files in a temporary location from where they will be copied to a designated persistent storage folder (called data-server). Quarkus automatically removes the files from the temporary location after serving the request
  • While no total storage limit is imposed on the server side, a limit of 10MB is set on each file we want to upload. This is driven by the config quarkus.http.limits.max-form-attribute-size and any file exceeding that size will yield a HTTP 413 (CLI can also fetch this limit via a GET to /v1/stats/fileUploadSizeLimit)
  • This initial REST API version is /v1

Running the Client

Using Docker

# List files
docker run --network host file-storage-client:latest -l

# Upload a file
docker run --network host -v $(pwd):/data file-storage-client:latest -u /data/myfile.txt

# Delete a file
docker run --network host file-storage-client:latest -d myfile.txt

Using Java

From a separate command line, navigate to build/fsclient and then run one of the three possible commands:

Listing all uploaded files

java -jar file-storage-client-1.0.0-SNAPSHOT.jar --list-files

or

java -jar file-storage-client-1.0.0-SNAPSHOT.jar -l

Uploading a file

java -jar file-storage-client-1.0.0-SNAPSHOT.jar --upload-file <relative_or_absolute_path_to_file>

or

java -jar file-storage-client-1.0.0-SNAPSHOT.jar -u <relative_or_absolute_path_to_file>

Deleting an uploaded file

java -jar file-storage-client-1.0.0-SNAPSHOT.jar --delete-file <file_name>

or

java -jar file-storage-client-1.0.0-SNAPSHOT.jar -d <file_name>

For deletion, only file name is sufficient, no need to provide a path.

By default, the client will attempt to find the server at http://127.0.0.1:8080. You can however choose to overwrite this value by passing the system property -Dfsserver.api.rootUrl to the CLI executable e.g.

java -Dfsserver.api.rootUrl="http://192.168.11.7:8085" -jar file-storage-client-1.0.0-SNAPSHOT.jar -l

Client Notes

  • The client will expect certain command line options and/or arguments. If they are not provided, or provided wrongly the CLI will exit and a usage guide will be printed

πŸ“Š Observability

Prometheus Metrics

The server exposes Prometheus-compatible metrics at /q/metrics:

curl http://localhost:8080/q/metrics

Metrics include:

  • JVM metrics (memory, GC, threads)
  • HTTP server metrics (requests, response times)
  • System metrics (CPU, file descriptors)

Health Checks

# Overall health
curl http://localhost:8080/q/health

# Liveness probe
curl http://localhost:8080/q/health/live

# Readiness probe
curl http://localhost:8080/q/health/ready

Structured Logging

The server supports structured logging with configurable formats. See application.properties for logging configuration.


πŸ›  Development

Code Quality Tools

This project uses multiple static analysis tools:

  • Checkstyle: Code style enforcement
  • PMD: Code quality analysis
  • SpotBugs: Bug pattern detection

Run static analysis:

./gradlew checkstyleMain pmdMain spotbugsMain

Integration Tests

Comprehensive integration tests using Testcontainers:

./gradlew :integration-tests:test

The integration tests cover:

  • βœ… Happy paths (upload, list, delete)
  • ❌ Error scenarios (duplicate files, oversized files, missing files)
  • πŸ“Š Metrics and health endpoints
  • πŸ“– API documentation endpoints

Local Development with Docker Compose

# Start all services in development mode
docker-compose up

# Rebuild and restart after code changes
docker-compose up --build

# View logs
docker-compose logs -f

# Run integration tests against the environment
./gradlew :integration-tests:test

Testing

  • Unit Tests: JUnit 5 with Mockito
  • Integration Tests: Testcontainers + Docker Compose
  • Code Coverage: Jacoco reporting with 70% threshold
    • Test reports visible in build/jacocoHtml/index.html
  • Mutation Testing: PIT (PITest) with enforced thresholds
    • Client: 40% mutation score threshold
    • Server: 70% mutation score threshold
    • Reports visible in build/reports/pitest/index.html
  • Continuous Integration: GitHub Actions with automated testing

Run all tests:

./gradlew clean test

Run with coverage:

./gradlew clean build
# View coverage report: open file-storage-server/build/jacocoHtml/index.html

Run mutation testing:

./gradlew pitest
# View client report: open file-storage-client/build/reports/pitest/index.html
# View server report: open file-storage-server/build/reports/pitest/index.html

Note: Mutation testing is automatically run as part of the build. The build will fail if mutation scores drop below the configured thresholds.


πŸ“š API Documentation

After starting the server, interactive API documentation is available at:


🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ”„ Continuous Integration

  • Build: Automated builds on every push
  • Tests: Comprehensive test suite with coverage reporting
  • Security: CodeQL scanning for security vulnerabilities
  • Dependencies: Automated dependency updates via Dependabot
  • Docker: Automated Docker image builds and publishing
  • Releases: Automated release creation and artifact publishing

Built with ❀️ using Quarkus and Java 21

Packages

 
 
 

Contributors 3

  •  
  •  
  •