Skip to content

Commit 5f7bdcc

Browse files
GollapudiSrikanthsnicoll
authored andcommitted
Add support for configuring start timeout for JMS health checks
See gh-50957 Signed-off-by: Venkata Naga Sai Srikanth Gollapudi <42247688+gollapudisrikanth@users.noreply.github.com>
1 parent 9d472e8 commit 5f7bdcc

5 files changed

Lines changed: 139 additions & 8 deletions

File tree

module/spring-boot-jms/src/main/java/org/springframework/boot/jms/autoconfigure/health/JmsHealthContributorAutoConfiguration.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2525
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
27+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2728
import org.springframework.boot.health.autoconfigure.contributor.CompositeHealthContributorConfiguration;
2829
import org.springframework.boot.health.autoconfigure.contributor.ConditionalOnEnabledHealthIndicator;
2930
import org.springframework.boot.health.contributor.HealthContributor;
@@ -35,17 +36,19 @@
3536
* {@link EnableAutoConfiguration Auto-configuration} for {@link JmsHealthIndicator}.
3637
*
3738
* @author Stephane Nicoll
39+
* @author Venkata Naga Sai Srikanth Gollapudi
3840
* @since 4.0.0
3941
*/
4042
@AutoConfiguration(after = JmsAutoConfiguration.class)
4143
@ConditionalOnClass({ ConnectionFactory.class, JmsHealthIndicator.class })
4244
@ConditionalOnBean(ConnectionFactory.class)
4345
@ConditionalOnEnabledHealthIndicator("jms")
46+
@EnableConfigurationProperties(JmsHealthIndicatorProperties.class)
4447
public final class JmsHealthContributorAutoConfiguration
4548
extends CompositeHealthContributorConfiguration<JmsHealthIndicator, ConnectionFactory> {
4649

47-
JmsHealthContributorAutoConfiguration() {
48-
super(JmsHealthIndicator::new);
50+
JmsHealthContributorAutoConfiguration(JmsHealthIndicatorProperties properties) {
51+
super((connectionFactory) -> new JmsHealthIndicator(connectionFactory, properties.getTimeout()));
4952
}
5053

5154
@Bean
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2012-present 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+
* https://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.jms.autoconfigure.health;
18+
19+
import java.time.Duration;
20+
21+
import org.springframework.boot.context.properties.ConfigurationProperties;
22+
import org.springframework.boot.jms.health.JmsHealthIndicator;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* External configuration properties for {@link JmsHealthIndicator}.
27+
*
28+
* @author Venkata Naga Sai Srikanth Gollapudi
29+
* @since 4.x
30+
*/
31+
@ConfigurationProperties("management.health.jms")
32+
public class JmsHealthIndicatorProperties {
33+
34+
/**
35+
* Timeout to use when starting a connection for the health check.
36+
*/
37+
private Duration timeout = Duration.ofSeconds(5);
38+
39+
public Duration getTimeout() {
40+
return this.timeout;
41+
}
42+
43+
public void setTimeout(Duration timeout) {
44+
Assert.notNull(timeout, "'timeout' must not be null");
45+
Assert.isTrue(timeout.compareTo(Duration.ZERO) > 0, "'timeout' must be greater than 0");
46+
this.timeout = timeout;
47+
}
48+
49+
}

module/spring-boot-jms/src/main/java/org/springframework/boot/jms/health/JmsHealthIndicator.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.jms.health;
1818

19+
import java.time.Duration;
1920
import java.util.concurrent.CountDownLatch;
2021
import java.util.concurrent.TimeUnit;
2122

@@ -25,25 +26,40 @@
2526
import org.apache.commons.logging.Log;
2627
import org.apache.commons.logging.LogFactory;
2728

29+
import org.springframework.boot.convert.DurationStyle;
2830
import org.springframework.boot.health.contributor.AbstractHealthIndicator;
2931
import org.springframework.boot.health.contributor.Health;
3032
import org.springframework.boot.health.contributor.HealthIndicator;
33+
import org.springframework.core.log.LogMessage;
34+
import org.springframework.util.Assert;
3135

3236
/**
3337
* {@link HealthIndicator} for a JMS {@link ConnectionFactory}.
3438
*
3539
* @author Stephane Nicoll
40+
* @author Venkata Naga Sai Srikanth Gollapudi
3641
* @since 4.0.0
3742
*/
3843
public class JmsHealthIndicator extends AbstractHealthIndicator {
3944

45+
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5);
46+
4047
private final Log logger = LogFactory.getLog(JmsHealthIndicator.class);
4148

4249
private final ConnectionFactory connectionFactory;
4350

51+
private final Duration timeout;
52+
4453
public JmsHealthIndicator(ConnectionFactory connectionFactory) {
54+
this(connectionFactory, DEFAULT_TIMEOUT);
55+
}
56+
57+
public JmsHealthIndicator(ConnectionFactory connectionFactory, Duration timeout) {
4558
super("JMS health check failed");
59+
Assert.notNull(timeout, "'timeout' must not be null");
60+
Assert.isTrue(timeout.compareTo(Duration.ZERO) > 0, "'timeout' must be greater than 0");
4661
this.connectionFactory = connectionFactory;
62+
this.timeout = timeout;
4763
}
4864

4965
@Override
@@ -67,9 +83,10 @@ private final class MonitoredConnection {
6783
void start() throws JMSException {
6884
new Thread(() -> {
6985
try {
70-
if (!this.latch.await(5, TimeUnit.SECONDS)) {
86+
if (!this.latch.await(JmsHealthIndicator.this.timeout.toNanos(), TimeUnit.NANOSECONDS)) {
7187
JmsHealthIndicator.this.logger
72-
.warn("Connection failed to start within 5 seconds and will be closed.");
88+
.warn(LogMessage.format("Connection failed to start within %s and will be closed.",
89+
DurationStyle.SIMPLE.print(JmsHealthIndicator.this.timeout)));
7390
closeConnection();
7491
}
7592
}

module/spring-boot-jms/src/test/java/org/springframework/boot/jms/autoconfigure/health/JmsHealthContributorAutoConfigurationTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.jms.autoconfigure.health;
1818

19+
import java.time.Duration;
20+
1921
import jakarta.jms.ConnectionFactory;
2022
import org.junit.jupiter.api.Test;
2123

@@ -31,6 +33,7 @@
3133
* Tests for {@link JmsHealthContributorAutoConfiguration}.
3234
*
3335
* @author Phillip Webb
36+
* @author Venkata Naga Sai Srikanth Gollapudi
3437
*/
3538
class JmsHealthContributorAutoConfigurationTests {
3639

@@ -44,6 +47,27 @@ void runShouldCreateIndicator() {
4447
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(JmsHealthIndicator.class));
4548
}
4649

50+
@Test
51+
void runWhenTimeoutIsConfiguredShouldCreateIndicatorWithConfiguredTimeout() {
52+
this.contextRunner.withPropertyValues("management.health.jms.timeout=10ms").run((context) -> {
53+
assertThat(context).hasSingleBean(JmsHealthIndicator.class);
54+
assertThat(context).hasSingleBean(JmsHealthIndicatorProperties.class);
55+
assertThat(context.getBean(JmsHealthIndicatorProperties.class).getTimeout())
56+
.isEqualTo(Duration.ofMillis(10));
57+
assertThat(context.getBean(JmsHealthIndicator.class)).hasFieldOrPropertyWithValue("timeout",
58+
Duration.ofMillis(10));
59+
});
60+
}
61+
62+
@Test
63+
void runWhenTimeoutIsZeroShouldFail() {
64+
this.contextRunner.withPropertyValues("management.health.jms.timeout=0ms")
65+
.run((context) -> assertThat(context).hasFailed()
66+
.getFailure()
67+
.rootCause()
68+
.hasMessage("'timeout' must be greater than 0"));
69+
}
70+
4771
@Test
4872
void runWhenDisabledShouldNotCreateIndicator() {
4973
this.contextRunner.withPropertyValues("management.health.jms.enabled:false")

module/spring-boot-jms/src/test/java/org/springframework/boot/jms/health/JmsHealthIndicatorTests.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.jms.health;
1818

19+
import java.time.Duration;
20+
1921
import jakarta.jms.Connection;
2022
import jakarta.jms.ConnectionFactory;
2123
import jakarta.jms.ConnectionMetaData;
@@ -28,6 +30,7 @@
2830
import org.springframework.boot.health.contributor.Status;
2931

3032
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3134
import static org.mockito.BDDMockito.given;
3235
import static org.mockito.BDDMockito.then;
3336
import static org.mockito.BDDMockito.willAnswer;
@@ -38,9 +41,33 @@
3841
* Tests for {@link JmsHealthIndicator}.
3942
*
4043
* @author Stephane Nicoll
44+
* @author Venkata Naga Sai Srikanth Gollapudi
4145
*/
4246
class JmsHealthIndicatorTests {
4347

48+
@Test
49+
@SuppressWarnings("NullAway") // Test null check
50+
void createWhenTimeoutIsNullThrowsException() {
51+
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
52+
assertThatIllegalArgumentException().isThrownBy(() -> new JmsHealthIndicator(connectionFactory, null))
53+
.withMessage("'timeout' must not be null");
54+
}
55+
56+
@Test
57+
void createWhenTimeoutIsZeroThrowsException() {
58+
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
59+
assertThatIllegalArgumentException().isThrownBy(() -> new JmsHealthIndicator(connectionFactory, Duration.ZERO))
60+
.withMessage("'timeout' must be greater than 0");
61+
}
62+
63+
@Test
64+
void createWhenTimeoutIsNegativeThrowsException() {
65+
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
66+
assertThatIllegalArgumentException()
67+
.isThrownBy(() -> new JmsHealthIndicator(connectionFactory, Duration.ofMillis(-1)))
68+
.withMessage("'timeout' must be greater than 0");
69+
}
70+
4471
@Test
4572
void jmsBrokerIsUp() throws JMSException {
4673
ConnectionMetaData connectionMetaData = mock(ConnectionMetaData.class);
@@ -98,6 +125,19 @@ void jmsBrokerUsesFailover() throws JMSException {
98125

99126
@Test
100127
void whenConnectionStartIsUnresponsiveStatusIsDown() throws JMSException {
128+
Health health = healthWhenConnectionStartIsUnresponsive(Duration.ofSeconds(5));
129+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
130+
assertThat((String) health.getDetails().get("error")).contains("Connection closed");
131+
}
132+
133+
@Test
134+
void whenConnectionStartIsUnresponsiveUsesConfiguredTimeout() throws JMSException {
135+
Health health = healthWhenConnectionStartIsUnresponsive(Duration.ofMillis(10));
136+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
137+
assertThat((String) health.getDetails().get("error")).contains("Connection closed");
138+
}
139+
140+
private Health healthWhenConnectionStartIsUnresponsive(Duration timeout) throws JMSException {
101141
ConnectionMetaData connectionMetaData = mock(ConnectionMetaData.class);
102142
given(connectionMetaData.getJMSProviderName()).willReturn("JMS test provider");
103143
Connection connection = mock(Connection.class);
@@ -109,10 +149,8 @@ void whenConnectionStartIsUnresponsiveStatusIsDown() throws JMSException {
109149
}).given(connection).close();
110150
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
111151
given(connectionFactory.createConnection()).willReturn(connection);
112-
JmsHealthIndicator indicator = new JmsHealthIndicator(connectionFactory);
113-
Health health = indicator.health();
114-
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
115-
assertThat((String) health.getDetails().get("error")).contains("Connection closed");
152+
JmsHealthIndicator indicator = new JmsHealthIndicator(connectionFactory, timeout);
153+
return indicator.health();
116154
}
117155

118156
private static final class UnresponsiveStartAnswer implements Answer<Void> {

0 commit comments

Comments
 (0)