diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java index 252aaea3742..5d685be9eb1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.couchbase.core.CouchbaseOperations; @@ -41,6 +42,7 @@ import org.springframework.data.couchbase.core.CouchbaseOperations; * {@link CouchbaseHealthIndicator}. * * @author EddĂș MelĂ©ndez + * @author Stephane Nicoll * @since 2.0.0 */ @Configuration @@ -49,14 +51,19 @@ import org.springframework.data.couchbase.core.CouchbaseOperations; @ConditionalOnEnabledHealthIndicator("couchbase") @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) @AutoConfigureAfter(CouchbaseDataAutoConfiguration.class) +@EnableConfigurationProperties(CouchbaseHealthIndicatorProperties.class) public class CouchbaseHealthIndicatorAutoConfiguration extends CompositeHealthIndicatorConfiguration { private final Map couchbaseOperations; + private final CouchbaseHealthIndicatorProperties properties; + public CouchbaseHealthIndicatorAutoConfiguration( - Map couchbaseOperations) { + Map couchbaseOperations, + CouchbaseHealthIndicatorProperties properties) { this.couchbaseOperations = couchbaseOperations; + this.properties = properties; } @Bean @@ -65,4 +72,11 @@ public class CouchbaseHealthIndicatorAutoConfiguration extends return createHealthIndicator(this.couchbaseOperations); } + @Override + protected CouchbaseHealthIndicator createHealthIndicator( + CouchbaseOperations couchbaseOperations) { + return new CouchbaseHealthIndicator(couchbaseOperations, + this.properties.getTimeout()); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorProperties.java new file mode 100644 index 00000000000..2b4c3162a27 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorProperties.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import java.time.Duration; + +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for {@link CouchbaseHealthIndicator}. + * + * @author Stephane Nicoll + * @since 2.0.5 + */ +@ConfigurationProperties(prefix = "management.health.couchbase") +public class CouchbaseHealthIndicatorProperties { + + /** + * Timeout for getting the Bucket information from the server. + */ + private Duration timeout = Duration.ofMillis(1000); + + public Duration getTimeout() { + return this.timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java index fe73b085443..21fce0037bb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -35,6 +36,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link CouchbaseHealthIndicatorAutoConfiguration}. * * @author Phillip Webb + * @author Stephane Nicoll */ public class CouchbaseHealthIndicatorAutoConfigurationTests { @@ -50,6 +52,17 @@ public class CouchbaseHealthIndicatorAutoConfigurationTests { .doesNotHaveBean(ApplicationHealthIndicator.class)); } + @Test + public void runWithCustomTimeoutShouldCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.timeout=2s") + .run((context) -> { + assertThat(context).hasSingleBean(CouchbaseHealthIndicator.class); + assertThat(ReflectionTestUtils.getField( + context.getBean(CouchbaseHealthIndicator.class), "timeout")) + .isEqualTo(2000L); + }); + } + @Test public void runWhenDisabledShouldNotCreateIndicator() { this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java index 9af7487bc50..42ed4cdacab 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java @@ -16,6 +16,10 @@ package org.springframework.boot.actuate.couchbase; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + import com.couchbase.client.java.bucket.BucketInfo; import com.couchbase.client.java.cluster.ClusterInfo; @@ -35,26 +39,57 @@ import org.springframework.util.StringUtils; */ public class CouchbaseHealthIndicator extends AbstractHealthIndicator { - private CouchbaseOperations operations; + private final CouchbaseOperations operations; - public CouchbaseHealthIndicator() { - super("Couchbase health check failed"); - } + private final long timeout; - public CouchbaseHealthIndicator(CouchbaseOperations couchbaseOperations) { + /** + * Create an indicator with the specified {@link CouchbaseOperations} and + * {@code timeout}. + * @param couchbaseOperations the couchbase operations + * @param timeout the request timeout + */ + public CouchbaseHealthIndicator(CouchbaseOperations couchbaseOperations, + Duration timeout) { super("Couchbase health check failed"); Assert.notNull(couchbaseOperations, "CouchbaseOperations must not be null"); + Assert.notNull(timeout, "Timeout must not be null"); this.operations = couchbaseOperations; + this.timeout = timeout.toMillis(); + } + + /** + * Create an indicator with the specified {@link CouchbaseOperations}. + * @param couchbaseOperations the couchbase operations + * @deprecated as of 2.0.5 in favour of + * {@link #CouchbaseHealthIndicator(CouchbaseOperations, Duration)} + */ + @Deprecated + public CouchbaseHealthIndicator(CouchbaseOperations couchbaseOperations) { + this(couchbaseOperations, Duration.ofSeconds(1)); } @Override protected void doHealthCheck(Health.Builder builder) throws Exception { ClusterInfo cluster = this.operations.getCouchbaseClusterInfo(); - BucketInfo bucket = this.operations.getCouchbaseBucket().bucketManager().info(); + BucketInfo bucket = getBucketInfo(); String versions = StringUtils .collectionToCommaDelimitedString(cluster.getAllVersions()); String nodes = StringUtils.collectionToCommaDelimitedString(bucket.nodeList()); builder.up().withDetail("versions", versions).withDetail("nodes", nodes); } + private BucketInfo getBucketInfo() throws Exception { + try { + return this.operations.getCouchbaseBucket().bucketManager().info(this.timeout, + TimeUnit.MILLISECONDS); + } + catch (RuntimeException ex) { + if (ex.getCause() instanceof TimeoutException) { + throw (TimeoutException) ex.getCause(); + } + throw ex; + } + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicatorTests.java index 516a514c7e3..bacd1291166 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,7 +18,10 @@ package org.springframework.boot.actuate.couchbase; import java.net.InetAddress; import java.net.UnknownHostException; +import java.time.Duration; import java.util.Collections; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.bucket.BucketInfo; @@ -51,7 +54,7 @@ public class CouchbaseHealthIndicatorTests { given(bucketInfo.nodeList()).willReturn( Collections.singletonList(InetAddress.getByName("127.0.0.1"))); BucketManager bucketManager = mock(BucketManager.class); - given(bucketManager.info()).willReturn(bucketInfo); + given(bucketManager.info(2000, TimeUnit.MILLISECONDS)).willReturn(bucketInfo); Bucket bucket = mock(Bucket.class); given(bucket.bucketManager()).willReturn(bucketManager); ClusterInfo clusterInfo = mock(ClusterInfo.class); @@ -61,7 +64,7 @@ public class CouchbaseHealthIndicatorTests { given(couchbaseOperations.getCouchbaseBucket()).willReturn(bucket); given(couchbaseOperations.getCouchbaseClusterInfo()).willReturn(clusterInfo); CouchbaseHealthIndicator healthIndicator = new CouchbaseHealthIndicator( - couchbaseOperations); + couchbaseOperations, Duration.ofSeconds(2)); Health health = healthIndicator.health(); assertThat(health.getStatus()).isEqualTo(Status.UP); assertThat(health.getDetails()).containsOnly(entry("versions", "1.2.3"), @@ -70,13 +73,29 @@ public class CouchbaseHealthIndicatorTests { verify(bucketInfo).nodeList(); } + @Test + public void couchbaseTimeout() { + BucketManager bucketManager = mock(BucketManager.class); + given(bucketManager.info(1500, TimeUnit.MILLISECONDS)).willThrow( + new RuntimeException(new TimeoutException("timeout, expected"))); + Bucket bucket = mock(Bucket.class); + given(bucket.bucketManager()).willReturn(bucketManager); + CouchbaseOperations couchbaseOperations = mock(CouchbaseOperations.class); + given(couchbaseOperations.getCouchbaseBucket()).willReturn(bucket); + CouchbaseHealthIndicator healthIndicator = new CouchbaseHealthIndicator( + couchbaseOperations, Duration.ofMillis(1500)); + Health health = healthIndicator.health(); + assertThat((String) health.getDetails().get("error")) + .contains("timeout, expected"); + } + @Test public void couchbaseIsDown() { CouchbaseOperations couchbaseOperations = mock(CouchbaseOperations.class); given(couchbaseOperations.getCouchbaseClusterInfo()) .willThrow(new IllegalStateException("test, expected")); CouchbaseHealthIndicator healthIndicator = new CouchbaseHealthIndicator( - couchbaseOperations); + couchbaseOperations, Duration.ofSeconds(1)); Health health = healthIndicator.health(); assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat((String) health.getDetails().get("error")).contains("test, expected"); 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 0873761c7d0..0f1329786ac 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 @@ -1276,6 +1276,7 @@ content into your application. Rather, pick only the properties that you need. management.health.db.enabled=true # Whether to enable database health check. management.health.cassandra.enabled=true # Whether to enable Cassandra health check. management.health.couchbase.enabled=true # Whether to enable Couchbase health check. + management.health.couchbase.timeout=1000ms # Timeout for getting the Bucket information from the server. management.health.defaults.enabled=true # Whether to enable default health indicators. management.health.diskspace.enabled=true # Whether to enable disk space health check. management.health.diskspace.path= # Path used to compute the available disk space.