Try out chunking

This commit is contained in:
pratik
2025-10-21 15:19:49 -05:00
parent 9ad9e6b7b8
commit fdcc92eeb6
9 changed files with 849 additions and 2 deletions

View File

@@ -2,7 +2,10 @@ package com.cfdeployer.controller;
import com.cfdeployer.model.CfDeployRequest;
import com.cfdeployer.model.CfDeployResponse;
import com.cfdeployer.model.ChunkUploadRequest;
import com.cfdeployer.model.ChunkUploadResponse;
import com.cfdeployer.service.CfCliService;
import com.cfdeployer.service.ChunkedUploadService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@@ -22,6 +25,7 @@ public class CfDeployController {
private static final Logger log = LoggerFactory.getLogger(CfDeployController.class);
private final CfCliService cfCliService;
private final ChunkedUploadService chunkedUploadService;
private final ObjectMapper objectMapper;
@PostMapping(value = "/deploy", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@@ -118,6 +122,126 @@ public class CfDeployController {
}
}
// Chunked upload endpoints
@PostMapping("/upload/init")
public ResponseEntity<ChunkUploadResponse> initUpload(@RequestBody String requestJson) {
try {
log.info("Initializing chunked upload session");
// Validate the request JSON
CfDeployRequest request = objectMapper.readValue(requestJson, CfDeployRequest.class);
log.info("Creating upload session for app: {}", request.getAppName());
String sessionId = chunkedUploadService.createUploadSession(requestJson);
return ResponseEntity.ok(ChunkUploadResponse.builder()
.success(true)
.uploadSessionId(sessionId)
.message("Upload session created successfully")
.build());
} catch (Exception e) {
log.error("Error initializing upload session", e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ChunkUploadResponse.failure("Failed to initialize upload: " + e.getMessage()));
}
}
@PostMapping("/upload/chunk")
public ResponseEntity<ChunkUploadResponse> uploadChunk(
@RequestParam("uploadSessionId") String uploadSessionId,
@RequestParam("fileType") String fileType,
@RequestParam("chunkIndex") Integer chunkIndex,
@RequestParam("totalChunks") Integer totalChunks,
@RequestParam(value = "fileName", required = false) String fileName,
@RequestPart("chunk") MultipartFile chunk) {
try {
log.debug("Receiving chunk {}/{} for session: {}, fileType: {}",
chunkIndex + 1, totalChunks, uploadSessionId, fileType);
// Validate file type
if (!fileType.equals("jarFile") && !fileType.equals("manifest")) {
throw new IllegalArgumentException("Invalid file type. Must be 'jarFile' or 'manifest'");
}
chunkedUploadService.uploadChunk(uploadSessionId, fileType, fileName,
chunkIndex, totalChunks, chunk);
var session = chunkedUploadService.getSession(uploadSessionId);
var fileState = session.getFileStates().get(fileType);
return ResponseEntity.ok(ChunkUploadResponse.success(
uploadSessionId, fileType, chunkIndex, totalChunks,
fileState.getReceivedChunkCount()));
} catch (Exception e) {
log.error("Error uploading chunk", e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ChunkUploadResponse.failure("Failed to upload chunk: " + e.getMessage()));
}
}
@PostMapping("/upload/finalize")
public ResponseEntity<CfDeployResponse> finalizeUpload(@RequestParam("uploadSessionId") String uploadSessionId) {
try {
log.info("Finalizing upload for session: {}", uploadSessionId);
if (!chunkedUploadService.isSessionReady(uploadSessionId)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(CfDeployResponse.failure("Upload incomplete. Not all file chunks received.", null));
}
var session = chunkedUploadService.getSession(uploadSessionId);
if (session == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(CfDeployResponse.failure("Upload session not found or expired", null));
}
CfDeployRequest request = objectMapper.readValue(session.getRequestJson(), CfDeployRequest.class);
log.info("Starting deployment for app: {} from session: {}",
request.getAppName(), uploadSessionId);
// Get file paths from session
var jarState = session.getFileStates().get("jarFile");
var manifestState = session.getFileStates().get("manifest");
CfDeployResponse response = cfCliService.deployApplicationFromPaths(
request,
jarState.getTargetPath(),
manifestState.getTargetPath());
// Clean up session after deployment
chunkedUploadService.deleteSession(uploadSessionId);
if (Boolean.TRUE.equals(response.getSuccess())) {
return ResponseEntity.ok(response);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
} catch (Exception e) {
log.error("Error finalizing upload", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(CfDeployResponse.failure(
"Failed to finalize deployment: " + e.getMessage(),
e.toString()));
}
}
@GetMapping("/upload/status/{uploadSessionId}")
public ResponseEntity<?> getUploadStatus(@PathVariable String uploadSessionId) {
try {
var session = chunkedUploadService.getSession(uploadSessionId);
if (session == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("Upload session not found or expired");
}
return ResponseEntity.ok(session.getFileStates());
} catch (Exception e) {
log.error("Error getting upload status", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to get upload status: " + e.getMessage());
}
}
private void validateFiles(MultipartFile jarFile, MultipartFile manifest) {
if (jarFile.isEmpty()) {
throw new IllegalArgumentException("JAR file is empty");