Conversation
|
Claude finished @dsolistorres's task in 4m 39s —— View job 🔍 dotCMS Backend Review
Review posted. Found 2 High and 2 Medium issues across the 4 specialized reviewers. See the backend review comment for details. |
|
Rollback safety analysis complete. Label AI: Safe To Rollback applied. No unsafe categories matched — this is a pure Java thread-safety fix with no DB/ES/API/OSGi changes. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is kicking off a free cloud agent to fix this issue. This run is complimentary, but you can enable autofix for all future PRs in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2c3f0b8. Configure here.
|
Rollback safety analysis complete (re-run for new commits). Label AI: Safe To Rollback confirmed. No unsafe categories matched for the 2 new commits (
This is a pure Java thread-safety fix (moving request snapshot to HTTP thread) plus improved error handling in the async import path. |
… prevent recycling crash (#33908) The Tomcat RequestFacade was being accessed from a background thread after the HTTP response completed, causing IllegalStateException for large CSV imports. Moved mock request construction to the HTTP thread before submitting the Runnable, so the background thread never touches the recycled request. Refs: #33908 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… on import error (#33908) Add HibernateUtil.rollbackTransaction() in the async catch block to match the behavior of DotPortletAction._handleException() and prevent dirty transactions. Introduce STATUS_FAILED in ImportAuditUtil so failed imports are no longer marked as completed, giving the UI a way to surface the error. Refs: #33908 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f3a6691 to
894f2dc
Compare
🔍 dotCMS Backend Review[🟡 Medium]
importErrorMessage = ae.getMessage(); // ImportContentletsAction.java:296
// ...
dc.addParam(UtilMethods.isSet(errorMessage) && errorMessage.length() > 500 // ImportAuditUtil.java:311
? errorMessage.substring(0, 500) : errorMessage);💡 Use [🟡 Medium]
importErrorMessage = ae.getMessage(); // null for NullPointerException
// ...
ImportAuditUtil.setAuditRecordAsFailed(importId, importErrorMessage); // passes null💡 Fallback: [🟡 Medium]
if(ImportAuditUtil.cancelledImports.containsKey(importId)){
ImportAuditUtil.cancelledImports.remove(importId);
} else if(importFailed){ // never reached if cancelled+failed
ImportAuditUtil.setAuditRecordAsFailed(importId, importErrorMessage);💡 Separate the concerns: always remove the cancelled entry, then independently check ImportAuditUtil.cancelledImports.remove(importId);
if (importFailed) {
ImportAuditUtil.setAuditRecordAsFailed(importId, importErrorMessage);
}Next steps
|
…Failed and truncate error message (#33908) Make setAuditRecordAsFailed consistent with setAuditRecordCompleted by handling DB exceptions internally. Truncate the error message to 500 characters before persisting to bound stored size. Refs: #33908 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
You have used all of your free Bugbot PR reviews. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
… edge case (#33908) Fall back to exception class name when ae.getMessage() is null (e.g. NullPointerException). Always clean up cancelledImports entry, then independently check importFailed so both cancelled and failed states are handled correctly. Refs: #33908 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🔍 dotCMS Backend Review[🟠 High]
ImportAuditUtil.cancelledImports.remove(importId); // key gone before check
if(importFailed){
ImportAuditUtil.setAuditRecordAsFailed(importId, importErrorMessage);
} else {
ImportAuditUtil.setAuditRecordCompleted(importId); // overwrites USERSTOPPED
}💡 Snapshot the flag before removing, then branch on it: boolean wasCancelled = ImportAuditUtil.cancelledImports.containsKey(importId);
ImportAuditUtil.cancelledImports.remove(importId);
if (importFailed) {
ImportAuditUtil.setAuditRecordAsFailed(importId, importErrorMessage);
} else if (!wasCancelled) {
ImportAuditUtil.setAuditRecordCompleted(importId);
}
// if wasCancelled && !importFailed: STATUS_USERSTOPPED already written, leave it[🟠 High]
importErrorMessage = ae.getMessage() != null
? ae.getMessage() : ae.getClass().getSimpleName();💡 Store a user-safe summary; keep detailed logging for the server: Logger.error(this, "Import failed for ID " + importId + ": " + ae.getMessage(), ae);
importErrorMessage = "Import failed due to an internal error. Check server logs for details.";[🟡 Medium]
} catch (Exception e) {
Logger.error(ImportAuditUtil.class, e.getMessage(), e);
// caller sees normal return; audit record stays in prior state
}💡 Either propagate the exception (letting the outer [🟡 Medium]
dc.addParam(UtilMethods.isSet(errorMessage) && errorMessage.length() > 500
? errorMessage.substring(0, 500) : errorMessage);💡 Add a named constant: Next steps
|

Summary
RequestFacadebefore the async import thread finishes processing.MockAttributeRequest/MockSessionRequest/MockHeaderRequestconstruction from_processFile()(runs on the background thread) toprocessAction()(runs on the HTTP thread while the request is still alive)._handleException(ae, req)inside the asyncRunnablecatch block withLogger.error()to avoid accessing the recycled request on error paths.Test plan
IllegalStateException: The request object has been recycledIMPORT_CONTENTLETS_ASYNC=true(default) triggers the async pathCloses #33908
🤖 Generated with Claude Code
Note
Medium Risk
Changes the async content import execution path and its transaction/audit updates; mistakes could leave imports incorrectly marked or hide/over-report failures, especially under load.
Overview
Fixes async content imports by snapshotting the
HttpServletRequest(headers/session/attributes viaMock*Requestwrappers) inprocessAction()before handing work to the background thread, and passing the safe request into_processFile().Adds an
import_auditFAILED status (STATUS_FAILED) plussetAuditRecordAsFailed(), and updates the async import error/finally handling to log exceptions, attemptHibernateUtil.rollbackTransaction(), and mark audit records as failed (instead of always completed) when the import throws.Reviewed by Cursor Bugbot for commit 8395b5a. Bugbot is set up for automated code reviews on this repo. Configure here.