diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfiguration.java index bd01f0592ef..fd3073f2b29 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfiguration.java @@ -29,7 +29,7 @@ import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.cloud.CloudPlatform; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -50,17 +50,16 @@ public class ProbesHealthContributorAutoConfiguration { @Bean @ConditionalOnEnabledHealthIndicator("livenessProbe") @ConditionalOnMissingBean - public LivenessProbeHealthIndicator livenessProbeHealthIndicator( - ApplicationAvailabilityProvider applicationAvailabilityProvider) { - return new LivenessProbeHealthIndicator(applicationAvailabilityProvider); + public LivenessProbeHealthIndicator livenessProbeHealthIndicator(ApplicationAvailability applicationAvailability) { + return new LivenessProbeHealthIndicator(applicationAvailability); } @Bean @ConditionalOnEnabledHealthIndicator("readinessProbe") @ConditionalOnMissingBean public ReadinessProbeHealthIndicator readinessProbeHealthIndicator( - ApplicationAvailabilityProvider applicationAvailabilityProvider) { - return new ReadinessProbeHealthIndicator(applicationAvailabilityProvider); + ApplicationAvailability applicationAvailability) { + return new ReadinessProbeHealthIndicator(applicationAvailability); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfigurationTests.java index fa919c537e7..3223ed18016 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/kubernetes/ProbesHealthContributorAutoConfigurationTests.java @@ -23,7 +23,7 @@ import org.springframework.boot.actuate.availability.ReadinessProbeHealthIndicat import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -40,7 +40,7 @@ class ProbesHealthContributorAutoConfigurationTests { @Test void probesNotConfiguredIfNotKubernetes() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ApplicationAvailabilityProvider.class) + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class) .doesNotHaveBean(LivenessProbeHealthIndicator.class) .doesNotHaveBean(ReadinessProbeHealthIndicator.class) .doesNotHaveBean(HealthEndpointGroupsRegistryCustomizer.class)); @@ -49,7 +49,7 @@ class ProbesHealthContributorAutoConfigurationTests { @Test void probesConfiguredIfProperty() { this.contextRunner.withPropertyValues("management.health.probes.enabled=true") - .run((context) -> assertThat(context).hasSingleBean(ApplicationAvailabilityProvider.class) + .run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class) .hasSingleBean(LivenessProbeHealthIndicator.class) .hasSingleBean(ReadinessProbeHealthIndicator.class) .hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class)); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicator.java index 0e0c8a3f892..af5111b16c8 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicator.java @@ -19,7 +19,8 @@ package org.springframework.boot.actuate.availability; import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.availability.LivenessState; /** @@ -30,20 +31,16 @@ import org.springframework.boot.availability.LivenessState; */ public class LivenessProbeHealthIndicator extends AbstractHealthIndicator { - private final ApplicationAvailabilityProvider applicationAvailabilityProvider; + private final ApplicationAvailability applicationAvailability; - public LivenessProbeHealthIndicator(ApplicationAvailabilityProvider applicationAvailabilityProvider) { - this.applicationAvailabilityProvider = applicationAvailabilityProvider; + public LivenessProbeHealthIndicator(ApplicationAvailability applicationAvailability) { + this.applicationAvailability = applicationAvailability; } @Override protected void doHealthCheck(Health.Builder builder) throws Exception { - if (LivenessState.LIVE.equals(this.applicationAvailabilityProvider.getLivenessState())) { - builder.up(); - } - else { - builder.down(); - } + LivenessState state = this.applicationAvailability.getLivenessState(); + builder.status(LivenessState.CORRECT == state ? Status.UP : Status.DOWN); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicator.java index 004ec729cd8..9d64d6bd1b2 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicator.java @@ -19,7 +19,8 @@ package org.springframework.boot.actuate.availability; import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.availability.ReadinessState; /** @@ -30,20 +31,16 @@ import org.springframework.boot.availability.ReadinessState; */ public class ReadinessProbeHealthIndicator extends AbstractHealthIndicator { - private final ApplicationAvailabilityProvider applicationAvailabilityProvider; + private final ApplicationAvailability applicationAvailability; - public ReadinessProbeHealthIndicator(ApplicationAvailabilityProvider applicationAvailabilityProvider) { - this.applicationAvailabilityProvider = applicationAvailabilityProvider; + public ReadinessProbeHealthIndicator(ApplicationAvailability applicationAvailability) { + this.applicationAvailability = applicationAvailability; } @Override protected void doHealthCheck(Health.Builder builder) throws Exception { - if (ReadinessState.READY.equals(this.applicationAvailabilityProvider.getReadinessState())) { - builder.up(); - } - else { - builder.outOfService(); - } + ReadinessState state = this.applicationAvailability.getReadinessState(); + builder.status(ReadinessState.ACCEPTING_TRAFFIC == state ? Status.UP : Status.OUT_OF_SERVICE); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicatorTests.java index b738b87082b..e609580327d 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/LivenessProbeHealthIndicatorTests.java @@ -20,11 +20,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.health.Status; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.availability.LivenessState; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.when; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** @@ -34,25 +34,25 @@ import static org.mockito.Mockito.mock; */ class LivenessProbeHealthIndicatorTests { - private ApplicationAvailabilityProvider stateProvider; + private ApplicationAvailability availability; private LivenessProbeHealthIndicator healthIndicator; @BeforeEach void setUp() { - this.stateProvider = mock(ApplicationAvailabilityProvider.class); - this.healthIndicator = new LivenessProbeHealthIndicator(this.stateProvider); + this.availability = mock(ApplicationAvailability.class); + this.healthIndicator = new LivenessProbeHealthIndicator(this.availability); } @Test void livenessIsLive() { - when(this.stateProvider.getLivenessState()).thenReturn(LivenessState.LIVE); + given(this.availability.getLivenessState()).willReturn(LivenessState.CORRECT); assertThat(this.healthIndicator.health().getStatus()).isEqualTo(Status.UP); } @Test void livenessIsBroken() { - when(this.stateProvider.getLivenessState()).thenReturn(LivenessState.BROKEN); + given(this.availability.getLivenessState()).willReturn(LivenessState.BROKEN); assertThat(this.healthIndicator.health().getStatus()).isEqualTo(Status.DOWN); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicatorTests.java index 9036839e32d..f2669152e97 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/availability/ReadinessProbeHealthIndicatorTests.java @@ -20,11 +20,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.health.Status; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.availability.ReadinessState; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.when; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** @@ -34,25 +34,25 @@ import static org.mockito.Mockito.mock; */ class ReadinessProbeHealthIndicatorTests { - private ApplicationAvailabilityProvider stateProvider; + private ApplicationAvailability availability; private ReadinessProbeHealthIndicator healthIndicator; @BeforeEach void setUp() { - this.stateProvider = mock(ApplicationAvailabilityProvider.class); - this.healthIndicator = new ReadinessProbeHealthIndicator(this.stateProvider); + this.availability = mock(ApplicationAvailability.class); + this.healthIndicator = new ReadinessProbeHealthIndicator(this.availability); } @Test void readinessIsReady() { - when(this.stateProvider.getReadinessState()).thenReturn(ReadinessState.READY); + given(this.availability.getReadinessState()).willReturn(ReadinessState.ACCEPTING_TRAFFIC); assertThat(this.healthIndicator.health().getStatus()).isEqualTo(Status.UP); } @Test void readinessIsUnready() { - when(this.stateProvider.getReadinessState()).thenReturn(ReadinessState.UNREADY); + given(this.availability.getReadinessState()).willReturn(ReadinessState.REFUSING_TRAFFIC); assertThat(this.healthIndicator.health().getStatus()).isEqualTo(Status.OUT_OF_SERVICE); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/ApplicationAvailabilityAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/ApplicationAvailabilityAutoConfiguration.java index 32f62894036..ed0e3d8a9d2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/ApplicationAvailabilityAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/ApplicationAvailabilityAutoConfiguration.java @@ -16,13 +16,13 @@ package org.springframework.boot.autoconfigure.availability; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.availability.ApplicationAvailabilityBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration} for - * {@link ApplicationAvailabilityProvider}. + * {@link ApplicationAvailabilityBean}. * * @author Brian Clozel * @since 2.3.0 @@ -31,8 +31,8 @@ import org.springframework.context.annotation.Configuration; public class ApplicationAvailabilityAutoConfiguration { @Bean - public ApplicationAvailabilityProvider applicationAvailabilityProvider() { - return new ApplicationAvailabilityProvider(); + public ApplicationAvailabilityBean applicationAvailability() { + return new ApplicationAvailabilityBean(); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kubernetes/ApplicationAvailabilityAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kubernetes/ApplicationAvailabilityAutoConfigurationTests.java index 3e598c37f67..b3cddc7fd19 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kubernetes/ApplicationAvailabilityAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kubernetes/ApplicationAvailabilityAutoConfigurationTests.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; -import org.springframework.boot.availability.ApplicationAvailabilityProvider; +import org.springframework.boot.availability.ApplicationAvailability; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -37,7 +37,7 @@ class ApplicationAvailabilityAutoConfigurationTests { @Test void providerIsPresent() { - this.contextRunner.run(((context) -> assertThat(context).hasSingleBean(ApplicationAvailabilityProvider.class))); + this.contextRunner.run(((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class))); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailability.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailability.java new file mode 100644 index 00000000000..3ab16c164ec --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailability.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.availability; + +import org.springframework.context.ApplicationContext; + +/** + * Provides {@link AvailabilityState availability state} information for the application. + *
+ * Components can inject this class to get the current state information. To update the
+ * state of the application an {@link AvailabilityChangeEvent} should be
+ * {@link ApplicationContext#publishEvent published} to the application context with
+ * directly or via {@link AvailabilityChangeEvent#publish}.
+ *
+ * @author Brian Clozel
+ * @author Phillip Webb
+ * @since 2.3.0
+ */
+public interface ApplicationAvailability {
+
+ /**
+ * Return the {@link LivenessState} of the application.
+ * @return the liveness state
+ */
+ default LivenessState getLivenessState() {
+ return getState(LivenessState.class, LivenessState.BROKEN);
+ }
+
+ /**
+ * Return the {@link ReadinessState} of the application.
+ * @return the readiness state
+ */
+ default ReadinessState getReadinessState() {
+ return getState(ReadinessState.class, ReadinessState.REFUSING_TRAFFIC);
+ }
+
+ /**
+ * Return {@link AvailabilityState} information for the application.
+ * @param
- * Other application components can get the current state information from the
- * {@code ApplicationAvailabilityProvider}, or publish application evens such as
- * {@link ReadinessStateChangedEvent} and {@link LivenessStateChangedEvent} to update the
- * state of the application.
- *
- * @author Brian Clozel
- * @since 2.3.0
- */
-public class ApplicationAvailabilityProvider implements ApplicationListener
+ * Any application component can send such events to update the state of the application.
+ *
+ * @param
- * Any application component can send such events to update the state of the application.
- *
- * @author Brian Clozel
- * @since 2.3.0
- */
-public class LivenessStateChangedEvent extends ApplicationEvent {
-
- private final String cause;
-
- LivenessStateChangedEvent(LivenessState state, String cause) {
- super(state);
- this.cause = cause;
- }
-
- public LivenessState getLivenessState() {
- return (LivenessState) getSource();
- }
-
- /**
- * Create a new {@code ApplicationEvent} signaling that the {@link LivenessState} is
- * live.
- * @param cause the cause of the live internal state of the application
- * @return the application event
- */
- public static LivenessStateChangedEvent live(String cause) {
- return new LivenessStateChangedEvent(LivenessState.LIVE, cause);
- }
-
- /**
- * Create a new {@code ApplicationEvent} signaling that the {@link LivenessState} is
- * broken.
- * @param cause the cause of the broken internal state of the application
- * @return the application event
- */
- public static LivenessStateChangedEvent broken(String cause) {
- return new LivenessStateChangedEvent(LivenessState.BROKEN, cause);
- }
-
- /**
- * Create a new {@code ApplicationEvent} signaling that the {@link LivenessState} is
- * broken.
- * @param throwable the exception that caused the broken internal state of the
- * application
- * @return the application event
- */
- public static LivenessStateChangedEvent broken(Throwable throwable) {
- return new LivenessStateChangedEvent(LivenessState.BROKEN, throwable.getMessage());
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessState.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessState.java
index 50cc39242d6..6c372f0f445 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessState.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessState.java
@@ -26,16 +26,16 @@ package org.springframework.boot.availability;
* @author Brian Clozel
* @since 2.3.0
*/
-public enum ReadinessState {
-
- /**
- * The application is not willing to receive traffic.
- */
- UNREADY,
+public enum ReadinessState implements AvailabilityState {
/**
* The application is ready to receive traffic.
*/
- READY
+ ACCEPTING_TRAFFIC,
+
+ /**
+ * The application is not willing to receive traffic.
+ */
+ REFUSING_TRAFFIC
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessStateChangedEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessStateChangedEvent.java
deleted file mode 100644
index c38b15db399..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ReadinessStateChangedEvent.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2012-2020 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
- *
- * https://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.availability;
-
-import org.springframework.context.ApplicationEvent;
-
-/**
- * {@link ApplicationEvent} sent when the {@link ReadinessState} of the application
- * changes.
- *
- * Any application component can send such events to update the state of the application.
- *
- * @author Brian Clozel
- * @since 2.3.0
- */
-public class ReadinessStateChangedEvent extends ApplicationEvent {
-
- ReadinessStateChangedEvent(ReadinessState state) {
- super(state);
- }
-
- public ReadinessState getReadinessState() {
- return (ReadinessState) getSource();
- }
-
- /**
- * Create a new {@code ApplicationEvent} signaling that the {@link ReadinessState} is
- * ready.
- * @return the application event
- */
- public static ReadinessStateChangedEvent ready() {
- return new ReadinessStateChangedEvent(ReadinessState.READY);
- }
-
- /**
- * Create a new {@code ApplicationEvent} signaling that the {@link ReadinessState} is
- * unready.
- * @return the application event
- */
- public static ReadinessStateChangedEvent unready() {
- return new ReadinessStateChangedEvent(ReadinessState.UNREADY);
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java
index da413c628d3..bd095161a19 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java
@@ -21,8 +21,9 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
-import org.springframework.boot.availability.LivenessStateChangedEvent;
-import org.springframework.boot.availability.ReadinessStateChangedEvent;
+import org.springframework.boot.availability.AvailabilityChangeEvent;
+import org.springframework.boot.availability.LivenessState;
+import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
@@ -99,13 +100,13 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
- context.publishEvent(LivenessStateChangedEvent.live("Application started"));
+ AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
- context.publishEvent(ReadinessStateChangedEvent.ready());
+ AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
@Override
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
index 700da375292..9bc7db28e4f 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
@@ -22,7 +22,8 @@ import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.boot.availability.ReadinessStateChangedEvent;
+import org.springframework.boot.availability.AvailabilityChangeEvent;
+import org.springframework.boot.availability.ReadinessState;
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServer;
@@ -149,7 +150,7 @@ public class ReactiveWebServerApplicationContext extends GenericReactiveWebAppli
@Override
protected void doClose() {
- publishEvent(ReadinessStateChangedEvent.unready());
+ AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
WebServer webServer = getWebServer();
if (webServer != null) {
webServer.shutDownGracefully();
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
index 0b793071d99..f2f68447763 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
@@ -37,7 +37,8 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.boot.availability.ReadinessStateChangedEvent;
+import org.springframework.boot.availability.AvailabilityChangeEvent;
+import org.springframework.boot.availability.ReadinessState;
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
@@ -169,7 +170,7 @@ public class ServletWebServerApplicationContext extends GenericWebApplicationCon
@Override
protected void doClose() {
- publishEvent(ReadinessStateChangedEvent.unready());
+ AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.shutDownGracefully();
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
index 9c867979624..6353a44810d 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
@@ -33,6 +33,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mockito;
@@ -46,8 +47,10 @@ import org.springframework.beans.factory.support.BeanDefinitionOverrideException
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
-import org.springframework.boot.availability.LivenessStateChangedEvent;
-import org.springframework.boot.availability.ReadinessStateChangedEvent;
+import org.springframework.boot.availability.AvailabilityChangeEvent;
+import org.springframework.boot.availability.AvailabilityState;
+import org.springframework.boot.availability.LivenessState;
+import org.springframework.boot.availability.ReadinessState;
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
@@ -108,6 +111,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.atLeastOnce;
@@ -410,9 +414,10 @@ class SpringApplicationTests {
inOrder.verify(listener).onApplicationEvent(isA(ApplicationPreparedEvent.class));
inOrder.verify(listener).onApplicationEvent(isA(ContextRefreshedEvent.class));
inOrder.verify(listener).onApplicationEvent(isA(ApplicationStartedEvent.class));
- inOrder.verify(listener).onApplicationEvent(isA(LivenessStateChangedEvent.class));
+ inOrder.verify(listener).onApplicationEvent(argThat(isAvailabilityChangeEventWithState(LivenessState.CORRECT)));
inOrder.verify(listener).onApplicationEvent(isA(ApplicationReadyEvent.class));
- inOrder.verify(listener).onApplicationEvent(isA(ReadinessStateChangedEvent.class));
+ inOrder.verify(listener)
+ .onApplicationEvent(argThat(isAvailabilityChangeEventWithState(ReadinessState.ACCEPTING_TRAFFIC)));
inOrder.verifyNoMoreInteractions();
}
@@ -886,7 +891,7 @@ class SpringApplicationTests {
this.context = application.run();
assertThat(events).hasAtLeastOneElementOfType(ApplicationPreparedEvent.class);
assertThat(events).hasAtLeastOneElementOfType(ContextRefreshedEvent.class);
- verifyTestListenerEvents();
+ verifyRegisteredListenerSuccessEvents();
}
@Test
@@ -899,24 +904,21 @@ class SpringApplicationTests {
this.context = application.run();
assertThat(events).hasAtLeastOneElementOfType(ApplicationPreparedEvent.class);
assertThat(events).hasAtLeastOneElementOfType(ContextRefreshedEvent.class);
- verifyTestListenerEvents();
+ verifyRegisteredListenerSuccessEvents();
}
@SuppressWarnings("unchecked")
- private void verifyTestListenerEvents() {
+ private void verifyRegisteredListenerSuccessEvents() {
ApplicationListener the state type
+ * @param stateType the state type
+ * @param defaultState the default state to return if no event of the given type has
+ * been published yet (must not be {@code null}.
+ * @return the readiness state
+ * @see #getState(Class)
+ */
+ S getState(Class stateType, S defaultState);
+
+ /**
+ * Return {@link AvailabilityState} information for the application.
+ * @param the state type
+ * @param stateType the state type
+ * @return the readiness state or {@code null} if no event of the given type has been
+ * published yet
+ * @see #getState(Class, AvailabilityState)
+ */
+ S getState(Class stateType);
+
+ /**
+ * Return the last {@link AvailabilityChangeEvent} received for a given state type.
+ * @param the state type
+ * @param stateType the state type
+ * @return the readiness state or {@code null} if no event of the given type has been
+ * published yet
+ */
+ AvailabilityChangeEvent getLastChangeEvent(Class stateType);
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityBean.java
new file mode 100644
index 00000000000..279e2b09b15
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityBean.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.availability;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.context.ApplicationListener;
+import org.springframework.util.Assert;
+
+/**
+ * Bean that provides an {@link ApplicationAvailability} implementation by listening for
+ * {@link AvailabilityChangeEvent change events}.
+ *
+ * @author Brian Clozel
+ * @author Phillip Webb
+ * @since 2.3.0
+ * @see ApplicationAvailability
+ */
+public class ApplicationAvailabilityBean
+ implements ApplicationAvailability, ApplicationListener S getState(Class stateType, S defaultState) {
+ Assert.notNull(stateType, "StateType must not be null");
+ Assert.notNull(defaultState, "DefaultState must not be null");
+ S state = getState(stateType);
+ return (state != null) ? state : defaultState;
+ }
+
+ @Override
+ public S getState(Class stateType) {
+ AvailabilityChangeEvent event = getLastChangeEvent(stateType);
+ return (event != null) ? event.getState() : null;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public AvailabilityChangeEvent getLastChangeEvent(Class stateType) {
+ return (AvailabilityChangeEvent) this.events.get(stateType);
+ }
+
+ @Override
+ public void onApplicationEvent(AvailabilityChangeEvent> event) {
+ Class extends AvailabilityState> stateType = getStateType(event.getState());
+ this.events.put(stateType, event);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class extends AvailabilityState> getStateType(AvailabilityState state) {
+ if (state instanceof Enum) {
+ return (Class extends AvailabilityState>) ((Enum>) state).getDeclaringClass();
+ }
+ return state.getClass();
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java
deleted file mode 100644
index 4e9f9e403aa..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2012-2020 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
- *
- * https://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.availability;
-
-import org.springframework.context.ApplicationEvent;
-import org.springframework.context.ApplicationListener;
-import org.springframework.util.Assert;
-
-/**
- * Holds the availability state of the application.
- * the availability state type
+ * @author Brian Clozel
+ * @author Phillip Webb
+ * @since 2.3.0
+ */
+public class AvailabilityChangeEvent extends ApplicationEvent {
+
+ private final S state;
+
+ /**
+ * Create a new {@link AvailabilityChangeEvent} instance.
+ * @param source the source of the event
+ * @param state the availability state (never {@code null})
+ */
+ public AvailabilityChangeEvent(Object source, S state) {
+ super(source);
+ Assert.notNull(state, "State must not be null");
+ this.state = state;
+ }
+
+ /**
+ * Return the changed availability state.
+ * @return the availability state
+ */
+ public S getState() {
+ return this.state;
+ }
+
+ /**
+ * Convenience method that can be used to publish an {@link AvailabilityChangeEvent}
+ * to the given application context.
+ * @param the availability state type
+ * @param context the context used to publish the event
+ * @param state the changed availability state
+ */
+ public static void publish(ApplicationContext context, S state) {
+ Assert.notNull(context, "Context must not be null");
+ publish(context, context, state);
+ }
+
+ /**
+ * Convenience method that can be used to publish an {@link AvailabilityChangeEvent}
+ * to the given application context.
+ * @param the availability state type
+ * @param publisher the publisher used to publish the event
+ * @param source the source of the event
+ * @param state the changed availability state
+ */
+ public static void publish(ApplicationEventPublisher publisher, Object source,
+ S state) {
+ Assert.notNull(publisher, "Publisher must not be null");
+ publisher.publishEvent(new AvailabilityChangeEvent<>(source, state));
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/AvailabilityState.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/AvailabilityState.java
new file mode 100644
index 00000000000..b78daab83b7
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/AvailabilityState.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.availability;
+
+/**
+ * Tagging interface used on {@link ApplicationAvailability} states. This interface is
+ * usually implemented on an {@code enum} type.
+ *
+ * @author Phillip Webb
+ * @since 2.3.0
+ * @see LivenessState
+ * @see ReadinessState
+ */
+public interface AvailabilityState {
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessState.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessState.java
index 303eb5d47a9..885d7c27bfb 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessState.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessState.java
@@ -26,15 +26,15 @@ package org.springframework.boot.availability;
* @author Brian Clozel
* @since 2.3.0
*/
-public enum LivenessState {
+public enum LivenessState implements AvailabilityState {
/**
* The application is running and its internal state is correct.
*/
- LIVE,
+ CORRECT,
/**
- * The internal state of the application is broken.
+ * The application is running but its internal state is broken.
*/
BROKEN
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessStateChangedEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessStateChangedEvent.java
deleted file mode 100644
index 21d9ba32d5f..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/LivenessStateChangedEvent.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2012-2020 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
- *
- * https://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.availability;
-
-import org.springframework.context.ApplicationEvent;
-
-/**
- * {@link ApplicationEvent} sent when the {@link LivenessState} of the application
- * changes.
- * ArgumentMatcher