mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-07-23 13:45:21 +00:00
AOP Fixes for v2 async (#3934)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
parent
bbf5d5f6d4
commit
9e41c625a1
@ -43,6 +43,7 @@ public class AutoJobAspect {
|
|||||||
// This aspect will run before any audit aspects due to @Order(0)
|
// This aspect will run before any audit aspects due to @Order(0)
|
||||||
// Extract parameters from the request and annotation
|
// Extract parameters from the request and annotation
|
||||||
boolean async = Boolean.parseBoolean(request.getParameter("async"));
|
boolean async = Boolean.parseBoolean(request.getParameter("async"));
|
||||||
|
log.debug("AutoJobAspect: Processing {} {} with async={}", request.getMethod(), request.getRequestURI(), async);
|
||||||
long timeout = autoJobPostMapping.timeout();
|
long timeout = autoJobPostMapping.timeout();
|
||||||
int retryCount = autoJobPostMapping.retryCount();
|
int retryCount = autoJobPostMapping.retryCount();
|
||||||
boolean trackProgress = autoJobPostMapping.trackProgress();
|
boolean trackProgress = autoJobPostMapping.trackProgress();
|
||||||
@ -54,19 +55,8 @@ public class AutoJobAspect {
|
|||||||
retryCount,
|
retryCount,
|
||||||
trackProgress);
|
trackProgress);
|
||||||
|
|
||||||
// Copy and process arguments
|
// Process arguments in-place to avoid type mismatch issues
|
||||||
// In a test environment, we might need to update the original objects for verification
|
Object[] args = processArgsInPlace(joinPoint.getArgs(), async);
|
||||||
boolean isTestEnvironment = false;
|
|
||||||
try {
|
|
||||||
isTestEnvironment = Class.forName("org.junit.jupiter.api.Test") != null;
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// Not in a test environment
|
|
||||||
}
|
|
||||||
|
|
||||||
Object[] args =
|
|
||||||
isTestEnvironment
|
|
||||||
? processArgsInPlace(joinPoint.getArgs(), async)
|
|
||||||
: copyAndProcessArgs(joinPoint.getArgs(), async);
|
|
||||||
|
|
||||||
// Extract queueable and resourceWeight parameters and validate
|
// Extract queueable and resourceWeight parameters and validate
|
||||||
boolean queueable = autoJobPostMapping.queueable();
|
boolean queueable = autoJobPostMapping.queueable();
|
||||||
@ -229,79 +219,10 @@ public class AutoJobAspect {
|
|||||||
resourceWeight);
|
resourceWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates deep copies of arguments when needed to avoid mutating the original objects
|
|
||||||
* Particularly important for PDFFile objects that might be reused by Spring
|
|
||||||
*
|
|
||||||
* @param originalArgs The original arguments
|
|
||||||
* @param async Whether this is an async operation
|
|
||||||
* @return A new array with safely processed arguments
|
|
||||||
*/
|
|
||||||
private Object[] copyAndProcessArgs(Object[] originalArgs, boolean async) {
|
|
||||||
if (originalArgs == null || originalArgs.length == 0) {
|
|
||||||
return originalArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object[] processedArgs = new Object[originalArgs.length];
|
|
||||||
|
|
||||||
// Copy all arguments
|
|
||||||
for (int i = 0; i < originalArgs.length; i++) {
|
|
||||||
Object arg = originalArgs[i];
|
|
||||||
|
|
||||||
if (arg instanceof PDFFile pdfFile) {
|
|
||||||
// Create a copy of PDFFile to avoid mutating the original
|
|
||||||
// Using direct property access instead of reflection for better performance
|
|
||||||
PDFFile pdfFileCopy = new PDFFile();
|
|
||||||
pdfFileCopy.setFileId(pdfFile.getFileId());
|
|
||||||
pdfFileCopy.setFileInput(pdfFile.getFileInput());
|
|
||||||
|
|
||||||
// Case 1: fileId is provided but no fileInput
|
|
||||||
if (pdfFileCopy.getFileInput() == null && pdfFileCopy.getFileId() != null) {
|
|
||||||
try {
|
|
||||||
log.debug("Using fileId {} to get file content", pdfFileCopy.getFileId());
|
|
||||||
MultipartFile file = fileStorage.retrieveFile(pdfFileCopy.getFileId());
|
|
||||||
pdfFileCopy.setFileInput(file);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Failed to resolve file by ID: " + pdfFileCopy.getFileId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Case 2: For async requests, we need to make a copy of the MultipartFile
|
|
||||||
else if (async && pdfFileCopy.getFileInput() != null) {
|
|
||||||
try {
|
|
||||||
log.debug("Making persistent copy of uploaded file for async processing");
|
|
||||||
MultipartFile originalFile = pdfFileCopy.getFileInput();
|
|
||||||
String fileId = fileStorage.storeFile(originalFile);
|
|
||||||
|
|
||||||
// Store the fileId for later reference
|
|
||||||
pdfFileCopy.setFileId(fileId);
|
|
||||||
|
|
||||||
// Replace the original MultipartFile with our persistent copy
|
|
||||||
MultipartFile persistentFile = fileStorage.retrieveFile(fileId);
|
|
||||||
pdfFileCopy.setFileInput(persistentFile);
|
|
||||||
|
|
||||||
log.debug("Created persistent file copy with fileId: {}", fileId);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Failed to create persistent copy of uploaded file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processedArgs[i] = pdfFileCopy;
|
|
||||||
} else {
|
|
||||||
// For non-PDFFile objects, just pass the original reference
|
|
||||||
// If other classes need copy-on-write, add them here
|
|
||||||
processedArgs[i] = arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return processedArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes arguments in-place for testing purposes This is similar to our original
|
* Processes arguments in-place to handle file resolution and async file persistence.
|
||||||
* implementation before introducing copy-on-write It's only used in test environments to
|
* This approach avoids type mismatch issues by modifying the original objects directly.
|
||||||
* maintain test compatibility
|
|
||||||
*
|
*
|
||||||
* @param originalArgs The original arguments
|
* @param originalArgs The original arguments
|
||||||
* @param async Whether this is an async operation
|
* @param async Whether this is an async operation
|
||||||
|
@ -6,6 +6,8 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -28,6 +30,7 @@ public class JobResult {
|
|||||||
private String error;
|
private String error;
|
||||||
|
|
||||||
/** List of result files for jobs that produce files */
|
/** List of result files for jobs that produce files */
|
||||||
|
@JsonIgnore
|
||||||
private List<ResultFile> resultFiles;
|
private List<ResultFile> resultFiles;
|
||||||
|
|
||||||
/** Time when the job was created */
|
/** Time when the job was created */
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package stirling.software.common.service;
|
package stirling.software.common.service;
|
||||||
|
|
||||||
import io.github.pixee.security.ZipSecurity;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -21,6 +20,8 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.ZipSecurity;
|
||||||
|
|
||||||
import jakarta.annotation.PreDestroy;
|
import jakarta.annotation.PreDestroy;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -361,7 +362,8 @@ public class TaskManager {
|
|||||||
MultipartFile zipFile = fileStorage.retrieveFile(zipFileId);
|
MultipartFile zipFile = fileStorage.retrieveFile(zipFileId);
|
||||||
|
|
||||||
try (ZipInputStream zipIn =
|
try (ZipInputStream zipIn =
|
||||||
ZipSecurity.createHardenedInputStream(new ByteArrayInputStream(zipFile.getBytes()))) {
|
ZipSecurity.createHardenedInputStream(
|
||||||
|
new ByteArrayInputStream(zipFile.getBytes()))) {
|
||||||
ZipEntry entry;
|
ZipEntry entry;
|
||||||
while ((entry = zipIn.getNextEntry()) != null) {
|
while ((entry = zipIn.getNextEntry()) != null) {
|
||||||
if (!entry.isDirectory()) {
|
if (!entry.isDirectory()) {
|
||||||
|
@ -29,7 +29,8 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
|
|||||||
"type",
|
"type",
|
||||||
"principal",
|
"principal",
|
||||||
"startDate",
|
"startDate",
|
||||||
"endDate");
|
"endDate",
|
||||||
|
"async");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(
|
public boolean preHandle(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user