com.sgoertzen.sonarbreak.QueryExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sonar-break-maven-plugin Show documentation
Show all versions of sonar-break-maven-plugin Show documentation
A maven plugin that will fail a maven build if sonar finds errors with your project.
package com.sgoertzen.sonarbreak;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sgoertzen.sonarbreak.qualitygate.Query;
import com.sgoertzen.sonarbreak.qualitygate.Result;
import org.apache.commons.io.IOUtils;
import org.apache.maven.plugin.logging.Log;
import org.joda.time.DateTime;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
/**
* Execute a query against Sonar to fetch the quality gate status for a build. This will look for a sonar status that
* matches the current build number and that we run in the last minute. The query will wait up to ten minutes for
* the results to become available on the sonar server.
*/
public class QueryExecutor {
public static final String SONAR_FORMAT_PATH = "api/resources/index?resource=%s&metrics=quality_gate_details";
public static final int SONAR_CONNECTION_RETRIES = 10;
public static final int SONAR_PROCESSING_WAIT_TIME = 10000; // wait time between sonar checks in milliseconds
private final URL sonarURL;
private final int sonarLookBackSeconds;
private final int waitForProcessingSeconds;
private final Log log;
/**
* Creates a new executor for running queries against sonar.
*
* @param sonarServer Fully qualified URL to the sonar server
* @param sonarLookBackSeconds Amount of time to look back into sonar history for the results of this build
* @param waitForProcessingSeconds Amount of time to wait for sonar to finish processing the job
*@param log Log for logging @return Results indicating if the build passed the sonar quality gate checks @throws MalformedURLException
*/
public QueryExecutor(String sonarServer, int sonarLookBackSeconds, int waitForProcessingSeconds, Log log) throws MalformedURLException {
this.sonarURL = new URL(sonarServer);
this.sonarLookBackSeconds = sonarLookBackSeconds;
this.waitForProcessingSeconds = waitForProcessingSeconds;
this.log = log;
}
/**
* Execute the given query on the specified sonar server.
* @param query The query specifying the project and version of the build
* @throws SonarBreakException
* @throws IOException
*/
public Result execute(Query query) throws SonarBreakException, IOException {
URL queryURL = buildURL(sonarURL, query);
log.debug("Built a sonar query url of: " + queryURL.toString());
if (!isURLAvailable(queryURL, SONAR_CONNECTION_RETRIES)){
throw new SonarBreakException(String.format("Unable to get a valid response after %d tries", SONAR_CONNECTION_RETRIES));
}
return fetchSonarStatusWithRetries(queryURL, query.getVersion());
}
/**
* Get the status from sonar for the currently executing build. This waits for sonar to complete its processing
* before returning the results.
*
* @param queryURL The sonar URL to get the results from
* @param version The current project version number
* @return Matching result object for this build
* @throws IOException
* @throws SonarBreakException
*/
private Result fetchSonarStatusWithRetries(URL queryURL, String version) throws IOException, SonarBreakException {
DateTime oneMinuteAgo = DateTime.now().minusSeconds(sonarLookBackSeconds);
DateTime waitUntil = DateTime.now().plusSeconds(waitForProcessingSeconds);
do {
Result result = fetchSonarStatus(queryURL);
if (result.getVersion().equals(version) && result.getDatetime().isAfter(oneMinuteAgo)) {
log.debug("Found a sonar job run that matches version and in the correct time frame");
return result;
}
try {
String message = String.format("Sleeping while waiting for sonar to process job. Target Version: %s. " +
"Sonar reporting Version: %s. Looking back until: %s Last result time: %s", version,
result.getVersion(), oneMinuteAgo.toString(), result.getDatetime().toString());
log.debug(message);
Thread.sleep(SONAR_PROCESSING_WAIT_TIME);
} catch (InterruptedException e) {
// Do nothing
}
} while (!waitUntil.isBeforeNow());
String message = String.format("Timed out while waiting for Sonar. Waited %d seconds. This time can be extended " +
"using the \"waitForProcessingSeconds\" configuration parameter.", waitForProcessingSeconds);
throw new SonarBreakException(message);
}
/**
* Get the status of a build project from sonar. This returns the current status that sonar has and does not
* do any checking to ensure it matches the current project
*
* @param queryURL The sonar URL to hit to get the status
* @return The sonar response include quality gate status
* @throws IOException
* @throws SonarBreakException
*/
private static Result fetchSonarStatus(URL queryURL) throws IOException, SonarBreakException {
InputStream in = null;
try {
URLConnection connection = queryURL.openConnection();
connection.setRequestProperty("Accept", "application/json");
in = connection.getInputStream();
String response = IOUtils.toString(in);
return parseResponse(response);
}
finally {
IOUtils.closeQuietly(in);
}
}
/**
* Pings a HTTP URL. This effectively sends a HEAD request and returns true
if the response code is in
* the 200-399 range.
* @param url The HTTP URL to be pinged.
* @return true
if the given HTTP URL has returned response code 200-399 on a HEAD request,
* otherwise false
.
*/
protected boolean isURLAvailable(URL url, int retryCount) throws IOException {
boolean serviceFound = false;
for (int i=0; i results;
try {
results = mapper.readValue(response, new TypeReference>() {});
} catch (IOException e) {
throw new SonarBreakException("Unable to parse the json into a List of QualityGateResults. Json is: " + response, e);
}
if (results == null || results.size() != 1){
throw new SonarBreakException("Unable to deserialize JSON response: " + response);
}
return results.get(0);
}
}