fix(jobs): trigger-now runs a job on the engine it declares (#6305) - #6625
Merged
Conversation
#6375 made client-Java `@Scheduled` / `JobHandler` jobs first-class `Job` definitions on the shared Quartz scheduler, so they are listed, monitored and manageable in the Jobs perspective like any `.job`. One half of that was still missing: the Jobs perspective's Trigger action. `JobService.trigger` — the manual path behind `POST /services/jobs/trigger` — ran the handler through the JavaScript code runner unconditionally, ignoring the job's `engine`. A client-Java job's handler is a class name, not a repository path to a JS module, so triggering one answered 500 while the same job ran fine on its cron. The dispatch now lives in exactly one place, `JobHandlerRunner`, used by both ways a job can start: the scheduled fire (`JobExecutionService`) and the manual trigger. That is the actual defect — the two dispatches were written out separately and drifted. The engine-java guide's job bullet was also still describing the private `ThreadPoolTaskScheduler` that #6375 replaced; it now records the shared Quartz registration and this single dispatch point. Verified against the client-Java job sample: `JavaJobDecoratorSampleProjectIT` triggers a listed `engine: java` job over REST and fails on the pre-fix code with "Expected status code <200> but was <500>", passing with the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 9, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #6305.
Where the issue stands
The issue asked for a decision between three directions, and option 1 (full integration) already shipped in #6375:
ScheduledClassConsumerno longer keeps client-Java jobs on a private in-JVMThreadPoolTaskScheduler— it registers a real per-tenantJobrow on the platform's shared Quartz scheduler (under the syntheticRUNTIME_LOCATION_PREFIXlocation, so the job synchronizer does not reap it), and the jobs engine dispatches back through theJavaJobExecutorSPI at fire time. So listing, the execution log and cluster-safe firing are all in place, andJavaJobDecoratorSampleProjectITcovers it.This PR closes the one part of that feature set which was still broken.
The defect
JobService.trigger— the manual path behindPOST /services/jobs/trigger/{job}, i.e. the Jobs perspective's Trigger action and the Monitoring shell's "run now" — ran the handler throughDirigibleJavascriptCodeRunnerunconditionally, ignoring the job'sengine:A client-Java job's handler is a class name (
app.jobs.CleanupJob, optionally#method), not a repository path to a JS module — so triggering one from the IDE answered 500, while the very same job ran fine on its cron. The scheduled path (JobExecutionService) had the engine branch; the manual one never got it.The fix
The dispatch now lives in exactly one place — a small
JobHandlerRunner— used by both ways a job can start. That is the actual defect: the two dispatches were written out separately and drifted, so re-uniting them is what keeps them from drifting again.JobExecutionService(scheduled fire) delegates to it, unchanged in behavior, inside the same JobLog wrapping.JobService.triggerdelegates to it, so the manual trigger honorsenginetoo. Parameter handling (theConfigurationset/restore memento) is untouched.JobServicecannot depend onJobExecutionServicedirectly —JobExecutionService → JobLogService → JobServicewould close a constructor cycle — which is the other reason the dispatch is its own tiny bean.Deliberately not changed
JobLogentry, for a Java job exactly as for a JS one. Adding it would change existing JS behavior; if operators want manual runs in the execution log, that is its own change.enabledis still not preserved across re-registration:ScheduledClassConsumersetsenabled=truewhenever it registers, so an operator's Disable is undone on the next hot-reload or restart..jobartefacts behave the same way (the synchronizer rebuilds the row from the artefact), so this is platform-wide rather than client-Java-specific — happy to file it separately if you want it fixed.Tests
JobHandlerRunnerTest— the Java branch reaches the executor, a missing executor fails loudly rather than silently no-op'ing, and a JS handler never touches the Java executor.JavaJobDecoratorSampleProjectIT— picks a listedengine: javajob and triggers it over REST. The status is the whole assertion: the Java dispatch either resolves the client bean and invokes it or throws, and the endpoint surfaces a throw as 500 — it cannot answer 200 without having run the job. Verified both ways locally against the sample project:Expected status code <200> but was <500>on the pre-fix code, green with the fix.mvn formatter:validate, the release-profile javadoc pass and theengine-jobsunit suite are clean.🤖 Generated with Claude Code