package hudson.plugins.build_timeout.impl; import hudson.Extension; import hudson.model.BuildListener; import hudson.model.AbstractBuild; import hudson.plugins.build_timeout.BuildTimeOutStrategy; import hudson.plugins.build_timeout.BuildTimeOutStrategyDescriptor; import hudson.util.FormValidation; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; /** * If the build reaches <tt>deadlineTime</tt>, it will be terminated. * * @author Fernando Miguélez Palomo (fernando.miguelez@gmail.com) */ public class DeadlineTimeOutStrategy extends BuildTimeOutStrategy { public static final int MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES = 1; protected static final String DEADLINE_REGEXP = "[0-2]?[0-9]:[0-5][0-9](:[0-5][0-9])?"; protected static final String TIME_LONG_FORMAT_PATTERN = "H:mm:ss"; protected static final String TIME_SHORT_FORMAT_PATTERN = "H:mm"; protected static final String TIMESTAMP_FORMAT_PATTERN = "yyyy-MM-dd H:mm:ss"; private final String deadlineTime; private final int deadlineToleranceInMinutes; /** * @return deadline time */ public String getDeadlineTime() { return deadlineTime; } /** * @return deadline tolerance in minutes */ public int getDeadlineToleranceInMinutes() { return deadlineToleranceInMinutes; } @DataBoundConstructor public DeadlineTimeOutStrategy(String deadlineTime, int deadlineToleranceInMinutes) { this.deadlineTime = deadlineTime; this.deadlineToleranceInMinutes = deadlineToleranceInMinutes <= MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES ? MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES : deadlineToleranceInMinutes; } @Override public long getTimeOut(AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException, MacroEvaluationException, IOException, IllegalArgumentException { Calendar now = Calendar.getInstance(); Calendar deadlineTimestamp = Calendar.getInstance(); String expandedDeadlineTime = expandAll(build, listener, deadlineTime); deadlineTimestamp.setTime(parseDeadline(expandedDeadlineTime)); deadlineTimestamp.set(Calendar.YEAR, now.get(Calendar.YEAR)); deadlineTimestamp.set(Calendar.MONTH, now.get(Calendar.MONTH)); deadlineTimestamp.set(Calendar.DAY_OF_MONTH, now.get(Calendar.DAY_OF_MONTH)); Calendar deadlineTimestampWithTolerance = (Calendar) deadlineTimestamp.clone(); deadlineTimestampWithTolerance.add(Calendar.MINUTE, deadlineToleranceInMinutes); if (deadlineTimestamp.compareTo(now) <= 0 && deadlineTimestampWithTolerance.compareTo(now) > 0) { // Deadline time is a past moment but inside tolerance period. Interrupt build immediately. listener.getLogger().println( Messages.DeadlineTimeOutStrategy_ImmediatelyAbort(expandedDeadlineTime, deadlineToleranceInMinutes)); return 0; } while (deadlineTimestamp.before(now)) { // Deadline time is a past moment. Increment one day till deadline timestamp is in the future. deadlineTimestamp.add(Calendar.DAY_OF_MONTH, 1); } listener.getLogger().println( Messages.DeadlineTimeOutStrategy_NextDeadline(new SimpleDateFormat(TIMESTAMP_FORMAT_PATTERN).format(deadlineTimestamp .getTime()))); return deadlineTimestamp.getTimeInMillis() - now.getTimeInMillis(); } private static Date parseDeadline(String deadline) throws IllegalArgumentException { if (deadline.matches(DEADLINE_REGEXP)) { try { if (deadline.length() > 5) { return new SimpleDateFormat(TIME_LONG_FORMAT_PATTERN).parse(deadline); } else { return new SimpleDateFormat(TIME_SHORT_FORMAT_PATTERN).parse(deadline); } } catch (ParseException e) { } } throw new IllegalArgumentException(Messages.DeadlineTimeOutStrategy_InvalidDeadlineFormat(deadline)); } @Extension public static class DescriptorImpl extends BuildTimeOutStrategyDescriptor { @Override public String getDisplayName() { return Messages.DeadlineTimeOutStrategy_DisplayName(); } public FormValidation doCheckDeadlineTime(@QueryParameter String value) { if (hasMacros(value)) { return FormValidation.warning(Messages.DeadlineTimeOutStrategy_DeadlineFormatWithMacros()); } else { try { parseDeadline(value); return FormValidation.ok(); } catch (IllegalArgumentException e) { return FormValidation.error(e.getMessage()); } } } @Override public boolean isApplicableAsBuildStep() { return true; } } }