package net.sourceforge.cruisecontrol;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
* This class has the logic to determine current build status message.
*
* <br/><br/>Note the use of the <code>READ_*_LINES</code> constants for the
* <code>maxReadLines</code> parameter:
* <ul>
* <li>READ_ONLY_STATUS_LINES only returns the first two lines that contain the
* current summary build status and time.</li>
* <li>READ_ALL_LINES returns the entire contents for the build status file.
* This includes any output from other loggers, such as the XmlLoggerWithStatus.</li>
* </ul>
* You can always pass a number to get a specific number of lines.
*
* @author <a href="mailto:jeffjensen@upstairstechnology.com">Jeff Jensen </a>
*/
public class BuildStatus {
/** Constant meaning to read all lines from the build status file. */
public static final int READ_ALL_LINES = -2;
/**
* Constant meaning to read only the project status and time lines from the
* build status file.
*/
public static final int READ_ONLY_STATUS_LINES = 2;
protected BuildStatus() {
}
/**
* Generate the current build status string formatted for plain text.
*
* @param isSingleProject
* Specify true if this is a single project config, or false if
* it is a multi project config.
* @param dir
* The dir containing the build status file.
* @param projectName
* The name of the project to get the build status for.
* @param statusFileName
* The name of the status file.
* @param maxReadLines
* The maximum number of lines to read from the file. Use the
* READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
* (value of 2) constants when applicable.
* @return The build status string formatted for plain text usage or the
* text <code>(build status file not found)</code> if the
* status file cannot be not found.
*/
public static String getStatusPlain(boolean isSingleProject, String dir,
String projectName, String statusFileName, int maxReadLines) {
String status = genStatus(isSingleProject, dir, projectName,
statusFileName, false, maxReadLines);
return status;
}
/**
* Generate the current build status string formatted for HTML.
*
* @param isSingleProject
* Specify true if this is a single project config, or false if
* it is a multi project config.
* @param dir
* The dir containing the build status file.
* @param projectName
* The name of the project to get the build status for.
* @param statusFileName
* The name of the status file.
* @param maxReadLines
* The maximum number of lines to read from the file. Use the
* READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
* (value of 2) constants when applicable.
* @return The build status string formatted for plain text usage or the
* text <code>(build status file not found)</code> if the
* status file cannot be not found.
*/
public static String getStatusHtml(boolean isSingleProject, String dir,
String projectName, String statusFileName, int maxReadLines) {
String status = genStatus(isSingleProject, dir, projectName,
statusFileName, true, maxReadLines);
return status;
}
/**
* Generate the current build status string.
*
* @param isSingleProject
* Specify true if this is a single project config, or false if
* it is a multi project config.
* @param dir
* The dir containing the build status file.
* @param projectName
* The name of the project to get the build status for.
* @param statusFileName
* The name of the status file.
* @param insertBreaks
* If true, inserts XHTML br tag between content lines from the
* status file.
* @param maxReadLines
* The maximum number of lines to read from the file. Use the
* READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
* (value of 2) constants when applicable.
* @return The build status string formatted as per the insertBreaks param
* or the text <code>(build status file not found)</code> if the
* status file is not found.
*/
private static String genStatus(boolean isSingleProject, String dir,
String projectName, String statusFileName, boolean insertBreaks,
int maxReadLines) {
File statusFile = getFile(isSingleProject, dir, projectName,
statusFileName);
String status;
if (statusFile.exists()) {
status = getStatus(statusFile, insertBreaks, maxReadLines);
} else {
status = "(build status file not found)";
}
return status;
}
/**
* Get the status from the build status file. Need to consider a <br>
* a new line to account for XmlLoggerWithStatus output.
*
* @param statusFile
* The status file to get the status info from.
* @param insertBreaks
* true to insert HTML break elements at newlines.
* @param maxReadLines
* The maximum number of lines to read from the file. Use the
* READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
* (value of 2) constants when applicable.
* @return The status string from the specified status file.
*/
private static String getStatus(File statusFile, boolean insertBreaks,
int maxReadLines) {
StringBuffer sb = new StringBuffer(101);
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(statusFile));
String line = br.readLine();
int linesRead = 1;
while (line != null && readMoreLines(linesRead, maxReadLines)) {
if (line.indexOf("<br>") == -1) {
addLine(line, sb, insertBreaks);
} else {
int startIndex = 0;
for (int endIndex = line.indexOf("<br>");
endIndex != -1 && readMoreLines(linesRead, maxReadLines);
linesRead++) {
String substring = line.substring(startIndex, endIndex);
if (substring.length() > 0) {
addLine(substring, sb, insertBreaks);
} else {
linesRead--;
}
startIndex = endIndex + "<br>".length();
endIndex = line.indexOf("<br>", startIndex);
}
String substring = line.substring(startIndex);
if (substring.length() > 0 && readMoreLines(linesRead, maxReadLines)) {
addLine(substring, sb, insertBreaks);
}
}
line = br.readLine();
linesRead++;
}
} catch (IOException e) {
throw new CruiseControlWebAppException(
"Error reading status file: " + statusFile.getName() + " : "
+ e.getMessage(), e);
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
// skip action on close error
}
br = null;
}
return sb.toString();
}
private static boolean readMoreLines(int linesRead, int maxReadLines) {
return linesRead <= maxReadLines || maxReadLines == READ_ALL_LINES;
}
private static void addLine(String line, StringBuffer sb, boolean insertBreaks) {
sb.append(line);
sb.append('\n');
if (insertBreaks) {
sb.append("<br/>");
}
}
/**
* Get the status file to read the status info from.
*
* @param isSingleProject
* Specify true if this is a single project config, or false if
* it is a multi project config.
* @param dir
* The dir containing the build status file.
* @param projectName
* The name of the project to get the build status for.
* @param statusFileName
* The name of the status file.
* @return The status file.
*/
private static File getFile(boolean isSingleProject, String dir,
String projectName, String statusFileName) {
String statusFileDir = null;
if (isSingleProject) {
statusFileDir = dir;
} else {
statusFileDir = dir + System.getProperty("file.separator")
+ projectName;
}
File statusFile = new File(statusFileDir, statusFileName);
if (statusFile.isDirectory()) {
final String msg = "CruiseControl: currentBuildStatusFile "
+ statusFile.getAbsolutePath() + " is a directory."
+ " Edit the web.xml to provide the path to the correct file.";
throw new CruiseControlWebAppException(msg);
}
return statusFile;
}
}