NIHVIVO-3454 Refine SolrSmokeTest. If the initial connection succeeds, try a 'ping' to the Solr server.

This commit is contained in:
j2blake 2011-12-08 22:39:41 +00:00
parent 7ee7aedc2b
commit 4acb4ccf7e

View file

@ -36,6 +36,12 @@ import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread;
public class SolrSmokeTest implements ServletContextListener { public class SolrSmokeTest implements ServletContextListener {
private static final Log log = LogFactory.getLog(SolrSmokeTest.class); private static final Log log = LogFactory.getLog(SolrSmokeTest.class);
/*
* We don't want to treat socket timeout as a non-recoverable error like the
* other exceptions. So pretend there's a status code for it instead.
*/
private static final int SOCKET_TIMEOUT_STATUS = -500;
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
final StartupStatus ss = StartupStatus.getBean(sce.getServletContext()); final StartupStatus ss = StartupStatus.getBean(sce.getServletContext());
@ -70,16 +76,9 @@ public class SolrSmokeTest implements ServletContextListener {
} }
private static class SolrSmokeTestThread extends VitroBackgroundThread { private static class SolrSmokeTestThread extends VitroBackgroundThread {
/* Use this code instead of an exception to track socket timeout. */
private static final int SOCKET_TIMEOUT_STATUS = -500;
private static final long SLEEP_INTERVAL = 10000; // 10 seconds
private final SolrSmokeTest listener; private final SolrSmokeTest listener;
private final URL solrUrl; private final URL solrUrl;
private final StartupStatus ss; private final StartupStatus ss;
private final HttpClient httpClient = new HttpClient();
private int statusCode;
public SolrSmokeTestThread(SolrSmokeTest listener, URL solrUrl, public SolrSmokeTestThread(SolrSmokeTest listener, URL solrUrl,
StartupStatus ss) { StartupStatus ss) {
@ -96,75 +95,56 @@ public class SolrSmokeTest implements ServletContextListener {
@Override @Override
public void run() { public void run() {
try { try {
tryToConnect(); new SolrHomePager(solrUrl).connect();
if (!isDone()) {
sleep();
tryToConnect();
if (!isDone()) {
sleep();
tryToConnect();
}
}
if (statusCode == HttpStatus.SC_OK) {
reportSuccess(); reportSuccess();
} else if (statusCode == HttpStatus.SC_FORBIDDEN) { } catch (SolrProblemException e) {
warnForbidden(); reportProblem(e);
} else if (statusCode == SOCKET_TIMEOUT_STATUS) { return;
warnSocketTimeout();
} else {
warnBadHttpStatus();
}
} catch (HttpException e) {
warnProtocolViolation(e);
} catch (UnknownHostException e) {
warnUnknownHost(e);
} catch (ConnectException e) {
warnConnectionRefused(e);
} catch (IOException e) {
warnTransportError(e);
}
} }
private void tryToConnect() throws IOException {
GetMethod method = new GetMethod(solrUrl.toExternalForm());
try { try {
log.debug("Trying to connect to Solr"); new SolrPinger(solrUrl).ping();
statusCode = httpClient.executeMethod(method); reportGoodPing();
log.debug("HTTP status was " + statusCode); } catch (SolrProblemException e) {
reportPingProblem(e);
// clear the buffer. return;
InputStream stream = method.getResponseBodyAsStream();
stream.close();
} catch (SocketTimeoutException e) {
// Catch the exception so we can retry this.
// Save the status so we know why we failed.
statusCode = SOCKET_TIMEOUT_STATUS;
} finally {
method.releaseConnection();
}
}
/**
* Stop trying to connect if we succeed, or if we receive an error that
* won't change on retry.
*/
private boolean isDone() {
return (statusCode == HttpStatus.SC_OK)
|| (statusCode == HttpStatus.SC_FORBIDDEN);
}
private void sleep() {
try {
Thread.sleep(SLEEP_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace(); // Should never happen
} }
} }
private void reportSuccess() { private void reportSuccess() {
ss.info(listener, ss.info(listener,
"Successfully connected to the Solr search engine."); "Successfully connected to the Solr search server.");
}
private void reportGoodPing() {
ss.info(listener, "The Solr search server responded to a 'ping'.");
}
private void reportProblem(SolrProblemException e) {
int status = e.getStatusCode();
Throwable cause = e.getCause();
if (status == HttpStatus.SC_FORBIDDEN) {
warnForbidden();
} else if (status == SOCKET_TIMEOUT_STATUS) {
warnSocketTimeout();
} else if (status != 0) {
warnBadHttpStatus(status);
} else if (cause instanceof HttpException) {
warnProtocolViolation((HttpException) cause);
} else if (cause instanceof UnknownHostException) {
warnUnknownHost((UnknownHostException) cause);
} else if (cause instanceof ConnectException) {
warnConnectionRefused((ConnectException) cause);
} else if (cause instanceof IOException) {
warnTransportError((IOException) cause);
} else {
warnUnknownProblem(e);
}
}
private void reportPingProblem(SolrProblemException e) {
ss.warning(listener, "The Solr search engine did not respond to a 'ping' request", e);
} }
private void warnSocketTimeout() { private void warnSocketTimeout() {
@ -183,9 +163,9 @@ public class SolrSmokeTest implements ServletContextListener {
+ "does it authorize access from this IP address?"); + "does it authorize access from this IP address?");
} }
private void warnBadHttpStatus() { private void warnBadHttpStatus(int status) {
ss.warning(listener, "Can't connect to the Solr search engine. " ss.warning(listener, "Can't connect to the Solr search engine. "
+ "The Solr server returned a status code of " + statusCode + "The Solr server returned a status code of " + status
+ ". Check the value of vitro.local.solr.url in " + ". Check the value of vitro.local.solr.url in "
+ "deploy.properties."); + "deploy.properties.");
} }
@ -215,5 +195,140 @@ public class SolrSmokeTest implements ServletContextListener {
+ "Detected a transport error: " + e.getMessage(), e); + "Detected a transport error: " + e.getMessage(), e);
} }
private void warnUnknownProblem(SolrProblemException e) {
ss.warning(listener, "Can't connect to the Solr search engine. "
+ "Unrecognized error: " + e.getMessage(), e);
}
}
/**
* <pre>
* This gets a little tricky. Try to connect until:
* -- we suceed
* -- we detect a non-recoverable error
* -- we fail three times
* </pre>
*/
private static class SolrHomePager {
private static final long SLEEP_INTERVAL = 10000; // 10 seconds
private final URL solrUrl;
private final HttpClient httpClient = new HttpClient();
private int statusCode;
public SolrHomePager(URL solrUrl) {
this.solrUrl = solrUrl;
}
public void connect() throws SolrProblemException {
tryToConnect();
if (!isDone()) {
sleep();
tryToConnect();
}
if (!isDone()) {
sleep();
tryToConnect();
}
if (statusCode != HttpStatus.SC_OK) {
throw new SolrProblemException(statusCode);
}
}
private void tryToConnect() throws SolrProblemException {
GetMethod method = new GetMethod(solrUrl.toExternalForm());
try {
SolrSmokeTest.log.debug("Trying to connect to Solr");
statusCode = httpClient.executeMethod(method);
SolrSmokeTest.log.debug("HTTP status was " + statusCode);
// clear the buffer.
InputStream stream = method.getResponseBodyAsStream();
stream.close();
} catch (SocketTimeoutException e) {
// Catch the exception so we can retry this.
// Save the status so we know why we failed.
statusCode = SolrSmokeTest.SOCKET_TIMEOUT_STATUS;
} catch (Exception e) {
throw new SolrProblemException(e);
} finally {
method.releaseConnection();
}
}
/**
* Stop trying to connect if we succeed, or if we receive an error that
* won't change on retry.
*/
private boolean isDone() {
return (statusCode == HttpStatus.SC_OK)
|| (statusCode == HttpStatus.SC_FORBIDDEN);
}
private void sleep() {
try {
Thread.sleep(SLEEP_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace(); // Should never happen
}
}
}
/**
* Issue a "ping" to Solr. If we get here, we've already established
* contact, so any error is a fatal one.
*/
private static class SolrPinger {
private final URL solrUrl;
private final HttpClient httpClient = new HttpClient();
public SolrPinger(URL solrUrl) {
this.solrUrl = solrUrl;
}
public void ping() throws SolrProblemException {
GetMethod method = new GetMethod(solrUrl.toExternalForm() + "/admin/ping");
try {
SolrSmokeTest.log.debug("Trying to ping Solr");
int statusCode = httpClient.executeMethod(method);
SolrSmokeTest.log.debug("Finished pinging Solr");
// clear the buffer.
InputStream stream = method.getResponseBodyAsStream();
stream.close();
if (statusCode != HttpStatus.SC_OK) {
throw new SolrProblemException(statusCode);
}
} catch (IOException e) {
throw new SolrProblemException(e);
} finally {
method.releaseConnection();
}
}
}
private static class SolrProblemException extends Exception {
private final int statusCode;
SolrProblemException(int statusCode) {
super("HTTP status code = " + statusCode);
this.statusCode = statusCode;
}
SolrProblemException(Throwable cause) {
super(cause);
this.statusCode = 0;
}
int getStatusCode() {
return this.statusCode;
}
} }
} }