0001: /*
0002: * Copyright 2004-2005 OpenSymphony
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License. You may obtain a copy
0006: * of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013: * License for the specific language governing permissions and limitations
0014: * under the License.
0015: *
0016: */
0017:
0018: /*
0019: * Previously Copyright (c) 2001-2004 James House
0020: */
0021: package org.quartz;
0022:
0023: import java.text.ParseException;
0024: import java.util.Calendar;
0025: import java.util.Date;
0026: import java.util.TimeZone;
0027:
0028: /**
0029: * <p>
0030: * A concrete <code>{@link Trigger}</code> that is used to fire a <code>{@link org.quartz.JobDetail}</code>
0031: * at given moments in time, defined with Unix 'cron-like' definitions.
0032: * </p>
0033: *
0034: * <p>
0035: * For those unfamiliar with "cron", this means being able to create a firing
0036: * schedule such as: "At 8:00am every Monday through Friday" or "At 1:30am
0037: * every last Friday of the month".
0038: * </p>
0039: *
0040: * <p>
0041: * The format of a "Cron-Expression" string is documented on the
0042: * {@link org.quartz.CronExpression} class.
0043: * </p>
0044: *
0045: * <p>
0046: * Here are some full examples: <br><table cellspacing="8">
0047: * <tr>
0048: * <th align="left">Expression</th>
0049: * <th align="left"> </th>
0050: * <th align="left">Meaning</th>
0051: * </tr>
0052: * <tr>
0053: * <td align="left"><code>"0 0 12 * * ?"</code></td>
0054: * <td align="left"> </th>
0055: * <td align="left"><code>Fire at 12pm (noon) every day</code></td>
0056: * </tr>
0057: * <tr>
0058: * <td align="left"><code>"0 15 10 ? * *"</code></td>
0059: * <td align="left"> </th>
0060: * <td align="left"><code>Fire at 10:15am every day</code></td>
0061: * </tr>
0062: * <tr>
0063: * <td align="left"><code>"0 15 10 * * ?"</code></td>
0064: * <td align="left"> </th>
0065: * <td align="left"><code>Fire at 10:15am every day</code></td>
0066: * </tr>
0067: * <tr>
0068: * <td align="left"><code>"0 15 10 * * ? *"</code></td>
0069: * <td align="left"> </th>
0070: * <td align="left"><code>Fire at 10:15am every day</code></td>
0071: * </tr>
0072: * <tr>
0073: * <td align="left"><code>"0 15 10 * * ? 2005"</code></td>
0074: * <td align="left"> </th>
0075: * <td align="left"><code>Fire at 10:15am every day during the year 2005</code>
0076: * </td>
0077: * </tr>
0078: * <tr>
0079: * <td align="left"><code>"0 * 14 * * ?"</code></td>
0080: * <td align="left"> </th>
0081: * <td align="left"><code>Fire every minute starting at 2pm and ending at 2:59pm, every day</code>
0082: * </td>
0083: * </tr>
0084: * <tr>
0085: * <td align="left"><code>"0 0/5 14 * * ?"</code></td>
0086: * <td align="left"> </th>
0087: * <td align="left"><code>Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day</code>
0088: * </td>
0089: * </tr>
0090: * <tr>
0091: * <td align="left"><code>"0 0/5 14,18 * * ?"</code></td>
0092: * <td align="left"> </th>
0093: * <td align="left"><code>Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day</code>
0094: * </td>
0095: * </tr>
0096: * <tr>
0097: * <td align="left"><code>"0 0-5 14 * * ?"</code></td>
0098: * <td align="left"> </th>
0099: * <td align="left"><code>Fire every minute starting at 2pm and ending at 2:05pm, every day</code>
0100: * </td>
0101: * </tr>
0102: * <tr>
0103: * <td align="left"><code>"0 10,44 14 ? 3 WED"</code></td>
0104: * <td align="left"> </th>
0105: * <td align="left"><code>Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.</code>
0106: * </td>
0107: * </tr>
0108: * <tr>
0109: * <td align="left"><code>"0 15 10 ? * MON-FRI"</code></td>
0110: * <td align="left"> </th>
0111: * <td align="left"><code>Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday</code>
0112: * </td>
0113: * </tr>
0114: * <tr>
0115: * <td align="left"><code>"0 15 10 15 * ?"</code></td>
0116: * <td align="left"> </th>
0117: * <td align="left"><code>Fire at 10:15am on the 15th day of every month</code>
0118: * </td>
0119: * </tr>
0120: * <tr>
0121: * <td align="left"><code>"0 15 10 L * ?"</code></td>
0122: * <td align="left"> </th>
0123: * <td align="left"><code>Fire at 10:15am on the last day of every month</code>
0124: * </td>
0125: * </tr>
0126: * <tr>
0127: * <td align="left"><code>"0 15 10 ? * 6L"</code></td>
0128: * <td align="left"> </th>
0129: * <td align="left"><code>Fire at 10:15am on the last Friday of every month</code>
0130: * </td>
0131: * </tr>
0132: * <tr>
0133: * <td align="left"><code>"0 15 10 ? * 6L"</code></td>
0134: * <td align="left"> </th>
0135: * <td align="left"><code>Fire at 10:15am on the last Friday of every month</code>
0136: * </td>
0137: * </tr>
0138: * <tr>
0139: * <td align="left"><code>"0 15 10 ? * 6L 2002-2005"</code></td>
0140: * <td align="left"> </th>
0141: * <td align="left"><code>Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005</code>
0142: * </td>
0143: * </tr>
0144: * <tr>
0145: * <td align="left"><code>"0 15 10 ? * 6#3"</code></td>
0146: * <td align="left"> </th>
0147: * <td align="left"><code>Fire at 10:15am on the third Friday of every month</code>
0148: * </td>
0149: * </tr>
0150: * </table>
0151: * </p>
0152: *
0153: * <p>
0154: * Pay attention to the effects of '?' and '*' in the day-of-week and
0155: * day-of-month fields!
0156: * </p>
0157: *
0158: * <p>
0159: * <b>NOTES:</b>
0160: * <ul>
0161: * <li>Support for specifying both a day-of-week and a day-of-month value is
0162: * not complete (you'll need to use the '?' character in on of these fields).
0163: * </li>
0164: * <li>Be careful when setting fire times between mid-night and 1:00 AM -
0165: * "daylight savings" can cause a skip or a repeat depending on whether the
0166: * time moves back or jumps forward.</li>
0167: * </ul>
0168: * </p>
0169: *
0170: * @see Trigger
0171: * @see SimpleTrigger
0172: * @see TriggerUtils
0173: *
0174: * @author Sharada Jambula, James House
0175: * @author Contributions from Mads Henderson
0176: */
0177: public class CronTrigger extends Trigger {
0178:
0179: /*
0180: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0181: *
0182: * Constants.
0183: *
0184: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0185: */
0186:
0187: /**
0188: * <p>
0189: * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
0190: * situation, the <code>{@link CronTrigger}</code> wants to be fired now
0191: * by <code>Scheduler</code>.
0192: * </p>
0193: */
0194: public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
0195:
0196: /**
0197: * <p>
0198: * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
0199: * situation, the <code>{@link CronTrigger}</code> wants to have it's
0200: * next-fire-time updated to the next time in the schedule after the
0201: * current time (taking into account any associated <code>{@link Calendar}</code>,
0202: * but it does not want to be fired now.
0203: * </p>
0204: */
0205: public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
0206:
0207: /*
0208: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0209: *
0210: * Data members.
0211: *
0212: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0213: */
0214:
0215: private CronExpression cronEx = null;
0216: private Date startTime = null;
0217: private Date endTime = null;
0218: private Date nextFireTime = null;
0219: private Date previousFireTime = null;
0220: private transient TimeZone timeZone = null;
0221:
0222: /*
0223: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0224: *
0225: * Constructors.
0226: *
0227: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0228: */
0229:
0230: /**
0231: * <p>
0232: * Create a <code>CronTrigger</code> with no settings.
0233: * </p>
0234: *
0235: * <p>
0236: * The start-time will also be set to the current time, and the time zone
0237: * will be set the the system's default time zone.
0238: * </p>
0239: */
0240: public CronTrigger() {
0241: super ();
0242: setStartTime(new Date());
0243: setTimeZone(TimeZone.getDefault());
0244: }
0245:
0246: /**
0247: * <p>
0248: * Create a <code>CronTrigger</code> with the given name and group.
0249: * </p>
0250: *
0251: * <p>
0252: * The start-time will also be set to the current time, and the time zone
0253: * will be set the the system's default time zone.
0254: * </p>
0255: */
0256: public CronTrigger(String name, String group) {
0257: super (name, group);
0258: setStartTime(new Date());
0259: setTimeZone(TimeZone.getDefault());
0260: }
0261:
0262: /**
0263: * <p>
0264: * Create a <code>CronTrigger</code> with the given name, group and
0265: * expression.
0266: * </p>
0267: *
0268: * <p>
0269: * The start-time will also be set to the current time, and the time zone
0270: * will be set the the system's default time zone.
0271: * </p>
0272: */
0273: public CronTrigger(String name, String group, String cronExpression)
0274: throws ParseException {
0275:
0276: super (name, group);
0277:
0278: setCronExpression(cronExpression);
0279:
0280: setStartTime(new Date());
0281: setTimeZone(TimeZone.getDefault());
0282: }
0283:
0284: /**
0285: * <p>
0286: * Create a <code>CronTrigger</code> with the given name and group, and
0287: * associated with the identified <code>{@link org.quartz.JobDetail}</code>.
0288: * </p>
0289: *
0290: * <p>
0291: * The start-time will also be set to the current time, and the time zone
0292: * will be set the the system's default time zone.
0293: * </p>
0294: */
0295: public CronTrigger(String name, String group, String jobName,
0296: String jobGroup) {
0297: super (name, group, jobName, jobGroup);
0298: setStartTime(new Date());
0299: setTimeZone(TimeZone.getDefault());
0300: }
0301:
0302: /**
0303: * <p>
0304: * Create a <code>CronTrigger</code> with the given name and group,
0305: * associated with the identified <code>{@link org.quartz.JobDetail}</code>,
0306: * and with the given "cron" expression.
0307: * </p>
0308: *
0309: * <p>
0310: * The start-time will also be set to the current time, and the time zone
0311: * will be set the the system's default time zone.
0312: * </p>
0313: */
0314: public CronTrigger(String name, String group, String jobName,
0315: String jobGroup, String cronExpression)
0316: throws ParseException {
0317: this (name, group, jobName, jobGroup, null, null,
0318: cronExpression, TimeZone.getDefault());
0319: }
0320:
0321: /**
0322: * <p>
0323: * Create a <code>CronTrigger</code> with the given name and group,
0324: * associated with the identified <code>{@link org.quartz.JobDetail}</code>,
0325: * and with the given "cron" expression resolved with respect to the <code>TimeZone</code>.
0326: * </p>
0327: */
0328: public CronTrigger(String name, String group, String jobName,
0329: String jobGroup, String cronExpression, TimeZone timeZone)
0330: throws ParseException {
0331: this (name, group, jobName, jobGroup, null, null,
0332: cronExpression, timeZone);
0333: }
0334:
0335: /**
0336: * <p>
0337: * Create a <code>CronTrigger</code> that will occur at the given time,
0338: * until the given end time.
0339: * </p>
0340: *
0341: * <p>
0342: * If null, the start-time will also be set to the current time, the time
0343: * zone will be set the the system's default.
0344: * </p>
0345: *
0346: * @param startTime
0347: * A <code>Date</code> set to the time for the <code>Trigger</code>
0348: * to fire.
0349: * @param endTime
0350: * A <code>Date</code> set to the time for the <code>Trigger</code>
0351: * to quit repeat firing.
0352: */
0353: public CronTrigger(String name, String group, String jobName,
0354: String jobGroup, Date startTime, Date endTime,
0355: String cronExpression) throws ParseException {
0356: super (name, group, jobName, jobGroup);
0357:
0358: setCronExpression(cronExpression);
0359:
0360: if (startTime == null) {
0361: startTime = new Date();
0362: }
0363: setStartTime(startTime);
0364: if (endTime != null) {
0365: setEndTime(endTime);
0366: }
0367: setTimeZone(TimeZone.getDefault());
0368:
0369: }
0370:
0371: /**
0372: * <p>
0373: * Create a <code>CronTrigger</code> with fire time dictated by the
0374: * <code>cronExpression</code> resolved with respect to the specified
0375: * <code>timeZone</code> occuring from the <code>startTime</code> until
0376: * the given <code>endTime</code>.
0377: * </p>
0378: *
0379: * <p>
0380: * If null, the start-time will also be set to the current time. If null,
0381: * the time zone will be set to the system's default.
0382: * </p>
0383: *
0384: * @param name
0385: * of the <code>Trigger</code>
0386: * @param group
0387: * of the <code>Trigger</code>
0388: * @param jobName
0389: * name of the <code>{@link org.quartz.JobDetail}</code>
0390: * executed on firetime
0391: * @param jobGroup
0392: * group of the <code>{@link org.quartz.JobDetail}</code>
0393: * executed on firetime
0394: * @param startTime
0395: * A <code>Date</code> set to the earliest time for the <code>Trigger</code>
0396: * to start firing.
0397: * @param endTime
0398: * A <code>Date</code> set to the time for the <code>Trigger</code>
0399: * to quit repeat firing.
0400: * @param cronExpression
0401: * A cron expression dictating the firing sequence of the <code>Trigger</code>
0402: * @param timeZone
0403: * Specifies for which time zone the <code>cronExpression</code>
0404: * should be interprted, i.e. the expression 0 0 10 * * ?, is
0405: * resolved to 10:00 am in this time zone.
0406: * @throws ParseException
0407: * if the <code>cronExpression</code> is invalid.
0408: */
0409: public CronTrigger(String name, String group, String jobName,
0410: String jobGroup, Date startTime, Date endTime,
0411: String cronExpression, TimeZone timeZone)
0412: throws ParseException {
0413: super (name, group, jobName, jobGroup);
0414:
0415: setCronExpression(cronExpression);
0416:
0417: if (startTime == null) {
0418: startTime = new Date();
0419: }
0420: setStartTime(startTime);
0421: if (endTime != null) {
0422: setEndTime(endTime);
0423: }
0424: if (timeZone == null) {
0425: setTimeZone(TimeZone.getDefault());
0426: } else {
0427: setTimeZone(timeZone);
0428: }
0429: }
0430:
0431: /*
0432: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0433: *
0434: * Interface.
0435: *
0436: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0437: */
0438:
0439: public Object clone() {
0440: CronTrigger copy = (CronTrigger) super .clone();
0441: copy.setCronExpression((CronExpression) cronEx.clone());
0442: return copy;
0443: }
0444:
0445: public void setCronExpression(String cronExpression)
0446: throws ParseException {
0447: this .cronEx = new CronExpression(cronExpression);
0448: this .cronEx.setTimeZone(getTimeZone());
0449: }
0450:
0451: public String getCronExpression() {
0452: return cronEx == null ? null : cronEx.getCronExpression();
0453: }
0454:
0455: public void setCronExpression(CronExpression cronExpression) {
0456: this .cronEx = cronExpression;
0457: this .timeZone = cronExpression.getTimeZone();
0458: }
0459:
0460: /**
0461: * <p>
0462: * Get the time at which the <code>CronTrigger</code> should occur.
0463: * </p>
0464: */
0465: public Date getStartTime() {
0466: return this .startTime;
0467: }
0468:
0469: public void setStartTime(Date startTime) {
0470: if (startTime == null) {
0471: throw new IllegalArgumentException(
0472: "Start time cannot be null");
0473: }
0474:
0475: Date eTime = getEndTime();
0476: if (eTime != null && startTime != null
0477: && eTime.before(startTime)) {
0478: throw new IllegalArgumentException(
0479: "End time cannot be before start time");
0480: }
0481:
0482: // round off millisecond...
0483: // Note timeZone is not needed here as parameter for
0484: // Calendar.getInstance(),
0485: // since time zone is implicit when using a Date in the setTime method.
0486: Calendar cl = Calendar.getInstance();
0487: cl.setTime(startTime);
0488: cl.set(Calendar.MILLISECOND, 0);
0489:
0490: this .startTime = cl.getTime();
0491: }
0492:
0493: /**
0494: * <p>
0495: * Get the time at which the <code>CronTrigger</code> should quit
0496: * repeating - even if repeastCount isn't yet satisfied.
0497: * </p>
0498: *
0499: * @see #getFinalFireTime()
0500: */
0501: public Date getEndTime() {
0502: return this .endTime;
0503: }
0504:
0505: public void setEndTime(Date endTime) {
0506: Date sTime = getStartTime();
0507: if (sTime != null && endTime != null && sTime.after(endTime)) {
0508: throw new IllegalArgumentException(
0509: "End time cannot be before start time");
0510: }
0511:
0512: this .endTime = endTime;
0513: }
0514:
0515: /**
0516: * <p>
0517: * Returns the next time at which the <code>CronTrigger</code> will fire.
0518: * If the trigger will not fire again, <code>null</code> will be
0519: * returned. The value returned is not guaranteed to be valid until after
0520: * the <code>Trigger</code> has been added to the scheduler.
0521: * </p>
0522: */
0523: public Date getNextFireTime() {
0524: return this .nextFireTime;
0525: }
0526:
0527: /**
0528: * <p>
0529: * Returns the previous time at which the <code>CronTrigger</code> will
0530: * fire. If the trigger has not yet fired, <code>null</code> will be
0531: * returned.
0532: */
0533: public Date getPreviousFireTime() {
0534: return this .previousFireTime;
0535: }
0536:
0537: /**
0538: * <p>
0539: * Sets the next time at which the <code>CronTrigger</code> will fire.
0540: * <b>This method should not be invoked by client code.</b>
0541: * </p>
0542: */
0543: public void setNextFireTime(Date nextFireTime) {
0544: this .nextFireTime = nextFireTime;
0545: }
0546:
0547: /**
0548: * <p>
0549: * Set the previous time at which the <code>CronTrigger</code> fired.
0550: * </p>
0551: *
0552: * <p>
0553: * <b>This method should not be invoked by client code.</b>
0554: * </p>
0555: */
0556: public void setPreviousFireTime(Date previousFireTime) {
0557: this .previousFireTime = previousFireTime;
0558: }
0559:
0560: /**
0561: * <p>
0562: * Returns the time zone for which the <code>cronExpression</code> of
0563: * this <code>CronTrigger</code> will be resolved.
0564: * </p>
0565: */
0566: public TimeZone getTimeZone() {
0567:
0568: if (cronEx != null) {
0569: return cronEx.getTimeZone();
0570: }
0571:
0572: if (timeZone == null) {
0573: timeZone = TimeZone.getDefault();
0574: }
0575: return timeZone;
0576: }
0577:
0578: /**
0579: * <p>
0580: * Sets the time zone for which the <code>cronExpression</code> of this
0581: * <code>CronTrigger</code> will be resolved.
0582: * </p>
0583: */
0584: public void setTimeZone(TimeZone timeZone) {
0585: if (cronEx != null) {
0586: cronEx.setTimeZone(timeZone);
0587: }
0588: this .timeZone = timeZone;
0589: }
0590:
0591: /**
0592: * <p>
0593: * Returns the next time at which the <code>CronTrigger</code> will fire,
0594: * after the given time. If the trigger will not fire after the given time,
0595: * <code>null</code> will be returned.
0596: * </p>
0597: *
0598: * <p>
0599: * Note that the date returned is NOT validated against the related
0600: * org.quartz.Calendar (if any)
0601: * </p>
0602: */
0603: public Date getFireTimeAfter(Date afterTime) {
0604: if (afterTime == null) {
0605: afterTime = new Date();
0606: }
0607:
0608: if (getStartTime().after(afterTime)) {
0609: afterTime = new Date(getStartTime().getTime() - 1000l);
0610: }
0611:
0612: if (getEndTime() != null
0613: && (afterTime.compareTo(getEndTime()) >= 0)) {
0614: return null;
0615: }
0616:
0617: Date pot = getTimeAfter(afterTime);
0618: if (getEndTime() != null && pot != null
0619: && pot.after(getEndTime())) {
0620: return null;
0621: }
0622:
0623: return pot;
0624: }
0625:
0626: /**
0627: * <p>
0628: * NOT YET IMPLEMENTED: Returns the final time at which the
0629: * <code>CronTrigger</code> will fire.
0630: * </p>
0631: *
0632: * <p>
0633: * Note that the return time *may* be in the past. and the date returned is
0634: * not validated against org.quartz.calendar
0635: * </p>
0636: */
0637: public Date getFinalFireTime() {
0638: Date resultTime;
0639: if (getEndTime() != null) {
0640: resultTime = getTimeBefore(new Date(
0641: getEndTime().getTime() + 1000l));
0642: } else {
0643: resultTime = (cronEx == null) ? null : cronEx
0644: .getFinalFireTime();
0645: }
0646:
0647: if ((resultTime != null) && (getStartTime() != null)
0648: && (resultTime.before(getStartTime()))) {
0649: return null;
0650: }
0651:
0652: return resultTime;
0653: }
0654:
0655: /**
0656: * <p>
0657: * Determines whether or not the <code>CronTrigger</code> will occur
0658: * again.
0659: * </p>
0660: */
0661: public boolean mayFireAgain() {
0662: return (getNextFireTime() != null);
0663: }
0664:
0665: protected boolean validateMisfireInstruction(int misfireInstruction) {
0666: if (misfireInstruction < MISFIRE_INSTRUCTION_SMART_POLICY) {
0667: return false;
0668: }
0669:
0670: if (misfireInstruction > MISFIRE_INSTRUCTION_DO_NOTHING) {
0671: return false;
0672: }
0673:
0674: return true;
0675: }
0676:
0677: /**
0678: * <p>
0679: * Updates the <code>CronTrigger</code>'s state based on the
0680: * MISFIRE_INSTRUCTION_XXX that was selected when the <code>CronTrigger</code>
0681: * was created.
0682: * </p>
0683: *
0684: * <p>
0685: * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
0686: * then the following scheme will be used: <br>
0687: * <ul>
0688: * <li>The instruction will be interpreted as <code>MISFIRE_INSTRUCTION_FIRE_ONCE_NOW</code>
0689: * </ul>
0690: * </p>
0691: */
0692: public void updateAfterMisfire(org.quartz.Calendar cal) {
0693: int instr = getMisfireInstruction();
0694:
0695: if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) {
0696: instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
0697: }
0698:
0699: if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) {
0700: Date newFireTime = getFireTimeAfter(new Date());
0701: while (newFireTime != null && cal != null
0702: && !cal.isTimeIncluded(newFireTime.getTime())) {
0703: newFireTime = getFireTimeAfter(newFireTime);
0704: }
0705: setNextFireTime(newFireTime);
0706: } else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
0707: setNextFireTime(new Date());
0708: }
0709: }
0710:
0711: /**
0712: * <p>
0713: * Determines whether the date and (optionally) time of the given Calendar
0714: * instance falls on a scheduled fire-time of this trigger.
0715: * </p>
0716: *
0717: * <p>
0718: * Equivalent to calling <code>willFireOn(cal, false)</code>.
0719: * </p>
0720: *
0721: * @param test the date to compare
0722: *
0723: * @see #willFireOn(Calendar, boolean)
0724: */
0725: public boolean willFireOn(Calendar test) {
0726: return willFireOn(test, false);
0727: }
0728:
0729: /**
0730: * <p>
0731: * Determines whether the date and (optionally) time of the given Calendar
0732: * instance falls on a scheduled fire-time of this trigger.
0733: * </p>
0734: *
0735: * <p>
0736: * Note that the value returned is NOT validated against the related
0737: * org.quartz.Calendar (if any)
0738: * </p>
0739: *
0740: * @param test the date to compare
0741: * @param dayOnly if set to true, the method will only determine if the
0742: * trigger will fire during the day represented by the given Calendar
0743: * (hours, minutes and seconds will be ignored).
0744: * @see #willFireOn(Calendar)
0745: */
0746: public boolean willFireOn(Calendar test, boolean dayOnly) {
0747:
0748: test = (Calendar) test.clone();
0749:
0750: test.set(Calendar.MILLISECOND, 0); // don't compare millis.
0751:
0752: if (dayOnly) {
0753: test.set(Calendar.HOUR, 0);
0754: test.set(Calendar.MINUTE, 0);
0755: test.set(Calendar.SECOND, 0);
0756: }
0757:
0758: Date testTime = test.getTime();
0759:
0760: Date fta = getFireTimeAfter(new Date(
0761: test.getTime().getTime() - 1000));
0762:
0763: Calendar p = Calendar.getInstance(test.getTimeZone());
0764: p.setTime(fta);
0765:
0766: int year = p.get(Calendar.YEAR);
0767: int month = p.get(Calendar.MONTH);
0768: int day = p.get(Calendar.DATE);
0769:
0770: if (dayOnly) {
0771: return (year == test.get(Calendar.YEAR)
0772: && month == test.get(Calendar.MONTH) && day == test
0773: .get(Calendar.DATE));
0774: }
0775:
0776: while (fta.before(testTime)) {
0777: fta = getFireTimeAfter(fta);
0778: }
0779:
0780: if (fta.equals(testTime)) {
0781: return true;
0782: }
0783:
0784: return false;
0785: }
0786:
0787: /**
0788: * <p>
0789: * Called after the <code>{@link Scheduler}</code> has executed the
0790: * <code>{@link org.quartz.JobDetail}</code> associated with the <code>Trigger</code>
0791: * in order to get the final instruction code from the trigger.
0792: * </p>
0793: *
0794: * @param context
0795: * is the <code>JobExecutionContext</code> that was used by the
0796: * <code>Job</code>'s<code>execute(xx)</code> method.
0797: * @param result
0798: * is the <code>JobExecutionException</code> thrown by the
0799: * <code>Job</code>, if any (may be null).
0800: * @return one of the Trigger.INSTRUCTION_XXX constants.
0801: *
0802: * @see #INSTRUCTION_NOOP
0803: * @see #INSTRUCTION_RE_EXECUTE_JOB
0804: * @see #INSTRUCTION_DELETE_TRIGGER
0805: * @see #INSTRUCTION_SET_TRIGGER_COMPLETE
0806: * @see #triggered(Calendar)
0807: */
0808: public int executionComplete(JobExecutionContext context,
0809: JobExecutionException result) {
0810: if (result != null && result.refireImmediately()) {
0811: return INSTRUCTION_RE_EXECUTE_JOB;
0812: }
0813:
0814: if (result != null && result.unscheduleFiringTrigger()) {
0815: return INSTRUCTION_SET_TRIGGER_COMPLETE;
0816: }
0817:
0818: if (result != null && result.unscheduleAllTriggers()) {
0819: return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
0820: }
0821:
0822: if (!mayFireAgain()) {
0823: return INSTRUCTION_DELETE_TRIGGER;
0824: }
0825:
0826: return INSTRUCTION_NOOP;
0827: }
0828:
0829: /**
0830: * <p>
0831: * Called when the <code>{@link Scheduler}</code> has decided to 'fire'
0832: * the trigger (execute the associated <code>Job</code>), in order to
0833: * give the <code>Trigger</code> a chance to update itself for its next
0834: * triggering (if any).
0835: * </p>
0836: *
0837: * @see #executionComplete(JobExecutionContext, JobExecutionException)
0838: */
0839: public void triggered(org.quartz.Calendar calendar) {
0840: previousFireTime = nextFireTime;
0841: nextFireTime = getFireTimeAfter(nextFireTime);
0842:
0843: while (nextFireTime != null && calendar != null
0844: && !calendar.isTimeIncluded(nextFireTime.getTime())) {
0845: nextFireTime = getFireTimeAfter(nextFireTime);
0846: }
0847: }
0848:
0849: /**
0850: *
0851: * @see org.quartz.Trigger#updateWithNewCalendar(org.quartz.Calendar, long)
0852: */
0853: public void updateWithNewCalendar(org.quartz.Calendar calendar,
0854: long misfireThreshold) {
0855: nextFireTime = getFireTimeAfter(previousFireTime);
0856:
0857: Date now = new Date();
0858: do {
0859: while (nextFireTime != null && calendar != null
0860: && !calendar.isTimeIncluded(nextFireTime.getTime())) {
0861: nextFireTime = getFireTimeAfter(nextFireTime);
0862: }
0863:
0864: if (nextFireTime != null && nextFireTime.before(now)) {
0865: long diff = now.getTime() - nextFireTime.getTime();
0866: if (diff >= misfireThreshold) {
0867: nextFireTime = getFireTimeAfter(nextFireTime);
0868: continue;
0869: }
0870: }
0871: } while (false);
0872: }
0873:
0874: /**
0875: * <p>
0876: * Called by the scheduler at the time a <code>Trigger</code> is first
0877: * added to the scheduler, in order to have the <code>Trigger</code>
0878: * compute its first fire time, based on any associated calendar.
0879: * </p>
0880: *
0881: * <p>
0882: * After this method has been called, <code>getNextFireTime()</code>
0883: * should return a valid answer.
0884: * </p>
0885: *
0886: * @return the first time at which the <code>Trigger</code> will be fired
0887: * by the scheduler, which is also the same value <code>getNextFireTime()</code>
0888: * will return (until after the first firing of the <code>Trigger</code>).
0889: * </p>
0890: */
0891: public Date computeFirstFireTime(org.quartz.Calendar calendar) {
0892: nextFireTime = getFireTimeAfter(new Date(getStartTime()
0893: .getTime() - 1000l));
0894:
0895: while (nextFireTime != null && calendar != null
0896: && !calendar.isTimeIncluded(nextFireTime.getTime())) {
0897: nextFireTime = getFireTimeAfter(nextFireTime);
0898: }
0899:
0900: return nextFireTime;
0901: }
0902:
0903: public String getExpressionSummary() {
0904: return cronEx == null ? null : cronEx.getExpressionSummary();
0905: }
0906:
0907: ////////////////////////////////////////////////////////////////////////////
0908: //
0909: // Computation Functions
0910: //
0911: ////////////////////////////////////////////////////////////////////////////
0912:
0913: protected Date getTimeAfter(Date afterTime) {
0914: return (cronEx == null) ? null : cronEx.getTimeAfter(afterTime);
0915: }
0916:
0917: /**
0918: * NOT YET IMPLEMENTED: Returns the time before the given time
0919: * that this <code>CronTrigger</code> will fire.
0920: */
0921: protected Date getTimeBefore(Date endTime) {
0922: return (cronEx == null) ? null : cronEx.getTimeBefore(endTime);
0923: }
0924:
0925: public static void main(String[] args) // TODO: remove method after good
0926: // unit testing
0927: throws Exception {
0928:
0929: String expr = "15 10 0/4 * * ?";
0930: if (args != null && args.length > 0 && args[0] != null) {
0931: expr = args[0];
0932: }
0933:
0934: CronTrigger ct = new CronTrigger("t", "g", "j", "g",
0935: new Date(), null, expr);
0936: ct.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
0937: System.err.println(ct.getExpressionSummary());
0938: System.err.println("tz=" + ct.getTimeZone().getID());
0939: System.err.println();
0940:
0941: java.util.List times = TriggerUtils.computeFireTimes(ct, null,
0942: 25);
0943:
0944: for (int i = 0; i < times.size(); i++) {
0945: System.err.println("firetime = " + times.get(i));
0946: }
0947:
0948: Calendar tt = Calendar.getInstance();
0949: tt.set(Calendar.DATE, 17);
0950: tt.set(Calendar.MONTH, 5 - 1);
0951: tt.set(Calendar.HOUR, 11);
0952: tt.set(Calendar.MINUTE, 0);
0953: tt.set(Calendar.SECOND, 7);
0954:
0955: System.err.println("\nWill fire on: " + tt.getTime() + " -- "
0956: + ct.willFireOn(tt, false));
0957:
0958: // CRON Expression: 0 0 9 * * ?
0959: //
0960: // TimeZone.getDefault().getDisplayName() = Central African Time
0961: // TimeZone.getDefault().getID() = Africa/Harare
0962: //
0963: //// System.err.println();
0964: //// System.err.println();
0965: //// System.err.println();
0966: //// System.err.println("Daylight test:");
0967: ////
0968: //// CronTrigger trigger = new CronTrigger();
0969: ////
0970: //// TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
0971: //// // TimeZone timeZone = TimeZone.getDefault();
0972: ////
0973: //// trigger.setTimeZone(timeZone);
0974: //// trigger.setCronExpression("0 0 1 ? 4 *");
0975: ////
0976: //// Date start = new Date(1056319200000L);
0977: //// Date end = new Date(1087682399000L);
0978: ////
0979: //// trigger.setStartTime(start);
0980: //// trigger.setEndTime(end);
0981: ////
0982: //// Date next = new Date(1056232800000L);
0983: //// while (next != null) {
0984: //// next = trigger.getFireTimeAfter(next);
0985: //// if (next != null) {
0986: //// Calendar cal = Calendar.getInstance();
0987: //// cal.setTimeZone(timeZone);
0988: //// cal.setTime(next);
0989: //// System.err.println(cal.get(Calendar.MONTH) + "/"
0990: //// + cal.get(Calendar.DATE) + "/" + cal.get(Calendar.YEAR)
0991: //// + " - " + cal.get(Calendar.HOUR_OF_DAY) + ":"
0992: //// + cal.get(Calendar.MINUTE));
0993: //// }
0994: //// }
0995: ////
0996: //// System.err.println();
0997: //// System.err.println();
0998: //// System.err.println();
0999: //// System.err.println("Midnight test:");
1000: ////
1001: //// trigger = new CronTrigger();
1002: ////
1003: //// timeZone = TimeZone.getTimeZone("Asia/Jerusalem");
1004: //// // timeZone = TimeZone.getTimeZone("America/Los_Angeles");
1005: //// // TimeZone timeZone = TimeZone.getDefault();
1006: ////
1007: //// trigger.setTimeZone(timeZone);
1008: //// trigger.setCronExpression("0 /15 * ? 4 *");
1009: ////
1010: //// start = new Date(1056319200000L);
1011: //// end = new Date(1087682399000L);
1012: ////
1013: //// trigger.setStartTime(start);
1014: //// trigger.setEndTime(end);
1015: ////
1016: //// next = new Date(1056232800000L);
1017: //// while (next != null) {
1018: //// next = trigger.getFireTimeAfter(next);
1019: //// if (next != null) {
1020: //// Calendar cal = Calendar.getInstance();
1021: //// cal.setTimeZone(timeZone);
1022: //// cal.setTime(next);
1023: //// System.err.println(cal.get(Calendar.MONTH) + "/"
1024: //// + cal.get(Calendar.DATE) + "/" + cal.get(Calendar.YEAR)
1025: //// + " - " + cal.get(Calendar.HOUR_OF_DAY) + ":"
1026: //// + cal.get(Calendar.MINUTE));
1027: //// }
1028: //// }
1029:
1030: }
1031: }
|