Skip to content

Commit c6b8378

Browse files
committed
Make sure that starting a JMS connection does not block infinitely
Starting a JMS connection may block infinitely if the factory is not correctly setup. For example if ActiveMQ with failover transport is used then the default connection retries infinitely, which leads to ActiveMQ trying to establish a connection and never succedding in case none of the configured brokers is up. Fixes spring-projectsgh-10809
1 parent 2ab9593 commit c6b8378

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jms/JmsHealthIndicator.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@
1616

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

19+
import java.util.concurrent.CompletableFuture;
20+
import java.util.concurrent.ExecutionException;
21+
import java.util.concurrent.TimeUnit;
22+
import java.util.concurrent.TimeoutException;
23+
1924
import javax.jms.Connection;
2025
import javax.jms.ConnectionFactory;
26+
import javax.jms.JMSException;
2127

2228
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
2329
import org.springframework.boot.actuate.health.Health;
@@ -27,6 +33,7 @@
2733
* {@link HealthIndicator} for a JMS {@link ConnectionFactory}.
2834
*
2935
* @author Stephane Nicoll
36+
* @author Filip Hrisfov
3037
* @since 2.0.0
3138
*/
3239
public class JmsHealthIndicator extends AbstractHealthIndicator {
@@ -40,9 +47,31 @@ public JmsHealthIndicator(ConnectionFactory connectionFactory) {
4047
@Override
4148
protected void doHealthCheck(Health.Builder builder) throws Exception {
4249
try (Connection connection = this.connectionFactory.createConnection()) {
43-
connection.start();
44-
builder.up().withDetail("provider",
45-
connection.getMetaData().getJMSProviderName());
50+
CompletableFuture<Exception> future = CompletableFuture.supplyAsync(() -> {
51+
try {
52+
connection.start();
53+
return null;
54+
}
55+
catch (JMSException e) {
56+
return e;
57+
}
58+
});
59+
try {
60+
Exception exception = future.get(100, TimeUnit.MILLISECONDS);
61+
if (exception != null) {
62+
throw exception;
63+
}
64+
builder.up().withDetail("provider",
65+
connection.getMetaData().getJMSProviderName());
66+
}
67+
catch (TimeoutException ex) {
68+
builder.unknown().withDetail("provider",
69+
connection.getMetaData().getJMSProviderName())
70+
.withDetail("cause", "Could not connect for 100 milliseconds");
71+
}
72+
catch (ExecutionException ex) {
73+
throw ex;
74+
}
4675
}
4776
}
4877

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/jms/JmsHealthIndicatorTests.java

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

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

19+
import java.util.concurrent.CountDownLatch;
20+
import java.util.concurrent.TimeUnit;
21+
1922
import javax.jms.Connection;
2023
import javax.jms.ConnectionFactory;
2124
import javax.jms.ConnectionMetaData;
@@ -27,7 +30,9 @@
2730
import org.springframework.boot.actuate.health.Status;
2831

2932
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.entry;
3034
import static org.mockito.BDDMockito.given;
35+
import static org.mockito.BDDMockito.willAnswer;
3136
import static org.mockito.BDDMockito.willThrow;
3237
import static org.mockito.Mockito.mock;
3338
import static org.mockito.Mockito.times;
@@ -37,6 +42,7 @@
3742
* Tests for {@link JmsHealthIndicator}.
3843
*
3944
* @author Stephane Nicoll
45+
* @author Filip Hrisafov
4046
*/
4147
public class JmsHealthIndicatorTests {
4248

@@ -97,4 +103,25 @@ public void jmsBrokerUsesFailover() throws JMSException {
97103
assertThat(health.getDetails().get("provider")).isNull();
98104
}
99105

106+
@Test public void jmsBrokerUsesInfiniteFailover() throws JMSException {
107+
CountDownLatch latch = new CountDownLatch(1);
108+
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
109+
ConnectionMetaData connectionMetaData = mock(ConnectionMetaData.class);
110+
given(connectionMetaData.getJMSProviderName()).willReturn("JMS test provider");
111+
Connection connection = mock(Connection.class);
112+
given(connection.getMetaData()).willReturn(connectionMetaData);
113+
willAnswer(invocationOnMock -> latch.await(1, TimeUnit.SECONDS)).given(connection)
114+
.start();
115+
given(connectionFactory.createConnection()).willReturn(connection);
116+
JmsHealthIndicator indicator = new JmsHealthIndicator(connectionFactory);
117+
Health health = indicator.health();
118+
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
119+
assertThat(health.getDetails()).as("Health Details")
120+
.containsExactly(entry("provider", "JMS test provider"),
121+
entry("cause", "Could not connect for 100 milliseconds"));
122+
verify(connection, times(1)).close();
123+
verify(connection, times(1)).start();
124+
latch.countDown();
125+
}
126+
100127
}

0 commit comments

Comments
 (0)