/* * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.quartz; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Set; import org.quartz.DateBuilder.IntervalUnit; import org.quartz.impl.triggers.DailyTimeIntervalTriggerImpl; import org.quartz.spi.MutableTrigger; /** * A {@link ScheduleBuilder} implementation that build schedule for DailyTimeIntervalTrigger. * * <p>This builder provide an extra convenient method for you to set the trigger's endTimeOfDay. You may * use either endingDailyAt() or endingDailyAfterCount() to set the value. The later will auto calculate * your endTimeOfDay by using the interval, intervalUnit and startTimeOfDay to perform the calculation. * * <p>When using endingDailyAfterCount(), you should note that it is used to calculating endTimeOfDay. So * if your startTime on the first day is already pass by a time that would not add up to the count you * expected, until the next day comes. Remember that DailyTimeIntervalTrigger will use startTimeOfDay * and endTimeOfDay as fresh per each day! * * <p>Quartz provides a builder-style API for constructing scheduling-related * entities via a Domain-Specific Language (DSL). The DSL can best be * utilized through the usage of static imports of the methods on the classes * <code>TriggerBuilder</code>, <code>JobBuilder</code>, * <code>DateBuilder</code>, <code>JobKey</code>, <code>TriggerKey</code> * and the various <code>ScheduleBuilder</code> implementations.</p> * * <p>Client code can then use the DSL to write code such as this:</p> * <pre> * JobDetail job = newJob(MyJob.class) * .withIdentity("myJob") * .build(); * * Trigger trigger = newTrigger() * .withIdentity(triggerKey("myTrigger", "myTriggerGroup")) * .withSchedule(onDaysOfTheWeek(MONDAY, THURSDAY)) * .startAt(futureDate(10, MINUTES)) * .build(); * * scheduler.scheduleJob(job, trigger); * <pre> * * @since 2.1.0 * * @author James House * @author Zemian Deng <saltnlight5@gmail.com> */ public class DailyTimeIntervalScheduleBuilder extends ScheduleBuilder<DailyTimeIntervalTrigger> { private int interval = 1; private IntervalUnit intervalUnit = IntervalUnit.MINUTE; private Set<Integer> daysOfWeek; private TimeOfDay startTimeOfDay; private TimeOfDay endTimeOfDay; private int repeatCount = DailyTimeIntervalTrigger.REPEAT_INDEFINITELY; private int misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_SMART_POLICY; /** * A set of all days of the week. * * The set contains all values between {@link java.util.Calendar#SUNDAY} and {@link java.util.Calendar#SATURDAY} * (the integers from 1 through 7). */ public static final Set<Integer> ALL_DAYS_OF_THE_WEEK; /** * A set of the business days of the week (for locales similar to the USA). * * The set contains all values between {@link java.util.Calendar#MONDAY} and {@link java.util.Calendar#FRIDAY} * (the integers from 2 through 6). */ public static final Set<Integer> MONDAY_THROUGH_FRIDAY; /** * A set of the weekend days of the week (for locales similar to the USA). * * The set contains {@link java.util.Calendar#SATURDAY} and {@link java.util.Calendar#SUNDAY} */ public static final Set<Integer> SATURDAY_AND_SUNDAY; static { Set<Integer> t = new HashSet<Integer>(7); for(int i=Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) t.add(i); ALL_DAYS_OF_THE_WEEK = Collections.unmodifiableSet(t); t = new HashSet<Integer>(5); for(int i=Calendar.MONDAY; i <= Calendar.FRIDAY; i++) t.add(i); MONDAY_THROUGH_FRIDAY = Collections.unmodifiableSet(t); t = new HashSet<Integer>(2); t.add(Calendar.SUNDAY); t.add(Calendar.SATURDAY); SATURDAY_AND_SUNDAY = Collections.unmodifiableSet(t); } protected DailyTimeIntervalScheduleBuilder() { } /** * Create a DailyTimeIntervalScheduleBuilder. * * @return the new DailyTimeIntervalScheduleBuilder */ public static DailyTimeIntervalScheduleBuilder dailyTimeIntervalSchedule() { return new DailyTimeIntervalScheduleBuilder(); } /** * Build the actual Trigger -- NOT intended to be invoked by end users, * but will rather be invoked by a TriggerBuilder which this * ScheduleBuilder is given to. * * @see TriggerBuilder#withSchedule(ScheduleBuilder) */ @Override public MutableTrigger build() { DailyTimeIntervalTriggerImpl st = new DailyTimeIntervalTriggerImpl(); st.setRepeatInterval(interval); st.setRepeatIntervalUnit(intervalUnit); st.setMisfireInstruction(misfireInstruction); st.setRepeatCount(repeatCount); if(daysOfWeek != null) st.setDaysOfWeek(daysOfWeek); else st.setDaysOfWeek(ALL_DAYS_OF_THE_WEEK); if(startTimeOfDay != null) st.setStartTimeOfDay(startTimeOfDay); else st.setStartTimeOfDay(TimeOfDay.hourAndMinuteOfDay(0, 0)); if(endTimeOfDay != null) st.setEndTimeOfDay(endTimeOfDay); else st.setEndTimeOfDay(TimeOfDay.hourMinuteAndSecondOfDay(23, 59, 59)); return st; } /** * Specify the time unit and interval for the Trigger to be produced. * * @param timeInterval the interval at which the trigger should repeat. * @param unit the time unit (IntervalUnit) of the interval. The only intervals that are valid for this type of * trigger are {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. * @return the updated DailyTimeIntervalScheduleBuilder * @see DailyTimeIntervalTrigger#getRepeatInterval() * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit() */ public DailyTimeIntervalScheduleBuilder withInterval(int timeInterval, IntervalUnit unit) { if (unit == null || !(unit.equals(IntervalUnit.SECOND) || unit.equals(IntervalUnit.MINUTE) ||unit.equals(IntervalUnit.HOUR))) throw new IllegalArgumentException("Invalid repeat IntervalUnit (must be SECOND, MINUTE or HOUR)."); validateInterval(timeInterval); this.interval = timeInterval; this.intervalUnit = unit; return this; } /** * Specify an interval in the IntervalUnit.SECOND that the produced * Trigger will repeat at. * * @param intervalInSeconds the number of seconds at which the trigger should repeat. * @return the updated DailyTimeIntervalScheduleBuilder * @see DailyTimeIntervalTrigger#getRepeatInterval() * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit() */ public DailyTimeIntervalScheduleBuilder withIntervalInSeconds(int intervalInSeconds) { withInterval(intervalInSeconds, IntervalUnit.SECOND); return this; } /** * Specify an interval in the IntervalUnit.MINUTE that the produced * Trigger will repeat at. * * @param intervalInMinutes the number of minutes at which the trigger should repeat. * @return the updated CalendarIntervalScheduleBuilder * @see DailyTimeIntervalTrigger#getRepeatInterval() * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit() */ public DailyTimeIntervalScheduleBuilder withIntervalInMinutes(int intervalInMinutes) { withInterval(intervalInMinutes, IntervalUnit.MINUTE); return this; } /** * Specify an interval in the IntervalUnit.HOUR that the produced * Trigger will repeat at. * * @param intervalInHours the number of hours at which the trigger should repeat. * @return the updated DailyTimeIntervalScheduleBuilder * @see DailyTimeIntervalTrigger#getRepeatInterval() * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit() */ public DailyTimeIntervalScheduleBuilder withIntervalInHours(int intervalInHours) { withInterval(intervalInHours, IntervalUnit.HOUR); return this; } /** * Set the trigger to fire on the given days of the week. * * @param onDaysOfWeek a Set containing the integers representing the days of the week, per the values 1-7 as defined by * {@link java.util.Calendar#SUNDAY} - {@link java.util.Calendar#SATURDAY}. * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder onDaysOfTheWeek(Set<Integer> onDaysOfWeek) { if(onDaysOfWeek == null || onDaysOfWeek.size() == 0) throw new IllegalArgumentException("Days of week must be an non-empty set."); for (Integer day : onDaysOfWeek) if (!ALL_DAYS_OF_THE_WEEK.contains(day)) throw new IllegalArgumentException("Invalid value for day of week: " + day); this.daysOfWeek = onDaysOfWeek; return this; } /** * Set the trigger to fire on the given days of the week. * * @param onDaysOfWeek a variable length list of Integers representing the days of the week, per the values 1-7 as * defined by {@link java.util.Calendar#SUNDAY} - {@link java.util.Calendar#SATURDAY}. * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder onDaysOfTheWeek(Integer ... onDaysOfWeek) { Set<Integer> daysAsSet = new HashSet<Integer>(12); Collections.addAll(daysAsSet, onDaysOfWeek); return onDaysOfTheWeek(daysAsSet); } /** * Set the trigger to fire on the days from Monday through Friday. * * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder onMondayThroughFriday() { this.daysOfWeek = MONDAY_THROUGH_FRIDAY; return this; } /** * Set the trigger to fire on the days Saturday and Sunday. * * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder onSaturdayAndSunday() { this.daysOfWeek = SATURDAY_AND_SUNDAY; return this; } /** * Set the trigger to fire on all days of the week. * * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder onEveryDay() { this.daysOfWeek = ALL_DAYS_OF_THE_WEEK; return this; } /** * Set the trigger to begin firing each day at the given time. * * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder startingDailyAt(TimeOfDay timeOfDay) { if(timeOfDay == null) throw new IllegalArgumentException("Start time of day cannot be null!"); this.startTimeOfDay = timeOfDay; return this; } /** * Set the startTimeOfDay for this trigger to end firing each day at the given time. * * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder endingDailyAt(TimeOfDay timeOfDay) { this.endTimeOfDay = timeOfDay; return this; } /** * Calculate and set the endTimeOfDay using count, interval and starTimeOfDay. This means * that these must be set before this method is call. * * @return the updated DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder endingDailyAfterCount(int count) { if(count <=0) throw new IllegalArgumentException("Ending daily after count must be a positive number!"); if(startTimeOfDay == null) throw new IllegalArgumentException("You must set the startDailyAt() before calling this endingDailyAfterCount()!"); Date today = new Date(); Date startTimeOfDayDate = startTimeOfDay.getTimeOfDayForDate(today); Date maxEndTimeOfDayDate = TimeOfDay.hourMinuteAndSecondOfDay(23, 59, 59).getTimeOfDayForDate(today); long remainingMillisInDay = maxEndTimeOfDayDate.getTime() - startTimeOfDayDate.getTime(); long intervalInMillis; if (intervalUnit == IntervalUnit.SECOND) intervalInMillis = interval * 1000L; else if (intervalUnit == IntervalUnit.MINUTE) intervalInMillis = interval * 1000L * 60; else if (intervalUnit == IntervalUnit.HOUR) intervalInMillis = interval * 1000L * 60 * 24; else throw new IllegalArgumentException("The IntervalUnit: " + intervalUnit + " is invalid for this trigger."); if (remainingMillisInDay - intervalInMillis <= 0) throw new IllegalArgumentException("The startTimeOfDay is too late with given Interval and IntervalUnit values."); long maxNumOfCount = (remainingMillisInDay / intervalInMillis); if (count > maxNumOfCount) throw new IllegalArgumentException("The given count " + count + " is too large! The max you can set is " + maxNumOfCount); long incrementInMillis = (count - 1) * intervalInMillis; Date endTimeOfDayDate = new Date(startTimeOfDayDate.getTime() + incrementInMillis); if (endTimeOfDayDate.getTime() > maxEndTimeOfDayDate.getTime()) throw new IllegalArgumentException("The given count " + count + " is too large! The max you can set is " + maxNumOfCount); Calendar cal = Calendar.getInstance(); cal.setTime(endTimeOfDayDate); int hour = cal.get(Calendar.HOUR_OF_DAY); int minute = cal.get(Calendar.MINUTE); int second = cal.get(Calendar.SECOND); endTimeOfDay = TimeOfDay.hourMinuteAndSecondOfDay(hour, minute, second); return this; } /** * If the Trigger misfires, use the * {@link Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY} instruction. * * @return the updated DailyTimeIntervalScheduleBuilder * @see Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY */ public DailyTimeIntervalScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() { misfireInstruction = Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY; return this; } /** * If the Trigger misfires, use the * {@link DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING} instruction. * * @return the updated DailyTimeIntervalScheduleBuilder * @see DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING */ public DailyTimeIntervalScheduleBuilder withMisfireHandlingInstructionDoNothing() { misfireInstruction = DailyTimeIntervalTrigger.MISFIRE_INSTRUCTION_DO_NOTHING; return this; } /** * If the Trigger misfires, use the * {@link DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW} instruction. * * @return the updated DailyTimeIntervalScheduleBuilder * @see DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW */ public DailyTimeIntervalScheduleBuilder withMisfireHandlingInstructionFireAndProceed() { misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW; return this; } /** * Set number of times for interval to repeat. * * <p>Note: if you want total count = 1 (at start time) + repeatCount</p> * * @return the new DailyTimeIntervalScheduleBuilder */ public DailyTimeIntervalScheduleBuilder withRepeatCount(int repeatCount) { this.repeatCount = repeatCount; return this; } private void validateInterval(int timeInterval) { if(timeInterval <= 0) throw new IllegalArgumentException("Interval must be a positive value."); } }