Vert.x-based microservice for saving JSON data via REST and gRPC, with admin endpoints for monitoring, file management, and dynamic configuration of a downloadable package (pack.zip). Designed for reliability, configurability, and operational visibility.
- ✅ REST JSON Saving: Save incoming JSON to files with UUID names.
- ✅ gRPC FileService: Unary, server-streaming, bidirectional methods.
- ✅ Configurable Endpoints:
- Multiple POST paths via
HTTP_ENDPOINT - Admin prefix via
PREFIX
- Multiple POST paths via
- ✅ Admin API:
GET /{prefix}/info— service status, file countGET /{prefix}/files— list saved.jsonfilesDELETE /{prefix}/files— delete all.jsonfilesPOST /{prefix}/setpack— upload and replacepack.zip(≤200 MB)GET /{prefix}/setmd5— calculate MD5 ofpack.zipGET /{prefix}/setmd5/{hex}— manually set expected MD5GET /getpack— downloadpack.zipwith correctX-File-MD5andContent-MD5headers
- ✅ Environment-driven config (ENV)
- ✅ Thread-safe MD5 updates via
volatile - ✅ Clean architecture: handlers separated by responsibility
- ✅ Simple echo server on port
8989
Saves JSON body to a .json file in UPLOAD_DIR.
Request:
{ "name": "John", "age": 30 }Response:
{
"success": true,
"message": "File saved successfully",
"filename": "a1b2c3d4-e5f6-7890.json",
"path": "/tmp/uploads/a1b2c3d4-e5f6-7890.json"
}Note: Actual endpoint(s) depend on
HTTP_ENDPOINT(e.g.,/api/users).
Returns service metadata and file statistics.
Example:
{
"service": "File Saver Service",
"http_port": 8888,
"grpc_port": 50051,
"upload_dir": "/tmp/uploads",
"files_count": 42,
"endpoints": {
"POST /api/users": "Save JSON to file",
"gRPC": {
"SaveFileUnary": "Unary RPC call",
"SaveFileStream": "Server streaming",
"SaveFileBidirectional": "Bidirectional streaming"
}
}
}Lists all .json files in upload directory.
Response:
{
"directory": "/tmp/uploads",
"total_files": 3,
"files": [
{
"filename": "a1b2c3.json",
"size": 256,
"created": 1719845678000,
"modified": 1719845678000
}
]
}Deletes all .json files from UPLOAD_DIR.
Response:
{
"success": true,
"message": "Cleanup completed",
"deleted_files": 3
}Uploads a new pack.zip file (≤ 200 MB). Automatically recalculates and updates PACK_FILE_MD5.
Request:
curl -X POST http://localhost:8888/api/v1/setpack \
-H "Content-Type: application/octet-stream" \
--data-binary @new_pack.zipResponse:
{
"success": true,
"message": "File saved successfully",
"path": "/storage/pack.zip",
"file_size": 104857600,
"file_md5": "d41d8cd98f00b204e9800998ecf8427e"
}- Without param: calculates MD5 of current
pack.zip - With param: sets
PACK_FILE_MD5manually
Calculate:
curl http://localhost:8888/api/v1/setmd5{ "file_md5": "d41d8cd98f00b204e9800998ecf8427e" }Set manually:
curl http://localhost:8888/api/v1/setmd5/7bbce66cdc6de3bd07f0798e27dfa262{
"success": true,
"message": "MD5 set manually",
"pack_file_md5": "7bbce66cdc6de3bd07f0798e27dfa262"
}Downloads the current pack.zip with correct headers.
Headers:
X-File-MD5: 7bbce66cdc6de3bd07f0798e27dfa262
Content-MD5: N2JiY2U2NmNkYzZkZTNiZDA3ZjA3OThlMjdkZmEyNjI=
Content-MD5— Base64-encoded MD5
SaveFileUnarySaveFileStreamSaveFileBidirectional
-
Build:
mvn clean package
-
Run:
java -jar target/json-file-saver-1.0.0-SNAPSHOT-fat.jar
or
mvn exec:java
-
Services:
- HTTP/REST:
http://localhost:8888 - gRPC:
localhost:50051 - Simple HTTP:
http://localhost:8989
- Default upload dir:
./uploads/
All settings via environment variables:
| Env Var | Default | Description |
|---|---|---|
UPLOAD_DIR |
./uploads/ |
Directory for JSON files |
STORAGE_DIR |
./storage/ |
Directory for pack.zip |
HTTP_PORT |
8888 |
REST server port |
GRPC_PORT |
50051 |
gRPC server port |
HTTP_ENDPOINT |
/save |
Comma-separated POST endpoints |
PREFIX |
/ |
Prefix for admin endpoints |
PACK_FILE_PATH |
pack.zip |
Name of downloadable file |
PACK_FILE_MD5 |
7bbce... |
Expected MD5 (used in headers) |
export UPLOAD_DIR=/tmp/my-uploads/
export STORAGE_DIR=/opt/storage/
export HTTP_PORT=8080
export HTTP_ENDPOINT=/save,/upload
export PREFIX=/api/v1
export GRPC_PORT=9090
mvn exec:javaNow accessible at:
POST http://localhost:8080/saveGET http://localhost:8080/api/v1/infoPOST http://localhost:8080/api/v1/setpackGET http://localhost:8080/getpack
- Modular handlers: each endpoint has its own class
- Thread-safe config:
packFileMd5isvolatile - Safe I/O: streams closed, size checks, error handling
- No global BodyHandler: avoids conflicts with binary uploads
- Supplier/Consumer pattern: dynamic MD5 updates
src/main/java/com/example/
├── RestRequestJsonFileSaver.java → main router
├── MainVerticle.java → deploys all services
├── config/ServiceConfig.java → config holder
├── handler/rest/JsonSaveHandler.java → saves JSON
├── handler/admin/InfoHandler.java → /info
├── handler/admin/FilesListHandler.java → /files (GET)
├── handler/admin/FilesDeleteHandler.java → /files (DELETE)
├── handler/storage/GetPackHandler.java → /getpack
├── handler/storage/SetPackHandler.java → /setpack
├── handler/storage/SetMd5Handler.java → /setmd5
└── util/Md5Util.java → MD5 calculation
| Method | Path | Purpose |
|---|---|---|
GET |
/{prefix}/info |
Service info + file count |
GET |
/{prefix}/files |
List uploaded JSON files |
DELETE |
/{prefix}/files |
Delete all JSON files |
POST |
/{prefix}/setpack |
Upload new pack.zip |
GET |
/{prefix}/setmd5 |
Calculate current MD5 |
GET |
/{prefix}/setmd5/{hex} |
Set MD5 manually |
Replace
{prefix}withPREFIXvalue
- ✅ Use
Dockerfor deployment - ✅ Add health check:
GET /health→{"status":"UP"} - ✅ Enable logging with
logback.xml - ✅ Monitor file count and disk usage
- ✅ Protect admin endpoints in production (future enhancement)
MIT