Skip to content

Commit f79dad6

Browse files
committed
Merge pull request #16477 from schuch
* pr/16477: Polish "Fallback to ping if Solr URL references core" Fallback to ping if Solr URL references core Closes gh-16477
2 parents ca6395c + e8d9b6f commit f79dad6

File tree

2 files changed

+161
-15
lines changed

2 files changed

+161
-15
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/solr/SolrHealthIndicator.java

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package org.springframework.boot.actuate.solr;
1818

1919
import org.apache.solr.client.solrj.SolrClient;
20+
import org.apache.solr.client.solrj.impl.HttpSolrClient.RemoteSolrException;
2021
import org.apache.solr.client.solrj.request.CoreAdminRequest;
21-
import org.apache.solr.client.solrj.response.CoreAdminResponse;
2222
import org.apache.solr.common.params.CoreAdminParams;
2323

2424
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
@@ -31,25 +31,106 @@
3131
*
3232
* @author Andy Wilkinson
3333
* @author Stephane Nicoll
34+
* @author Markus Schuch
35+
* @author Phillip Webb
3436
* @since 2.0.0
3537
*/
3638
public class SolrHealthIndicator extends AbstractHealthIndicator {
3739

40+
private static final int HTTP_NOT_FOUND_STATUS = 404;
41+
3842
private final SolrClient solrClient;
3943

44+
private volatile StatusCheck statusCheck;
45+
4046
public SolrHealthIndicator(SolrClient solrClient) {
4147
super("Solr health check failed");
4248
this.solrClient = solrClient;
4349
}
4450

4551
@Override
4652
protected void doHealthCheck(Health.Builder builder) throws Exception {
47-
CoreAdminRequest request = new CoreAdminRequest();
48-
request.setAction(CoreAdminParams.CoreAdminAction.STATUS);
49-
CoreAdminResponse response = request.process(this.solrClient);
50-
int statusCode = response.getStatus();
53+
int statusCode = initializeStatusCheck();
5154
Status status = (statusCode != 0) ? Status.DOWN : Status.UP;
52-
builder.status(status).withDetail("status", statusCode);
55+
builder.status(status).withDetail("status", statusCode).withDetail("detectedPathType",
56+
this.statusCheck.getPathType());
57+
}
58+
59+
private int initializeStatusCheck() throws Exception {
60+
StatusCheck statusCheck = this.statusCheck;
61+
if (statusCheck != null) {
62+
// Already initilized
63+
return statusCheck.getStatus(this.solrClient);
64+
}
65+
try {
66+
return initializeStatusCheck(new RootStatusCheck());
67+
}
68+
catch (RemoteSolrException ex) {
69+
// 404 is thrown when SolrClient has a baseUrl pointing to a particular core.
70+
if (ex.code() == HTTP_NOT_FOUND_STATUS) {
71+
return initializeStatusCheck(new ParticularCoreStatusCheck());
72+
}
73+
throw ex;
74+
}
75+
}
76+
77+
private int initializeStatusCheck(StatusCheck statusCheck) throws Exception {
78+
int result = statusCheck.getStatus(this.solrClient);
79+
this.statusCheck = statusCheck;
80+
return result;
81+
}
82+
83+
/**
84+
* Strategy used to perform the status check.
85+
*/
86+
private abstract static class StatusCheck {
87+
88+
private final String pathType;
89+
90+
StatusCheck(String pathType) {
91+
this.pathType = pathType;
92+
}
93+
94+
abstract int getStatus(SolrClient client) throws Exception;
95+
96+
String getPathType() {
97+
return this.pathType;
98+
}
99+
100+
}
101+
102+
/**
103+
* {@link StatusCheck} used when {@code baseUrl} points to the root context.
104+
*/
105+
private static class RootStatusCheck extends StatusCheck {
106+
107+
RootStatusCheck() {
108+
super("root");
109+
}
110+
111+
@Override
112+
public int getStatus(SolrClient client) throws Exception {
113+
CoreAdminRequest request = new CoreAdminRequest();
114+
request.setAction(CoreAdminParams.CoreAdminAction.STATUS);
115+
return request.process(client).getStatus();
116+
}
117+
118+
}
119+
120+
/**
121+
* {@link StatusCheck} used when {@code baseUrl} points to the particular core.
122+
*/
123+
private static class ParticularCoreStatusCheck extends StatusCheck {
124+
125+
ParticularCoreStatusCheck() {
126+
super("particular core");
127+
}
128+
129+
@Override
130+
public int getStatus(SolrClient client) throws Exception {
131+
return client.ping().getStatus();
132+
}
133+
53134
}
54135

55136
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/solr/SolrHealthIndicatorTests.java

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import java.io.IOException;
2020

2121
import org.apache.solr.client.solrj.SolrClient;
22+
import org.apache.solr.client.solrj.impl.HttpSolrClient.RemoteSolrException;
2223
import org.apache.solr.client.solrj.request.CoreAdminRequest;
24+
import org.apache.solr.client.solrj.response.SolrPingResponse;
2325
import org.apache.solr.common.util.NamedList;
2426
import org.junit.After;
2527
import org.junit.Test;
@@ -33,11 +35,16 @@
3335
import static org.mockito.ArgumentMatchers.isNull;
3436
import static org.mockito.BDDMockito.given;
3537
import static org.mockito.Mockito.mock;
38+
import static org.mockito.Mockito.times;
39+
import static org.mockito.Mockito.verify;
40+
import static org.mockito.Mockito.verifyNoMoreInteractions;
3641

3742
/**
3843
* Tests for {@link SolrHealthIndicator}
3944
*
4045
* @author Andy Wilkinson
46+
* @author Markus Schuch
47+
* @author Phillip Webb
4148
*/
4249
public class SolrHealthIndicatorTests {
4350

@@ -51,34 +58,86 @@ public void close() {
5158
}
5259

5360
@Test
54-
public void solrIsUp() throws Exception {
61+
public void healthWhenSolrStatusUpAndBaseUrlPointsToRootReturnsUp() throws Exception {
5562
SolrClient solrClient = mock(SolrClient.class);
5663
given(solrClient.request(any(CoreAdminRequest.class), isNull())).willReturn(mockResponse(0));
5764
SolrHealthIndicator healthIndicator = new SolrHealthIndicator(solrClient);
58-
Health health = healthIndicator.health();
59-
assertThat(health.getStatus()).isEqualTo(Status.UP);
60-
assertThat(health.getDetails().get("status")).isEqualTo(0);
65+
assertHealth(healthIndicator, Status.UP, 0, "root");
66+
verify(solrClient, times(1)).request(any(CoreAdminRequest.class), isNull());
67+
verifyNoMoreInteractions(solrClient);
6168
}
6269

6370
@Test
64-
public void solrIsUpAndRequestFailed() throws Exception {
71+
public void healthWhenSolrStatusDownAndBaseUrlPointsToRootReturnsDown() throws Exception {
6572
SolrClient solrClient = mock(SolrClient.class);
6673
given(solrClient.request(any(CoreAdminRequest.class), isNull())).willReturn(mockResponse(400));
6774
SolrHealthIndicator healthIndicator = new SolrHealthIndicator(solrClient);
68-
Health health = healthIndicator.health();
69-
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
70-
assertThat(health.getDetails().get("status")).isEqualTo(400);
75+
assertHealth(healthIndicator, Status.DOWN, 400, "root");
76+
verify(solrClient, times(1)).request(any(CoreAdminRequest.class), isNull());
77+
verifyNoMoreInteractions(solrClient);
78+
}
79+
80+
@Test
81+
public void healthWhenSolrStatusUpAndBaseUrlPointsToParticularCoreReturnsUp() throws Exception {
82+
SolrClient solrClient = mock(SolrClient.class);
83+
given(solrClient.request(any(CoreAdminRequest.class), isNull()))
84+
.willThrow(new RemoteSolrException("mock", 404, "", null));
85+
given(solrClient.ping()).willReturn(mockPingResponse(0));
86+
SolrHealthIndicator healthIndicator = new SolrHealthIndicator(solrClient);
87+
assertHealth(healthIndicator, Status.UP, 0, "particular core");
88+
verify(solrClient, times(1)).request(any(CoreAdminRequest.class), isNull());
89+
verify(solrClient, times(1)).ping();
90+
verifyNoMoreInteractions(solrClient);
91+
}
92+
93+
@Test
94+
public void healthWhenSolrStatusDownAndBaseUrlPointsToParticularCoreReturnsDown() throws Exception {
95+
SolrClient solrClient = mock(SolrClient.class);
96+
given(solrClient.request(any(CoreAdminRequest.class), isNull()))
97+
.willThrow(new RemoteSolrException("mock", 404, "", null));
98+
given(solrClient.ping()).willReturn(mockPingResponse(400));
99+
SolrHealthIndicator healthIndicator = new SolrHealthIndicator(solrClient);
100+
assertHealth(healthIndicator, Status.DOWN, 400, "particular core");
101+
verify(solrClient, times(1)).request(any(CoreAdminRequest.class), isNull());
102+
verify(solrClient, times(1)).ping();
103+
verifyNoMoreInteractions(solrClient);
71104
}
72105

73106
@Test
74-
public void solrIsDown() throws Exception {
107+
public void healthWhenSolrConnectionFailsReturnsDown() throws Exception {
75108
SolrClient solrClient = mock(SolrClient.class);
76109
given(solrClient.request(any(CoreAdminRequest.class), isNull()))
77110
.willThrow(new IOException("Connection failed"));
78111
SolrHealthIndicator healthIndicator = new SolrHealthIndicator(solrClient);
79112
Health health = healthIndicator.health();
80113
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
81114
assertThat((String) health.getDetails().get("error")).contains("Connection failed");
115+
verify(solrClient, times(1)).request(any(CoreAdminRequest.class), isNull());
116+
verifyNoMoreInteractions(solrClient);
117+
}
118+
119+
@Test
120+
public void healthWhenMakingMultipleCallsRemembersStatusStrategy() throws Exception {
121+
SolrClient solrClient = mock(SolrClient.class);
122+
given(solrClient.request(any(CoreAdminRequest.class), isNull()))
123+
.willThrow(new RemoteSolrException("mock", 404, "", null));
124+
given(solrClient.ping()).willReturn(mockPingResponse(0));
125+
SolrHealthIndicator healthIndicator = new SolrHealthIndicator(solrClient);
126+
healthIndicator.health();
127+
verify(solrClient, times(1)).request(any(CoreAdminRequest.class), isNull());
128+
verify(solrClient, times(1)).ping();
129+
verifyNoMoreInteractions(solrClient);
130+
healthIndicator.health();
131+
verify(solrClient, times(2)).ping();
132+
verifyNoMoreInteractions(solrClient);
133+
}
134+
135+
private void assertHealth(SolrHealthIndicator healthIndicator, Status expectedStatus, int expectedStatusCode,
136+
String expectedPathType) {
137+
Health health = healthIndicator.health();
138+
assertThat(health.getStatus()).isEqualTo(expectedStatus);
139+
assertThat(health.getDetails().get("status")).isEqualTo(expectedStatusCode);
140+
assertThat(health.getDetails().get("detectedPathType")).isEqualTo(expectedPathType);
82141
}
83142

84143
private NamedList<Object> mockResponse(int status) {
@@ -89,4 +148,10 @@ private NamedList<Object> mockResponse(int status) {
89148
return response;
90149
}
91150

151+
private SolrPingResponse mockPingResponse(int status) {
152+
SolrPingResponse pingResponse = new SolrPingResponse();
153+
pingResponse.setResponse(mockResponse(status));
154+
return pingResponse;
155+
}
156+
92157
}

0 commit comments

Comments
 (0)