# Description of Changes
This pull request introduces a job management system with enhanced
capabilities for handling asynchronous tasks, file operations, and
progress tracking. Key changes include the addition of new annotations
and aspects for job execution, file management services, and models for
job progress and results.

### Job Execution Enhancements:
*
[`common/src/main/java/stirling/software/common/annotations/AutoJobPostMapping.java`](diffhunk://#diff-570304f67b974d5bd30a28d05d34759b86bcb4a35148d779e2b46904e8dd2904R1-R47):
Added a custom annotation to simplify job handling for POST requests,
including support for retries, progress tracking, and resource
management.
*
[`common/src/main/java/stirling/software/common/aop/AutoJobAspect.java`](diffhunk://#diff-5f725b1d99dbc47dfe9b1d07f37382ca7c81d587725dc35a62c644d1a25f9869R1-R231):
Implemented an aspect to integrate job execution logic, handling
retries, asynchronous processing, and file management seamlessly.

### File Management:
*
[`common/src/main/java/stirling/software/common/service/FileStorage.java`](diffhunk://#diff-f382e12c197ad6f7c5b01b1cea912e9b141a4b4e4ab7f12baafa1b69cb112962R1-R152):
Added a service for storing, retrieving, and managing files using unique
IDs, enabling persistent file handling for jobs.
*
[`common/src/main/java/stirling/software/common/service/FileOrUploadService.java`](diffhunk://#diff-e0637404eea2b1c1413cf5f3247208a9196b14388a90a896314d3e9c2949c893R1-R78):
Added utility methods for converting files to `MultipartFile` and
resolving file paths.

### Job Models:
*
[`common/src/main/java/stirling/software/common/model/job/JobProgress.java`](diffhunk://#diff-edc765f0e32ef4cb5a03dd3badafad450336a5248221ecc27976eb692280f003R1-R15):
Introduced a model to represent job progress, including completion
percentage and status messages.
*
[`common/src/main/java/stirling/software/common/model/job/JobResult.java`](diffhunk://#diff-b34316aa0ebfd849f41086339ae0323cb5cc2066b8200c38c6a39564e17b88f3R1-R94):
Added a model to encapsulate job results, supporting both file-based and
object-based outcomes.
*
[`common/src/main/java/stirling/software/common/model/job/JobResponse.java`](diffhunk://#diff-b02e9f86d44beda10ceb66650c79d1e032acd6f6a609887fb5f5596713048ab1R1-R14):
Created a model for job responses, including async execution details and
job IDs.
*
[`common/src/main/java/stirling/software/common/model/job/JobStats.java`](diffhunk://#diff-6067e6bd9e44d9dc40419d2435fa24d6753ec51e3baf7967dbcbc1a51e95e8afR1-R43):
Added a model for tracking job statistics, such as total jobs, success
rates, and average processing times.

### Other Changes:
*
[`common/src/main/java/stirling/software/common/model/api/PDFFile.java`](diffhunk://#diff-d2419d05a852acf8f8d0bd5c3673bbdd8e385b2d5cf1d80fbd8b66691ebd2cb2L17-R24):
Updated the `PDFFile` model to include a `fileId` field for server-side
file references, enhancing flexibility in file handling.
*
[`common/build.gradle`](diffhunk://#diff-824c1e8ad11e20caed0bec7162a99779b9a4bcf1178d99fae3e39f69889f8959R31):
Added the `spring-boot-starter-aop` dependency to enable aspect-oriented
programming.

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: a <a>
This commit is contained in:
Anthony Stirling
2025-06-23 13:11:44 +01:00
committed by GitHub
co-authored by Copilot a <a>
parent ee8030c1c4
commit 7d7f1272e4
28 changed files with 4397 additions and 1 deletions
@@ -26,6 +26,8 @@ import stirling.software.proprietary.service.AuditService;
@Component
@Slf4j
@RequiredArgsConstructor
@org.springframework.core.annotation.Order(
10) // Lower precedence (higher number) - executes after AutoJobAspect
public class AuditAspect {
private final AuditService auditService;
@@ -36,6 +36,8 @@ import stirling.software.proprietary.service.AuditService;
@Component
@Slf4j
@RequiredArgsConstructor
@org.springframework.core.annotation.Order(
10) // Lower precedence (higher number) - executes after AutoJobAspect
public class ControllerAuditAspect {
private final AuditService auditService;
@@ -77,6 +79,12 @@ public class ControllerAuditAspect {
return auditController(joinPoint, "PATCH");
}
/** Intercept all methods with AutoJobPostMapping annotation */
@Around("@annotation(stirling.software.common.annotations.AutoJobPostMapping)")
public Object auditAutoJobMethod(ProceedingJoinPoint joinPoint) throws Throwable {
return auditController(joinPoint, "POST");
}
private Object auditController(ProceedingJoinPoint joinPoint, String httpMethod)
throws Throwable {
MethodSignature sig = (MethodSignature) joinPoint.getSignature();
@@ -0,0 +1,83 @@
package stirling.software.proprietary.controller;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.job.JobStats;
import stirling.software.common.service.JobQueue;
import stirling.software.common.service.TaskManager;
/**
* Admin controller for job management. These endpoints require admin privileges and provide insight
* into system jobs and queues.
*/
@RestController
@RequiredArgsConstructor
@Slf4j
public class AdminJobController {
private final TaskManager taskManager;
private final JobQueue jobQueue;
/**
* Get statistics about jobs in the system (admin only)
*
* @return Job statistics
*/
@GetMapping("/api/v1/admin/job/stats")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<JobStats> getJobStats() {
JobStats stats = taskManager.getJobStats();
log.info(
"Admin requested job stats: {} active, {} completed jobs",
stats.getActiveJobs(),
stats.getCompletedJobs());
return ResponseEntity.ok(stats);
}
/**
* Get statistics about the job queue (admin only)
*
* @return Queue statistics
*/
@GetMapping("/api/v1/admin/job/queue/stats")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<?> getQueueStats() {
Map<String, Object> queueStats = jobQueue.getQueueStats();
log.info("Admin requested queue stats: {} queued jobs", queueStats.get("queuedJobs"));
return ResponseEntity.ok(queueStats);
}
/**
* Manually trigger cleanup of old jobs (admin only)
*
* @return A response indicating how many jobs were cleaned up
*/
@PostMapping("/api/v1/admin/job/cleanup")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<?> cleanupOldJobs() {
int beforeCount = taskManager.getJobStats().getTotalJobs();
taskManager.cleanupOldJobs();
int afterCount = taskManager.getJobStats().getTotalJobs();
int removedCount = beforeCount - afterCount;
log.info(
"Admin triggered job cleanup: removed {} jobs, {} remaining",
removedCount,
afterCount);
return ResponseEntity.ok(
Map.of(
"message", "Cleanup complete",
"removedJobs", removedCount,
"remainingJobs", afterCount));
}
}