Java tutorial
/* * The MIT License * * Copyright (c) 2013, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.cloudbees.hudson.plugins.folder.computed; import antlr.ANTLRException; import hudson.Extension; import hudson.model.Cause; import hudson.model.Item; import hudson.model.Items; import hudson.triggers.TimerTrigger; import hudson.triggers.Trigger; import hudson.triggers.TriggerDescriptor; import hudson.util.ListBoxModel; import hudson.util.TimeUnit2; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; /** * A {@link Trigger} for {@link ComputedFolder}s that will only trigger a recomputation if none has been * triggered within a specified timeframe. * * @author Stephen Connolly */ public class PeriodicFolderTrigger extends Trigger<ComputedFolder<?>> { private static final Logger LOGGER = Logger.getLogger(PeriodicFolderTrigger.class.getName()); /** * The interval between successive indexings. */ private final long interval; /** * Timestamp when we last {@link #run}. * Normally we rely on {@link FolderComputation#getTimestamp} but there is a slight delay * between {@link ComputedFolder#scheduleBuild(int, Cause)} and {@link ComputedFolder#createExecutable}. */ private transient long lastTriggered; // could be volatile or AtomicLong, but FolderCron will not run >1 concurrently anyway /** * Constructor. * * @param interval the interval. * @throws ANTLRException impossible but we cannot suppress it */ @DataBoundConstructor public PeriodicFolderTrigger(String interval) throws ANTLRException { super(toCrontab(interval)); long intervalMillis = toIntervalMillis(interval); this.interval = intervalMillis; } /** * Turns an interval into a suitable crontab. * * @param interval the interval. * @return the crontab. */ private static String toCrontab(String interval) { long millis = toIntervalMillis(interval); if (millis < TimeUnit2.MINUTES.toMillis(5)) { return "* * * * *"; } if (millis < TimeUnit2.MINUTES.toMillis(10)) { return "*/12 * * * *"; } if (millis < TimeUnit2.MINUTES.toMillis(30)) { return "*/6 * * * *"; } if (millis < TimeUnit2.HOURS.toMillis(1)) { return "*/2 * * * *"; } if (millis < TimeUnit2.HOURS.toMillis(8)) { return "H * * * *"; } return "H H * * *"; } /** * Turns an interval into milliseconds./ * * @param interval the interval. * @return the milliseconds. */ private static long toIntervalMillis(String interval) { TimeUnit2 units = TimeUnit2.MINUTES; interval = interval.toLowerCase(); if (interval.endsWith("h")) { units = TimeUnit2.HOURS; interval = StringUtils.removeEnd(interval, "h"); } if (interval.endsWith("m")) { interval = StringUtils.removeEnd(interval, "m"); } else if (interval.endsWith("d")) { units = TimeUnit2.DAYS; interval = StringUtils.removeEnd(interval, "d"); } else if (interval.endsWith("ms")) { units = TimeUnit2.SECONDS; interval = StringUtils.removeEnd(interval, "ms"); } else if (interval.endsWith("s")) { units = TimeUnit2.SECONDS; interval = StringUtils.removeEnd(interval, "s"); } long value = 0; try { value = Long.parseLong(interval); } catch (NumberFormatException e) { value = 1; } return Math.min(TimeUnit2.DAYS.toMillis(30), Math.max(TimeUnit2.MINUTES.toMillis(1), units.toMillis(value))); } /** * Returns the number of milliseconds between indexing. * * @return the number of milliseconds between indexing. */ @SuppressWarnings("unused") // used by Jelly EL public long getIntervalMillis() { return interval; } /** * Returns the interval between indexing. * * @return the interval between indexing. */ @SuppressWarnings("unused") // used by Jelly EL public String getInterval() { if (interval < TimeUnit2.SECONDS.toMillis(1)) { return Long.toString(interval) + "ms"; } if (interval < TimeUnit2.MINUTES.toMillis(1)) { return Long.toString(TimeUnit2.MILLISECONDS.toSeconds(interval)) + "s"; } if (interval < TimeUnit2.HOURS.toMillis(1)) { return Long.toString(TimeUnit2.MILLISECONDS.toMinutes(interval)) + "m"; } if (interval < TimeUnit2.DAYS.toMillis(1)) { return Long.toString(TimeUnit2.MILLISECONDS.toHours(interval)) + "h"; } return Long.toString(TimeUnit2.MILLISECONDS.toDays(interval)) + "d"; } /** * {@inheritDoc} */ @Override public void run() { if (job == null) { return; } long now = System.currentTimeMillis(); FolderComputation<?> computation = job.getComputation(); if (computation != null) { long delay = now - computation.getTimestamp().getTimeInMillis(); if (delay < interval) { LOGGER.log(Level.FINE, "Too early to reschedule {0} based on last computation", job); return; } } if (now - lastTriggered < interval) { LOGGER.log(Level.FINE, "Too early to reschedule {0} based on last triggering", job); return; } if (job.scheduleBuild(0, new TimerTrigger.TimerTriggerCause())) { lastTriggered = now; } else { LOGGER.log(Level.WARNING, "Queue refused to schedule {0}", job); } } /** * Our {@link hudson.model.Descriptor} */ @Extension @SuppressWarnings("unused") // instantiated by Jenkins public static class DescriptorImpl extends TriggerDescriptor { /** * {@inheritDoc} */ public boolean isApplicable(Item item) { return item instanceof ComputedFolder; } /** * {@inheritDoc} */ public String getDisplayName() { return Messages.PeriodicFolderTrigger_DisplayName(); } /** * Fills the interval list box. * * @return the interval list box. */ @SuppressWarnings("unused") // used by Jelly public ListBoxModel doFillIntervalItems() { ListBoxModel model = new ListBoxModel(); model.add("1 minute", "1m"); model.add("2 minutes", "2m"); model.add("5 minutes", "5m"); model.add("10 minutes", "10m"); model.add("15 minutes", "15m"); model.add("20 minutes", "20m"); model.add("25 minutes", "25m"); model.add("30 minutes", "30m"); model.add("1 hour", "1h"); model.add("2 hours", "2h"); model.add("4 hours", "4h"); model.add("8 hours", "8h"); model.add("12 hours", "12h"); model.add("1 day", "1d"); model.add("2 days", "2d"); model.add("1 week", "7d"); model.add("2 weeks", "14d"); model.add("4 weeks", "28d"); return model; } static { Items.XSTREAM2.addCompatibilityAlias("jenkins.branch.IndexAtLeastTrigger", PeriodicFolderTrigger.class); } } }