001/* 002 * #%L 003 * Netarchivesuite - harvester 004 * %% 005 * Copyright (C) 2005 - 2018 The Royal Danish Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023 024package dk.netarkivet.harvester.datamodel; 025 026import java.util.Date; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import dk.netarkivet.common.exceptions.ArgumentNotValid; 032import dk.netarkivet.common.exceptions.NotImplementedException; 033 034/** 035 * This class defines various frequencies at which things can happen, such as midnight every day, 13:45 the first monday 036 * of a month, etc. 037 */ 038public abstract class Frequency { 039 040 private static final Logger log = LoggerFactory.getLogger(Frequency.class); 041 042 /** How many units of time between each event? */ 043 private int numUnits; 044 045 /** If this Frequency happens any time rather than at a specified time. */ 046 private boolean isAnytime; 047 048 /** 049 * Initialise a frequency with information about how many periods between events, and whether it's at a specified 050 * time in the period. 051 * <p> 052 * The actual length of the period is defined by subclasses 053 * 054 * @param numUnits Number of periods between events 055 * @param isAnytime Whether it's at a specified time in the period 056 * @throws ArgumentNotValid if numUnits if 0 or negative 057 */ 058 public Frequency(int numUnits, boolean isAnytime) { 059 ArgumentNotValid.checkPositive(numUnits, "numUnits"); 060 061 this.numUnits = numUnits; 062 this.isAnytime = isAnytime; 063 } 064 065 /** 066 * Given when the last event happened, tell us when the next event should happen (even if the new event is in the 067 * past). 068 * <p> 069 * The time of the next event is guaranteed to be later that lastEvent. For certain frequencies (e.g. once a day, 070 * any time of day), the time of the next event is derived from lastEvent, for others (e.g. once a day at 13:00) the 071 * time of the next event is the first matching time after lastEvent. 072 * <p> 073 * These methods are used by the schedule methods for calculating when events should happen. 074 * 075 * @param lastEvent A time from which the next event should be calculated. 076 * @return At what point the event should happen next. 077 */ 078 public abstract Date getNextEvent(Date lastEvent); 079 080 /** 081 * Given a starting time, tell us when the first event should happen. 082 * <p> 083 * This method is used by the schedule methods for calculating when events should happen. 084 * 085 * @param startTime The earliest time the event can happen. 086 * @return At what point the event should happen the first time. 087 */ 088 public abstract Date getFirstEvent(Date startTime); 089 090 /** 091 * Returns the number of periods between events. 092 * 093 * @return that number 094 */ 095 public int getNumUnits() { 096 return numUnits; 097 } 098 099 /** 100 * Returns whether this frequency allows events to happen any time of day, rather than at a specific time. 101 * 102 * @return true if the events may happen at any time. 103 */ 104 public boolean isAnytime() { 105 return isAnytime; 106 } 107 108 /** 109 * Autogenerated equals. 110 * 111 * @param o The object to compare with 112 * @return Whether objects are equal 113 */ 114 public boolean equals(Object o) { 115 if (this == o) { 116 return true; 117 } 118 if (!(o instanceof Frequency)) { 119 return false; 120 } 121 122 final Frequency frequency = (Frequency) o; 123 124 if (isAnytime != frequency.isAnytime) { 125 return false; 126 } 127 if (numUnits != frequency.numUnits) { 128 return false; 129 } 130 131 return true; 132 } 133 134 /** 135 * Autogenerated hashcode method. 136 * 137 * @return the hashcode 138 */ 139 public int hashCode() { 140 int result; 141 result = numUnits; 142 result = 29 * result + (isAnytime ? 1 : 0); 143 return result; 144 } 145 146 /** 147 * Return the exact minute event should happen on, or null if this is an anyTime event or doesn't define what minute 148 * it should happen on. 149 * 150 * @return the exact minute event should happen on 151 */ 152 public abstract Integer getOnMinute(); 153 154 /** 155 * Return the exact hour event should happen on, or null if this is an anyTime event or doesn't define what hour it 156 * should happen on. 157 * 158 * @return the exact hour event should happen on 159 */ 160 public abstract Integer getOnHour(); 161 162 /** 163 * Return the exact day of week event should happen on, or null if this is an anyTime event or doesn't define what 164 * day of week it should happen on. 165 * 166 * @return the exact day of week event should happen on 167 */ 168 public abstract Integer getOnDayOfWeek(); 169 170 /** 171 * Return the exact day of month event should happen on, or null if this is an anyTime event or doesn't define what 172 * day of month it should happen on. 173 * 174 * @return the exact day of month event should happen on 175 */ 176 public abstract Integer getOnDayOfMonth(); 177 178 /** 179 * Return an integer that can be used to identify the kind of frequency. No two subclasses should use the same 180 * integer 181 * 182 * @return Return an integer that can be used to identify the kind of frequency 183 */ 184 public abstract int ordinal(); 185 186 /** 187 * Get a new instance of Frequency. The type of Frequency (either Hourly, Daily, Monthly, or Weekly) returned 188 * depends on the 'timeunit' argument. 189 * 190 * @param timeunit The type or frequency 191 * @param anytime Allow events to start anytime. If false, the starting point is described precisely. If true, the 192 * starting point will be immediately. 193 * @param numtimeunits The number of periods between events 194 * @param minute A given minute. Used to create hourly, daily, and monthly frequencies, if anytime is false. 195 * @param hour A given hour. Used to create hourly, daily, and monthly frequencies, if anytime is false. 196 * @param dayofweek A given day of the week. Used only to create weekly frequencies, if anytime is false. 197 * @param dayofmonth A given day of month. Used only to create monthly frequencies, if anytime is false. 198 * @return a new instance of the Frequency class. 199 * @throws ArgumentNotValid If the given timeunit is illegal, or the values of timeunit and numtimeunits is 200 * negative. 201 * @throws NotImplementedException If we can't yet make a Frequency for a legal timeunit. 202 */ 203 public static Frequency getNewInstance(int timeunit, boolean anytime, int numtimeunits, Integer minute, 204 Integer hour, Integer dayofweek, Integer dayofmonth) { 205 ArgumentNotValid.checkPositive(timeunit, "int timeunit"); 206 ArgumentNotValid.checkPositive(numtimeunits, "int timeunits"); 207 208 Frequency freq; 209 TimeUnit tu = TimeUnit.fromOrdinal(timeunit); 210 log.debug("Creating a " + tu.name() + " frequency."); 211 if (!anytime) { 212 ArgumentNotValid.checkTrue(minute != null, "Arg. minute should not be null, if anytime is false"); 213 ArgumentNotValid 214 .checkTrue(hour != null || tu.equals(TimeUnit.HOURLY), 215 "Arg. hour should not be null, if anytime is false unless" 216 + " we are creating a Hourly frequency."); 217 ArgumentNotValid.checkTrue(dayofweek != null || !tu.equals(TimeUnit.WEEKLY), 218 "Arg. dayofweek should not be null, if anytime is false " 219 + " and we are creating a Weekly frequency."); 220 ArgumentNotValid.checkTrue(dayofmonth != null || !tu.equals(TimeUnit.MONTHLY), 221 "Arg. dayofmonth should not be null, if anytime is false " 222 + "and we are creating a Monthly frequency."); 223 } 224 225 switch (tu) { 226 case HOURLY: 227 if (anytime) { 228 freq = new HourlyFrequency(numtimeunits); 229 } else { 230 freq = new HourlyFrequency(numtimeunits, minute); 231 } 232 break; 233 case DAILY: 234 if (anytime) { 235 freq = new DailyFrequency(numtimeunits); 236 } else { 237 freq = new DailyFrequency(numtimeunits, hour, minute); 238 } 239 break; 240 case WEEKLY: 241 if (anytime) { 242 freq = new WeeklyFrequency(numtimeunits); 243 } else { 244 freq = new WeeklyFrequency(numtimeunits, dayofweek, hour, minute); 245 } 246 break; 247 case MONTHLY: 248 if (anytime) { 249 freq = new MonthlyFrequency(numtimeunits); 250 } else { 251 freq = new MonthlyFrequency(numtimeunits, dayofmonth, hour, minute); 252 } 253 break; 254 case MINUTE: // Minute frequencies are always "anytime" 255 freq = new MinuteFrequency(numtimeunits); 256 break; 257 default: 258 throw new NotImplementedException("We don't know how to make a Frequency for timeunit " + timeunit); 259 } 260 return freq; 261 } 262 263}