A modern, containerized file storage service with REST API, built with Quarkus and Java 21.
- Features
- Prerequisites
- Quick Start with Docker
- Build & Packaging Instructions
- Running the Server
- Running the Client
- Observability
- Development
- Testing
- π 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
- Docker 20.10+
- Docker Compose 2.0+
- JDK 21 or newer (download from Adoptium)
- JAVA_HOME environment variable set to the JDK installation directory
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 downThe server will be available at http://localhost:8080 with:
- REST API: http://localhost:8080/v1/files
- Swagger UI: http://localhost:8080/q/swagger-ui/
- Prometheus Metrics: http://localhost:8080/q/metrics
- Health Check: http://localhost:8080/q/health
Run one of the following commands from the repository root directory to build the binaries of both client and server.
gradlew clean build distClient distServerchmod +x gradlew && ./gradlew clean build distClient distServerClient and server jars will be deployed in the build folder under fsclient/ and fsserver/ directories.
# 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 .docker run -p 8080:8080 -v $(pwd)/data-server:/app/data-server file-storage-server:latestTo run the file storage server navigate to build/fsserver and then run:
java -jar file-storage-server-1.0.0-SNAPSHOT.jarProperties 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- 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
# 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.txtFrom a separate command line, navigate to build/fsclient and then run one of the three possible commands:
java -jar file-storage-client-1.0.0-SNAPSHOT.jar --list-filesor
java -jar file-storage-client-1.0.0-SNAPSHOT.jar -ljava -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>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- 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
The server exposes Prometheus-compatible metrics at /q/metrics:
curl http://localhost:8080/q/metricsMetrics include:
- JVM metrics (memory, GC, threads)
- HTTP server metrics (requests, response times)
- System metrics (CPU, file descriptors)
# 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/readyThe server supports structured logging with configurable formats. See application.properties for logging configuration.
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 spotbugsMainComprehensive integration tests using Testcontainers:
./gradlew :integration-tests:testThe integration tests cover:
- β Happy paths (upload, list, delete)
- β Error scenarios (duplicate files, oversized files, missing files)
- π Metrics and health endpoints
- π API documentation endpoints
# 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- 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 testRun with coverage:
./gradlew clean build
# View coverage report: open file-storage-server/build/jacocoHtml/index.htmlRun 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.htmlNote: Mutation testing is automatically run as part of the build. The build will fail if mutation scores drop below the configured thresholds.
After starting the server, interactive API documentation is available at:
- Swagger UI: http://localhost:8080/q/swagger-ui/
- OpenAPI Spec: http://localhost:8080/q/openapi
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- 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