001: package net.sourceforge.cruisecontrol;
002:
003: import java.io.BufferedReader;
004: import java.io.File;
005: import java.io.FileReader;
006: import java.io.IOException;
007:
008: /**
009: * This class has the logic to determine current build status message.
010: *
011: * <br/><br/>Note the use of the <code>READ_*_LINES</code> constants for the
012: * <code>maxReadLines</code> parameter:
013: * <ul>
014: * <li>READ_ONLY_STATUS_LINES only returns the first two lines that contain the
015: * current summary build status and time.</li>
016: * <li>READ_ALL_LINES returns the entire contents for the build status file.
017: * This includes any output from other loggers, such as the XmlLoggerWithStatus.</li>
018: * </ul>
019: * You can always pass a number to get a specific number of lines.
020: *
021: * @author <a href="mailto:jeffjensen@upstairstechnology.com">Jeff Jensen </a>
022: */
023: public class BuildStatus {
024: /** Constant meaning to read all lines from the build status file. */
025: public static final int READ_ALL_LINES = -2;
026:
027: /**
028: * Constant meaning to read only the project status and time lines from the
029: * build status file.
030: */
031: public static final int READ_ONLY_STATUS_LINES = 2;
032:
033: protected BuildStatus() {
034: }
035:
036: /**
037: * Generate the current build status string formatted for plain text.
038: *
039: * @param isSingleProject
040: * Specify true if this is a single project config, or false if
041: * it is a multi project config.
042: * @param dir
043: * The dir containing the build status file.
044: * @param projectName
045: * The name of the project to get the build status for.
046: * @param statusFileName
047: * The name of the status file.
048: * @param maxReadLines
049: * The maximum number of lines to read from the file. Use the
050: * READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
051: * (value of 2) constants when applicable.
052: * @return The build status string formatted for plain text usage or the
053: * text <code>(build status file not found)</code> if the
054: * status file cannot be not found.
055: */
056: public static String getStatusPlain(boolean isSingleProject,
057: String dir, String projectName, String statusFileName,
058: int maxReadLines) {
059: String status = genStatus(isSingleProject, dir, projectName,
060: statusFileName, false, maxReadLines);
061:
062: return status;
063: }
064:
065: /**
066: * Generate the current build status string formatted for HTML.
067: *
068: * @param isSingleProject
069: * Specify true if this is a single project config, or false if
070: * it is a multi project config.
071: * @param dir
072: * The dir containing the build status file.
073: * @param projectName
074: * The name of the project to get the build status for.
075: * @param statusFileName
076: * The name of the status file.
077: * @param maxReadLines
078: * The maximum number of lines to read from the file. Use the
079: * READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
080: * (value of 2) constants when applicable.
081: * @return The build status string formatted for plain text usage or the
082: * text <code>(build status file not found)</code> if the
083: * status file cannot be not found.
084: */
085: public static String getStatusHtml(boolean isSingleProject,
086: String dir, String projectName, String statusFileName,
087: int maxReadLines) {
088: String status = genStatus(isSingleProject, dir, projectName,
089: statusFileName, true, maxReadLines);
090:
091: return status;
092: }
093:
094: /**
095: * Generate the current build status string.
096: *
097: * @param isSingleProject
098: * Specify true if this is a single project config, or false if
099: * it is a multi project config.
100: * @param dir
101: * The dir containing the build status file.
102: * @param projectName
103: * The name of the project to get the build status for.
104: * @param statusFileName
105: * The name of the status file.
106: * @param insertBreaks
107: * If true, inserts XHTML br tag between content lines from the
108: * status file.
109: * @param maxReadLines
110: * The maximum number of lines to read from the file. Use the
111: * READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
112: * (value of 2) constants when applicable.
113: * @return The build status string formatted as per the insertBreaks param
114: * or the text <code>(build status file not found)</code> if the
115: * status file is not found.
116: */
117: private static String genStatus(boolean isSingleProject,
118: String dir, String projectName, String statusFileName,
119: boolean insertBreaks, int maxReadLines) {
120:
121: File statusFile = getFile(isSingleProject, dir, projectName,
122: statusFileName);
123:
124: String status;
125: if (statusFile.exists()) {
126: status = getStatus(statusFile, insertBreaks, maxReadLines);
127: } else {
128: status = "(build status file not found)";
129: }
130:
131: return status;
132: }
133:
134: /**
135: * Get the status from the build status file. Need to consider a <br>
136: * a new line to account for XmlLoggerWithStatus output.
137: *
138: * @param statusFile
139: * The status file to get the status info from.
140: * @param insertBreaks
141: * true to insert HTML break elements at newlines.
142: * @param maxReadLines
143: * The maximum number of lines to read from the file. Use the
144: * READ_ALL_LINES (value of zero) and READ_ONLY_STATUS_LINES
145: * (value of 2) constants when applicable.
146: * @return The status string from the specified status file.
147: */
148: private static String getStatus(File statusFile,
149: boolean insertBreaks, int maxReadLines) {
150: StringBuffer sb = new StringBuffer(101);
151: BufferedReader br = null;
152:
153: try {
154: br = new BufferedReader(new FileReader(statusFile));
155: String line = br.readLine();
156: int linesRead = 1;
157:
158: while (line != null
159: && readMoreLines(linesRead, maxReadLines)) {
160:
161: if (line.indexOf("<br>") == -1) {
162: addLine(line, sb, insertBreaks);
163: } else {
164: int startIndex = 0;
165: for (int endIndex = line.indexOf("<br>"); endIndex != -1
166: && readMoreLines(linesRead, maxReadLines); linesRead++) {
167: String substring = line.substring(startIndex,
168: endIndex);
169: if (substring.length() > 0) {
170: addLine(substring, sb, insertBreaks);
171: } else {
172: linesRead--;
173: }
174: startIndex = endIndex + "<br>".length();
175: endIndex = line.indexOf("<br>", startIndex);
176: }
177: String substring = line.substring(startIndex);
178: if (substring.length() > 0
179: && readMoreLines(linesRead, maxReadLines)) {
180: addLine(substring, sb, insertBreaks);
181: }
182: }
183:
184: line = br.readLine();
185: linesRead++;
186: }
187: } catch (IOException e) {
188: throw new CruiseControlWebAppException(
189: "Error reading status file: "
190: + statusFile.getName() + " : "
191: + e.getMessage(), e);
192: } finally {
193: try {
194: if (br != null) {
195: br.close();
196: }
197: } catch (IOException e) {
198: // skip action on close error
199: }
200: br = null;
201: }
202:
203: return sb.toString();
204: }
205:
206: private static boolean readMoreLines(int linesRead, int maxReadLines) {
207: return linesRead <= maxReadLines
208: || maxReadLines == READ_ALL_LINES;
209: }
210:
211: private static void addLine(String line, StringBuffer sb,
212: boolean insertBreaks) {
213: sb.append(line);
214: sb.append('\n');
215: if (insertBreaks) {
216: sb.append("<br/>");
217: }
218: }
219:
220: /**
221: * Get the status file to read the status info from.
222: *
223: * @param isSingleProject
224: * Specify true if this is a single project config, or false if
225: * it is a multi project config.
226: * @param dir
227: * The dir containing the build status file.
228: * @param projectName
229: * The name of the project to get the build status for.
230: * @param statusFileName
231: * The name of the status file.
232: * @return The status file.
233: */
234: private static File getFile(boolean isSingleProject, String dir,
235: String projectName, String statusFileName) {
236: String statusFileDir = null;
237:
238: if (isSingleProject) {
239: statusFileDir = dir;
240: } else {
241: statusFileDir = dir + System.getProperty("file.separator")
242: + projectName;
243: }
244:
245: File statusFile = new File(statusFileDir, statusFileName);
246:
247: if (statusFile.isDirectory()) {
248: final String msg = "CruiseControl: currentBuildStatusFile "
249: + statusFile.getAbsolutePath()
250: + " is a directory."
251: + " Edit the web.xml to provide the path to the correct file.";
252: throw new CruiseControlWebAppException(msg);
253: }
254:
255: return statusFile;
256: }
257: }
|