diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfiguration.java new file mode 100644 index 0000000000..61d73a2920 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.scheduling; + +import java.util.Collections; +import java.util.List; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.config.ScheduledTaskHolder; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for {@link ScheduledTasksEndpoint}. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +public class ScheduledTasksEndpointAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledEndpoint + public ScheduledTasksEndpoint scheduledTasksEndpoint( + ObjectProvider> holders) { + return new ScheduledTasksEndpoint(holders.getIfAvailable(Collections::emptyList)); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index f69a690fdc..930d73f44d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -30,6 +30,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration, org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.solr.SolrHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java new file mode 100644 index 0000000000..1183afce93 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.scheduling; + +import java.util.Collections; + +import org.junit.Test; + +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ScheduledTasksEndpointAutoConfiguration}. + * + * @author Andy Wilkinson + */ +public class ScheduledTasksEndpointAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(ScheduledTasksEndpointAutoConfiguration.class)); + + @Test + public void endpointIsAutoConfigured() { + this.contextRunner.run((context) -> assertThat(context) + .hasSingleBean(ScheduledTasksEndpoint.class)); + } + + @Test + public void endpointCanBeDisabled() { + this.contextRunner.withPropertyValues("endpoints.scheduledtasks.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(ScheduledTasksEndpoint.class)); + } + + @Test + public void endpointBacksOffWhenUserProvidedEndpointIsPresent() { + this.contextRunner.withUserConfiguration(CustomEndpointConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(ScheduledTasksEndpoint.class) + .hasBean("customEndpoint")); + } + + private static class CustomEndpointConfiguration { + + @Bean + public CustomEndpoint customEndpoint() { + return new CustomEndpoint(); + } + + } + + private static final class CustomEndpoint extends ScheduledTasksEndpoint { + + private CustomEndpoint() { + super(Collections.emptyList()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java new file mode 100644 index 0000000000..75bc03488e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java @@ -0,0 +1,284 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.scheduling; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.scheduling.Trigger; +import org.springframework.scheduling.config.CronTask; +import org.springframework.scheduling.config.FixedDelayTask; +import org.springframework.scheduling.config.FixedRateTask; +import org.springframework.scheduling.config.IntervalTask; +import org.springframework.scheduling.config.ScheduledTask; +import org.springframework.scheduling.config.ScheduledTaskHolder; +import org.springframework.scheduling.config.Task; +import org.springframework.scheduling.config.TriggerTask; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.scheduling.support.PeriodicTrigger; +import org.springframework.scheduling.support.ScheduledMethodRunnable; + +/** + * {@link Endpoint} to expose information about an application's scheduled tasks. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +@Endpoint(id = "scheduledtasks") +public class ScheduledTasksEndpoint { + + private final Collection scheduledTaskHolders; + + public ScheduledTasksEndpoint(Collection scheduledTaskHolders) { + this.scheduledTaskHolders = scheduledTaskHolders; + } + + @ReadOperation + public ScheduledTasksReport scheduledTasks() { + Map> descriptionsByType = this.scheduledTaskHolders + .stream().flatMap((holder) -> holder.getScheduledTasks().stream()) + .map(ScheduledTask::getTask).map(TaskDescription::of) + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(TaskDescription::getType)); + return new ScheduledTasksReport(descriptionsByType); + } + + /** + * A report of an application's scheduled {@link Task Tasks}, primarily intended for + * serialization to JSON. + */ + public static final class ScheduledTasksReport { + + private final List cron; + + private final List fixedDelay; + + private final List fixedRate; + + private ScheduledTasksReport( + Map> descriptionsByType) { + this.cron = descriptionsByType.getOrDefault(TaskType.CRON, + Collections.emptyList()); + this.fixedDelay = descriptionsByType.getOrDefault(TaskType.FIXED_DELAY, + Collections.emptyList()); + this.fixedRate = descriptionsByType.getOrDefault(TaskType.FIXED_RATE, + Collections.emptyList()); + } + + public List getCron() { + return this.cron; + } + + public List getFixedDelay() { + return this.fixedDelay; + } + + public List getFixedRate() { + return this.fixedRate; + } + + } + + /** + * Base class for descriptions of a {@link Task}. + */ + public static abstract class TaskDescription { + + private static final Map, Function> describers = new LinkedHashMap<>(); + + static { + describers.put(FixedRateTask.class, + (task) -> new FixedRateTaskDescription((FixedRateTask) task)); + describers.put(FixedDelayTask.class, + (task) -> new FixedDelayTaskDescription((FixedDelayTask) task)); + describers.put(CronTask.class, + (task) -> new CronTaskDescription((CronTask) task)); + describers.put(TriggerTask.class, + (task) -> describeTriggerTask((TriggerTask) task)); + } + + private TaskType type; + + private final RunnableDescription runnable; + + private static TaskDescription of(Task task) { + return describers.entrySet().stream() + .filter((entry) -> entry.getKey().isInstance(task)) + .map((entry) -> entry.getValue().apply(task)).findFirst() + .orElse(null); + } + + private static TaskDescription describeTriggerTask(TriggerTask triggerTask) { + Trigger trigger = triggerTask.getTrigger(); + if (trigger instanceof CronTrigger) { + return new CronTaskDescription(triggerTask, (CronTrigger) trigger); + } + if (trigger instanceof PeriodicTrigger) { + PeriodicTrigger periodicTrigger = (PeriodicTrigger) trigger; + if (periodicTrigger.isFixedRate()) { + return new FixedRateTaskDescription(triggerTask, periodicTrigger); + } + return new FixedDelayTaskDescription(triggerTask, periodicTrigger); + } + return null; + } + + protected TaskDescription(TaskType type, Runnable runnable) { + this.type = type; + this.runnable = new RunnableDescription(runnable); + } + + private TaskType getType() { + return this.type; + } + + public final RunnableDescription getRunnable() { + return this.runnable; + } + + } + + /** + * A description of an {@link IntervalTask}. + */ + public static class IntervalTaskDescription extends TaskDescription { + + private final long initialDelay; + + private final long interval; + + protected IntervalTaskDescription(TaskType type, IntervalTask task) { + super(type, task.getRunnable()); + this.initialDelay = task.getInitialDelay(); + this.interval = task.getInterval(); + } + + protected IntervalTaskDescription(TaskType type, TriggerTask task, + PeriodicTrigger trigger) { + super(type, task.getRunnable()); + this.initialDelay = trigger.getInitialDelay(); + this.interval = trigger.getPeriod(); + } + + public long getInitialDelay() { + return this.initialDelay; + } + + public long getInterval() { + return this.interval; + } + + } + + /** + * A description of a {@link FixedDelayTask} or a {@link TriggerTask} with a + * fixed-delay {@link PeriodicTrigger}. + */ + public static final class FixedDelayTaskDescription extends IntervalTaskDescription { + + private FixedDelayTaskDescription(FixedDelayTask task) { + super(TaskType.FIXED_DELAY, task); + } + + private FixedDelayTaskDescription(TriggerTask task, PeriodicTrigger trigger) { + super(TaskType.FIXED_DELAY, task, trigger); + } + + } + + /** + * A description of a {@link FixedRateTask} or a {@link TriggerTask} with a fixed-rate + * {@link PeriodicTrigger}. + */ + public static final class FixedRateTaskDescription extends IntervalTaskDescription { + + private FixedRateTaskDescription(FixedRateTask task) { + super(TaskType.FIXED_RATE, task); + } + + private FixedRateTaskDescription(TriggerTask task, PeriodicTrigger trigger) { + super(TaskType.FIXED_RATE, task, trigger); + } + + } + + /** + * A description of an {@link CronTask} or a {@link TriggerTask} with a + * {@link CronTrigger}. + */ + public static final class CronTaskDescription extends TaskDescription { + + private final String expression; + + private CronTaskDescription(CronTask task) { + super(TaskType.CRON, task.getRunnable()); + this.expression = task.getExpression(); + } + + private CronTaskDescription(TriggerTask task, CronTrigger trigger) { + super(TaskType.CRON, task.getRunnable()); + this.expression = trigger.getExpression(); + } + + public String getExpression() { + return this.expression; + } + + } + + /** + * A description of a {@link Task Task's} {@link Runnable}. + * + * @author Andy Wilkinson + */ + public static final class RunnableDescription { + + private final String target; + + private RunnableDescription(Runnable runnable) { + if (runnable instanceof ScheduledMethodRunnable) { + Method method = ((ScheduledMethodRunnable) runnable).getMethod(); + this.target = method.getDeclaringClass().getName() + "." + + method.getName(); + } + else { + this.target = runnable.getClass().getName(); + } + } + + public String getTarget() { + return this.target; + } + + } + + private enum TaskType { + + CRON, FIXED_DELAY, FIXED_RATE; + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java new file mode 100644 index 0000000000..66439adc26 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java @@ -0,0 +1,242 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.scheduling; + +import java.util.Collection; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import org.junit.Test; + +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.CronTaskDescription; +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.FixedDelayTaskDescription; +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.FixedRateTaskDescription; +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.ScheduledTasksReport; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskHolder; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.scheduling.support.PeriodicTrigger; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ScheduledTasksEndpoint}. + * + * @author Andy Wilkinson + */ +public class ScheduledTasksEndpointTests { + + private final ApplicationContextRunner runner = new ApplicationContextRunner() + .withUserConfiguration(BaseConfiguration.class); + + @Test + public void cronScheduledMethodIsReported() { + run(CronScheduledMethod.class, (tasks) -> { + assertThat(tasks.getFixedDelay()).isEmpty(); + assertThat(tasks.getFixedRate()).isEmpty(); + assertThat(tasks.getCron()).hasSize(1); + CronTaskDescription description = (CronTaskDescription) tasks.getCron() + .get(0); + assertThat(description.getExpression()).isEqualTo("0 0 0/3 1/1 * ?"); + assertThat(description.getRunnable().getTarget()) + .isEqualTo(CronScheduledMethod.class.getName() + ".cron"); + }); + } + + @Test + public void cronTriggerIsReported() { + run(CronTriggerTask.class, (tasks) -> { + assertThat(tasks.getFixedRate()).isEmpty(); + assertThat(tasks.getFixedDelay()).isEmpty(); + assertThat(tasks.getCron()).hasSize(1); + CronTaskDescription description = (CronTaskDescription) tasks.getCron() + .get(0); + assertThat(description.getExpression()).isEqualTo("0 0 0/6 1/1 * ?"); + assertThat(description.getRunnable().getTarget()) + .isEqualTo(CronTriggerRunnable.class.getName()); + }); + } + + @Test + public void fixedDelayScheduledMethodIsReported() { + run(FixedDelayScheduledMethod.class, (tasks) -> { + assertThat(tasks.getCron()).isEmpty(); + assertThat(tasks.getFixedRate()).isEmpty(); + assertThat(tasks.getFixedDelay()).hasSize(1); + FixedDelayTaskDescription description = (FixedDelayTaskDescription) tasks + .getFixedDelay().get(0); + assertThat(description.getInitialDelay()).isEqualTo(2); + assertThat(description.getInterval()).isEqualTo(1); + assertThat(description.getRunnable().getTarget()) + .isEqualTo(FixedDelayScheduledMethod.class.getName() + ".fixedDelay"); + }); + } + + @Test + public void fixedDelayTriggerIsReported() { + run(FixedDelayTriggerTask.class, (tasks) -> { + assertThat(tasks.getCron()).isEmpty(); + assertThat(tasks.getFixedRate()).isEmpty(); + assertThat(tasks.getFixedDelay()).hasSize(1); + FixedDelayTaskDescription description = (FixedDelayTaskDescription) tasks + .getFixedDelay().get(0); + assertThat(description.getInitialDelay()).isEqualTo(2000); + assertThat(description.getInterval()).isEqualTo(1000); + assertThat(description.getRunnable().getTarget()) + .isEqualTo(FixedDelayTriggerRunnable.class.getName()); + }); + } + + @Test + public void fixedRateScheduledMethodIsReported() { + run(FixedRateScheduledMethod.class, (tasks) -> { + assertThat(tasks.getCron()).isEmpty(); + assertThat(tasks.getFixedDelay()).isEmpty(); + assertThat(tasks.getFixedRate()).hasSize(1); + FixedRateTaskDescription description = (FixedRateTaskDescription) tasks + .getFixedRate().get(0); + assertThat(description.getInitialDelay()).isEqualTo(4); + assertThat(description.getInterval()).isEqualTo(3); + assertThat(description.getRunnable().getTarget()) + .isEqualTo(FixedRateScheduledMethod.class.getName() + ".fixedRate"); + }); + } + + @Test + public void fixedRateTriggerIsReported() { + run(FixedRateTriggerTask.class, (tasks) -> { + assertThat(tasks.getCron()).isEmpty(); + assertThat(tasks.getFixedDelay()).isEmpty(); + assertThat(tasks.getFixedRate()).hasSize(1); + FixedRateTaskDescription description = (FixedRateTaskDescription) tasks + .getFixedRate().get(0); + assertThat(description.getInitialDelay()).isEqualTo(3000); + assertThat(description.getInterval()).isEqualTo(2000); + assertThat(description.getRunnable().getTarget()) + .isEqualTo(FixedRateTriggerRunnable.class.getName()); + }); + } + + private void run(Class configuration, Consumer consumer) { + this.runner.withUserConfiguration(configuration).run((context) -> consumer + .accept(context.getBean(ScheduledTasksEndpoint.class).scheduledTasks())); + } + + @EnableScheduling + private static class BaseConfiguration { + + @Bean + public ScheduledTasksEndpoint endpoint( + Collection scheduledTaskHolders) { + return new ScheduledTasksEndpoint(scheduledTaskHolders); + } + + } + + private static class FixedDelayScheduledMethod { + + @Scheduled(fixedDelay = 1, initialDelay = 2) + public void fixedDelay() { + + } + + } + + private static class FixedRateScheduledMethod { + + @Scheduled(fixedRate = 3, initialDelay = 4) + public void fixedRate() { + + } + + } + + private static class CronScheduledMethod { + + @Scheduled(cron = "0 0 0/3 1/1 * ?") + public void cron() { + + } + + } + + private static class FixedDelayTriggerTask implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + PeriodicTrigger trigger = new PeriodicTrigger(1, TimeUnit.SECONDS); + trigger.setInitialDelay(2); + taskRegistrar.addTriggerTask(new FixedDelayTriggerRunnable(), trigger); + } + + } + + private static class FixedRateTriggerTask implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + PeriodicTrigger trigger = new PeriodicTrigger(2, TimeUnit.SECONDS); + trigger.setInitialDelay(3); + trigger.setFixedRate(true); + taskRegistrar.addTriggerTask(new FixedRateTriggerRunnable(), trigger); + } + + } + + private static class CronTriggerTask implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.addTriggerTask(new CronTriggerRunnable(), + new CronTrigger("0 0 0/6 1/1 * ?")); + } + + } + + private static class CronTriggerRunnable implements Runnable { + + @Override + public void run() { + + } + + } + + private static class FixedDelayTriggerRunnable implements Runnable { + + @Override + public void run() { + + } + + } + + private static class FixedRateTriggerRunnable implements Runnable { + + @Override + public void run() { + + } + + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index f3076e8aeb..082a1a1f45 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -1181,6 +1181,13 @@ content into your application; rather pick only the properties that you need. endpoints.prometheus.web.enabled= # Expose the metrics endpoint as a Web endpoint. endpoints.prometheus.web.path=prometheus # Path of the prometheus endpoint. + # SCHEDULED TASKS ENDPOINT ({sc-spring-boot-actuator}/scheduling/ScheduledTasksEndpoint.{sc-ext}[ScheduledTasksEndpoint]) + endpoints.scheduledtasks.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + endpoints.scheduledtasks.enabled= # Enable the scheduled tasks endpoint. + endpoints.scheduledtasks.jmx.enabled= # Expose the scheduled tasks endpoint as a JMX MBean. + endpoints.scheduledtasks.web.enabled= # Expose the scheduled tasks endpoint as a Web endpoint. + endpoints.scheduledtasks.web.path=sessions # Path of the scheduled tasks endpoint. + # SESSIONS ENDPOINT ({sc-spring-boot-actuator}/session/SessionsEndpoint.{sc-ext}[SessionsEndpoint]) endpoints.sessions.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.sessions.enabled= # Enable the sessions endpoint. diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index f56343921f..717df0c784 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -105,6 +105,9 @@ The following technology-agnostic endpoints are available: |`mappings` |Displays a collated list of all `@RequestMapping` paths. +|`scheduledtasks` +|Displays the scheduled tasks in your application. + |`sessions` |Allows retrieval and deletion of user sessions from a Spring Session-backed session store. Not available when using Spring Session's support for reactive web applications.