/** * Hudson Serenitec plugin * * @author Georges Bossert <gbossert@gmail.com> * @version $Revision: 1.5 $ * @since $Date: 2008/07/23 12:05:04 ${date} * @copyright Universit� de Rennes 1 */ package hudson.plugins.serenitec.util; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Result; import hudson.tasks.BuildStep; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Recorder; import java.io.IOException; import java.io.PrintStream; import org.apache.commons.lang.StringUtils; /** * A base class for publishers with the following two characteristics: * <ul> * <li>It provides a unstable threshold, that could be enabled and set in the configuration screen. If the number of annotations in a build * exceeds this value then the build is considered as {@link Result#UNSTABLE UNSTABLE}. </li> * <li>It provides thresholds for the build health, that could be adjusted in the configuration screen. These values are used by the * {@link HealthReportBuilder} to compute the health and the health trend graph.</li> * </ul> * * @author Ulli Hafner */ public abstract class HealthAwarePublisher extends Recorder { /** Annotation threshold to be reached if a build should be considered as unstable. */ // private final String threshold; private final String severityMax; /** Determines whether to use the provided threshold to mark a build as unstable. */ private boolean thresholdEnabled; /** Integer threshold to be reached if a build should be considered as unstable. */ private int minimumAnnotations; /** Report health as 100% when the number of warnings is less than this value. */ private final String healthy; /** Report health as 0% when the number of warnings is greater than this value. */ private final String unHealthy; /** Report health as 100% when the number of warnings is less than this value. */ private int healthyPatterns; /** Report health as 0% when the number of warnings is greater than this value. */ private int unHealthyPatterns; /** Determines whether to use the provided healthy thresholds. */ private boolean healthyReportEnabled; /** Determines the height of the trend graph. */ private final String height; /** The name of the plug-in. */ private final String pluginName; /** * Creates a new instance of <code>HealthAwarePublisher</code>. * * @param threshold * Tasks threshold to be reached if a build should be considered as unstable. * @param healthy * Report health as 100% when the number of open tasks is less than this value * @param unHealthy * Report health as 0% when the number of open tasks is greater than this value * @param height * the height of the trend graph * @param pluginName * the name of the plug-in */ public HealthAwarePublisher(final String severityMax, final String healthy, final String unHealthy, final String height, final String pluginName) { super(); this.severityMax = severityMax; this.healthy = healthy; this.unHealthy = unHealthy; this.height = height; this.pluginName = "[" + pluginName + "] "; if (!StringUtils.isEmpty(severityMax)) { try { minimumAnnotations = Integer.valueOf(severityMax); if (minimumAnnotations >= 0) { thresholdEnabled = true; } } catch (final NumberFormatException exception) { // nothing to do, we use the default value } } if (!StringUtils.isEmpty(healthy) && !StringUtils.isEmpty(unHealthy)) { try { healthyPatterns = Integer.valueOf(healthy); unHealthyPatterns = Integer.valueOf(unHealthy); /* * if (healthyPatterns >= 0 && unHealthyPatterns > healthyPatterns) { healthyReportEnabled = true; } */ healthyReportEnabled = true; } catch (final NumberFormatException exception) { // nothing to do, we use the default value } } } /** * Returns whether the publisher can continue processing. This default implementation returns <code>true</code> if the build is not * aborted or failed. * * @param result * build result * @return <code>true</code> if the build can continue */ protected boolean canContinue(final Result result) { return result != Result.ABORTED && result != Result.FAILURE; } /** * Creates a new instance of <code>HealthReportBuilder</code>. * * @param reportSingleCount * message to be shown if there is exactly one item found * @param reportMultipleCount * message to be shown if there are zero or more than one items found * @return the new health report builder */ protected HealthReportBuilder createHealthReporter(final String reportSingleCount, final String reportMultipleCount) { return new HealthReportBuilder(thresholdEnabled, minimumAnnotations, true, healthyPatterns, unHealthyPatterns, reportSingleCount, reportMultipleCount); } /** * Evaluates the build result. The build is marked as unstable if the threshold has been exceeded. * * @param build * the build to create the action for * @param logger * the logger * @param project * the project with the annotations */ private void evaluateBuildResult(final AbstractBuild<?, ?> build, final PrintStream logger, final Project project) { final int numberOfEntry = project.getNumberOfEntry(); final int severityMaxDiscovered = project.getMaxSeverityDiscovered(); if (numberOfEntry > 0) { // We determines if should consider the build as unstable or "nuageux" if (isThresholdEnabled() && severityMaxDiscovered > Integer.parseInt(getseverityMax()) && !project.IsFixed()) { build.setResult(Result.UNSTABLE); } } else { log(logger, "No entry have been found."); } } /** * Returns the healthy threshold, i.e. when health is reported as 100%. * * @return the 100% healthiness */ public String getHealthy() { return healthy; } /** * Returns the healthy threshold for annotations, i.e. when health is reported as 100%. * * @return the 100% healthiness */ public int getHealthyPatterns() { return healthyPatterns; } /** * Returns the height of the trend graph. * * @return the height of the trend graph */ public String getHeight() { return height; } /** * Returns the threshold to be reached if a build should be considered as unstable. * * @return the threshold to be reached if a build should be considered as unstable */ public int getMinimumAnnotations() { return minimumAnnotations; } /** * Returns the annotation threshold to be reached if a build should be considered as unstable. * * @return the annotation threshold to be reached if a build should be considered as unstable. */ public String getseverityMax() { return severityMax; } /** * Returns the height of the trend graph. * * @return the height of the trend graph */ public int getTrendHeight() { return new TrendReportSize(height).getHeight(); } /** * Returns the unhealthy threshold, i.e. when health is reported as 0%. * * @return the 0% unhealthiness */ public String getUnHealthy() { return unHealthy; } /** * Returns the unhealthy threshold of annotations, i.e. when health is reported as 0%. * * @return the 0% unhealthiness */ public int getUnHealthyPatterns() { return unHealthyPatterns; } /** * Returns the isHealthyReportEnabled. * * @return the isHealthyReportEnabled */ public boolean isHealthyReportEnabled() { return healthyReportEnabled; } /** * Determines whether a threshold has been defined. * * @return <code>true</code> if a threshold has been defined */ public boolean isThresholdEnabled() { return thresholdEnabled; } /** * Logs the specified message. * * @param logger * the logger * @param message * the message */ protected void log(final PrintStream logger, final String message) { logger.println(StringUtils.defaultString(pluginName) + message); } /** {@inheritDoc} */ @Override public final boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher, final BuildListener listener) throws InterruptedException, IOException { final PrintStream logger = listener.getLogger(); if (canContinue(build.getResult())) { try { final Project project = perform(build, logger); evaluateBuildResult(build, logger, project); } catch (final Exception exception) { log(logger, "Error : " + exception.toString()); build.setResult(Result.FAILURE); return false; } } return true; } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.STEP; } /** * Performs the publishing of the results of this plug-in. * * @param build * the build * @param logger * the logger to report the progress to * @return the java project containing the found annotations * @throws InterruptedException * If the build is interrupted by the user (in an attempt to abort the build.) Normally the {@link BuildStep} * implementations may simply forward the exception it got from its lower-level functions. * @throws IOException * If the implementation wants to abort the processing when an {@link IOException} happens, it can simply propagate the * exception to the caller. This will cause the build to fail, with the default error message. Implementations are * encouraged to catch {@link IOException} on its own to provide a better error message, if it can do so, so that users have * better understanding on why it failed. */ protected abstract Project perform(AbstractBuild<?, ?> build, PrintStream logger) throws InterruptedException, IOException; }