/*
* 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.");
}
}