Skip to content

Commit e99ff64

Browse files
committed
Add Quartz actuator endpoint
1 parent 671bff2 commit e99ff64

File tree

11 files changed

+420
-0
lines changed

11 files changed

+420
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.quartz;
18+
19+
import org.quartz.Scheduler;
20+
21+
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
22+
import org.springframework.boot.actuate.quartz.QuartzEndpoint;
23+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
24+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
26+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28+
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Configuration;
31+
32+
/**
33+
* {@link EnableAutoConfiguration Auto-configuration} for {@link QuartzEndpoint}.
34+
*
35+
* @author Vedran Pavic
36+
* @since 2.0.0
37+
*/
38+
@Configuration
39+
@ConditionalOnClass(Scheduler.class)
40+
@AutoConfigureAfter(QuartzAutoConfiguration.class)
41+
public class QuartzEndpointAutoConfiguration {
42+
43+
@Bean
44+
@ConditionalOnBean(Scheduler.class)
45+
@ConditionalOnMissingBean
46+
@ConditionalOnEnabledEndpoint
47+
public QuartzEndpoint quartzEndpoint(Scheduler scheduler) {
48+
return new QuartzEndpoint(scheduler);
49+
}
50+
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Auto-configuration for actuator Quartz Scheduler concerns.
19+
*/
20+
package org.springframework.boot.actuate.autoconfigure.quartz;

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAuto
2828
org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration,\
2929
org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\
3030
org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\
31+
org.springframework.boot.actuate.autoconfigure.quartz.QuartzEndpointAutoConfiguration,\
3132
org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\
3233
org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration,\
3334
org.springframework.boot.actuate.autoconfigure.solr.SolrHealthIndicatorAutoConfiguration,\
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.quartz;
18+
19+
import org.junit.Test;
20+
import org.quartz.Scheduler;
21+
22+
import org.springframework.boot.actuate.quartz.QuartzEndpoint;
23+
import org.springframework.boot.autoconfigure.AutoConfigurations;
24+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.mockito.Mockito.mock;
30+
31+
/**
32+
* Tests for {@link QuartzEndpointAutoConfiguration}.
33+
*
34+
* @author Vedran Pavic
35+
*/
36+
public class QuartzEndpointAutoConfigurationTests {
37+
38+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
39+
.withConfiguration(
40+
AutoConfigurations.of(QuartzEndpointAutoConfiguration.class))
41+
.withUserConfiguration(QuartzConfiguration.class);
42+
43+
@Test
44+
public void runShouldHaveEndpointBean() {
45+
this.contextRunner.run(
46+
(context) -> assertThat(context).hasSingleBean(QuartzEndpoint.class));
47+
}
48+
49+
@Test
50+
public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean()
51+
throws Exception {
52+
this.contextRunner.withPropertyValues("endpoints.quartz.enabled:false").run(
53+
(context) -> assertThat(context).doesNotHaveBean(QuartzEndpoint.class));
54+
}
55+
56+
@Configuration
57+
static class QuartzConfiguration {
58+
59+
@Bean
60+
public Scheduler scheduler() {
61+
return mock(Scheduler.class);
62+
}
63+
64+
}
65+
66+
}

spring-boot-project/spring-boot-actuator/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@
106106
<artifactId>liquibase-core</artifactId>
107107
<optional>true</optional>
108108
</dependency>
109+
<dependency>
110+
<groupId>org.quartz-scheduler</groupId>
111+
<artifactId>quartz</artifactId>
112+
</dependency>
109113
<dependency>
110114
<groupId>org.springframework</groupId>
111115
<artifactId>spring-jdbc</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.quartz;
18+
19+
import java.util.ArrayList;
20+
import java.util.Date;
21+
import java.util.LinkedHashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.stream.Collectors;
25+
26+
import org.quartz.Job;
27+
import org.quartz.JobDetail;
28+
import org.quartz.JobKey;
29+
import org.quartz.Scheduler;
30+
import org.quartz.SchedulerException;
31+
import org.quartz.Trigger;
32+
import org.quartz.impl.matchers.GroupMatcher;
33+
34+
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
35+
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
36+
import org.springframework.boot.actuate.endpoint.annotation.Selector;
37+
import org.springframework.util.Assert;
38+
39+
/**
40+
* {@link Endpoint} to expose Quartz Scheduler info.
41+
*
42+
* @author Vedran Pavic
43+
* @since 2.0.0
44+
*/
45+
@Endpoint(id = "quartz")
46+
public class QuartzEndpoint {
47+
48+
private final Scheduler scheduler;
49+
50+
public QuartzEndpoint(Scheduler scheduler) {
51+
Assert.notNull(scheduler, "Scheduler must not be null");
52+
this.scheduler = scheduler;
53+
}
54+
55+
@ReadOperation
56+
public Map<String, Object> quartzReport() {
57+
Map<String, Object> result = new LinkedHashMap<>();
58+
try {
59+
for (String groupName : this.scheduler.getJobGroupNames()) {
60+
List<String> jobs = this.scheduler
61+
.getJobKeys(GroupMatcher.jobGroupEquals(groupName)).stream()
62+
.map(JobKey::getName).collect(Collectors.toList());
63+
result.put(groupName, jobs);
64+
}
65+
}
66+
catch (SchedulerException ignored) {
67+
}
68+
return result;
69+
}
70+
71+
@ReadOperation
72+
public QuartzJob quartzJob(@Selector String groupName, @Selector String jobName) {
73+
try {
74+
JobKey jobKey = JobKey.jobKey(jobName, groupName);
75+
JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
76+
List<? extends Trigger> triggers = this.scheduler.getTriggersOfJob(jobKey);
77+
return new QuartzJob(jobDetail, triggers);
78+
}
79+
catch (SchedulerException e) {
80+
return null;
81+
}
82+
}
83+
84+
/**
85+
* Details of a {@link Job Quartz Job}.
86+
*/
87+
public static final class QuartzJob {
88+
89+
private final String jobGroup;
90+
91+
private final String jobName;
92+
93+
private final String className;
94+
95+
private final List<QuartzTrigger> triggers = new ArrayList<>();
96+
97+
QuartzJob(JobDetail jobDetail, List<? extends Trigger> triggers) {
98+
this.jobGroup = jobDetail.getKey().getGroup();
99+
this.jobName = jobDetail.getKey().getName();
100+
this.className = jobDetail.getJobClass().getName();
101+
triggers.forEach(trigger -> this.triggers.add(new QuartzTrigger(trigger)));
102+
}
103+
104+
public String getJobGroup() {
105+
return this.jobGroup;
106+
}
107+
108+
public String getJobName() {
109+
return this.jobName;
110+
}
111+
112+
public String getClassName() {
113+
return this.className;
114+
}
115+
116+
public List<QuartzTrigger> getTriggers() {
117+
return this.triggers;
118+
}
119+
120+
}
121+
122+
/**
123+
* Details of a {@link Trigger Quartz Trigger}.
124+
*/
125+
public static final class QuartzTrigger {
126+
127+
private final String triggerGroup;
128+
129+
private final String triggerName;
130+
131+
private final Date lastFireTime;
132+
133+
private final Date nextFireTime;
134+
135+
QuartzTrigger(Trigger trigger) {
136+
this.triggerGroup = trigger.getKey().getGroup();
137+
this.triggerName = trigger.getKey().getName();
138+
this.lastFireTime = trigger.getFinalFireTime();
139+
this.nextFireTime = trigger.getNextFireTime();
140+
}
141+
142+
public String getTriggerGroup() {
143+
return this.triggerGroup;
144+
}
145+
146+
public String getTriggerName() {
147+
return this.triggerName;
148+
}
149+
150+
public Date getLastFireTime() {
151+
return this.lastFireTime;
152+
}
153+
154+
public Date getNextFireTime() {
155+
return this.nextFireTime;
156+
}
157+
158+
}
159+
160+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Actuator support for Quartz Scheduler.
19+
*/
20+
package org.springframework.boot.actuate.quartz;

0 commit comments

Comments
 (0)