/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.jenkins.results.parser;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.dom4j.Element;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* @author Kevin Yen
*/
public abstract class BaseBuild implements Build {
@Override
public void addDownstreamBuilds(String... urls) {
for (String url : urls) {
try {
url = JenkinsResultsParserUtil.getLocalURL(
JenkinsResultsParserUtil.decode(url));
}
catch (UnsupportedEncodingException uee) {
throw new IllegalArgumentException(
"Unable to decode " + url, uee);
}
if (!hasBuildURL(url)) {
downstreamBuilds.add(BuildFactory.newBuild(url, this));
}
}
}
@Override
public void archive(final String archiveName) {
if (!_status.equals("completed")) {
throw new RuntimeException("Invalid build status: " + _status);
}
this.archiveName = archiveName;
File archiveDir = new File(getArchivePath());
if (archiveDir.exists()) {
archiveDir.delete();
}
if (downstreamBuilds != null) {
ExecutorService executorService = getExecutorService();
for (final Build downstreamBuild : downstreamBuilds) {
if (executorService != null) {
Runnable runnable = new Runnable() {
@Override
public void run() {
downstreamBuild.archive(archiveName);
}
};
executorService.execute(runnable);
}
else {
downstreamBuild.archive(archiveName);
}
}
if (executorService != null) {
executorService.shutdown();
while (!executorService.isTerminated()) {
JenkinsResultsParserUtil.sleep(100);
}
}
}
try {
writeArchiveFile(
Long.toString(System.currentTimeMillis()),
getArchivePath() + "/archive-marker");
}
catch (IOException ioe) {
throw new RuntimeException(
"Unable to to write archive-marker file", ioe);
}
archiveConsoleLog();
archiveJSON();
}
@Override
public String getAppServer() {
return null;
}
@Override
public String getArchivePath() {
StringBuilder sb = new StringBuilder(archiveName);
if (!archiveName.endsWith("/")) {
sb.append("/");
}
sb.append(getMaster());
sb.append("/");
sb.append(getJobName());
sb.append("/");
sb.append(getBuildNumber());
return sb.toString();
}
@Override
public List<String> getBadBuildURLs() {
List<String> badBuildURLs = new ArrayList<>();
String jobURL = getJobURL();
for (Integer badBuildNumber : badBuildNumbers) {
badBuildURLs.add(
JenkinsResultsParserUtil.combine(
jobURL, "/", Integer.toString(badBuildNumber), "/"));
}
return badBuildURLs;
}
@Override
public String getBaseRepositoryName() {
if (repositoryName == null) {
Properties buildProperties = null;
try {
buildProperties = JenkinsResultsParserUtil.getBuildProperties();
}
catch (IOException ioe) {
throw new RuntimeException(
"Unable to get build.properties", ioe);
}
TopLevelBuild topLevelBuild = getTopLevelBuild();
repositoryName = topLevelBuild.getParameterValue("REPOSITORY_NAME");
if ((repositoryName != null) && !repositoryName.isEmpty()) {
return repositoryName;
}
repositoryName = buildProperties.getProperty(
JenkinsResultsParserUtil.combine(
"repository[", topLevelBuild.getJobName(), "]"));
if (repositoryName == null) {
throw new RuntimeException(
"Unable to get repository name for job " +
topLevelBuild.getJobName());
}
}
return repositoryName;
}
@Override
public String getBaseRepositorySHA(String repositoryName) {
TopLevelBuild topLevelBuild = getTopLevelBuild();
if (repositoryName.equals("liferay-jenkins-ee")) {
Map<String, String> topLevelBuildStartPropertiesTempMap =
topLevelBuild.getStartPropertiesTempMap();
return topLevelBuildStartPropertiesTempMap.get(
"JENKINS_GITHUB_UPSTREAM_BRANCH_SHA");
}
Map<String, String> repositoryGitDetailsTempMap =
topLevelBuild.getBaseGitRepositoryDetailsTempMap();
return repositoryGitDetailsTempMap.get("github.upstream.branch.sha");
}
@Override
public String getBranchName() {
return branchName;
}
@Override
public String getBrowser() {
return null;
}
@Override
public JSONObject getBuildJSONObject() {
try {
return JenkinsResultsParserUtil.toJSONObject(
JenkinsResultsParserUtil.getLocalURL(
getBuildURL() + "api/json"),
false);
}
catch (IOException ioe) {
throw new RuntimeException("Unable to get build JSON object", ioe);
}
}
@Override
public int getBuildNumber() {
return _buildNumber;
}
@Override
public String getBuildURL() {
try {
String jobURL = getJobURL();
if ((jobURL == null) || (_buildNumber == -1)) {
return null;
}
if (fromArchive) {
return jobURL + "/" + _buildNumber + "/";
}
jobURL = JenkinsResultsParserUtil.decode(jobURL);
return JenkinsResultsParserUtil.encode(
jobURL + "/" + _buildNumber + "/");
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getBuildURLRegex() {
StringBuffer sb = new StringBuffer();
sb.append("http[s]*:\\/\\/");
sb.append(JenkinsResultsParserUtil.getRegexLiteral(getMaster()));
sb.append("[^\\/]*");
sb.append("[\\/]+job[\\/]+");
String jobNameRegexLiteral = JenkinsResultsParserUtil.getRegexLiteral(
getJobName());
jobNameRegexLiteral = jobNameRegexLiteral.replace("\\(", "(\\(|%28)");
jobNameRegexLiteral = jobNameRegexLiteral.replace("\\)", "(\\)|%29)");
sb.append(jobNameRegexLiteral);
sb.append("[\\/]+");
sb.append(getBuildNumber());
sb.append("[\\/]*");
String buildURLRegex = sb.toString();
return buildURLRegex;
}
@Override
public String getConsoleText() {
if (_consoleText != null) {
return _consoleText;
}
String buildURL = getBuildURL();
if (buildURL != null) {
JenkinsConsoleTextLoader jenkinsConsoleTextLoader =
new JenkinsConsoleTextLoader(getBuildURL());
String consoleText = jenkinsConsoleTextLoader.getConsoleText();
if (consoleText.contains("\nFinished:") &&
(getParentBuild() == null)) {
_consoleText = consoleText;
}
return consoleText;
}
return "";
}
@Override
public String getDatabase() {
return null;
}
@Override
public String getDisplayName() {
StringBuilder sb = new StringBuilder();
sb.append(getJobName());
String jobVariant = getParameterValue("JOB_VARIANT");
if ((jobVariant != null) && !jobVariant.isEmpty()) {
sb.append("/");
sb.append(jobVariant);
}
return sb.toString();
}
@Override
public int getDownstreamBuildCount(String status) {
List<Build> downstreamBuilds = getDownstreamBuilds(status);
return downstreamBuilds.size();
}
@Override
public List<Build> getDownstreamBuilds(String status) {
if (status == null) {
return downstreamBuilds;
}
List<Build> filteredDownstreamBuilds = new ArrayList<>();
for (Build downstreamBuild : downstreamBuilds) {
if (status.equals(downstreamBuild.getStatus())) {
filteredDownstreamBuilds.add(downstreamBuild);
}
}
return filteredDownstreamBuilds;
}
@Override
public long getDuration() {
JSONObject buildJSONObject = getBuildJSONObject("duration,timestamp");
long duration = buildJSONObject.getLong("duration");
if (duration == 0) {
long timestamp = buildJSONObject.getLong("timestamp");
duration = System.currentTimeMillis() - timestamp;
}
return duration;
}
@Override
public Element getGitHubMessageBuildAnchorElement() {
getResult();
int i = 0;
while (result == null) {
if (i == 20) {
throw new RuntimeException(
JenkinsResultsParserUtil.combine(
"Unable to create build anchor element. The process ",
"timed out while waiting for a build result for ",
getBuildURL(), "."));
}
JenkinsResultsParserUtil.sleep(1000 * 30);
getResult();
i++;
}
if (result.equals("SUCCESS")) {
return Dom4JUtil.getNewAnchorElement(
getBuildURL(), getDisplayName());
}
return Dom4JUtil.getNewAnchorElement(
getBuildURL(), null,
Dom4JUtil.getNewElement(
"strike", null,
Dom4JUtil.getNewElement("strong", null, getDisplayName())));
}
@Override
public Element getGitHubMessageElement() {
String status = getStatus();
if (!status.equals("completed") && (getParentBuild() != null)) {
return null;
}
String result = getResult();
if (result.equals("SUCCESS")) {
return null;
}
Element messageElement = Dom4JUtil.getNewElement("div");
Dom4JUtil.addToElement(
messageElement,
Dom4JUtil.getNewElement(
"h5", null,
Dom4JUtil.getNewAnchorElement(getBuildURL(), getDisplayName())),
getGitHubMessageJobResultsElement());
if (result.equals("ABORTED") && (getDownstreamBuildCount(null) == 0)) {
messageElement.add(
Dom4JUtil.toCodeSnippetElement("Build was aborted"));
return messageElement;
}
Element failureMessageElement = getFailureMessageElement();
if (failureMessageElement != null) {
messageElement.add(failureMessageElement);
}
return messageElement;
}
@Override
public String getInvocationURL() {
String jobURL = getJobURL();
if (jobURL == null) {
return null;
}
StringBuffer sb = new StringBuffer(jobURL);
sb.append("/buildWithParameters?");
Map<String, String> parameters = getParameters();
parameters.put("token", "raen3Aib");
for (Map.Entry<String, String> parameter : parameters.entrySet()) {
String value = parameter.getValue();
if ((value != null) && !value.isEmpty()) {
sb.append(parameter.getKey());
sb.append("=");
sb.append(parameter.getValue());
sb.append("&");
}
}
sb.deleteCharAt(sb.length() - 1);
try {
return JenkinsResultsParserUtil.encode(sb.toString());
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getJDK() {
return null;
}
@Override
public String getJobName() {
return jobName;
}
@Override
public String getJobURL() {
if ((master == null) || (jobName == null)) {
return null;
}
if (fromArchive) {
return "${dependencies.url}/" + archiveName + "/" + master + "/" +
jobName;
}
try {
return JenkinsResultsParserUtil.encode(
JenkinsResultsParserUtil.combine(
"https://", master, ".liferay.com/job/", jobName));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getJobVariant() {
String batchName = getParameterValue("JOB_VARIANT");
if ((batchName == null) || batchName.isEmpty()) {
batchName = getParameterValue("JENKINS_JOB_VARIANT");
}
return batchName;
}
@Override
public Long getLatestStartTimestamp() {
Long latestStartTimestamp = getStartTimestamp();
if (latestStartTimestamp == null) {
return null;
}
for (Build downstreamBuild : getDownstreamBuilds(null)) {
Long downstreamBuildLatestStartTimestamp =
downstreamBuild.getLatestStartTimestamp();
if (downstreamBuildLatestStartTimestamp == null) {
return null;
}
latestStartTimestamp = Math.max(
latestStartTimestamp,
downstreamBuild.getLatestStartTimestamp());
}
return latestStartTimestamp;
}
@Override
public String getMaster() {
return master;
}
@Override
public String getOperatingSystem() {
return null;
}
@Override
public Map<String, String> getParameters() {
return new HashMap<>(_parameters);
}
@Override
public String getParameterValue(String name) {
return _parameters.get(name);
}
@Override
public Build getParentBuild() {
return _parentBuild;
}
@Override
public String getResult() {
if ((result == null) && (getBuildURL() != null)) {
try {
JSONObject resultJSONObject = getBuildJSONObject("result");
result = resultJSONObject.optString("result");
if (result.equals("")) {
result = null;
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
return result;
}
@Override
public Map<String, String> getStartPropertiesTempMap() {
return getTempMap("start.properties");
}
@Override
public Long getStartTimestamp() {
JSONObject buildJSONObject = getBuildJSONObject("timestamp");
if (buildJSONObject == null) {
return null;
}
long timestamp = buildJSONObject.getLong("timestamp");
if (timestamp == 0) {
return null;
}
return timestamp;
}
@Override
public String getStatus() {
return _status;
}
@Override
public long getStatusAge() {
return System.currentTimeMillis() - statusModifiedTime;
}
@Override
public String getStatusReport() {
return getStatusReport(0);
}
@Override
public String getStatusReport(int indentSize) {
StringBuffer indentStringBuffer = new StringBuffer();
for (int i = 0; i < indentSize; i++) {
indentStringBuffer.append(" ");
}
StringBuilder sb = new StringBuilder();
sb.append(indentStringBuffer);
sb.append("Build \"");
sb.append(jobName);
sb.append("\"");
String status = getStatus();
if (status.equals("completed")) {
sb.append(" completed at ");
sb.append(getBuildURL());
sb.append(". ");
sb.append(getResult());
return sb.toString();
}
if (status.equals("missing")) {
sb.append(" is missing ");
sb.append(getJobURL());
sb.append(".");
return sb.toString();
}
if (status.equals("queued")) {
sb.append(" is queued at ");
sb.append(getJobURL());
sb.append(".");
return sb.toString();
}
if (status.equals("running")) {
sb.append(" running at ");
sb.append(getBuildURL());
sb.append(".\n");
if (getDownstreamBuildCount(null) > 0) {
sb.append("\n");
for (Build downstreamBuild : getDownstreamBuilds("running")) {
sb.append(downstreamBuild.getStatusReport(indentSize + 4));
}
sb.append("\n");
sb.append(indentStringBuffer);
sb.append(getStatusSummary());
sb.append("\n");
}
return sb.toString();
}
if (status.equals("starting")) {
sb.append(" invoked at ");
sb.append(getJobURL());
sb.append(".");
return sb.toString();
}
throw new RuntimeException("Unknown status: " + status);
}
@Override
public String getStatusSummary() {
return JenkinsResultsParserUtil.combine(
Integer.toString(getDownstreamBuildCount("starting")),
" Starting ", "/ ",
Integer.toString(getDownstreamBuildCount("missing")), " Missing ",
"/ ",
Integer.toString(getDownstreamBuildCount("queued")), " Queued ",
"/ ",
Integer.toString(getDownstreamBuildCount("running")), " Running ",
"/ ",
Integer.toString(getDownstreamBuildCount("completed")),
" Completed ", "/ ",
Integer.toString(getDownstreamBuildCount(null)), " Total ");
}
@Override
public Map<String, String> getStopPropertiesTempMap() {
return getTempMap("stop.properties");
}
@Override
public JSONObject getTestReportJSONObject() {
try {
return JenkinsResultsParserUtil.toJSONObject(
JenkinsResultsParserUtil.getLocalURL(
getBuildURL() + "testReport/api/json"),
false);
}
catch (IOException ioe) {
throw new RuntimeException(
"Unable to get test report JSON object", ioe);
}
}
@Override
public List<TestResult> getTestResults(String testStatus) {
List<TestResult> testResults = new ArrayList<>();
for (Build downstreamBuild : getDownstreamBuilds(null)) {
testResults.addAll(downstreamBuild.getTestResults(testStatus));
}
return testResults;
}
@Override
public TopLevelBuild getTopLevelBuild() {
Build topLevelBuild = this;
while ((topLevelBuild != null) &&
!(topLevelBuild instanceof TopLevelBuild)) {
topLevelBuild = topLevelBuild.getParentBuild();
}
return (TopLevelBuild)topLevelBuild;
}
@Override
public boolean hasBuildURL(String buildURL) {
try {
buildURL = JenkinsResultsParserUtil.decode(buildURL);
}
catch (Exception e) {
throw new RuntimeException(e);
}
buildURL = JenkinsResultsParserUtil.getLocalURL(buildURL);
String thisBuildURL = getBuildURL();
if (thisBuildURL != null) {
thisBuildURL = JenkinsResultsParserUtil.getLocalURL(thisBuildURL);
if (thisBuildURL.equals(buildURL)) {
return true;
}
}
for (Build downstreamBuild : downstreamBuilds) {
if (downstreamBuild.hasBuildURL(buildURL)) {
return true;
}
}
return false;
}
@Override
public void reinvoke() {
reinvoke(null);
}
@Override
public void reinvoke(ReinvokeRule reinvokeRule) {
String hostName = JenkinsResultsParserUtil.getHostName("");
Build parentBuild = getParentBuild();
String parentBuildStatus = parentBuild.getStatus();
if (!parentBuildStatus.equals("running") ||
!hostName.startsWith("cloud-10-0")) {
return;
}
if ((reinvokeRule != null) && !fromArchive) {
String message = JenkinsResultsParserUtil.combine(
reinvokeRule.getName(), " failure detected at ", getBuildURL(),
". This build will be reinvoked.\n\n", reinvokeRule.toString(),
"\n\n");
System.out.println(message);
TopLevelBuild topLevelBuild = getTopLevelBuild();
if (topLevelBuild != null) {
message = JenkinsResultsParserUtil.combine(
message, "Top Level Build URL: ",
topLevelBuild.getBuildURL());
}
String notificationList = reinvokeRule.getNotificationList();
if ((notificationList != null) && !notificationList.isEmpty()) {
try {
JenkinsResultsParserUtil.sendEmail(
message, "jenkins", "Build Reinvoked",
reinvokeRule.notificationList);
}
catch (Exception e) {
throw new RuntimeException(
"Unable to send reinvoke notification", e);
}
}
}
String invocationURL = getInvocationURL();
try {
JenkinsResultsParserUtil.toString(
JenkinsResultsParserUtil.getLocalURL(invocationURL));
}
catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println(getReinvokedMessage());
reset();
}
@Override
public String replaceBuildURL(String text) {
if ((text == null) || text.isEmpty()) {
return text;
}
if (downstreamBuilds != null) {
for (Build downstreamBuild : downstreamBuilds) {
Build downstreamBaseBuild = downstreamBuild;
text = downstreamBaseBuild.replaceBuildURL(text);
}
}
text = text.replaceAll(
getBuildURLRegex(),
Matcher.quoteReplacement(
"${dependencies.url}/" + getArchivePath()));
Build parentBuild = getParentBuild();
while (parentBuild != null) {
text = text.replaceAll(
parentBuild.getBuildURLRegex(),
Matcher.quoteReplacement(
"${dependencies.url}/" + parentBuild.getArchivePath()));
parentBuild = parentBuild.getParentBuild();
}
return text;
}
@Override
public void update() {
String status = getStatus();
if (!status.equals("completed")) {
try {
if (status.equals("missing") || status.equals("queued") ||
status.equals("starting")) {
JSONObject runningBuildJSONObject =
getRunningBuildJSONObject();
if (runningBuildJSONObject != null) {
setBuildNumber(runningBuildJSONObject.getInt("number"));
}
else {
JSONObject queueItemJSONObject =
getQueueItemJSONObject();
if (status.equals("starting") &&
(queueItemJSONObject != null)) {
setStatus("queued");
}
else if (status.equals("queued") &&
(queueItemJSONObject == null)) {
setStatus("missing");
}
}
}
status = getStatus();
if (downstreamBuilds != null) {
ExecutorService executorService = getExecutorService();
for (final Build downstreamBuild : downstreamBuilds) {
if (executorService != null) {
Runnable runnable = new Runnable() {
@Override
public void run() {
downstreamBuild.update();
}
};
executorService.execute(runnable);
}
else {
downstreamBuild.update();
}
}
if (executorService != null) {
executorService.shutdown();
while (!executorService.isTerminated()) {
JenkinsResultsParserUtil.sleep(100);
}
}
String result = getResult();
if ((downstreamBuilds.size() ==
getDownstreamBuildCount("completed")) &&
(result != null)) {
setStatus("completed");
}
findDownstreamBuilds();
if (this instanceof AxisBuild ||
this instanceof BatchBuild ||
this instanceof TopLevelBuild || fromArchive ||
(badBuildNumbers.size() >= MAX_REINVOCATIONS)) {
return;
}
if ((result != null) && !result.equals("SUCCESS")) {
for (ReinvokeRule reinvokeRule : reinvokeRules) {
if (!reinvokeRule.matches(this)) {
continue;
}
reinvoke(reinvokeRule);
break;
}
}
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static class BuildDisplayNameComparator
implements Comparator<Build> {
@Override
public int compare(Build build1, Build build2) {
String displayName1 = build1.getDisplayName();
String displayName2 = build2.getDisplayName();
return displayName1.compareTo(displayName2);
}
}
protected static boolean isHighPriorityBuildFailureElement(
Element gitHubMessage) {
String content = null;
try {
content = Dom4JUtil.format(gitHubMessage, false);
}
catch (IOException ioe) {
throw new RuntimeException("Unable to format github message.", ioe);
}
for (String contentFlag : _HIGH_PRIORITY_CONTENT_FLAGS) {
if (content.contains(contentFlag)) {
return true;
}
}
return false;
}
protected BaseBuild(String url) {
this(url, null);
}
protected BaseBuild(String url, Build parentBuild) {
_parentBuild = parentBuild;
if (url.contains("buildWithParameters")) {
setInvocationURL(url);
}
else {
setBuildURL(url);
}
update();
}
protected void archiveConsoleLog() {
downloadSampleURL(
getArchivePath(), true, getBuildURL(), "/consoleText");
}
protected void archiveJSON() {
downloadSampleURL(getArchivePath(), true, getBuildURL(), "api/json");
downloadSampleURL(
getArchivePath(), false, getBuildURL(), "testReport/api/json");
if (!getStartPropertiesTempMap().isEmpty()) {
try {
JSONObject startPropertiesTempMapJSONObject =
JenkinsResultsParserUtil.toJSONObject(
getStartPropertiesTempMapURL());
writeArchiveFile(
startPropertiesTempMapJSONObject.toString(4),
getArchivePath() + "/start.properties.json");
}
catch (IOException ioe) {
throw new RuntimeException(
"Unable to create start.properties.json", ioe);
}
}
if (!getStopPropertiesTempMap().isEmpty()) {
try {
JSONObject stopPropertiesTempMapJSONObject =
JenkinsResultsParserUtil.toJSONObject(
getStopPropertiesTempMapURL());
writeArchiveFile(
stopPropertiesTempMapJSONObject.toString(4),
getArchivePath() + "/stop.properties.json");
}
catch (IOException ioe) {
throw new RuntimeException(
"Unable to create stop.properties.json", ioe);
}
}
}
protected void checkForReinvocation(String consoleText) {
if ((consoleText == null) || consoleText.isEmpty()) {
return;
}
TopLevelBuild topLevelBuild = (TopLevelBuild)getTopLevelBuild();
if ((topLevelBuild == null) || topLevelBuild.fromArchive) {
return;
}
if (consoleText.contains(getReinvokedMessage())) {
reset();
update();
}
}
protected void downloadSampleURL(
String path, boolean required, String url, String urlSuffix) {
String urlString = url + urlSuffix;
if (urlString.endsWith("json")) {
urlString += "?pretty";
}
urlSuffix = JenkinsResultsParserUtil.fixFileName(urlSuffix);
String content = null;
try {
content = JenkinsResultsParserUtil.toString(
JenkinsResultsParserUtil.getLocalURL(urlString), false, 0, 0,
0);
}
catch (IOException ioe) {
if (required) {
throw new RuntimeException(
"Unable to download sample " + urlString, ioe);
}
else {
return;
}
}
try {
writeArchiveFile(content, path + "/" + urlSuffix);
}
catch (IOException ioe) {
throw new RuntimeException("Unable to write file", ioe);
}
}
protected void findDownstreamBuilds() {
String consoleText = getConsoleText();
List<String> foundDownstreamBuildURLs = new ArrayList<>(
findDownstreamBuildsInConsoleText(consoleText));
JSONObject buildJSONObject;
try {
buildJSONObject = getBuildJSONObject("runs[number,url]");
}
catch (Exception e) {
throw new RuntimeException(e);
}
if ((buildJSONObject != null) && buildJSONObject.has("runs")) {
JSONArray runsJSONArray = buildJSONObject.getJSONArray("runs");
if (runsJSONArray != null) {
for (int i = 0; i < runsJSONArray.length(); i++) {
JSONObject runJSONObject = runsJSONArray.getJSONObject(i);
if (runJSONObject.getInt("number") == _buildNumber) {
String url = runJSONObject.getString("url");
if (!hasBuildURL(url) &&
!foundDownstreamBuildURLs.contains(url)) {
foundDownstreamBuildURLs.add(url);
}
}
}
}
}
addDownstreamBuilds(
foundDownstreamBuildURLs.toArray(
new String[foundDownstreamBuildURLs.size()]));
for (Build downstreamBuild : downstreamBuilds) {
BaseBuild downstreamBaseBuild = (BaseBuild)downstreamBuild;
downstreamBaseBuild.checkForReinvocation(consoleText);
}
}
protected List<String> findDownstreamBuildsInConsoleText(
String consoleText) {
List<String> foundDownstreamBuildURLs = new ArrayList<>();
if ((consoleText == null) || consoleText.isEmpty()) {
return foundDownstreamBuildURLs;
}
Set<String> downstreamBuildURLs = new HashSet<>();
for (Build downstreamBuild : getDownstreamBuilds(null)) {
String downstreamBuildURL = downstreamBuild.getBuildURL();
if (downstreamBuildURL != null) {
downstreamBuildURLs.add(downstreamBuildURL);
}
}
if (getBuildURL() != null) {
int i = consoleText.lastIndexOf("\nstop-current-job:");
if (i != -1) {
consoleText = consoleText.substring(0, i);
}
Matcher downstreamBuildURLMatcher =
downstreamBuildURLPattern.matcher(
consoleText.substring(_consoleReadCursor));
_consoleReadCursor = consoleText.length();
while (downstreamBuildURLMatcher.find()) {
String url = downstreamBuildURLMatcher.group("url");
Pattern reinvocationPattern = Pattern.compile(
Pattern.quote(url) + " restarted at (?<url>[^\\s]*)\\.");
Matcher reinvocationMatcher = reinvocationPattern.matcher(
consoleText);
while (reinvocationMatcher.find()) {
url = reinvocationMatcher.group("url");
}
if (!foundDownstreamBuildURLs.contains(url) &&
!downstreamBuildURLs.contains(url)) {
foundDownstreamBuildURLs.add(url);
}
}
}
return foundDownstreamBuildURLs;
}
protected String getBaseRepositoryType() {
if (jobName.startsWith("test-subrepository-acceptance-pullrequest")) {
return getBaseRepositoryName();
}
if (jobName.contains("portal")) {
return "portal";
}
if (jobName.contains("plugins")) {
return "plugins";
}
return "jenkins";
}
protected JSONObject getBuildJSONObject(String tree) {
if (getBuildURL() == null) {
return null;
}
StringBuffer sb = new StringBuffer();
sb.append(JenkinsResultsParserUtil.getLocalURL(getBuildURL()));
sb.append("/api/json?pretty");
if (tree != null) {
sb.append("&tree=");
sb.append(tree);
}
try {
return JenkinsResultsParserUtil.toJSONObject(sb.toString(), false);
}
catch (IOException ioe) {
throw new RuntimeException("Unable to get build JSON", ioe);
}
}
protected String getBuildMessage() {
if (jobName != null) {
String status = getStatus();
StringBuilder sb = new StringBuilder();
sb.append("Build \"");
sb.append(jobName);
sb.append("\"");
if (status.equals("completed")) {
sb.append(" completed at ");
sb.append(getBuildURL());
sb.append(". ");
sb.append(getResult());
return sb.toString();
}
if (status.equals("missing")) {
sb.append(" is missing ");
sb.append(getJobURL());
sb.append(".");
return sb.toString();
}
if (status.equals("queued")) {
sb.append(" is queued at ");
sb.append(getJobURL());
sb.append(".");
return sb.toString();
}
if (status.equals("running")) {
if (badBuildNumbers.size() > 0) {
sb.append(" ");
List<String> badBuildURLs = getBadBuildURLs();
sb.append(badBuildURLs.get(badBuildNumbers.size() - 1));
sb.append(" restarted at ");
}
else {
sb.append(" started at ");
}
sb.append(getBuildURL());
sb.append(".\n");
return sb.toString();
}
if (status.equals("starting")) {
sb.append(" invoked at ");
sb.append(getJobURL());
sb.append(".");
return sb.toString();
}
throw new RuntimeException("Unknown status: " + status);
}
return "";
}
protected JSONArray getBuildsJSONArray() throws Exception {
JSONObject jsonObject = JenkinsResultsParserUtil.toJSONObject(
JenkinsResultsParserUtil.getLocalURL(
JenkinsResultsParserUtil.combine(
getJobURL(), "/api/json?tree=builds[actions[parameters",
"[name,type,value]],building,duration,number,result,url]")),
false);
return jsonObject.getJSONArray("builds");
}
protected int getDownstreamBuildCountByResult(String result) {
int count = 0;
List<Build> downstreamBuilds = getDownstreamBuilds(null);
if (result == null) {
return downstreamBuilds.size();
}
for (Build downstreamBuild : downstreamBuilds) {
String downstreamBuildResult = downstreamBuild.getResult();
if (downstreamBuildResult.equals(result)) {
count++;
}
}
return count;
}
protected ExecutorService getExecutorService() {
return null;
}
protected Element getFailureMessageElement() {
for (FailureMessageGenerator failureMessageGenerator :
getFailureMessageGenerators()) {
Element failureMessage = failureMessageGenerator.getMessageElement(
this);
if (failureMessage != null) {
return failureMessage;
}
}
return null;
}
protected FailureMessageGenerator[] getFailureMessageGenerators() {
return _FAILURE_MESSAGE_GENERATORS;
}
protected abstract Element getGitHubMessageJobResultsElement();
protected Set<String> getJobParameterNames() {
JSONObject jsonObject;
try {
jsonObject = JenkinsResultsParserUtil.toJSONObject(
JenkinsResultsParserUtil.getLocalURL(
JenkinsResultsParserUtil.combine(
getJobURL(), "/api/json?tree=actions[",
"parameterDefinitions[name,type,value]]")));
}
catch (IOException ioe) {
throw new RuntimeException("Unable to get build JSON", ioe);
}
JSONArray actionsJSONArray = jsonObject.getJSONArray("actions");
JSONObject firstActionJSONObject = actionsJSONArray.getJSONObject(0);
JSONArray parameterDefinitionsJSONArray =
firstActionJSONObject.getJSONArray("parameterDefinitions");
Set<String> parameterNames = new HashSet<>(
parameterDefinitionsJSONArray.length());
for (int i = 0; i < parameterDefinitionsJSONArray.length(); i++) {
JSONObject parameterDefinitionJSONObject =
parameterDefinitionsJSONArray.getJSONObject(i);
String type = parameterDefinitionJSONObject.getString("type");
if (type.equals("StringParameterDefinition")) {
parameterNames.add(
parameterDefinitionJSONObject.getString("name"));
}
}
return parameterNames;
}
protected Map<String, String> getParameters(JSONArray jsonArray)
throws Exception {
Map<String, String> parameters = new HashMap<>(jsonArray.length());
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
if (jsonObject.opt("value") instanceof String) {
String name = jsonObject.getString("name");
String value = jsonObject.getString("value");
if (!value.isEmpty()) {
parameters.put(name, value);
}
}
}
return parameters;
}
protected Map<String, String> getParameters(JSONObject buildJSONObject)
throws Exception {
JSONArray actionsJSONArray = buildJSONObject.getJSONArray("actions");
if (actionsJSONArray.length() == 0) {
return new HashMap<>();
}
JSONObject jsonObject = actionsJSONArray.getJSONObject(0);
if (jsonObject.has("parameters")) {
JSONArray parametersJSONArray = jsonObject.getJSONArray(
"parameters");
return getParameters(parametersJSONArray);
}
return new HashMap<>();
}
protected JSONObject getQueueItemJSONObject() throws Exception {
JSONArray queueItemsJSONArray = getQueueItemsJSONArray();
for (int i = 0; i < queueItemsJSONArray.length(); i++) {
JSONObject queueItemJSONObject = queueItemsJSONArray.getJSONObject(
i);
JSONObject taskJSONObject = queueItemJSONObject.getJSONObject(
"task");
String queueItemName = taskJSONObject.getString("name");
if (!queueItemName.equals(jobName)) {
continue;
}
if (_parameters.equals(getParameters(queueItemJSONObject))) {
return queueItemJSONObject;
}
}
return null;
}
protected JSONArray getQueueItemsJSONArray() throws Exception {
JSONObject jsonObject = JenkinsResultsParserUtil.toJSONObject(
"http://" + master +
"/queue/api/json?tree=items[actions[parameters" +
"[name,value]],task[name,url]]",
false);
return jsonObject.getJSONArray("items");
}
protected String getReinvokedMessage() {
StringBuffer sb = new StringBuffer();
sb.append("Reinvoked: ");
sb.append(getBuildURL());
sb.append(" at ");
sb.append(getInvocationURL());
return sb.toString();
}
protected JSONObject getRunningBuildJSONObject() throws Exception {
JSONArray buildsJSONArray = getBuildsJSONArray();
for (int i = 0; i < buildsJSONArray.length(); i++) {
JSONObject buildJSONObject = buildsJSONArray.getJSONObject(i);
Map<String, String> parameters = getParameters();
if (parameters.equals(getParameters(buildJSONObject)) &&
!badBuildNumbers.contains(buildJSONObject.getInt("number"))) {
return buildJSONObject;
}
}
return null;
}
protected String getStartPropertiesTempMapURL() {
if (fromArchive) {
return getBuildURL() + "/start.properties.json";
}
return getParameterValue("JSON_MAP_URL");
}
protected String getStopPropertiesTempMapURL() {
return null;
}
protected Map<String, String> getTempMap(String tempMapName) {
JSONObject tempMapJSONObject = null;
String tempMapURL = getTempMapURL(tempMapName);
if (tempMapURL == null) {
return Collections.emptyMap();
}
try {
tempMapJSONObject = JenkinsResultsParserUtil.toJSONObject(
JenkinsResultsParserUtil.getLocalURL(tempMapURL), false, 0, 0,
0);
}
catch (IOException ioe) {
}
if ((tempMapJSONObject == null) ||
!tempMapJSONObject.has("properties")) {
return Collections.emptyMap();
}
JSONArray propertiesJSONArray = tempMapJSONObject.getJSONArray(
"properties");
Map<String, String> tempMap = new HashMap<>(
propertiesJSONArray.length());
for (int i = 0; i < propertiesJSONArray.length(); i++) {
JSONObject propertyJSONObject = propertiesJSONArray.getJSONObject(
i);
String key = propertyJSONObject.getString("name");
String value = propertyJSONObject.optString("value");
if ((value != null) && !value.isEmpty()) {
tempMap.put(key, value);
}
}
return tempMap;
}
protected String getTempMapURL(String tempMapName) {
if (tempMapName.equals("start.properties")) {
return getStartPropertiesTempMapURL();
}
if (tempMapName.equals("stop.properties")) {
return getStopPropertiesTempMapURL();
}
return null;
}
protected boolean isParentBuildRoot() {
if (_parentBuild == null) {
return false;
}
if ((_parentBuild.getParentBuild() == null) &&
(_parentBuild instanceof TopLevelBuild)) {
return true;
}
return false;
}
protected void loadParametersFromBuildJSONObject() {
if (getBuildURL() == null) {
return;
}
JSONObject buildJSONObject = getBuildJSONObject(
"actions[parameters[*]]");
JSONArray actionsJSONArray = buildJSONObject.getJSONArray("actions");
if (actionsJSONArray.length() == 0) {
_parameters = new HashMap<>(0);
return;
}
JSONObject actionJSONObject = actionsJSONArray.getJSONObject(0);
if (actionJSONObject.has("parameters")) {
JSONArray parametersJSONArray = actionJSONObject.getJSONArray(
"parameters");
_parameters = new HashMap<>(parametersJSONArray.length());
for (int i = 0; i < parametersJSONArray.length(); i++) {
JSONObject parameterJSONObject =
parametersJSONArray.getJSONObject(i);
Object value = parameterJSONObject.opt("value");
if (value instanceof String) {
if (!value.toString().isEmpty()) {
_parameters.put(
parameterJSONObject.getString("name"),
value.toString());
}
}
}
return;
}
_parameters = Collections.emptyMap();
}
protected void loadParametersFromQueryString(String queryString) {
Set<String> jobParameterNames = getJobParameterNames();
for (String parameter : queryString.split("&")) {
String[] nameValueArray = parameter.split("=");
if ((nameValueArray.length == 2) &&
jobParameterNames.contains(nameValueArray[0])) {
_parameters.put(nameValueArray[0], nameValueArray[1]);
}
}
}
protected void reset() {
result = null;
badBuildNumbers.add(getBuildNumber());
setBuildNumber(-1);
downstreamBuilds.clear();
}
protected void setBuildNumber(int buildNumber) {
if (_buildNumber != buildNumber) {
_buildNumber = buildNumber;
_consoleReadCursor = 0;
_consoleText = null;
if (_buildNumber == -1) {
setStatus("starting");
}
else {
setStatus("running");
}
}
}
protected void setBuildURL(String buildURL) {
try {
buildURL = JenkinsResultsParserUtil.decode(buildURL);
}
catch (UnsupportedEncodingException uee) {
throw new IllegalArgumentException(
"Unable to decode " + buildURL, uee);
}
try {
BaseBuild parentBuild = (BaseBuild)getParentBuild();
if (parentBuild != null) {
fromArchive = parentBuild.fromArchive;
}
else {
String archiveMarkerContent = JenkinsResultsParserUtil.toString(
buildURL + "/archive-marker", false, 0, 0, 0);
fromArchive = (archiveMarkerContent != null) &&
!archiveMarkerContent.isEmpty();
}
}
catch (IOException ioe) {
fromArchive = false;
}
Matcher matcher = buildURLPattern.matcher(buildURL);
if (!matcher.find()) {
matcher = archiveBuildURLPattern.matcher(buildURL);
if (!matcher.find()) {
throw new IllegalArgumentException(
"Invalid build URL " + buildURL);
}
archiveName = matcher.group("archiveName");
}
setJobName(matcher.group("jobName"));
master = matcher.group("master");
_buildNumber = Integer.parseInt(matcher.group("buildNumber"));
loadParametersFromBuildJSONObject();
_consoleReadCursor = 0;
setStatus("running");
}
protected void setInvocationURL(String invocationURL) {
if (getBuildURL() == null) {
try {
invocationURL = JenkinsResultsParserUtil.decode(invocationURL);
}
catch (UnsupportedEncodingException uee) {
throw new IllegalArgumentException(
"Unable to decode " + invocationURL, uee);
}
Matcher invocationURLMatcher = invocationURLPattern.matcher(
invocationURL);
if (!invocationURLMatcher.find()) {
throw new RuntimeException("Invalid invocation URL");
}
setJobName(invocationURLMatcher.group("jobName"));
master = invocationURLMatcher.group("master");
loadParametersFromQueryString(invocationURL);
setStatus("starting");
}
}
protected void setJobName(String jobName) {
this.jobName = jobName;
Matcher matcher = jobNamePattern.matcher(jobName);
if (matcher.find()) {
branchName = matcher.group("branchName");
return;
}
branchName = "master";
}
protected void setStatus(String status) {
if (((status == null) && (_status != null)) ||
!status.equals(_status)) {
_status = status;
statusModifiedTime = System.currentTimeMillis();
if (isParentBuildRoot()) {
System.out.println(getBuildMessage());
}
}
}
protected void writeArchiveFile(String content, String path)
throws IOException {
JenkinsResultsParserUtil.write(
JenkinsResultsParserUtil.combine(
JenkinsResultsParserUtil.DEPENDENCIES_URL_FILE.substring(
"file:".length()),
"/", path),
JenkinsResultsParserUtil.redact(replaceBuildURL(content)));
}
protected static final int MAX_REINVOCATIONS = 1;
protected static final Pattern archiveBuildURLPattern = Pattern.compile(
JenkinsResultsParserUtil.combine(
"(", Pattern.quote("${dependencies.url}"), "|",
Pattern.quote(JenkinsResultsParserUtil.DEPENDENCIES_URL_FILE), "|",
Pattern.quote(JenkinsResultsParserUtil.DEPENDENCIES_URL_HTTP),
")/*(?<archiveName>.*)/(?<master>[^/]+)/+(?<jobName>[^/]+)",
".*/(?<buildNumber>\\d+)/?"));
protected static final Pattern buildURLPattern = Pattern.compile(
JenkinsResultsParserUtil.combine(
"\\w+://(?<master>[^/]+)/+job/+(?<jobName>[^/]+).*/(?<buildNumber>",
"\\d+)/?"));
protected static final Pattern downstreamBuildURLPattern = Pattern.compile(
"[\\'\\\"].*[\\'\\\"] started at (?<url>.+)\\.");
protected static final Pattern invocationURLPattern = Pattern.compile(
JenkinsResultsParserUtil.combine(
"\\w+://(?<master>[^/]+)/+job/+(?<jobName>[^/]+).*/",
"buildWithParameters\\?(?<queryString>.*)"));
protected static final Pattern jobNamePattern = Pattern.compile(
"(?<baseJob>[^\\(]+)\\((?<branchName>[^\\)]+)\\)");
protected static final String tempMapBaseURL =
"http://cloud-10-0-0-31.lax.liferay.com/osb-jenkins-web/map/";
protected String archiveName;
protected List<Integer> badBuildNumbers = new ArrayList<>();
protected String branchName;
protected List<Build> downstreamBuilds = new ArrayList<>();
protected boolean fromArchive;
protected String jobName;
protected String master;
protected List<ReinvokeRule> reinvokeRules =
ReinvokeRule.getReinvokeRules();
protected String repositoryName;
protected String result;
protected long statusModifiedTime;
private static final FailureMessageGenerator[] _FAILURE_MESSAGE_GENERATORS =
{
new GenericFailureMessageGenerator()
};
private static final String[] _HIGH_PRIORITY_CONTENT_FLAGS = new String[] {
"compileJSP", "SourceFormatter.format", "Unable to compile JSPs"
};
private int _buildNumber = -1;
private int _consoleReadCursor;
private String _consoleText;
private Map<String, String> _parameters = new HashMap<>();
private final Build _parentBuild;
private String _status;
}