Add the ability to trigger a Quartz job through an Actuator endpoint
Add new methods to `QuartzEndpoint` and `QuartzEndpointWebExtension` to allow a Quartz job to be triggered on demand. See gh-43086 Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
This commit is contained in:
parent
ddc45ea8ca
commit
fbeace397f
@ -156,6 +156,39 @@ The following table describes the structure of the response:
|
||||
include::partial$rest/actuator/quartz/job-details/response-fields.adoc[]
|
||||
|
||||
|
||||
[[quartz.trigger-job]]
|
||||
== Trigger Quartz Job On Demand
|
||||
|
||||
To trigger a particular Quartz job, make a `POST` request to `/actuator/quartz/jobs/\{groupName}/\{jobName}`, as shown in the following curl-based example:
|
||||
|
||||
include::partial$rest/actuator/quartz/trigger-job/curl-request.adoc[]
|
||||
|
||||
The preceding example demonstrates how to trigger a job that belongs to the `samples` group and is named `jobOne`.
|
||||
|
||||
The response will look similar to the following:
|
||||
|
||||
include::partial$rest/actuator/quartz/trigger-job/http-response.adoc[]
|
||||
|
||||
|
||||
[[quartz.trigger-job.request-structure]]
|
||||
=== Request Structure
|
||||
|
||||
The request specifies a desired `state` associated with a particular job.
|
||||
Sending an HTTP Request with a `"state": "running"` body indicates that the job should be run now.
|
||||
The following table describes the structure of the request:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::partial$rest/actuator/quartz/trigger-job/request-fields.adoc[]
|
||||
|
||||
[[quartz.trigger-job.response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains the details of a triggered job.
|
||||
The following table describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::partial$rest/actuator/quartz/trigger-job/response-fields.adoc[]
|
||||
|
||||
|
||||
[[quartz.trigger]]
|
||||
== Retrieving Details of a Trigger
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -24,6 +24,7 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@ -54,9 +55,11 @@ import org.quartz.spi.OperableTrigger;
|
||||
import org.springframework.boot.actuate.endpoint.Show;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpointWebExtension;
|
||||
import org.springframework.boot.json.JsonWriter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
import org.springframework.scheduling.quartz.DelegatingJob;
|
||||
@ -68,8 +71,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.relaxedResponseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
|
||||
@ -385,6 +392,23 @@ class QuartzEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
|
||||
.andWithPrefix("custom.", customTriggerSummary)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void quartzTriggerJob() throws Exception {
|
||||
mockJobs(jobOne);
|
||||
String json = JsonWriter.standard().writeToString(Map.of("state", "running"));
|
||||
assertThat(this.mvc.post()
|
||||
.content(json)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.uri("/actuator/quartz/jobs/samples/jobOne"))
|
||||
.hasStatusOk()
|
||||
.apply(document("quartz/trigger-job", preprocessRequest(), preprocessResponse(prettyPrint()),
|
||||
requestFields(fieldWithPath("state").description("The desired state of the job.")),
|
||||
responseFields(fieldWithPath("group").description("Name of the group."),
|
||||
fieldWithPath("name").description("Name of the job."),
|
||||
fieldWithPath("className").description("Fully qualified name of the job implementation."),
|
||||
fieldWithPath("triggerTime").description("Time the job is triggered."))));
|
||||
}
|
||||
|
||||
private <T extends Trigger> void setupTriggerDetails(TriggerBuilder<T> builder, TriggerState state)
|
||||
throws SchedulerException {
|
||||
T trigger = builder.withIdentity("example", "samples")
|
||||
|
@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.actuate.quartz;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
@ -212,6 +213,26 @@ public class QuartzEndpoint {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers (execute it now) a Quartz job by its group and job name.
|
||||
* @param groupName the name of the job's group
|
||||
* @param jobName the name of the job
|
||||
* @return a description of the triggered job or {@code null} if the job does not
|
||||
* exist
|
||||
* @throws SchedulerException if there is an error triggering the job
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public QuartzJobTriggerDescriptor triggerQuartzJob(String groupName, String jobName) throws SchedulerException {
|
||||
JobKey jobKey = JobKey.jobKey(jobName, groupName);
|
||||
JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
|
||||
if (jobDetail == null) {
|
||||
return null;
|
||||
}
|
||||
this.scheduler.triggerJob(jobKey);
|
||||
return new QuartzJobTriggerDescriptor(jobDetail.getKey().getGroup(), jobDetail.getKey().getName(),
|
||||
jobDetail.getJobClass().getName(), Instant.now());
|
||||
}
|
||||
|
||||
private static List<Map<String, Object>> extractTriggersSummary(List<? extends Trigger> triggers) {
|
||||
List<Trigger> triggersToSort = new ArrayList<>(triggers);
|
||||
triggersToSort.sort(TRIGGER_COMPARATOR);
|
||||
@ -387,6 +408,44 @@ public class QuartzEndpoint {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a triggered on demand {@link Job Quartz Job}.
|
||||
*/
|
||||
public static final class QuartzJobTriggerDescriptor {
|
||||
|
||||
private final String group;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String className;
|
||||
|
||||
private final Instant triggerTime;
|
||||
|
||||
private QuartzJobTriggerDescriptor(String group, String name, String className, Instant triggerTime) {
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.className = className;
|
||||
this.triggerTime = triggerTime;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return this.group;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
public Instant getTriggerTime() {
|
||||
return this.triggerTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a {@link Job Quartz Job}.
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -27,6 +27,7 @@ import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.Show;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzGroupsDescriptor;
|
||||
@ -79,6 +80,18 @@ public class QuartzEndpointWebExtension {
|
||||
() -> this.delegate.quartzTrigger(group, name, showUnsanitized));
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public WebEndpointResponse<Object> triggerQuartzJob(@Selector String jobs, @Selector String group,
|
||||
@Selector String name, String state) throws SchedulerException {
|
||||
if (!"jobs".equals(jobs)) {
|
||||
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_BAD_REQUEST);
|
||||
}
|
||||
if (!"running".equals(state)) {
|
||||
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_BAD_REQUEST);
|
||||
}
|
||||
return handleNull(this.delegate.triggerQuartzJob(group, name));
|
||||
}
|
||||
|
||||
private <T> WebEndpointResponse<T> handle(String jobsOrTriggers, ResponseSupplier<T> jobAction,
|
||||
ResponseSupplier<T> triggerAction) throws SchedulerException {
|
||||
if ("jobs".equals(jobsOrTriggers)) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -66,6 +66,7 @@ import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzDescriptor;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzJobDetailsDescriptor;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzJobGroupSummaryDescriptor;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzJobSummaryDescriptor;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzJobTriggerDescriptor;
|
||||
import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzTriggerGroupSummaryDescriptor;
|
||||
import org.springframework.scheduling.quartz.DelegatingJob;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
@ -73,9 +74,12 @@ import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.assertj.core.api.Assertions.within;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
/**
|
||||
* Tests for {@link QuartzEndpoint}.
|
||||
@ -755,6 +759,31 @@ class QuartzEndpointTests {
|
||||
entry("url", "******"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void quartzJobShouldBeTriggered() throws SchedulerException {
|
||||
JobDetail job = JobBuilder.newJob(Job.class)
|
||||
.withIdentity("hello", "samples")
|
||||
.withDescription("A sample job")
|
||||
.storeDurably()
|
||||
.requestRecovery(false)
|
||||
.build();
|
||||
mockJobs(job);
|
||||
QuartzJobTriggerDescriptor quartzJobTriggerDescriptor = this.endpoint.triggerQuartzJob("samples", "hello");
|
||||
assertThat(quartzJobTriggerDescriptor).isNotNull();
|
||||
assertThat(quartzJobTriggerDescriptor.getName()).isEqualTo("hello");
|
||||
assertThat(quartzJobTriggerDescriptor.getGroup()).isEqualTo("samples");
|
||||
assertThat(quartzJobTriggerDescriptor.getClassName()).isEqualTo("org.quartz.Job");
|
||||
assertThat(quartzJobTriggerDescriptor.getTriggerTime()).isCloseTo(Instant.now(), within(5, ChronoUnit.SECONDS));
|
||||
then(this.scheduler).should().triggerJob(new JobKey("hello", "samples"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void quartzJobShouldNotBeTriggeredJobDoesNotExist() throws SchedulerException {
|
||||
QuartzJobTriggerDescriptor quartzJobTriggerDescriptor = this.endpoint.triggerQuartzJob("samples", "hello");
|
||||
assertThat(quartzJobTriggerDescriptor).isNull();
|
||||
then(this.scheduler).should(never()).triggerJob(any());
|
||||
}
|
||||
|
||||
private void mockJobs(JobDetail... jobs) throws SchedulerException {
|
||||
MultiValueMap<String, JobKey> jobKeys = new LinkedMultiValueMap<>();
|
||||
for (JobDetail jobDetail : jobs) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.minidev.json.JSONArray;
|
||||
@ -42,10 +43,12 @@ import org.quartz.TriggerBuilder;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.quartz.impl.matchers.GroupMatcher;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.ApiVersion;
|
||||
import org.springframework.boot.actuate.endpoint.Show;
|
||||
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.scheduling.quartz.DelegatingJob;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
@ -62,6 +65,10 @@ import static org.mockito.Mockito.mock;
|
||||
*/
|
||||
class QuartzEndpointWebIntegrationTests {
|
||||
|
||||
private static final String V2_JSON = ApiVersion.V2.getProducedMimeType().toString();
|
||||
|
||||
private static final String V3_JSON = ApiVersion.V3.getProducedMimeType().toString();
|
||||
|
||||
private static final JobDetail jobOne = JobBuilder.newJob(Job.class)
|
||||
.withIdentity("jobOne", "samples")
|
||||
.usingJobData(new JobDataMap(Collections.singletonMap("name", "test")))
|
||||
@ -249,6 +256,92 @@ class QuartzEndpointWebIntegrationTests {
|
||||
client.get().uri("/actuator/quartz/triggers/tests/does-not-exist").exchange().expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void quartzTriggerJob(WebTestClient client) {
|
||||
client.post()
|
||||
.uri("/actuator/quartz/jobs/samples/jobOne")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Map.of("state", "running"))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("group")
|
||||
.isEqualTo("samples")
|
||||
.jsonPath("name")
|
||||
.isEqualTo("jobOne")
|
||||
.jsonPath("className")
|
||||
.isEqualTo("org.quartz.Job")
|
||||
.jsonPath("triggerTime")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void quartzTriggerJobV2(WebTestClient client) {
|
||||
client.post()
|
||||
.uri("/actuator/quartz/jobs/samples/jobOne")
|
||||
.contentType(MediaType.parseMediaType(V2_JSON))
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Map.of("state", "running"))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("group")
|
||||
.isEqualTo("samples")
|
||||
.jsonPath("name")
|
||||
.isEqualTo("jobOne")
|
||||
.jsonPath("className")
|
||||
.isEqualTo("org.quartz.Job")
|
||||
.jsonPath("triggerTime")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void quartzTriggerJobV3(WebTestClient client) {
|
||||
client.post()
|
||||
.uri("/actuator/quartz/jobs/samples/jobOne")
|
||||
.contentType(MediaType.parseMediaType(V3_JSON))
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Map.of("state", "running"))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("group")
|
||||
.isEqualTo("samples")
|
||||
.jsonPath("name")
|
||||
.isEqualTo("jobOne")
|
||||
.jsonPath("className")
|
||||
.isEqualTo("org.quartz.Job")
|
||||
.jsonPath("triggerTime")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void quartzTriggerJobWithUnknownJobKey(WebTestClient client) {
|
||||
client.post()
|
||||
.uri("/actuator/quartz/jobs/samples/does-not-exist")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Map.of("state", "running"))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isNotFound();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void quartzTriggerJobWithUnknownState(WebTestClient client) {
|
||||
client.post()
|
||||
.uri("/actuator/quartz/jobs/samples/jobOne")
|
||||
.contentType(MediaType.parseMediaType(V3_JSON))
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Map.of("state", "unknown"))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isBadRequest();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestConfiguration {
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -58,6 +58,15 @@ public class SampleQuartzApplication {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobDetail onDemandJobDetail() {
|
||||
return JobBuilder.newJob(SampleJob.class)
|
||||
.withIdentity("onDemandJob", "samples")
|
||||
.usingJobData("name", "On Demand Job")
|
||||
.storeDurably()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Trigger everyTwoSecTrigger() {
|
||||
return TriggerBuilder.newTrigger()
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,22 +16,29 @@
|
||||
|
||||
package smoketest.quartz;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.assertj.core.api.InstanceOfAssertFactory;
|
||||
import org.assertj.core.api.MapAssert;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.assertj.core.api.Assertions.within;
|
||||
|
||||
/**
|
||||
* Web tests for {@link SampleQuartzApplication}.
|
||||
@ -39,6 +46,7 @@ import static org.assertj.core.api.Assertions.entry;
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SampleQuartzApplicationWebTests {
|
||||
|
||||
@Autowired
|
||||
@ -91,6 +99,20 @@ class SampleQuartzApplicationWebTests {
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void quartzJobTriggeredManually(CapturedOutput output) {
|
||||
ResponseEntity<Map<String, Object>> result = asMapEntity(this.restTemplate.postForEntity(
|
||||
"/actuator/quartz/jobs/samples/onDemandJob", new HttpEntity<>(Map.of("state", "running")), Map.class));
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
Map<String, Object> content = result.getBody();
|
||||
assertThat(content).contains(entry("group", "samples"), entry("name", "onDemandJob"),
|
||||
entry("className", SampleJob.class.getName()));
|
||||
assertThat(content).extractingByKey("triggerTime", InstanceOfAssertFactories.STRING)
|
||||
.satisfies((triggerTime) -> assertThat(Instant.parse(triggerTime)).isCloseTo(Instant.now(),
|
||||
within(10, ChronoUnit.SECONDS)));
|
||||
assertThat(output).contains("Hello On Demand Job");
|
||||
}
|
||||
|
||||
private Map<String, Object> getContent(String path) {
|
||||
ResponseEntity<Map<String, Object>> entity = asMapEntity(this.restTemplate.getForEntity(path, Map.class));
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
Loading…
x
Reference in New Issue
Block a user