Skip to content

feat(maintenance): add REST endpoints for system jobs #35207#35831

Draft
hassandotcms wants to merge 1 commit into
mainfrom
35207-task-implement-system-jobs-list-and-delete-endpoints
Draft

feat(maintenance): add REST endpoints for system jobs #35207#35831
hassandotcms wants to merge 1 commit into
mainfrom
35207-task-implement-system-jobs-list-and-delete-endpoints

Conversation

@hassandotcms
Copy link
Copy Markdown
Member

@hassandotcms hassandotcms commented May 26, 2026

  • GET /v1/maintenance/_systemJobs lists all Quartz scheduler jobs with trigger details (next fire time, misfire policy, running state).
  • DELETE /v1/maintenance/_systemJobs/{group}/{name} removes a Quartz job (returns 404 when the scheduler reports no matching job).
  • Replaces the legacy system_jobs.jsp scriptlet and the Struts deleteScheduler action; behavior is preserved 1:1.
  • Errored jobs surface with an 'error' field so admins can clean up orphans left by upgrades (e.g. ClassNotFoundException).
  • Deletions audit-logged to SecurityLogger.

Proposed Changes

  • change 1
  • change 2

Checklist

  • Tests
  • Translations
  • Security Implications Contemplated (add notes if applicable)

Additional Info

** any additional useful context or info **

Screenshots

Original Updated
** original screenshot ** ** updated screenshot **

This PR fixes: #35207

- GET /v1/maintenance/_systemJobs lists all Quartz scheduler jobs with
  trigger details (next fire time, misfire policy, running state).
- DELETE /v1/maintenance/_systemJobs/{group}/{name} removes a Quartz job
  (returns 404 when the scheduler reports no matching job).
- Replaces the legacy system_jobs.jsp scriptlet and the Struts
  deleteScheduler action; behavior is preserved 1:1.
- Errored jobs surface with an 'error' field so admins can clean up
  orphans left by upgrades (e.g. ClassNotFoundException).
- Deletions audit-logged to SecurityLogger.
- Note: these are Quartz scheduler jobs; the newer JobQueueManager jobs
  at /api/v1/jobs are a separate system and intentionally untouched.
@hassandotcms hassandotcms linked an issue May 26, 2026 that may be closed by this pull request
@hassandotcms hassandotcms changed the title feat(maintenance): add REST endpoints for Quartz system jobs #35207 feat(maintenance): add REST endpoints for system jobs #35207 May 26, 2026
@github-actions github-actions Bot added the Area : Backend PR changes Java/Maven backend code label May 26, 2026
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 26, 2026

Claude finished @hassandotcms's task in 2m 34s —— View job


Review

  • Read MaintenanceResource changes
  • Read response view DTOs
  • Read integration test
  • Read openapi.yaml additions
  • Compare against legacy system_jobs.jsp + QuartzUtils.removeJob
  • Post review findings

Findings

1. Map<String, Object> payload contradicts the repo's REST guidance (medium)

MaintenanceResource.java:1530-1602, 1642-1678
The entity of both responses is Map<String, Object> (or List<Map<String, Object>>). dotCMS/src/main/java/com/dotcms/rest/CLAUDE.md explicitly calls this out as forbidden ("Don't use Map.class/HashMap.class; use a specific view class") and the regenerated openapi.yaml confirms the cost — both ResponseEntitySystemJobListView and ResponseEntitySystemJobDeleteView resolve to additionalProperties: type: object, so consumers can't discover the contract. The PR's nearby siblings (SessionView, ThreadDescriptorView, ThreadDumpView, KillSessionsResultView) all use typed Immutables-style builders. Recommend a SystemJobView (with optional error, optional misfireInstruction, etc.) and a SystemJobDeleteResultView(boolean deleted, String name, String group) — the OpenAPI then describes the actual fields and the Angular client can be strongly typed.

2. MISFIRE_INSTRUCTION_SMART_POLICY is reported as "UNKNOWN" — divergence from the legacy JSP (medium)

MaintenanceResource.java:1580-1589
The JSP only writes a label for DO_NOTHING / FIRE_ONCE_NOW and leaves the cell empty for everything else. The new code uses an else branch that emits "UNKNOWN", so any job using the default MISFIRE_INSTRUCTION_SMART_POLICY (value 0, which is what DotInitScheduler.CRON_EXPRESSION_* defaults shake out to) will now show as UNKNOWN to admins. The commit message claims behavior is preserved 1:1; this isn't. Either omit the key when not one of the two known values, or map SMART_POLICY to its actual name. The integration test only exercises DO_NOTHING, so this slipped through.

3. Path with {group}/{name} breaks on Quartz groups/names containing / (low–medium)

MaintenanceResource.java:1639
Quartz allows / in group and job names. As soon as one appears, JAX-RS routing slices the path differently and the call either 404s or deletes the wrong record. The legacy Struts action passed name/group as form params and didn't have this problem. Options: switch to query params (?group=&name=), or use {group:.+}/{name} with explicit splitting. Worth at least documenting the constraint if you keep the current shape.

4. Errored top-level group is silently dropped from the list (low)

MaintenanceResource.java:1547-1554
If scheduler.getJobNames(group) throws, the whole group is skipped with a Logger.warn. The whole point of the new error field is to let admins clean up orphans, so an unreadable group should still surface (probably as a single synthetic entry with error set on the group) rather than vanish from the UI.

5. Inner try/catch (Exception e) returns a partial map without running/durable etc. (low)

MaintenanceResource.java:1556-1599
When getJobDetail/getTriggersOfJob throws (the ClassNotFoundException case the PR is targeting), the catch puts only {name, group, error}. Any client that does job.misfireInstruction / job.running without null-checking will break — exactly the kind of bug that a typed view (finding #1) would prevent. Even keeping the Map, consider explicitly setting null for the missing keys so the response shape is stable.

6. Multi-trigger jobs only show the first trigger (low, pre-existing)

MaintenanceResource.java:1559-1560
triggers[0] mirrors the JSP, but if there are ever multiple triggers per job, the rest are invisible. Not your bug to fix here, but a triggers: [{...}] shape in a typed view would let you fix it later without breaking the API.

7. Test coverage gaps

  • No test for the error branch (the headline feature — ClassNotFoundException job that still gets deleted).
  • No test for MISFIRE_INSTRUCTION_SMART_POLICY (would have caught finding Test Branch and Commit #2).
  • No test for deleteSystemJob when QuartzUtils.removeJob throws a SchedulerException (the wrapping DotRuntimeException path on MaintenanceResource.java:1660-1664).
  • No test verifying SecurityLogger audit line is actually emitted on delete.

8. Schema description on type = "object" (nit, per CLAUDE.md)

The two new ResponseEntity* views resolve in openapi to additionalProperties: type: object with no description. dotCMS/src/main/java/com/dotcms/rest/CLAUDE.md requires "always add description" on type = "object". Folded into #1 if you go the typed-view route.


Not issues

  • SimpleDateFormat is method-local in listSystemJobs — thread safety isn't a concern.
  • Auth gating goes through assertBackendUserrequireAdmin(true) + requiredPortlet(MAINTENANCE), consistent with the rest of the resource.
  • The 404 semantics on deleteSystemJob correctly reflect Scheduler.deleteJob's contract.
  • volatile+double-checked locking around jobHelper is unrelated to this PR.
    · branch 35207-task-implement-system-jobs-list-and-delete-endpoints

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[TASK] Implement system jobs list and delete endpoints

1 participant